UNPKG

@phren/cli

Version:

Knowledge layer for AI agents - CLI, MCP server, and data layer

167 lines (166 loc) • 7.41 kB
import { z } from "zod"; import { type Json, type Provider } from "./protocol.js"; /** How many folders the archive keeps; the oldest beyond that are deleted. */ export declare const ARCHIVE_MAX_FOLDERS = 500; export declare const manifestSchema: z.ZodObject<{ schemaVersion: z.ZodLiteral<1>; id: z.ZodString; parent: z.ZodOptional<z.ZodObject<{ provider: z.ZodEnum<{ claude: "claude"; codex: "codex"; copilot: "copilot"; opencode: "opencode"; phren: "phren"; }>; session: z.ZodUnion<readonly [z.ZodString, z.ZodString]>; computer: z.ZodOptional<z.ZodString>; }, z.core.$strict>>; provider: z.ZodEnum<{ claude: "claude"; codex: "codex"; opencode: "opencode"; }>; session: z.ZodOptional<z.ZodUnion<readonly [z.ZodString, z.ZodString]>>; taskLabel: z.ZodString; cwd: z.ZodString; worktree: z.ZodString; model: z.ZodOptional<z.ZodString>; eventLog: z.ZodString; nativeTranscript: z.ZodOptional<z.ZodString>; unmanagedPid: z.ZodOptional<z.ZodNumber>; createdAt: z.ZodString; startedAt: z.ZodString; updatedAt: z.ZodString; finishedAt: z.ZodOptional<z.ZodString>; status: z.ZodEnum<{ cancelled: "cancelled"; completed: "completed"; failed: "failed"; queued: "queued"; running: "running"; }>; exitCode: z.ZodOptional<z.ZodNumber>; reason: z.ZodOptional<z.ZodString>; resumes: z.ZodOptional<z.ZodUnion<readonly [z.ZodString, z.ZodString]>>; schedule: z.ZodOptional<z.ZodObject<{ id: z.ZodString; project: z.ZodString; }, z.core.$strip>>; }, z.core.$strict>; export type FanoutManifest = z.infer<typeof manifestSchema>; export interface FanoutChild { /** Parent-scoped opaque ID. Filesystem paths never cross the bridge. */ id: string; provider: "opencode" | "codex" | "claude"; session?: string; /** The model that manifest named, so the phone can label the worker. */ model?: string; worktreeName?: string; branch?: string; /** The worker's own checkout, kept off the wire. */ cwd: string; path: string; callId: string; state: "running" | "completed" | "failed" | "gone"; /** `blocked: <type> <pattern>` when the plugin refused a permission. */ reason?: string; /** Actual launch time from a validated manifest; queued jobs omit it. */ startedAt?: string; finishedAt?: string; transcript: string; fanout: { resumable: boolean; }; children: FanoutChild[]; } export declare function storeRoot(env: NodeJS.ProcessEnv): string; export declare function fanoutRoot(env?: NodeJS.ProcessEnv): string; /** Bind public worker identities to the canonical store as well as their parent. */ export declare function fanoutChildID(root: string, manifest: FanoutManifest): string; export declare function containedFanoutRoot(env: NodeJS.ProcessEnv): Promise<string | undefined>; export declare function nativeClaudeTranscript(candidate: string, session: string): Promise<string | undefined>; /** Read only manifests explicitly bound to the already validated parent. */ export declare function fanoutChildren(parentProvider: Provider, parentSession: string, env?: NodeJS.ProcessEnv, parentComputer?: string): Promise<FanoutChild[]>; /** Every fan-out job's worktree with its task label and harness, whatever * conversation launched it, so the Changes screen can name the worker editing * a checkout. Newest first, bounded like the child listing. */ export declare function fanoutWorktrees(env?: NodeJS.ProcessEnv): Promise<Array<{ worktree: string; label: string; provider: FanoutManifest["provider"]; state: string; }>>; /** A blocked fan-out job, with the parent it belongs to, for a push. */ export interface BlockedFanout { id: string; provider: FanoutChild["provider"]; label: string; parent?: { provider: Provider; session: string; computer?: string; }; reason: string; at?: string; } /** Every fan-out job that left a blocked.json, for the Hook's push watcher. */ export declare function blockedFanouts(env?: NodeJS.ProcessEnv): Promise<BlockedFanout[]>; /** The running OpenCode fan-out job a permission request names, when its own * manifest confirms the worker session and a parent conversation: the parent * is where the phone answers the ask. */ export declare function fanoutAsking(job: unknown, session: string, env?: NodeJS.ProcessEnv): Promise<{ id: string; label: string; worktree: string; parent: NonNullable<FanoutManifest["parent"]>; } | undefined>; export interface FanoutArchiveResult { /** Job folders moved (or, on a dry run, that would be moved), by id. */ moved: string[]; /** Archive folders deleted past the cap (or that would be deleted). */ deleted: number; } /** Move finished fan-out job folders into the archive. * * A folder moves when it has exit.txt (without it the job is still running and * is never touched), its manifest status is completed, failed or cancelled, * and its finishedAt (or exit.txt's mtime when there is none) is over * ARCHIVE_AGE_MS old. A folder with no manifest goes too once its exit.txt is * that old, gaining a synthesized {status: failed, reason: "no manifest"} * manifest in the archive. The archive keeps ARCHIVE_MAX_FOLDERS folders, * deleting the oldest beyond that. `dryRun` reports the same work without * touching anything. `olderThanMs` replaces the 24 hour age (0 archives every * finished job), and `parent` limits the sweep to one parent session's jobs; * a folder without a manifest has no parent and is left alone then. */ export declare function archiveFinishedFanouts(env?: NodeJS.ProcessEnv, options?: { dryRun?: boolean; now?: number; olderThanMs?: number; parent?: { session: string; provider?: Provider; }; }): Promise<FanoutArchiveResult>; export declare const FANOUTS_ARCHIVE_USAGE = "Usage: phren bridge fanouts archive [--dry-run] [--parent <session-id>] [--older-than <minutes>]"; /** `--parent` limits the sweep to one parent chat's workers; `--older-than` * replaces the 24 hour age, and 0 archives every finished one. */ export declare function parseFanoutArchiveFlags(flags: string[]): { dryRun?: boolean; parent?: { session: string; }; olderThanMs?: number; }; /** Project raw `opencode run --format json` rows into the small public chat * contract. Reasoning, snapshots, costs, and metadata are omitted. Like the * Codex mapping, the command, URL or path a tool was given and a bounded * output tail cross the wire so the owner can see what a worker is doing. * An MCP tool keeps its own arguments and an edit, write or patch carries the * changed-file diff the phone draws under the card; both are bounded. */ export declare function visibleOpenCodeRunEvent(raw: Json, cwd?: string): Json | undefined; /** Project raw `codex exec --json` rows into the shape Codex's own rollout * files use, so the existing Codex transcript reader renders them unchanged. * Command text, a bounded output tail, and changed paths cross the wire so the * owner can see what a worker is doing; diffs, usage, and costs do not. */ export declare function visibleCodexExecEvent(raw: Json): Json | undefined;