/** * @fileoverview CLI adapter for `roy-agent tasks tree [--json] [--root-id N]`. * * Used by the Web homepage to render a hierarchical task list instead of * the flat per-task tool-call view. Mirrors the design of * `cli-tasks-adapter.ts` (typed errors, timeouts, byte caps, injectable * runner) so the HTTP layer can map errors the same way. * * The CLI's `--json` output for `tasks tree` is: * { * "total": , // total tasks in the filtered set * "rootCount": , // number of root nodes in the returned tree * "tree": [ // array of root nodes (recursively nested) * { * "task": { id, title, status, priority, type, progress, ... }, * "children": [ /* same shape, recursively *\/ ] * }, * ... * ] * } * * If `--root-id N` is provided, the response is a 1-element array whose * only root is task #N, and the rest of the tree hangs below it. We * transparently pass `--root-id` through to the CLI — callers don't have * to filter the result themselves. * * Design goals (matching cli-tasks-adapter.ts): * - Spawn the host CLI as a discrete arg array — never via shell string. * - Enforce a wall-clock timeout and a maximum stdout size. * - Locate the leading `{` in stdout to skip INFO/log lines the real * CLI emits before its JSON envelope. * - Validate the parsed envelope against a stable schema. * - Surface typed errors so callers can map them to HTTP status codes. * * The adapter is intentionally narrow: it knows nothing about HTTP, the * server, or the frontend. The `runner` is injectable so tests never * need a real subprocess. */ import type { TaskShowConfig } from "./types.js"; import { type TaskContextValue } from "./task-metadata.js"; /** Subset of the CLI's `task` payload that we surface to the UI. */ export interface TaskTreeNodeTask { id: number; title: string; description?: string; status: string; priority: string; type: string; progress?: number; current_status?: string; createdAt: string; updatedAt: string; tags: string[]; project_path?: string; parent_task_id?: number; /** Arbitrary JSON context, or its original ordinary/malformed string form. */ context?: TaskContextValue; /** Full task goals text retained alongside the tree summary fields. */ goals_and_expected_deliverables?: string; } /** One node in the returned tree (recursive). */ export interface TaskTreeNode { task: TaskTreeNodeTask; children: TaskTreeNode[]; } /** Successful parse result. */ export interface TasksTreeEnvelope { total: number; rootCount: number; tree: TaskTreeNode[]; /** When the source data was last fetched (ISO 8601). */ fetchedAt: string; /** True if this entry is past TTL but still served. */ stale: boolean; } /** Filter parameters understood by the underlying CLI. */ export interface TasksTreeFilter { status?: "todo" | "active" | "completed" | "paused" | "cancelled"; priority?: "low" | "medium" | "high"; type?: "normal" | "cycle" | "longterm"; /** Restrict the tree to one root (and its descendants). */ rootId?: number; includeArchived?: boolean; } export { AdapterError, TimeoutError, ParseError, SchemaError, } from "./cli-tasks-adapter.js"; export interface AdapterRunnerResult { stdout: string; stderr: string; exitCode: number; } export type AdapterRunner = (args: string[]) => Promise; export interface TasksTreeAdapterOptions { /** Absolute path to the `roy-agent` executable. */ cliPath: string; /** Mockable subprocess runner (defaults to `defaultRunner`). */ runner?: AdapterRunner; /** Wall-clock timeout (default 30000 ms — the tree JSON is large and the CLI runs migrations). */ timeoutMs?: number; /** Maximum stdout bytes to keep (default 8 MiB). */ maxBytes?: number; /** TaskShowConfig for sharing limits / defaults. */ cfg: TaskShowConfig; } export interface TasksTreeSource { getTasksTree(filter: TasksTreeFilter): Promise; } /** * Default subprocess runner. We deliberately avoid `shell: true` to keep * argv as a literal array — no shell metacharacter interpretation. * * Two adaptations for Bun / large outputs: * 1. File-descriptor stdio: under Bun, piping ≥1 MB via * `stdio: ["ignore", "pipe", "pipe"]` drops data because the * `data` and `end` events fire before every chunk has been * delivered. Redirecting stdout/stderr to temp files captures the * full output reliably. * 2. Poll-until-stable read: on Bun, the child process's `close` event * can fire BEFORE all writes have been flushed to the file * descriptor. We poll the file size until it stabilizes, then * read with createReadStream (more robust than readFileSync for * large files). This guarantees we don't return truncated output. * * Both adaptations are no-ops under Node.js (no observable behavior * change), so the same code runs reliably on either runtime. * * Temp files are cleaned up in `finally` so a crash doesn't leak dirs. */ export declare const defaultRunner: AdapterRunner; /** * Build the argv array for `roy-agent tasks tree [--status] [--priority] * [--type] [--root-id] [--include-archived] --json`. Pure function — easy * to unit-test. */ export declare function buildTasksTreeArgs(cliPath: string, filter: TasksTreeFilter): string[]; /** * Run `roy-agent tasks tree [--json] [--filter...]` and return a parsed * envelope. Errors are typed so the HTTP layer can map them to status * codes. * * Pass `runner: defaultRunner` in production. Tests pass a `fixedRunner`. */ export declare function runTasksTree(filter: TasksTreeFilter, options: TasksTreeAdapterOptions): Promise; /** * Convenience wrapper used by TasksTreeCache: a `TasksTreeSource` whose * `getTasksTree(filter)` returns the parsed envelope or throws. */ export declare function makeTasksTreeSource(opts: TasksTreeAdapterOptions): TasksTreeSource; //# sourceMappingURL=cli-tasks-tree-adapter.d.ts.map