@kontextmind/kxm
Version:
KXM local-first multi-agent orchestration and operator dashboard
646 lines (604 loc) • 27.4 kB
text/typescript
import { spawnSync } from "node:child_process";
import { chmodSync, existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
import { basename, dirname, join, resolve } from "node:path";
import { discoverKxmProjectRoot, kxmResourceIdentifier, loadKxmProject } from "../project-config.ts";
import { KxmRunEventStore, KxmRuntimeRegistry, kxmProjectRunEventsPath, kxmRuntimePaths } from "../runtime-store.ts";
import { attachKxmSupervisor, ensureKxmSupervisor, kxmRuntimeRequest, kxmSupervisorRecordIsLive } from "../runtime-supervisor.ts";
import { print, printPlan, workspaceDirs, type Runtime } from "./types.ts";
/** One worktree lane recorded in the control checkout. The sha is the base,
* not the ref: worktrees share refs, and a later fetch moves origin/main. */
export interface LaneRecord {
path: string;
branch: string;
baseRef: string;
baseSha: string;
createdAt: string;
lastRunId?: string;
}
interface LaneFile {
schema: "kxm.lanes.v1";
lanes: Record<string, LaneRecord>;
}
export interface LaneView extends LaneRecord {
unit: string;
dirty: number | null;
ahead: number | null;
behind: number | null;
exists: boolean;
}
const LANE_SCHEMA = "kxm.lanes.v1";
const SHA_RE = /^[a-f0-9]{40,64}$/;
const TIMESTAMP_RE = /^[0-9]{4}-[0-9]{2}-[0-9]{2}T[0-9]{2}:[0-9]{2}:[0-9]{2}(?:\.[0-9]{1,9})?Z$/;
const SETTLED_RUN = new Set(["completed", "failed", "cancelled"]);
const LANE_RUN_LIST_LIMIT = 10_000;
function lanesFile(runtime: Runtime): string {
return join(runtime.dirs.state, "lanes.json");
}
function gitEnv(): NodeJS.ProcessEnv {
return Object.fromEntries(Object.entries(process.env).filter(([name]) => !name.toUpperCase().startsWith("GIT_")));
}
function git(cwd: string, args: readonly string[]): { status: number; stdout: string; stderr: string } {
const result = spawnSync("git", ["-C", cwd, ...args], {
encoding: "utf8",
env: gitEnv(),
timeout: 30_000,
windowsHide: true,
});
return {
status: result.status ?? 1,
stdout: result.stdout ?? "",
stderr: `${result.stderr ?? ""}${result.error ? result.error.message : ""}`,
};
}
function refuse(runtime: Runtime, command: string, error: string, text: string, extra: Record<string, unknown> = {}): number {
print(runtime.io, runtime.json, { ok: false, command, error, ...extra }, text);
return 1;
}
function isLaneRecord(value: unknown): value is LaneRecord {
if (!value || typeof value !== "object" || Array.isArray(value)) return false;
const record = value as Record<string, unknown>;
const keys = Object.keys(record);
const allowed = new Set(["path", "branch", "baseRef", "baseSha", "createdAt", "lastRunId"]);
if (keys.some((key) => !allowed.has(key))) return false;
if (typeof record.path !== "string" || record.path.length === 0) return false;
if (typeof record.branch !== "string" || record.branch.length === 0) return false;
if (typeof record.baseRef !== "string" || record.baseRef.length === 0) return false;
if (typeof record.baseSha !== "string" || !SHA_RE.test(record.baseSha)) return false;
if (typeof record.createdAt !== "string" || !TIMESTAMP_RE.test(record.createdAt)) return false;
if (record.lastRunId !== undefined && (typeof record.lastRunId !== "string" || record.lastRunId.length === 0)) return false;
return true;
}
function isLaneFile(value: unknown): value is LaneFile {
if (!value || typeof value !== "object" || Array.isArray(value)) return false;
const file = value as Record<string, unknown>;
if (Object.keys(file).some((key) => key !== "schema" && key !== "lanes")) return false;
if (file.schema !== LANE_SCHEMA || !file.lanes || typeof file.lanes !== "object" || Array.isArray(file.lanes)) return false;
for (const [unit, record] of Object.entries(file.lanes as Record<string, unknown>)) {
if (!kxmResourceIdentifier(unit) || !isLaneRecord(record)) return false;
}
return true;
}
/** Read `<state>/lanes.json`. A missing file is an empty record. A malformed
* file is refused; it is never replaced with an empty record. */
function loadLanes(runtime: Runtime, command: string): LaneFile | number {
const file = lanesFile(runtime);
if (!existsSync(file)) return { schema: LANE_SCHEMA, lanes: {} };
try {
const parsed: unknown = JSON.parse(readFileSync(file, "utf8"));
if (!isLaneFile(parsed)) return refuse(runtime, command, "lanes_unreadable", `lane record ${file} is malformed`);
return parsed;
} catch {
return refuse(runtime, command, "lanes_unreadable", `lane record ${file} is unreadable`);
}
}
function writeLanes(runtime: Runtime, file: LaneFile): void {
const target = lanesFile(runtime);
mkdirSync(runtime.dirs.state, { recursive: true });
const lanes: Record<string, LaneRecord> = {};
for (const unit of Object.keys(file.lanes).sort()) {
const record = file.lanes[unit];
if (!record) continue;
lanes[unit] = {
path: record.path,
branch: record.branch,
baseRef: record.baseRef,
baseSha: record.baseSha,
createdAt: record.createdAt,
...(record.lastRunId ? { lastRunId: record.lastRunId } : {}),
};
}
writeFileSync(target, `${JSON.stringify({ schema: LANE_SCHEMA, lanes }, null, 2)}\n`, { encoding: "utf8", mode: 0o600 });
try { chmodSync(target, 0o600); } catch { /* Windows may not expose POSIX modes. */ }
}
function controlRoot(runtime: Runtime, command: string): string | number {
const root = discoverKxmProjectRoot(runtime.cwd);
if (!root) return refuse(runtime, command, "project_required", `${command} requires a KXM project (run kxm init first)`);
return root;
}
function refuseUnit(runtime: Runtime, command: string, unit: string): number | undefined {
if (kxmResourceIdentifier(unit)) return undefined;
return refuse(runtime, command, "lane_unit_invalid", `lane unit ${unit} must match the KXM identifier pattern`);
}
function laneWorktreePath(projectRoot: string, unit: string): string {
return resolve(dirname(projectRoot), `${basename(projectRoot)}-${unit}`);
}
function branchExists(projectRoot: string, unit: string): boolean {
return git(projectRoot, ["rev-parse", "--verify", "--quiet", `refs/heads/${unit}`]).status === 0;
}
function resolveBase(projectRoot: string, base: string): string | undefined {
const result = git(projectRoot, ["rev-parse", "--verify", "--end-of-options", `${base}^{commit}`]);
if (result.status !== 0) return undefined;
const sha = result.stdout.trim();
return SHA_RE.test(sha) ? sha : undefined;
}
/** The only `--lane` mechanism: discovery cwd and workspace dirs come from the lane path. */
export function withLaneCwd(runtime: Runtime, unit: string, command = "lane"): Runtime | number {
const invalid = refuseUnit(runtime, command, unit);
if (invalid !== undefined) return invalid;
const loaded = loadLanes(runtime, command);
if (typeof loaded === "number") return loaded;
const lane = loaded.lanes[unit];
if (!lane || !existsSync(lane.path)) {
return refuse(runtime, command, "lane_missing", `lane ${unit} is not recorded or its worktree is absent`, { unit });
}
return {
...runtime,
cwd: lane.path,
dirs: workspaceDirs(lane.path, undefined, runtime.env),
};
}
export function applyLane(runtime: Runtime, unit: string | undefined, command: string): Runtime | number {
if (unit === undefined) return runtime;
return withLaneCwd(runtime, unit, command);
}
/** `--lane` for `kxm runs`: same cwd swap as `withLaneCwd`, or the original runtime when unset. */
export function runtimeForLane(runtime: Runtime, unit: string | undefined): Runtime | number {
return applyLane(runtime, unit, "lane");
}
interface StartedRun {
code: number;
runId?: string;
}
/** Shared path for `kxm run --brief --lane` and `kxm lane run`. */
async function startRecordedRun(
runtime: Runtime,
workflow: string | undefined,
promptParts: string[],
options: { brief?: string; lane?: string; command: string; allowDefaultWorkflow?: boolean; skipOpenCheck?: boolean },
): Promise<StartedRun> {
let prompt: string | undefined;
if (options.brief !== undefined) {
if (promptParts.length > 0) {
return { code: refuse(runtime, options.command, "brief_and_prompt", "pass either --brief or a prompt, not both", { brief: options.brief }) };
}
const brief = readRunBrief(runtime, options.brief, options.command);
if (typeof brief === "number") return { code: brief };
prompt = brief.text;
}
let active = runtime;
if (options.lane !== undefined) {
const scoped = withLaneCwd(runtime, options.lane, options.command);
if (typeof scoped === "number") return { code: scoped };
active = scoped;
if (options.skipOpenCheck !== true) {
const open = await refuseIfLaneRunOpen(runtime, options.lane, options.command);
if (open !== undefined) return { code: open };
}
}
const project = await import("./project.ts");
const runIdOut: { runId?: string } = {};
const code = await project.cmdKxmRun(active, workflow, prompt === undefined ? promptParts : [], false, {
...(options.brief !== undefined ? { brief: options.brief } : {}),
...(prompt !== undefined ? { prompt } : {}),
...(options.allowDefaultWorkflow ? { allowDefaultWorkflow: true } : {}),
runIdOut,
});
if (code !== 0 || !runIdOut.runId) return { code, ...(runIdOut.runId ? { runId: runIdOut.runId } : {}) };
if (options.lane !== undefined) {
const recorded = recordLaneRun(runtime, options.lane, runIdOut.runId, options.command);
if (recorded !== undefined) return { code: recorded };
}
return { code: 0, runId: runIdOut.runId };
}
export async function cmdKxmRunCli(
runtime: Runtime,
workflow: string | undefined,
promptParts: string[],
options: { brief?: string; lane?: string } = {},
): Promise<number> {
const started = await startRecordedRun(runtime, workflow, promptParts, { ...options, command: "run" });
return started.code;
}
export function readRunBrief(runtime: Runtime, file: string, command: string): { text: string } | number {
try {
return { text: readFileSync(resolve(runtime.cwd, file), "utf8").trim() };
} catch {
return refuse(runtime, command, "brief_unreadable", `cannot read brief ${file}`, { brief: file });
}
}
function porcelainCount(path: string): number | null {
const result = git(path, ["status", "--porcelain"]);
if (result.status !== 0) return null;
return result.stdout.split("\n").filter((line) => line.length > 0).length;
}
/** Left count is commits on the base that HEAD lacks (behind). Right is the reverse (ahead). */
function aheadBehind(path: string, baseSha: string): { ahead: number | null; behind: number | null } {
const result = git(path, ["rev-list", "--left-right", "--count", `${baseSha}...HEAD`]);
const match = result.status === 0 ? result.stdout.trim().match(/^(\d+)\s+(\d+)$/) : null;
if (!match) return { ahead: null, behind: null };
return { behind: Number(match[1]), ahead: Number(match[2]) };
}
function laneView(unit: string, record: LaneRecord): LaneView {
const exists = existsSync(record.path);
const counts = exists ? aheadBehind(record.path, record.baseSha) : { ahead: null, behind: null };
return {
unit,
path: record.path,
branch: record.branch,
baseRef: record.baseRef,
baseSha: record.baseSha,
createdAt: record.createdAt,
...(record.lastRunId ? { lastRunId: record.lastRunId } : {}),
dirty: exists ? porcelainCount(record.path) : null,
ahead: counts.ahead,
behind: counts.behind,
exists,
};
}
function formatCount(value: number | null): string {
return value === null ? "?" : String(value);
}
function formatLaneLine(view: LaneView): string {
return [
view.unit,
view.path,
`branch=${view.branch}`,
`base=${view.baseSha}`,
`dirty=${formatCount(view.dirty)}`,
`ahead=${formatCount(view.ahead)}`,
`behind=${formatCount(view.behind)}`,
`exists=${view.exists}`,
].join(" ");
}
/** A running supervisor can answer. This never starts one. A test seam stands in for that process. */
async function projectedRunStatus(runtime: Runtime, projectRoot: string, runId: string, allowStart: boolean): Promise<string> {
try {
const project = await import("./project.ts");
const request = project.kxmDriveCliSeams.runtimeRequest ?? kxmRuntimeRequest;
// `lane status` never starts the supervisor. Run and drop may, except under --dry-run.
const handle = !allowStart || runtime.dryRun
? await attachKxmSupervisor({ env: runtime.env })
: await (project.kxmDriveCliSeams.ensureSupervisor ?? ensureKxmSupervisor)({ env: runtime.env });
if (!handle) return "unknown";
const result = await request(
handle,
"GET",
`/v1/runs/${encodeURIComponent(runId)}?projectRoot=${encodeURIComponent(projectRoot)}`,
);
const run = result.run;
if (!run || typeof run !== "object" || Array.isArray(run)) return "unknown";
const status = (run as { status?: unknown }).status;
return typeof status === "string" && status.length > 0 ? status : "unknown";
} catch {
return "unknown";
}
}
function isSettled(status: string): boolean {
return SETTLED_RUN.has(status);
}
/** Runs in the lane event store. Undefined when the lane has no store and no supervisor list. */
async function laneStoreRuns(runtime: Runtime, lanePath: string): Promise<Array<{ runId: string; status: string }> | undefined> {
const project = await import("./project.ts");
const request = project.kxmDriveCliSeams.runtimeRequest ?? kxmRuntimeRequest;
const handle = await attachKxmSupervisor({ env: runtime.env });
const projectId = (): string => String(loadKxmProject(lanePath, {}).project.value.id);
if (handle) {
try {
const result = await request(
handle,
"GET",
`/v1/projects/${encodeURIComponent(projectId())}/runs?projectRoot=${encodeURIComponent(lanePath)}&limit=${LANE_RUN_LIST_LIMIT}`,
);
const runs = Array.isArray(result.runs) ? result.runs : [];
return runs.flatMap((run) => {
if (!run || typeof run !== "object" || Array.isArray(run)) return [];
const record = run as { runId?: unknown; status?: unknown };
if (typeof record.runId !== "string" || record.runId.length === 0) return [];
const status = typeof record.status === "string" && record.status.length > 0 ? record.status : "unknown";
return [{ runId: record.runId, status }];
});
} catch {
// The supervisor could not list this root. The file read below still can.
}
}
const eventsPath = kxmProjectRunEventsPath(lanePath, runtime.env);
if (!existsSync(eventsPath)) return undefined;
const store = new KxmRunEventStore(eventsPath);
try {
return store.runsForProject(projectId(), LANE_RUN_LIST_LIMIT).map((run) => ({ runId: run.runId, status: run.status }));
} finally {
store.close();
}
}
async function refuseIfLaneStoreRunsOpen(runtime: Runtime, unit: string, command: string): Promise<number | undefined> {
const loaded = loadLanes(runtime, command);
if (typeof loaded === "number") return loaded;
const lane = loaded.lanes[unit];
if (!lane) return undefined;
let runs: Array<{ runId: string; status: string }> | undefined;
try {
runs = await laneStoreRuns(runtime, lane.path);
} catch (error) {
const detail = error instanceof Error ? error.message : "run list failed";
return refuse(runtime, command, "lane_run_open", `lane ${unit} runs could not be read: ${detail.slice(0, 200)}`, { unit });
}
if (!runs) return undefined;
const unsettled = runs.filter((run) => !isSettled(run.status)).map((run) => run.runId);
if (unsettled.length === 0) return undefined;
return refuse(
runtime,
command,
"lane_run_open",
`lane ${unit} run ${unsettled.join(", ")} is unsettled`,
{ unit, runIds: unsettled },
);
}
export async function refuseIfLaneRunOpen(runtime: Runtime, unit: string, command: string): Promise<number | undefined> {
const loaded = loadLanes(runtime, command);
if (typeof loaded === "number") return loaded;
const lane = loaded.lanes[unit];
if (!lane?.lastRunId) return undefined;
const runStatus = await projectedRunStatus(runtime, lane.path, lane.lastRunId, true);
if (isSettled(runStatus)) return undefined;
return refuse(
runtime,
command,
"lane_run_open",
`lane ${unit} run ${lane.lastRunId} is ${runStatus}`,
{ unit, runId: lane.lastRunId, runStatus },
);
}
export function recordLaneRun(runtime: Runtime, unit: string, runId: string, command: string): number | undefined {
const loaded = loadLanes(runtime, command);
if (typeof loaded === "number") return loaded;
const lane = loaded.lanes[unit];
if (!lane) return refuse(runtime, command, "lane_missing", `lane ${unit} is not recorded or its worktree is absent`, { unit });
lane.lastRunId = runId;
try {
writeLanes(runtime, loaded);
} catch {
return refuse(runtime, command, "lanes_unreadable", `lane record ${lanesFile(runtime)} could not be written`);
}
return undefined;
}
export async function cmdLaneCreate(runtime: Runtime, unit: string, options: { base?: string } = {}): Promise<number> {
const command = "lane create";
const invalid = refuseUnit(runtime, command, unit);
if (invalid !== undefined) return invalid;
const root = controlRoot(runtime, command);
if (typeof root === "number") return root;
const base = options.base?.trim() || "origin/main";
const loaded = loadLanes(runtime, command);
if (typeof loaded === "number") return loaded;
const path = laneWorktreePath(root, unit);
if (loaded.lanes[unit] || existsSync(path) || branchExists(root, unit)) {
return refuse(runtime, command, "lane_exists", `lane ${unit} already exists`, { unit });
}
const baseSha = resolveBase(root, base);
if (!baseSha) {
return refuse(
runtime,
command,
"lane_base_unresolved",
`base ${base} does not resolve; git fetch origin`,
{ unit, base, hint: "git fetch origin" },
);
}
if (runtime.dryRun) {
printPlan(runtime, { command, unit, base, baseSha, path }, [
{ action: "write", target: path },
{ action: "write", target: lanesFile(runtime) },
], `create lane ${unit} at ${path} from ${baseSha}`);
return 0;
}
const added = git(root, ["worktree", "add", "-b", unit, path, baseSha]);
if (added.status !== 0) {
return refuse(runtime, command, "lane_git_failed", `git worktree add failed: ${added.stderr.trim().slice(0, 300)}`, { unit });
}
// Same string `discoverKxmProjectRoot` will return when `--lane` swaps cwd.
const recordedPath = discoverKxmProjectRoot(path) ?? path;
if (!existsSync(join(recordedPath, ".kxm", "project.yaml"))) {
git(root, ["worktree", "remove", "--force", path]);
return refuse(runtime, command, "lane_not_project", `lane ${recordedPath} has no .kxm/project.yaml; the worktree was removed`, { unit, path: recordedPath });
}
const lane: LaneRecord = {
path: recordedPath,
branch: unit,
baseRef: base,
baseSha,
createdAt: new Date().toISOString(),
};
loaded.lanes[unit] = lane;
try {
writeLanes(runtime, loaded);
} catch {
git(root, ["worktree", "remove", "--force", path]);
return refuse(runtime, command, "lanes_unreadable", `lane record ${lanesFile(runtime)} could not be written`, { unit });
}
print(runtime.io, runtime.json, { ok: true, command, unit, lane }, formatLaneLine(laneView(unit, lane)));
return 0;
}
export async function cmdLaneList(runtime: Runtime): Promise<number> {
const command = "lane list";
const loaded = loadLanes(runtime, command);
if (typeof loaded === "number") return loaded;
const lanes = Object.keys(loaded.lanes).sort().map((unit) => laneView(unit, loaded.lanes[unit]!));
print(
runtime.io,
runtime.json,
{ ok: true, command, lanes },
lanes.length === 0 ? "no lanes" : lanes.map((lane) => formatLaneLine(lane)).join("\n"),
);
return 0;
}
export async function cmdLaneStatus(runtime: Runtime, unit: string): Promise<number> {
const command = "lane status";
const invalid = refuseUnit(runtime, command, unit);
if (invalid !== undefined) return invalid;
const loaded = loadLanes(runtime, command);
if (typeof loaded === "number") return loaded;
const record = loaded.lanes[unit];
if (!record) return refuse(runtime, command, "lane_missing", `lane ${unit} is not recorded`, { unit });
const view = laneView(unit, record);
const runStatus = record.lastRunId ? await projectedRunStatus(runtime, record.path, record.lastRunId, false) : "unknown";
print(
runtime.io,
runtime.json,
{ ok: true, command, root: record.path, lane: { ...view, status: runStatus } },
`${formatLaneLine(view)} status=${runStatus} root=${record.path}`,
);
return 0;
}
/** Remove the lane root from the Runtime registry. A stopped supervisor is not
* an error: the row is deleted from the registry file when that file exists.
* The delete holds the write lock and rechecks the supervisor row; a live
* supervisor is left for the unregister route. */
async function unregisterLaneRoot(runtime: Runtime, projectRoot: string, force: boolean): Promise<string | undefined> {
const body = force ? { projectRoot, force: true as const } : { projectRoot };
try {
const project = await import("./project.ts");
const request = project.kxmDriveCliSeams.runtimeRequest ?? kxmRuntimeRequest;
const post = async (handle: { runtimeId: string; port: number; token: string; started: boolean }): Promise<void> => {
await request(handle, "POST", "/v1/projects/unregister", body);
};
const handle = await attachKxmSupervisor({ env: runtime.env });
if (handle) {
await post(handle);
return undefined;
}
const paths = kxmRuntimePaths({ env: runtime.env });
if (!existsSync(paths.registryDb)) return undefined;
const registry = new KxmRuntimeRegistry(paths.registryDb);
let outcome: "removed" | "absent" | "supervisor_live";
try {
outcome = registry.unregisterProjectIfIdle(projectRoot, kxmSupervisorRecordIsLive);
} finally {
registry.close();
}
if (outcome === "supervisor_live") {
const live = await attachKxmSupervisor({ env: runtime.env });
if (!live) return "runtime supervisor is live but could not be reached";
await post(live);
}
return undefined;
} catch (error) {
return error instanceof Error ? error.message : "lane root could not be unregistered";
}
}
export async function cmdLaneDrop(runtime: Runtime, unit: string, options: { force?: boolean } = {}): Promise<number> {
const command = "lane drop";
const invalid = refuseUnit(runtime, command, unit);
if (invalid !== undefined) return invalid;
const root = controlRoot(runtime, command);
if (typeof root === "number") return root;
const loaded = loadLanes(runtime, command);
if (typeof loaded === "number") return loaded;
const record = loaded.lanes[unit];
if (!record) return refuse(runtime, command, "lane_missing", `lane ${unit} is not recorded`, { unit });
const force = options.force === true;
if (!force) {
if (existsSync(record.path)) {
const dirty = porcelainCount(record.path);
if (dirty === null) {
return refuse(runtime, command, "lane_git_failed", `lane ${unit} worktree status could not be read`, { unit });
}
if (dirty !== 0) {
return refuse(runtime, command, "lane_dirty", `lane ${unit} has ${dirty} uncommitted change(s)`, { unit, dirty });
}
}
const listed = await refuseIfLaneStoreRunsOpen(runtime, unit, command);
if (listed !== undefined) return listed;
const open = await refuseIfLaneRunOpen(runtime, unit, command);
if (open !== undefined) return open;
}
if (runtime.dryRun) {
printPlan(runtime, { command, unit, branchKept: record.branch }, [
{ action: "delete", target: `runtime registry ${record.path}` },
{ action: "delete", target: record.path },
{ action: "write", target: lanesFile(runtime) },
], `drop lane ${unit}; branch ${record.branch} is not deleted`);
return 0;
}
const unregistered = await unregisterLaneRoot(runtime, record.path, force);
if (unregistered !== undefined) {
return refuse(runtime, command, "lane_unregister_failed", `lane ${unit} could not be unregistered: ${unregistered.slice(0, 300)}`, { unit });
}
if (existsSync(record.path)) {
const removed = git(root, ["worktree", "remove", ...(force ? ["--force"] : []), record.path]);
if (removed.status !== 0) {
return refuse(runtime, command, "lane_git_failed", `git worktree remove failed: ${removed.stderr.trim().slice(0, 300)}`, { unit });
}
}
delete loaded.lanes[unit];
try {
writeLanes(runtime, loaded);
} catch {
return refuse(runtime, command, "lanes_unreadable", `lane record ${lanesFile(runtime)} could not be written`, { unit });
}
print(
runtime.io,
runtime.json,
{ ok: true, command, unit, branch: record.branch, branchDeleted: false },
`dropped lane ${unit}; branch ${record.branch} was not deleted`,
);
return 0;
}
export async function cmdLaneRun(
runtime: Runtime,
unit: string,
options: { brief?: string; workflow?: string; base?: string; wait?: boolean; timeoutMs?: string },
): Promise<number> {
const command = "lane run";
const invalid = refuseUnit(runtime, command, unit);
if (invalid !== undefined) return invalid;
if (!options.brief) return refuse(runtime, command, "brief_unreadable", "lane run requires --brief <file>");
const brief = readRunBrief(runtime, options.brief, command);
if (typeof brief === "number") return brief;
const loaded = loadLanes(runtime, command);
if (typeof loaded === "number") return loaded;
const existed = Boolean(loaded.lanes[unit]);
if (!existed) {
const created = await cmdLaneCreate(runtime, unit, options.base !== undefined ? { base: options.base } : {});
if (created !== 0) return created;
}
if (runtime.dryRun) {
if (existed) {
const open = await refuseIfLaneRunOpen(runtime, unit, command);
if (open !== undefined) return open;
}
const root = controlRoot(runtime, command);
const path = existed ? loaded.lanes[unit]!.path : (typeof root === "number" ? unit : laneWorktreePath(root, unit));
printPlan(runtime, { command, unit, brief: options.brief }, [
{ action: "request", target: `POST /v1/runs projectRoot=${path}` },
{ action: "request", target: `POST /v1/runs/<new>/drive projectRoot=${path}` },
], `run lane ${unit} from ${options.brief} and drive it`);
return 0;
}
const open = await refuseIfLaneRunOpen(runtime, unit, command);
if (open !== undefined) return open;
const outcome = await startRecordedRun(runtime, options.workflow, [], {
brief: options.brief,
lane: unit,
command,
allowDefaultWorkflow: true,
skipOpenCheck: true,
});
if (outcome.code !== 0 || !outcome.runId) return outcome.code === 0 ? 1 : outcome.code;
const scoped = withLaneCwd(runtime, unit, command);
if (typeof scoped === "number") return scoped;
const project = await import("./project.ts");
return project.cmdKxmRunDrive(scoped, outcome.runId, false, {
wait: options.wait === true,
...(options.timeoutMs !== undefined ? { timeoutMs: options.timeoutMs } : {}),
});
}