{"version":3,"file":"cli.d.ts","sourceRoot":"","sources":["../../../src/core/worker-daemon/cli.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AA+KH,wBAAgB,gBAAgB,IAAI,MAAM,CAOzC;AAED,wBAAsB,mBAAmB,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,OAAO,CAAC,CAkG1E","sourcesContent":["/**\n * Worker Daemon — CLI (2.13.0).\n *\n * `jensen worker run|status|list|show`\n *\n * `run` starts a long-lived daemon (or a single `--once` cycle) bound to one\n * logical executor. `status`/`list`/`show` expose the durable worker read model.\n * Machine-readable `--json` exposes stable DTOs; human output is a compact\n * key: value block.\n */\n\nimport chalk from \"chalk\";\nimport { getAgentDir } from \"../../config.js\";\nimport { AssignmentControlService, type BuildAssignedResumeLaunch } from \"../assignment/assignment-control-service.js\";\nimport { createFileAssignmentStore } from \"../assignment/file-assignment-store.js\";\nimport { defaultChildSessionDir } from \"../durable-child-session/index.js\";\nimport { createFileExecutorRegistry, ExecutorControlService } from \"../executor-registry/index.js\";\nimport { createFileDurableMissionStore } from \"../mission-durable/index.js\";\nimport { listWorkers, WorkerControlService } from \"./worker-control-service.js\";\nimport { type WorkerListResult, type WorkerStatus, workerIdForExecutor } from \"./worker-types.js\";\n\nconst SUBCOMMANDS = new Set([\"run\", \"status\", \"list\", \"show\"]);\n\nfunction valueArgs(args: string[]): string[] {\n\treturn args.filter((a) => !a.startsWith(\"--\"));\n}\n\nfunction flag(args: string[], name: string): boolean {\n\treturn args.includes(name);\n}\n\nfunction flagValue(args: string[], name: string): string | undefined {\n\tconst idx = args.indexOf(name);\n\tif (idx === -1) return undefined;\n\treturn args[idx + 1];\n}\n\nfunction optionalPositiveNumber(value: string | undefined): number | undefined {\n\tif (value === undefined) return undefined;\n\tconst n = Number(value);\n\treturn Number.isSafeInteger(n) && n > 0 ? n : undefined;\n}\n\nfunction printJson(payload: unknown): void {\n\tprocess.stdout.write(`${JSON.stringify(payload, null, 2)}\\n`);\n}\n\nfunction codeOf(error: unknown): string | undefined {\n\tif (typeof error === \"object\" && error !== null && \"code\" in error) {\n\t\treturn String((error as { code: unknown }).code);\n\t}\n\treturn undefined;\n}\n\nfunction renderError(error: unknown): void {\n\tconst message = error instanceof Error ? error.message : String(error);\n\tconst code = codeOf(error);\n\tprocess.stderr.write(`${chalk.red(code ? `${code}: ${message}` : message)}\\n`);\n}\n\n/** Build the concrete child CLI launch for a local durable-child resume. */\nfunction buildChildCliResumeLaunch(): BuildAssignedResumeLaunch {\n\tconst cliEntry = process.argv[1];\n\tconst command = process.execPath;\n\tconst prefixArgs = [...process.execArgv, cliEntry];\n\tconst sessionDir = defaultChildSessionDir(getAgentDir());\n\n\treturn ({ request, resumePrompt, childSessionId }) => {\n\t\tconst launchArgs = [\n\t\t\t...prefixArgs,\n\t\t\t\"--mode\",\n\t\t\t\"json\",\n\t\t\t\"-p\",\n\t\t\t\"--child-mission\",\n\t\t\trequest.missionId,\n\t\t\t\"--session-id\",\n\t\t\tchildSessionId,\n\t\t\t\"--session-dir\",\n\t\t\tsessionDir,\n\t\t];\n\t\tif (request.modelPolicy) {\n\t\t\tlaunchArgs.push(\"--provider\", request.modelPolicy.provider, \"--model\", request.modelPolicy.model);\n\t\t}\n\t\tif (request.capabilities && request.capabilities.length > 0) {\n\t\t\tlaunchArgs.push(\"--tools\", request.capabilities.join(\",\"));\n\t\t}\n\t\tlaunchArgs.push(resumePrompt);\n\t\tconst orchestration = request.orchestration;\n\t\treturn {\n\t\t\tcommand,\n\t\t\targs: launchArgs,\n\t\t\tcwd: request.workspaceScope?.cwd ?? process.cwd(),\n\t\t\tenv: {\n\t\t\t\tJENSEN_MISSION_ID: request.missionId,\n\t\t\t\t...(orchestration?.priority !== undefined\n\t\t\t\t\t? { JENSEN_INFERENCE_PRIORITY: String(orchestration.priority) }\n\t\t\t\t\t: {}),\n\t\t\t\t...(orchestration?.dependencyCriticality !== undefined\n\t\t\t\t\t? { JENSEN_INFERENCE_UNBLOCKS: String(orchestration.dependencyCriticality) }\n\t\t\t\t\t: {}),\n\t\t\t\t...(orchestration?.verification ? { JENSEN_INFERENCE_VERIFICATION: \"1\" } : {}),\n\t\t\t},\n\t\t};\n\t};\n}\n\ninterface WorkerServiceBundle {\n\texecutors: ExecutorControlService;\n\tassignments: AssignmentControlService;\n\tmissions: ReturnType<typeof createFileDurableMissionStore>;\n}\n\nfunction buildServices(): WorkerServiceBundle {\n\tconst missions = createFileDurableMissionStore();\n\tconst executors = new ExecutorControlService({ store: createFileExecutorRegistry() });\n\tconst assignments = new AssignmentControlService({\n\t\tstore: createFileAssignmentStore(),\n\t\tmissions,\n\t\texecutors,\n\t\tsessionDir: defaultChildSessionDir(getAgentDir()),\n\t});\n\treturn { executors, assignments, missions };\n}\n\nfunction renderList(result: WorkerListResult): void {\n\tif (result.entries.length === 0 && result.corrupt.length === 0) {\n\t\tprocess.stdout.write(\"(no workers)\\n\");\n\t\treturn;\n\t}\n\tfor (const entry of result.entries) {\n\t\tprocess.stdout.write(\n\t\t\t[\n\t\t\t\t`${entry.executorId}  [${entry.status}]`,\n\t\t\t\tentry.workerInstanceId ? ` instance=${entry.workerInstanceId}` : \"\",\n\t\t\t\t` epoch=${entry.workerEpoch}`,\n\t\t\t\tentry.currentAssignmentId ? ` assignment=${entry.currentAssignmentId}` : \"\",\n\t\t\t\tentry.currentAssignmentState ? ` assignmentState=${entry.currentAssignmentState}` : \"\",\n\t\t\t\tentry.currentMissionState ? ` missionState=${entry.currentMissionState}` : \"\",\n\t\t\t].join(\"\"),\n\t\t);\n\t\tprocess.stdout.write(\"\\n\");\n\t}\n\tfor (const corrupt of result.corrupt) {\n\t\tprocess.stdout.write(chalk.yellow(`${corrupt.executorId}  [CORRUPT] ${corrupt.diagnostic}\\n`));\n\t}\n}\n\nfunction renderStatus(status: WorkerStatus): void {\n\tconst i = status.identity;\n\tprocess.stdout.write(`WORKER\\n`);\n\tprocess.stdout.write(`  worker: ${i.workerId}\\n`);\n\tprocess.stdout.write(`  executor: ${i.executorId}\\n`);\n\tprocess.stdout.write(`  instance: ${i.workerInstanceId || \"(none)\"}\\n`);\n\tprocess.stdout.write(`  epoch: ${i.workerEpoch}\\n`);\n\tif (i.ownerId) process.stdout.write(`  owner: ${i.ownerId}\\n`);\n\tif (i.hostname) process.stdout.write(`  host: ${i.hostname}\\n`);\n\tif (i.pid !== undefined) process.stdout.write(`  pid: ${i.pid}\\n`);\n\tif (i.startedAtMs) process.stdout.write(`  started: ${i.startedAtMs}\\n`);\n\tprocess.stdout.write(`  liveness: ${status.liveness}\\n`);\n\tprocess.stdout.write(`  daemon: ${status.daemonState}\\n`);\n\tprocess.stdout.write(`  activity: ${status.activity}\\n`);\n\tif (status.currentAssignment) {\n\t\tconst a = status.currentAssignment;\n\t\tprocess.stdout.write(`ASSIGNMENT\\n`);\n\t\tprocess.stdout.write(`  id: ${a.assignmentId}\\n`);\n\t\tprocess.stdout.write(`  mission: ${a.missionId}\\n`);\n\t\tprocess.stdout.write(`  state: ${a.state}\\n`);\n\t\tif (a.attemptId) process.stdout.write(`  attempt: ${a.attemptId}\\n`);\n\t\tif (a.executionId) process.stdout.write(`  execution: ${a.executionId}\\n`);\n\t}\n\tif (status.currentExecution) {\n\t\tconst e = status.currentExecution;\n\t\tprocess.stdout.write(`EXECUTION\\n`);\n\t\tprocess.stdout.write(`  missionState: ${e.missionState}\\n`);\n\t\tif (e.currentExecutionId) process.stdout.write(`  execution: ${e.currentExecutionId}\\n`);\n\t\tif (e.waitReason) process.stdout.write(`  wait: ${e.waitReason}\\n`);\n\t}\n\tif (status.lastError) process.stdout.write(`  lastError: ${status.lastError}\\n`);\n}\n\n// =============================================================================\n// Handler\n// =============================================================================\n\nexport function printWorkerUsage(): string {\n\treturn [\n\t\t\"  worker run --executor <EXECUTOR_ID> [--once] [--poll-ms N] [--heartbeat-ms N] [--expiry-ms N] [--json]\",\n\t\t\"  worker status <EXECUTOR_ID> [--json]\",\n\t\t\"  worker list [--json]\",\n\t\t\"  worker show <EXECUTOR_ID> [--json]\",\n\t].join(\"\\n\");\n}\n\nexport async function handleWorkerCommand(args: string[]): Promise<boolean> {\n\tif (args[0] !== \"worker\") return false;\n\tconst sub = args[1];\n\tif (!sub || !SUBCOMMANDS.has(sub)) return false;\n\n\tconst json = flag(args, \"--json\");\n\n\ttry {\n\t\tswitch (sub) {\n\t\t\tcase \"run\": {\n\t\t\t\tconst executorId = flagValue(args, \"--executor\");\n\t\t\t\tif (!executorId) {\n\t\t\t\t\tprocess.stderr.write(\"worker run requires --executor <EXECUTOR_ID>\\n\");\n\t\t\t\t\tprocess.exitCode = 1;\n\t\t\t\t\treturn true;\n\t\t\t\t}\n\t\t\t\tconst bundle = buildServices();\n\t\t\t\tconst worker = new WorkerControlService({\n\t\t\t\t\texecutorId,\n\t\t\t\t\texecutors: bundle.executors,\n\t\t\t\t\tassignments: bundle.assignments,\n\t\t\t\t\tmissions: bundle.missions,\n\t\t\t\t\tbuildResumeLaunch: buildChildCliResumeLaunch(),\n\t\t\t\t\tpollMs: optionalPositiveNumber(flagValue(args, \"--poll-ms\")),\n\t\t\t\t\theartbeatMs: optionalPositiveNumber(flagValue(args, \"--heartbeat-ms\")),\n\t\t\t\t\texpiryMs: optionalPositiveNumber(flagValue(args, \"--expiry-ms\")),\n\t\t\t\t});\n\n\t\t\t\tconst started = await worker.start();\n\t\t\t\tif (json) printJson(started);\n\t\t\t\telse {\n\t\t\t\t\tprocess.stdout.write(\n\t\t\t\t\t\t`started ${workerIdForExecutor(executorId)} executor=${executorId} instance=${started.runtimeInstanceId} epoch=${started.runtimeEpoch}\\n`,\n\t\t\t\t\t);\n\t\t\t\t}\n\n\t\t\t\tif (flag(args, \"--once\")) {\n\t\t\t\t\tconst outcome = await worker.runOnce();\n\t\t\t\t\tif (json) printJson({ start: started, run: outcome });\n\t\t\t\t\telse if (outcome.kind === \"idle\") process.stdout.write(\"idle (no eligible assignment)\\n\");\n\t\t\t\t\telse if (outcome.kind === \"skipped\")\n\t\t\t\t\t\tprocess.stdout.write(`skipped ${outcome.assignmentId}: ${outcome.reason}\\n`);\n\t\t\t\t\telse\n\t\t\t\t\t\tprocess.stdout.write(\n\t\t\t\t\t\t\t`executed ${outcome.assignmentId} mission=${outcome.missionId} missionState=${outcome.missionState} success=${outcome.success}\\n`,\n\t\t\t\t\t\t);\n\t\t\t\t\tawait worker.stop();\n\t\t\t\t\treturn true;\n\t\t\t\t}\n\n\t\t\t\t// Long-lived daemon: keep timers alive, stop cleanly on signals.\n\t\t\t\tconst shutdown = async (reason: string) => {\n\t\t\t\t\tawait worker.stop(reason);\n\t\t\t\t\tprocess.exit(0);\n\t\t\t\t};\n\t\t\t\tprocess.once(\"SIGTERM\", () => void shutdown(\"SIGTERM\"));\n\t\t\t\tprocess.once(\"SIGINT\", () => void shutdown(\"SIGINT\"));\n\t\t\t\treturn true;\n\t\t\t}\n\n\t\t\tcase \"status\":\n\t\t\tcase \"show\": {\n\t\t\t\tconst executorId = valueArgs(args)[2];\n\t\t\t\tif (!executorId) {\n\t\t\t\t\tprocess.stderr.write(`worker ${sub} requires <EXECUTOR_ID>\\n`);\n\t\t\t\t\tprocess.exitCode = 1;\n\t\t\t\t\treturn true;\n\t\t\t\t}\n\t\t\t\tconst bundle = buildServices();\n\t\t\t\tconst worker = new WorkerControlService({\n\t\t\t\t\texecutorId,\n\t\t\t\t\texecutors: bundle.executors,\n\t\t\t\t\tassignments: bundle.assignments,\n\t\t\t\t\tmissions: bundle.missions,\n\t\t\t\t\tbuildResumeLaunch: buildChildCliResumeLaunch(),\n\t\t\t\t});\n\t\t\t\tconst status = await worker.status();\n\t\t\t\tif (json) printJson(status);\n\t\t\t\telse renderStatus(status);\n\t\t\t\treturn true;\n\t\t\t}\n\n\t\t\tcase \"list\": {\n\t\t\t\tconst bundle = buildServices();\n\t\t\t\tconst result = await listWorkers(bundle);\n\t\t\t\tif (json) printJson(result);\n\t\t\t\telse renderList(result);\n\t\t\t\treturn true;\n\t\t\t}\n\n\t\t\tdefault:\n\t\t\t\treturn false;\n\t\t}\n\t} catch (error) {\n\t\trenderError(error);\n\t\tprocess.exitCode = 1;\n\t\treturn true;\n\t}\n}\n"]}