{"version":3,"file":"cli.d.ts","sourceRoot":"","sources":["../../../src/core/executor-registry/cli.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAqKH,wBAAgB,kBAAkB,IAAI,MAAM,CAW3C;AAED,wBAAsB,qBAAqB,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,OAAO,CAAC,CA0G5E","sourcesContent":["/**\n * Executor Registry — CLI (2.10.0).\n *\n * `jensen executor list|show|status|register|activate|heartbeat|deactivate|retire`\n *\n * Machine-readable `--json` exposes stable DTOs; human output is a compact\n * key: value block. Operators never need to inspect registry files.\n */\n\nimport chalk from \"chalk\";\nimport { ExecutorControlService } from \"./executor-control-service.js\";\nimport type {\n\tActivateExecutorInput,\n\tExecutorActivationOutcome,\n\tExecutorCapabilities,\n\tExecutorDeactivateOutcome,\n\tExecutorDetail,\n\tExecutorHeartbeatOutcome,\n\tExecutorListResult,\n\tExecutorRetireOutcome,\n\tExecutorRuntimeProof,\n} from \"./executor-registry-types.js\";\nimport { createFileExecutorRegistry } from \"./file-executor-registry.js\";\nimport { ExecutorRuntimeRegistration } from \"./runtime-harness.js\";\n\nconst CONTROL_SUBCOMMANDS = new Set([\n\t\"list\",\n\t\"show\",\n\t\"status\",\n\t\"register\",\n\t\"activate\",\n\t\"heartbeat\",\n\t\"deactivate\",\n\t\"retire\",\n]);\n\nfunction buildService(): ExecutorControlService {\n\treturn new ExecutorControlService({ store: createFileExecutorRegistry() });\n}\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 flagValues(args: string[], name: string): string[] {\n\tconst values: string[] = [];\n\tfor (let i = 0; i < args.length; i++) {\n\t\tif (args[i] === name && args[i + 1] !== undefined && !args[i + 1].startsWith(\"--\")) {\n\t\t\tvalues.push(args[i + 1]);\n\t\t}\n\t}\n\treturn values;\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// =============================================================================\n// Human renderers\n// =============================================================================\n\nfunction renderList(result: ExecutorListResult): void {\n\tif (result.entries.length === 0 && result.corrupt.length === 0) {\n\t\tprocess.stdout.write(\"(no executors)\\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.runtimeInstanceId ? ` runtime=${entry.runtimeInstanceId}` : \"\",\n\t\t\t\t` epoch=${entry.runtimeEpoch}`,\n\t\t\t\tentry.hostname ? ` host=${entry.hostname}` : \"\",\n\t\t\t\tentry.platform ? ` platform=${entry.platform}` : \"\",\n\t\t\t\tentry.arch ? ` arch=${entry.arch}` : \"\",\n\t\t\t\tentry.lastHeartbeatAtMs ? ` heartbeat=${entry.lastHeartbeatAtMs}` : \"\",\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 renderDetail(detail: ExecutorDetail): void {\n\tprocess.stdout.write(`EXECUTOR\\n`);\n\tprocess.stdout.write(`  id: ${detail.executorId}\\n`);\n\tprocess.stdout.write(`  status: ${detail.status}\\n`);\n\tif (detail.displayName) process.stdout.write(`  display: ${detail.displayName}\\n`);\n\tprocess.stdout.write(`  epoch: ${detail.runtimeEpoch}\\n`);\n\tprocess.stdout.write(`  retired: ${detail.retired ? \"yes\" : \"no\"}\\n`);\n\tif (detail.labels.length > 0) process.stdout.write(`  labels: ${detail.labels.join(\", \")}\\n`);\n\tif (detail.runtime) {\n\t\tconst r = detail.runtime;\n\t\tprocess.stdout.write(`RUNTIME\\n`);\n\t\tprocess.stdout.write(`  instance: ${r.runtimeInstanceId}\\n`);\n\t\tprocess.stdout.write(`  owner: ${r.ownerId}\\n`);\n\t\tif (r.hostname) process.stdout.write(`  host: ${r.hostname}\\n`);\n\t\tif (r.pid !== undefined) process.stdout.write(`  pid: ${r.pid}\\n`);\n\t\tif (r.platform) process.stdout.write(`  platform: ${r.platform}/${r.arch ?? \"\"}\\n`);\n\t\tif (r.jensenVersion) process.stdout.write(`  version: ${r.jensenVersion}\\n`);\n\t\tprocess.stdout.write(`  started: ${r.startedAtMs}\\n`);\n\t\tprocess.stdout.write(`  last heartbeat: ${r.lastHeartbeatAtMs}\\n`);\n\t\tprocess.stdout.write(`  expires: ${r.expiresAtMs}\\n`);\n\t\tconst caps = r.advertisedCapabilities;\n\t\tif (caps.providers?.length) process.stdout.write(`  providers: ${caps.providers.join(\", \")}\\n`);\n\t\tif (caps.models?.length) process.stdout.write(`  models: ${caps.models.join(\", \")}\\n`);\n\t\tif (r.resources) {\n\t\t\tprocess.stdout.write(`  resources: observed=${r.resources.observedAtMs}`);\n\t\t\tif (r.resources.cpuLogicalCount !== undefined) process.stdout.write(` cpu=${r.resources.cpuLogicalCount}`);\n\t\t\tprocess.stdout.write(\"\\n\");\n\t\t}\n\t}\n}\n\nfunction renderActivation(outcome: ExecutorActivationOutcome): void {\n\tprocess.stdout.write(\n\t\t[\n\t\t\t`activated ${outcome.executorId}`,\n\t\t\t`instance=${outcome.runtimeInstanceId}`,\n\t\t\t`epoch=${outcome.runtimeEpoch}`,\n\t\t\t`expires=${outcome.expiresAtMs}`,\n\t\t].join(\" \"),\n\t);\n\tprocess.stdout.write(\"\\n\");\n}\n\nfunction renderHeartbeat(outcome: ExecutorHeartbeatOutcome): void {\n\tprocess.stdout.write(\n\t\t`heartbeat ${outcome.executorId} instance=${outcome.runtimeInstanceId} epoch=${outcome.runtimeEpoch} expires=${outcome.expiresAtMs}\\n`,\n\t);\n}\n\nfunction renderDeactivate(outcome: ExecutorDeactivateOutcome): void {\n\tprocess.stdout.write(\n\t\t`deactivated ${outcome.executorId} instance=${outcome.runtimeInstanceId} epoch=${outcome.runtimeEpoch}\\n`,\n\t);\n}\n\nfunction renderRetire(outcome: ExecutorRetireOutcome): void {\n\tprocess.stdout.write(`retired ${outcome.executorId} status=${outcome.status}\\n`);\n}\n\n// =============================================================================\n// Handler\n// =============================================================================\n\nexport function printExecutorUsage(): string {\n\treturn [\n\t\t\"  executor list [--json] [--status S] [--platform OS] [--arch A] [--label L] [--capability C] [--provider P] [--model M]\",\n\t\t\"  executor show <EXECUTOR_ID> [--json]\",\n\t\t\"  executor status <EXECUTOR_ID> [--json]\",\n\t\t\"  executor register <EXECUTOR_ID> [--json] [--display-name N] [--label L]... [--capability C]... [--provider P]... [--model M]...\",\n\t\t\"  executor activate <EXECUTOR_ID> [--json] [--provider P]... [--model M]... [--tool T]... [--label L]...\",\n\t\t\"  executor heartbeat <EXECUTOR_ID> --instance I --epoch N [--json]\",\n\t\t\"  executor deactivate <EXECUTOR_ID> --instance I --epoch N [--json]\",\n\t\t\"  executor retire <EXECUTOR_ID> [--json]\",\n\t].join(\"\\n\");\n}\n\nexport async function handleExecutorCommand(args: string[]): Promise<boolean> {\n\tif (args[0] !== \"executor\") return false;\n\tconst sub = args[1];\n\tif (!sub || !CONTROL_SUBCOMMANDS.has(sub)) return false;\n\n\tconst json = flag(args, \"--json\");\n\tconst service = buildService();\n\n\ttry {\n\t\tswitch (sub) {\n\t\t\tcase \"list\": {\n\t\t\t\tconst limitArg = flagValue(args, \"--limit\");\n\t\t\t\tconst result = await service.listExecutors({\n\t\t\t\t\tfilter: {\n\t\t\t\t\t\tstatus: flagValue(args, \"--status\") as ExecutorListResult[\"entries\"][number][\"status\"] | undefined,\n\t\t\t\t\t\tplatform: flagValue(args, \"--platform\"),\n\t\t\t\t\t\tarch: flagValue(args, \"--arch\"),\n\t\t\t\t\t\tlabel: flagValue(args, \"--label\"),\n\t\t\t\t\t\tcapability: flagValue(args, \"--capability\"),\n\t\t\t\t\t\tprovider: flagValue(args, \"--provider\"),\n\t\t\t\t\t\tmodel: flagValue(args, \"--model\"),\n\t\t\t\t\t},\n\t\t\t\t\tlimit: limitArg !== undefined ? Number(limitArg) : undefined,\n\t\t\t\t});\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\tcase \"show\":\n\t\t\tcase \"status\": {\n\t\t\t\tconst executorId = valueArgs(args)[2];\n\t\t\t\tif (!executorId) return missingExecutorId();\n\t\t\t\tconst detail = await service.getExecutor(executorId);\n\t\t\t\tif (json) printJson(detail);\n\t\t\t\telse renderDetail(detail);\n\t\t\t\treturn true;\n\t\t\t}\n\n\t\t\tcase \"register\": {\n\t\t\t\tconst executorId = valueArgs(args)[2];\n\t\t\t\tif (!executorId) return missingExecutorId();\n\t\t\t\tconst capabilities = capabilitiesFromFlags(args);\n\t\t\t\tconst outcome = await service.registerExecutor({\n\t\t\t\t\texecutorId,\n\t\t\t\t\tdisplayName: flagValue(args, \"--display-name\"),\n\t\t\t\t\tlabels: unique(flagValues(args, \"--label\")),\n\t\t\t\t\tconfiguredCapabilities: capabilities,\n\t\t\t\t});\n\t\t\t\tif (json) printJson(outcome);\n\t\t\t\telse process.stdout.write(`registered ${outcome.executorId} status=${outcome.status}\\n`);\n\t\t\t\treturn true;\n\t\t\t}\n\n\t\t\tcase \"activate\": {\n\t\t\t\tconst executorId = valueArgs(args)[2];\n\t\t\t\tif (!executorId) return missingExecutorId();\n\t\t\t\tconst capabilities = capabilitiesFromFlags(args);\n\t\t\t\tconst registration = new ExecutorRuntimeRegistration(service, executorId, {\n\t\t\t\t\texpiryMs: optionalNumber(flagValue(args, \"--expiry\")),\n\t\t\t\t});\n\t\t\t\tconst input: Parameters<ExecutorRuntimeRegistration[\"activate\"]>[0] = {\n\t\t\t\t\tadvertisedCapabilities: capabilities,\n\t\t\t\t};\n\t\t\t\tif (flagValue(args, \"--hostname\")) input.hostname = flagValue(args, \"--hostname\");\n\t\t\t\tconst outcome = await registration.activate(input as ActivateExecutorInput);\n\t\t\t\tif (json) printJson(outcome);\n\t\t\t\telse renderActivation(outcome);\n\t\t\t\treturn true;\n\t\t\t}\n\n\t\t\tcase \"heartbeat\": {\n\t\t\t\tconst proof = proofFromArgs(args);\n\t\t\t\tif (!proof) return missingProof();\n\t\t\t\tconst outcome = await service.heartbeatExecutor(proof);\n\t\t\t\tif (json) printJson(outcome);\n\t\t\t\telse renderHeartbeat(outcome);\n\t\t\t\treturn true;\n\t\t\t}\n\n\t\t\tcase \"deactivate\": {\n\t\t\t\tconst proof = proofFromArgs(args);\n\t\t\t\tif (!proof) return missingProof();\n\t\t\t\tconst outcome = await service.deactivateExecutor(proof);\n\t\t\t\tif (json) printJson(outcome);\n\t\t\t\telse renderDeactivate(outcome);\n\t\t\t\treturn true;\n\t\t\t}\n\n\t\t\tcase \"retire\": {\n\t\t\t\tconst executorId = valueArgs(args)[2];\n\t\t\t\tif (!executorId) return missingExecutorId();\n\t\t\t\tconst outcome = await service.retireExecutor(executorId);\n\t\t\t\tif (json) printJson(outcome);\n\t\t\t\telse renderRetire(outcome);\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\nfunction missingExecutorId(): boolean {\n\tprocess.stderr.write(\"missing executor id\\n\");\n\tprocess.exitCode = 1;\n\treturn true;\n}\n\nfunction missingProof(): boolean {\n\tprocess.stderr.write(\"missing runtime proof (--instance and --epoch are required)\\n\");\n\tprocess.exitCode = 1;\n\treturn true;\n}\n\nfunction optionalNumber(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 unique(values: string[]): string[] {\n\treturn [...new Set(values)].sort();\n}\n\nfunction proofFromArgs(args: string[]): ExecutorRuntimeProof | undefined {\n\tconst executorId = valueArgs(args)[2];\n\tconst instance = flagValue(args, \"--instance\");\n\tconst epochArg = flagValue(args, \"--epoch\");\n\tif (!executorId || !instance || epochArg === undefined) return undefined;\n\tconst epoch = Number(epochArg);\n\tif (!Number.isSafeInteger(epoch) || epoch < 1) return undefined;\n\treturn { executorId, runtimeInstanceId: instance, runtimeEpoch: epoch };\n}\n\nfunction capabilitiesFromFlags(args: string[]): ExecutorCapabilities {\n\tconst capabilities: ExecutorCapabilities = {};\n\tconst providers = unique(flagValues(args, \"--provider\"));\n\tconst models = unique(flagValues(args, \"--model\"));\n\tconst tools = unique(flagValues(args, \"--tool\"));\n\tconst specialized = unique(flagValues(args, \"--specialized\"));\n\tconst execution = unique(flagValues(args, \"--execution\"));\n\tconst extra = unique(flagValues(args, \"--capability\"));\n\tif (providers.length > 0) capabilities.providers = providers;\n\tif (models.length > 0) capabilities.models = models;\n\tif (tools.length > 0) capabilities.tools = tools;\n\tif (specialized.length > 0) capabilities.specialized = specialized;\n\tif (execution.length > 0) capabilities.execution = execution;\n\tif (extra.length > 0) capabilities.extra = extra;\n\treturn capabilities;\n}\n"]}