/** * Sessions dashboard card. * * The list view is compact, paginated, refreshable, and read-only except for * per-row detail entry. The detail view exposes locate, terminal, close/resume, * and back actions. * * Close/resume callbacks block on Route B and return a rebuilt card on success. * Failures return toast-only and leave the visible card unchanged for retry. * Chat-scope locate uses a direct `multi_url`; thread-scope locate posts a * Route B notification and returns toast-only. * * Security: * - `invokerOpenId` pins callbacks to the admin who opened the card. * - sender union_id never lands on `action.value`. * - command entry and callbacks both enforce the dashboard admin gate. * - row ids are routing keys only; Route B still enforces row ownership. * - write actions re-run the model availability matrix before POSTing. */ import type { SessionDetailDto } from '../../dashboard/session-card-model.js'; import type { DaemonClient } from '../../dashboard/daemon-internal-client.js'; import type { SessionRow } from '../../core/dashboard-rows.js'; import { type Locale } from '../../i18n/index.js'; import type { CardActionData } from './card-handler.js'; export declare const SESSIONS_ACTION_REFRESH: "dash_sessions_refresh"; export declare const SESSIONS_ACTION_PAGE: "dash_sessions_page"; export declare const SESSIONS_ACTION_DETAIL: "dash_sessions_detail"; export declare const SESSIONS_ACTION_CLOSE: "dash_sessions_close"; export declare const SESSIONS_ACTION_BACK_TO_LIST: "dash_sessions_back_to_list"; /** Thread-scope locate sends a mention into the original topic. */ export declare const SESSIONS_ACTION_LOCATE: "dash_sessions_locate"; /** Replaces close when status === 'closed'; refetches after resume. */ export declare const SESSIONS_ACTION_RESUME: "dash_sessions_resume"; export interface BuildSessionsCardOpts { invokerOpenId: string; locale: Locale; /** 1-based page index. Caller clamps; this just renders what's given. */ page: number; /** Page size override, threaded through every button value. */ pageSize?: number; /** Navigation origin. `'overview'` means this card was opened via * `/dashboard overview` β†’ goto sessions; the footer renders an extra * "πŸ”™ θΏ”ε›žζ€»θ§ˆ" button, and every button.value carries `origin=overview` * to keep that affordance across rebuilds. Undefined β†’ standalone card, * no overview link. */ origin?: 'overview'; /** Dashboard scope. `'global'` means `/dashboard` shows sessions from * every bot, and write callbacks route by the row's true owner. */ scope?: 'global'; } /** Build the sessions list card JSON from raw rows. Pure (composes + paginates). */ export declare function buildSessionsCard(rows: ReadonlyArray, opts: BuildSessionsCardOpts, nowMs: number): string; /** Options for the detail card. `invokerOpenId` plumbs the lock onto every callback button. */ export interface BuildSessionsDetailCardOpts { invokerOpenId: string; locale: Locale; /** Override `Date.now()` for the relative-time label. Tests pass a fixed value. */ nowMs?: number; /** Overview drilldown nav state β€” threaded into the "πŸ”™ θΏ”ε›ž" button so the * list rebuilt by `BACK_TO_LIST` is still drilldown-shaped (5/page + * return-to-overview). Detail itself does NOT render a return-to-overview * button (single back affordance). */ origin?: 'overview'; pageSize?: number; /** Source list page. Detail buttons round-trip this so BACK_TO_LIST restores * the page that opened the detail card (instead of always resetting to 1). */ sourcePage?: number; /** Dashboard scope. Threaded into locate/close/resume/back buttons. */ scope?: 'global'; /** Web terminal URL for the openTerminal button; null renders it disabled. */ terminalUrl?: string | null; /** Direct chat link for chat-scope locate; absent for thread-scope rows. */ feishuChatLink?: string | null; } /** * Build the session detail card: metadata, locate/terminal controls, close or * resume depending on status, and the back button. */ export declare function buildSessionsDetailCard(detail: SessionDetailDto, opts: BuildSessionsDetailCardOpts): string; /** Compute the Web Terminal URL for a SessionRow. Mirrors * `src/dashboard/web/sessions.ts:terminalHref`: proxy port wins (with the * `/s/{sessionId}` suffix); otherwise direct worker port. Returns null when * the session has no port at all (e.g. closed / starting). * * Closed sessions can carry a stale webPort, so closed rows are always * rejected here even if the raw row still has a port value. */ export declare function buildSessionTerminalUrl(row: SessionRow): string | null; /** ─── Handler ─────────────────────────────────────────────────────────── */ export interface SessionsCardHandlerDeps { /** Legacy owner test seam; prefer `getDashboardAdminOpenIds` for new tests. */ getOwnerOpenId?: (larkAppId: string) => string | undefined; getDashboardAdminOpenIds?: (larkAppId: string) => ReadonlyArray | undefined; /** Factory returning a Route B client for the given larkAppId. */ createClient: (larkAppId: string) => DaemonClient; /** Override locale resolution; production uses the caller-supplied locale. */ locale?: Locale; /** Override `Date.now()` so tests are deterministic. */ nowMs?: () => number; } export interface SessionsCardHandlerResult { /** Optional β€” success returns ONLY a `card` (single-pass render). Errors, * permission denials still return a toast (no card to render). */ toast?: { type: 'info' | 'success' | 'error'; content: string; }; card?: { type: 'raw'; data: Record; }; } /** * Dispatch a `dash_sessions_*` action callback. Awaits the Route B GET * inline and returns the rebuilt card body in the SAME response. */ export declare function handleSessionsCardAction(data: CardActionData, larkAppId: string, deps: SessionsCardHandlerDeps): Promise; //# sourceMappingURL=sessions-card.d.ts.map