@wrongstack/cli 1.0.28 → 1.0.32

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (135) hide show
  1. package/data/providers.json +8 -177
  2. package/dist/{acp-M4565YUW.js → acp-EDGUWU7N.js} +4 -7
  3. package/dist/{audit-46YK5DSC.js → audit-KCBTHUQI.js} +1 -1
  4. package/dist/{auth-TE55JF4Z.js → auth-SUHKFFO4.js} +136 -59
  5. package/dist/auth-menu/github-copilot-oauth.d.ts +1 -35
  6. package/dist/auth-menu/index-input.d.ts +3 -0
  7. package/dist/auth-menu/panel-flow-bridge.d.ts +29 -0
  8. package/dist/auth-menu/panel-service.d.ts +3 -34
  9. package/dist/auth-menu/provider-auth-login.d.ts +1 -1
  10. package/dist/auto-discover-providers-QIL34K3U.js +7 -0
  11. package/dist/automation-MCDFCIMV.js +369 -0
  12. package/dist/{bench-GTLPM22B.js → bench-MMZHFHDL.js} +152 -43
  13. package/dist/boot/brain-decision-log.d.ts +1 -1
  14. package/dist/boot/system-prompt-builder.d.ts +1 -1
  15. package/dist/boot/system-prompt-menu.d.ts +11 -2
  16. package/dist/boot/tui-coordinator-setup.d.ts +2 -0
  17. package/dist/boot/tui-startup-output.d.ts +11 -0
  18. package/dist/{boot-auto-thin-6Q56FY3F.js → boot-auto-thin-K2DHHYIW.js} +3 -3
  19. package/dist/{chronicle-UTFETHP6.js → chronicle-PBJS7BN6.js} +5 -3
  20. package/dist/{chunk-EPR42SWM.js → chunk-2EZ2DHOW.js} +60 -7
  21. package/dist/{chunk-7IQWQCBH.js → chunk-3LEFXLAW.js} +39 -39
  22. package/dist/{chunk-WSTVGC2C.js → chunk-4BX2YDTQ.js} +13 -6
  23. package/dist/{chunk-SFBFYIOW.js → chunk-4MDRHMX4.js} +3 -2
  24. package/dist/{chunk-RGFWGUAB.js → chunk-4UWCMBPG.js} +159 -3
  25. package/dist/{chunk-RDGGWMTI.js → chunk-65VNCAF6.js} +2 -22
  26. package/dist/{chunk-WQZW3RDP.js → chunk-7BNBD4PE.js} +2 -2
  27. package/dist/chunk-CFHWIFXL.js +123 -0
  28. package/dist/{chunk-RZ4ZDRYA.js → chunk-DIOODAYM.js} +2 -2
  29. package/dist/{chunk-L2RVEQWO.js → chunk-HHRYIDOW.js} +2 -2
  30. package/dist/chunk-LIZXJHZQ.js +61 -0
  31. package/dist/{chunk-YHYINL47.js → chunk-LKFKO4JW.js} +2 -2
  32. package/dist/{chunk-Q4INH2DG.js → chunk-MPQCHBDJ.js} +2 -1
  33. package/dist/{chunk-2ERX3RPN.js → chunk-P3GRLV47.js} +30 -17
  34. package/dist/{chunk-ZMX5HRLI.js → chunk-P3Q3KMFH.js} +6 -4
  35. package/dist/{chunk-KOEO4CJG.js → chunk-PGNERAZL.js} +4 -4
  36. package/dist/{chunk-ENRYW73H.js → chunk-RQ4UBKYH.js} +81 -10
  37. package/dist/{chunk-X756ZS72.js → chunk-SUDNPYWD.js} +2 -2
  38. package/dist/{chunk-LDCGZJTC.js → chunk-TWSQTQKR.js} +12 -4
  39. package/dist/chunk-TXAFIERF.js +37 -0
  40. package/dist/{chunk-UZ2TMHNH.js → chunk-UKP7H2U4.js} +10 -2
  41. package/dist/{chunk-5JYDKG5B.js → chunk-WHEYHDIE.js} +7 -2
  42. package/dist/{chunk-LBPLTTZ6.js → chunk-WTJDSTYD.js} +4 -4
  43. package/dist/{chunk-L5TD7UIG.js → chunk-WXJH4F6K.js} +7 -7
  44. package/dist/{chunk-IS2PPBQV.js → chunk-X4LW5CZM.js} +8 -4
  45. package/dist/{chunk-BBHWW6LR.js → chunk-YL4KCHZG.js} +18 -6
  46. package/dist/{chunk-LUMETJFO.js → chunk-ZYACRPLN.js} +2 -2
  47. package/dist/{cli-context-O2SHBDFH.js → cli-context-ITHGP2CE.js} +91 -84
  48. package/dist/cli-context.d.ts +3 -1
  49. package/dist/cli-infrastructure.d.ts +33 -0
  50. package/dist/{cli-main-BKG4ZVLN.js → cli-main-TQGLKB6D.js} +3783 -2753
  51. package/dist/config-doctor.d.ts +1 -1
  52. package/dist/{config-transfer-RUWLDCNK.js → config-transfer-UE37X4B5.js} +2 -2
  53. package/dist/{execution-THXWTHYT.js → execution-C2IECWF7.js} +5271 -5118
  54. package/dist/execution-chimera-handlers.d.ts +6 -0
  55. package/dist/execution-session-callbacks.d.ts +36 -0
  56. package/dist/execution-webui.d.ts +3 -0
  57. package/dist/execution.d.ts +1 -1
  58. package/dist/fleet/host-acp-runner.d.ts +15 -0
  59. package/dist/fleet/host-budget.d.ts +13 -0
  60. package/dist/fleet/host-context.d.ts +5 -0
  61. package/dist/fleet/host-explore-companion.d.ts +7 -16
  62. package/dist/fleet/host.d.ts +2 -0
  63. package/dist/fleet/status-broadcast.d.ts +1 -1
  64. package/dist/{hq-44F4OQH2.js → hq-KBFDKTDD.js} +13 -7
  65. package/dist/hq-publisher.d.ts +4 -1
  66. package/dist/hq-server/sage-sync-send.d.ts +5 -0
  67. package/dist/hq-server/server-lifecycle.d.ts +1 -1
  68. package/dist/hq-server/snapshot.d.ts +1 -1
  69. package/dist/{hq-server-UIB24LU6.js → hq-server-AVNKZ2PV.js} +2 -2
  70. package/dist/{hq-service-BMMJAHRT.js → hq-service-FGB7BY2D.js} +3 -3
  71. package/dist/hq-ttl-N4DAIAVX.js +7 -0
  72. package/dist/index.js +126 -10
  73. package/dist/{mailbox-serve-GNGXEQ2R.js → mailbox-serve-S536N66F.js} +4 -3
  74. package/dist/{modeldiag-W7XAE625.js → modeldiag-PQILD7TN.js} +26 -8
  75. package/dist/{per-subcommand-help-QDAQASPZ.js → per-subcommand-help-VKPV3OEY.js} +2 -2
  76. package/dist/{plugin-usage-CIGOOTX3.js → plugin-usage-3TZGROK7.js} +3 -3
  77. package/dist/{plugins-HQTXUD6A.js → plugins-ITH4SLO6.js} +4 -4
  78. package/dist/pre-launch/project-check.d.ts +2 -8
  79. package/dist/pre-launch.d.ts +1 -1
  80. package/dist/{project-manifest-7NSFVBM3.js → project-manifest-FTFDCCTZ.js} +2 -2
  81. package/dist/{project-picker-SVBXMPKX.js → project-picker-NZYJKQQC.js} +3 -3
  82. package/dist/provider-cloud-config.d.ts +4 -0
  83. package/dist/provider-config-utils.d.ts +1 -1
  84. package/dist/{providers-models-5Z2PJTEW.js → providers-models-W57I3QOA.js} +3 -3
  85. package/dist/{remote-GB5VXCCT.js → remote-C74BCNND.js} +5 -5
  86. package/dist/{rewind-JA3ZLCNM.js → rewind-7AULJS7F.js} +7 -5
  87. package/dist/sage-FFKUOCQL.js +475 -0
  88. package/dist/sage-connect.d.ts +62 -0
  89. package/dist/sage-hq-sync.d.ts +8 -0
  90. package/dist/sage-sync-ZMTHB477.js +69 -0
  91. package/dist/sandbox-TYGD6FI2.js +131 -0
  92. package/dist/services/sdd/project-context.d.ts +1 -1
  93. package/dist/{sessions-config-XHLXJ5C3.js → sessions-config-VE26SB56.js} +19 -8
  94. package/dist/short-circuit-flags-6LB3EGF7.js +10 -0
  95. package/dist/slash-commands/openai-quota.d.ts +12 -6
  96. package/dist/slash-commands/sandbox.d.ts +5 -0
  97. package/dist/slash-commands/scout-stats.d.ts +46 -0
  98. package/dist/slash-commands/sdd-inspect-commands.d.ts +9 -0
  99. package/dist/slash-commands/sdd-session-commands.d.ts +19 -0
  100. package/dist/slash-commands/sdd.d.ts +1 -1
  101. package/dist/slash-commands/zai-plan.d.ts +26 -0
  102. package/dist/{standalone-auto-update-TOTRGCSA.js → standalone-auto-update-VQC4GYCM.js} +3 -3
  103. package/dist/{standalone-update-LZO3MNLH.js → standalone-update-HZHDZZOR.js} +2 -2
  104. package/dist/subcommands/handlers/automation-credentials.d.ts +8 -0
  105. package/dist/subcommands/handlers/automation.d.ts +3 -0
  106. package/dist/subcommands/handlers/bench-experiments.d.ts +5 -0
  107. package/dist/subcommands/handlers/helpers.d.ts +1 -1
  108. package/dist/subcommands/handlers/sage-sync.d.ts +4 -0
  109. package/dist/subcommands/handlers/sage.d.ts +5 -0
  110. package/dist/subcommands/handlers/sandbox.d.ts +5 -0
  111. package/dist/{subcommands-SRRB3RAS.js → subcommands-QO7VYTSS.js} +2 -2
  112. package/dist/{tools-skills-GH24UCBY.js → tools-skills-33AI6K6S.js} +2 -2
  113. package/dist/tui-provider-quota-menu.d.ts +5 -0
  114. package/dist/{typesafe-WA5O3KVS.js → typesafe-NHC322W5.js} +27 -8
  115. package/dist/{typesafe-topic-replay-ZILIU3WS.js → typesafe-topic-replay-DB2AYL7V.js} +2 -2
  116. package/dist/{typesafe-triage-replay-FGAW7QM7.js → typesafe-triage-replay-27NDAZ3V.js} +2 -2
  117. package/dist/{update-TDZX4IL2.js → update-J5WHAFPE.js} +3 -3
  118. package/dist/{update-check-J5ZVKS73.js → update-check-VLVT4IV2.js} +2 -2
  119. package/dist/{version-help-XJJQ7YSH.js → version-help-ATOKGMTK.js} +2 -2
  120. package/dist/webui-server/prefs-seeding.d.ts +1 -0
  121. package/dist/webui-server/route-contexts.d.ts +1 -0
  122. package/dist/{webui-server-GCXIO3JY.js → webui-server-GQOVLQS3.js} +79 -40
  123. package/dist/webui-session-retirement.d.ts +10 -0
  124. package/dist/wiring/command-host-state.d.ts +2 -0
  125. package/dist/wiring/director-announcement.d.ts +3 -1
  126. package/dist/wiring/session-establishment.d.ts +10 -8
  127. package/dist/wiring/session-runtime.d.ts +1 -1
  128. package/dist/wiring/session.d.ts +3 -0
  129. package/package.json +30 -29
  130. package/dist/auto-discover-providers-DXR25GTG.js +0 -7
  131. package/dist/chunk-3ZTLPNEQ.js +0 -58
  132. package/dist/chunk-D5GB5MHA.js +0 -87
  133. package/dist/chunk-SIGUWXAA.js +0 -43
  134. package/dist/hq-ttl-VJR65OBJ.js +0 -7
  135. package/dist/short-circuit-flags-BSIPTAPD.js +0 -10
@@ -0,0 +1,369 @@
1
+
2
+ // src/subcommands/handlers/automation.ts
3
+ import { randomBytes } from "node:crypto";
4
+ import { mkdir, readFile as readFile2, unlink } from "node:fs/promises";
5
+ import * as path from "node:path";
6
+ import {
7
+ atomicWrite,
8
+ toErrorMessage,
9
+ withFileLock,
10
+ wstackGlobalRoot
11
+ } from "@wrongstack/core/utils";
12
+ import {
13
+ AutomationService,
14
+ AutomationStore,
15
+ createAutomationServer,
16
+ createHeadlessLaunchPlan,
17
+ evaluateGitHubFilters
18
+ } from "@wrongstack/runtime";
19
+ import {
20
+ exportAutomationJob,
21
+ importAutomationJob,
22
+ VERSIONED_AUTOMATION_TEMPLATES
23
+ } from "@wrongstack/webui-protocol";
24
+
25
+ // src/subcommands/handlers/automation-credentials.ts
26
+ import { readFile } from "node:fs/promises";
27
+ import { decryptConfigSecrets, isSecretField } from "@wrongstack/core/security";
28
+ import {
29
+ isCredentialEnvName
30
+ } from "@wrongstack/runtime";
31
+ async function resolveAutomationCredentialBundle(refs, paths, vault) {
32
+ const values = /* @__PURE__ */ Object.create(null);
33
+ const providers = /* @__PURE__ */ Object.create(null);
34
+ const profiles = /* @__PURE__ */ new Map();
35
+ for (const ref of refs) {
36
+ if (!/^[\w-]{1,64}$/.test(ref.profile) || !isCredentialEnvName(ref.envName))
37
+ throw new Error("Invalid automation credential reference");
38
+ let config = profiles.get(ref.profile);
39
+ if (!config) {
40
+ config = decryptConfigSecrets(
41
+ JSON.parse(await readFile(paths.profileConfig(ref.profile), "utf8")),
42
+ vault
43
+ );
44
+ profiles.set(ref.profile, config);
45
+ }
46
+ const provider = config.providers && Object.hasOwn(config.providers, ref.provider) ? config.providers[ref.provider] : void 0;
47
+ const key = provider?.apiKeys?.find((item) => item.label === ref.keyLabel);
48
+ if (!key || key.authMethod !== void 0 && key.authMethod !== "api_key" || typeof key.apiKey !== "string" || !key.apiKey.trim() || vault.isEncrypted(key.apiKey))
49
+ throw new Error(
50
+ `API credential reference unavailable: ${ref.profile}/${ref.provider}/${ref.keyLabel}`
51
+ );
52
+ if (Object.hasOwn(values, ref.envName))
53
+ throw new Error("Duplicate automation credential destination");
54
+ values[ref.envName] = key.apiKey;
55
+ if (Object.hasOwn(providers, ref.provider))
56
+ throw new Error("A provider cannot reference two credentials in one run");
57
+ const selected = { type: provider.type || ref.provider, apiKey: key.apiKey };
58
+ for (const field of [
59
+ "family",
60
+ "cloud",
61
+ "baseUrl",
62
+ "model",
63
+ "models",
64
+ "customModels",
65
+ "capabilities",
66
+ "quirks",
67
+ "autoDiscoverModels",
68
+ "modelDiscoveryPath",
69
+ "modelDiscoveryAuthoritative"
70
+ ]) {
71
+ if (provider[field] !== void 0)
72
+ selected[field] = structuredClone(provider[field]);
73
+ }
74
+ if (provider.headers)
75
+ selected.headers = Object.fromEntries(
76
+ Object.entries(provider.headers).filter(
77
+ ([name, value]) => !isSecretField(name) && !value.includes(key.apiKey)
78
+ )
79
+ );
80
+ providers[ref.provider] = selected;
81
+ }
82
+ return { env: values, providers };
83
+ }
84
+
85
+ // src/subcommands/handlers/automation.ts
86
+ var USAGE = `Usage: wstack automation <add|preview|templates|import|export|list|run|enable|disable|cancel|serve|prune> [id] [options]
87
+
88
+ Persistent agent jobs and run history. Jobs execute in a copied Docker workspace
89
+ and produce reviewable patches. No host project edits or automatic publishing.
90
+
91
+ add --name <name> --image <trusted-image> --prompt <task>
92
+ [--every <seconds>] [--provider <id>] [--model <id>] [--env NAME,...]
93
+ [--repository owner/repo --events issue_comment.created,pull_request.opened]
94
+ [--webhook-secret-env GITHUB_WEBHOOK_SECRET] [--timeout <seconds>]
95
+ [--credential ENV=provider/key-label] [--yolo] [--max-iterations <n>]
96
+ [--cron "0 9 * * 1-5" --timezone Europe/Istanbul]
97
+ [--mention @wrongstack] [--label agent] [--branch main]
98
+ [--conclusion failure] [--exclude-bots] [--exclude-drafts]
99
+ preview Same options as add; validate without writing or executing
100
+ [--event-file <json>] Preview GitHub conditions against a sample payload
101
+ templates List versioned templates and required setup
102
+ add --template <id> Use a versioned prompt template with existing project instructions/skills
103
+ export <id> Print a portable definition without project identity or credential values
104
+ import --file <json> Import into this project, disabled and with unattended tools off
105
+ [--dry-run] Validate the import without writing a job
106
+ run <id> Queue a manual run
107
+ list Show persistent jobs and run status
108
+ enable|disable <id> Change scheduling state
109
+ cancel <run-id> Cancel a queued run (running cancellation uses the API)
110
+ prune --days <n> Prune old terminal metadata; retain latest PR checkpoints and artifacts
111
+ serve [--port 3501] Start worker and loopback HTTP API until interrupted
112
+ --data-dir <path> Explicit state directory (default machine home/automation)
113
+
114
+ The API token is stored in token.txt, not printed. GitHub POSTs use
115
+ /hooks/<job-id>/github and HMAC-SHA256 verification. Control routes require
116
+ Bearer authentication; manual run POSTs require an Idempotency-Key.
117
+ Choose --every (fixed seconds) or --cron (five fields, timezone defaults to UTC).
118
+ Preview and add --dry-run print references only; values are resolved at execution.
119
+ `;
120
+ async function apiToken(directory) {
121
+ const file = path.join(directory, "token.txt");
122
+ return withFileLock(file, async () => {
123
+ try {
124
+ const value = (await readFile2(file, "utf8")).trim();
125
+ if (!/^[a-f0-9]{64}$/.test(value)) throw new Error("Invalid automation API token file");
126
+ return value;
127
+ } catch (error) {
128
+ if (error.code !== "ENOENT") throw error;
129
+ const value = randomBytes(32).toString("hex");
130
+ await atomicWrite(file, `${value}
131
+ `, { mode: 384 });
132
+ return value;
133
+ }
134
+ });
135
+ }
136
+ var automationCmd = async (args, deps) => {
137
+ const flags = deps.flags ?? {};
138
+ const action = args[0] ?? "list";
139
+ if (flags["help"] || action === "help") {
140
+ deps.renderer.write(USAGE);
141
+ return 0;
142
+ }
143
+ const value = (name) => typeof flags[name] === "string" ? flags[name] : void 0;
144
+ const directory = path.resolve(value("data-dir") ?? path.join(wstackGlobalRoot(), "automation"));
145
+ const store = new AutomationStore(directory);
146
+ try {
147
+ if (action === "add" || action === "preview") {
148
+ const template = value("template") ? VERSIONED_AUTOMATION_TEMPLATES.find((item) => item.id === value("template")) : void 0;
149
+ if (value("template") && !template) throw new Error("Unknown automation template");
150
+ if (value("timezone") && !value("cron")) throw new Error("--timezone requires --cron");
151
+ const credentials = value("credential")?.split(",").map((item) => {
152
+ const equal = item.indexOf("=");
153
+ const envName = item.slice(0, equal).trim();
154
+ const keyPath = item.slice(equal + 1).split("/");
155
+ if (equal < 1 || keyPath.length < 2)
156
+ throw new Error("Use --credential ENV=provider/key-label");
157
+ return {
158
+ envName,
159
+ profile: deps.paths.profileName,
160
+ provider: keyPath[0],
161
+ keyLabel: keyPath.slice(1).join("/")
162
+ };
163
+ });
164
+ const spec = {
165
+ name: value("name") ?? template?.name ?? "",
166
+ projectRoot: deps.projectRoot,
167
+ image: value("image") ?? "",
168
+ prompt: value("prompt") ?? template?.prompt ?? "",
169
+ template: template ? { id: template.id, version: template.version } : void 0,
170
+ provider: value("provider"),
171
+ model: value("model"),
172
+ envNames: value("env")?.split(",").map((name) => name.trim()).filter(Boolean) ?? [],
173
+ intervalMs: value("every") === void 0 ? void 0 : Number(value("every")) * 1e3,
174
+ schedule: value("cron") ? {
175
+ type: "cron",
176
+ expression: value("cron"),
177
+ timezone: value("timezone") ?? "UTC"
178
+ } : void 0,
179
+ enabled: true,
180
+ yolo: flags["yolo"] === true,
181
+ timeoutMs: Number(value("timeout") ?? 600) * 1e3,
182
+ maxIterations: Number(value("max-iterations") ?? 40),
183
+ credentials,
184
+ github: value("repository") ? {
185
+ repository: value("repository"),
186
+ events: value("events")?.split(",").map((event) => event.trim()) ?? ["issue_comment.created"],
187
+ secretEnv: value("webhook-secret-env") ?? "GITHUB_WEBHOOK_SECRET",
188
+ filters: {
189
+ mention: value("mention"),
190
+ label: value("label"),
191
+ branch: value("branch"),
192
+ conclusion: value("conclusion"),
193
+ draft: flags["exclude-drafts"] === true ? false : void 0,
194
+ excludeBots: flags["exclude-bots"] === true ? true : void 0
195
+ }
196
+ } : void 0
197
+ };
198
+ const plan = createHeadlessLaunchPlan(spec);
199
+ if (action === "preview" || flags["dry-run"] === true) {
200
+ const missing = spec.envNames.filter((name) => !process.env[name]);
201
+ for (const ref of spec.credentials ?? []) {
202
+ try {
203
+ const profile = JSON.parse(
204
+ await readFile2(deps.paths.profileConfig(ref.profile), "utf8")
205
+ );
206
+ const provider = profile.providers && Object.hasOwn(profile.providers, ref.provider) ? profile.providers[ref.provider] : void 0;
207
+ const key = provider?.apiKeys?.find(
208
+ (entry) => entry.label === ref.keyLabel
209
+ );
210
+ if (!key?.apiKey || key.authMethod && key.authMethod !== "api_key")
211
+ missing.push(`${ref.profile}/${ref.provider}/${ref.keyLabel}`);
212
+ } catch {
213
+ missing.push(`${ref.profile}/${ref.provider}/${ref.keyLabel}`);
214
+ }
215
+ }
216
+ const filterPreview = value("event-file") ? evaluateGitHubFilters(
217
+ spec.github?.filters,
218
+ JSON.parse(await readFile2(value("event-file"), "utf8"))
219
+ ) : void 0;
220
+ deps.renderer.write(
221
+ `${JSON.stringify({ ...plan, missingReferences: missing, filterPreview }, null, 2)}
222
+ `
223
+ );
224
+ return 0;
225
+ }
226
+ const job = await store.add(spec);
227
+ deps.renderer.write(`Automation: ${job.id}
228
+ `);
229
+ return 0;
230
+ }
231
+ if (action === "list") {
232
+ deps.renderer.write(`${JSON.stringify(await store.snapshot(), null, 2)}
233
+ `);
234
+ return 0;
235
+ }
236
+ if (action === "templates") {
237
+ deps.renderer.write(`${JSON.stringify(VERSIONED_AUTOMATION_TEMPLATES, null, 2)}
238
+ `);
239
+ return 0;
240
+ }
241
+ if (action === "export" && args[1]) {
242
+ const job = (await store.snapshot()).jobs.find((item) => item.id === args[1]);
243
+ if (!job) throw new Error("Automation job not found");
244
+ deps.renderer.write(`${JSON.stringify(exportAutomationJob(job), null, 2)}
245
+ `);
246
+ return 0;
247
+ }
248
+ if (action === "import" && value("file")) {
249
+ const raw = await readFile2(path.resolve(deps.cwd, value("file")), "utf8");
250
+ if (Buffer.byteLength(raw) > 128 * 1024)
251
+ throw new Error("Automation document exceeds 128 KB");
252
+ const spec = { ...importAutomationJob(JSON.parse(raw)), projectRoot: deps.projectRoot };
253
+ const plan = createHeadlessLaunchPlan(spec);
254
+ if (flags["dry-run"]) deps.renderer.write(`${JSON.stringify(plan, null, 2)}
255
+ `);
256
+ else deps.renderer.write(`Imported disabled job: ${(await store.add(spec)).id}
257
+ `);
258
+ return 0;
259
+ }
260
+ if (action === "run" && args[1]) {
261
+ const run = await store.enqueue(args[1]);
262
+ deps.renderer.write(`Queued: ${run.id}
263
+ `);
264
+ return 0;
265
+ }
266
+ if ((action === "enable" || action === "disable") && args[1]) {
267
+ await store.setEnabled(args[1], action === "enable");
268
+ return 0;
269
+ }
270
+ if (action === "cancel" && args[1]) {
271
+ await store.cancel(args[1]);
272
+ return 0;
273
+ }
274
+ if (action === "prune") {
275
+ const days = Number(value("days") ?? 30);
276
+ if (!Number.isFinite(days) || days < 0) throw new Error("Prune days must be nonnegative");
277
+ const count = await store.prune(Date.now() - days * 864e5);
278
+ deps.renderer.write(
279
+ `Pruned terminal metadata: ${count}
280
+ Artifacts and latest PR checkpoints retained.
281
+ `
282
+ );
283
+ return 0;
284
+ }
285
+ if (action !== "serve") {
286
+ deps.renderer.write(USAGE);
287
+ return 2;
288
+ }
289
+ const port = Number(value("port") ?? 3501);
290
+ if (!Number.isSafeInteger(port) || port < 1 || port > 65535)
291
+ throw new Error("Automation port must be 1 to 65535");
292
+ await mkdir(directory, { recursive: true, mode: 448 });
293
+ const token = await apiToken(directory);
294
+ const service = new AutomationService(
295
+ store,
296
+ void 0,
297
+ Number(value("max-concurrent") ?? 1),
298
+ (refs) => resolveAutomationCredentialBundle(refs, deps.paths, deps.vault)
299
+ );
300
+ const server = createAutomationServer(
301
+ service,
302
+ token,
303
+ (profile) => deps.paths.profileConfig(profile)
304
+ );
305
+ await new Promise((resolve2, reject) => {
306
+ server.once("error", reject);
307
+ server.listen(port, "127.0.0.1", () => {
308
+ server.removeListener("error", reject);
309
+ resolve2();
310
+ });
311
+ });
312
+ deps.renderer.write(
313
+ `Automation: http://127.0.0.1:${port}
314
+ Token file: ${path.join(directory, "token.txt")}
315
+ `
316
+ );
317
+ const abort = new AbortController();
318
+ const cancel = () => abort.abort();
319
+ process.once("SIGINT", cancel);
320
+ process.once("SIGTERM", cancel);
321
+ try {
322
+ const endpointFile = path.join(directory, "endpoint.json");
323
+ await withFileLock(
324
+ endpointFile,
325
+ () => atomicWrite(endpointFile, JSON.stringify({ port, workerId: service.workerId }), {
326
+ mode: 384
327
+ })
328
+ );
329
+ while (!abort.signal.aborted) {
330
+ await service.tick();
331
+ await new Promise((resolve2) => {
332
+ const done = () => {
333
+ clearTimeout(timer);
334
+ abort.signal.removeEventListener("abort", done);
335
+ resolve2();
336
+ };
337
+ const timer = setTimeout(done, 1e3);
338
+ abort.signal.addEventListener("abort", done, { once: true });
339
+ if (abort.signal.aborted) done();
340
+ });
341
+ }
342
+ } finally {
343
+ process.removeListener("SIGINT", cancel);
344
+ process.removeListener("SIGTERM", cancel);
345
+ try {
346
+ await service.stop();
347
+ } finally {
348
+ await new Promise((resolve2) => server.close(() => resolve2()));
349
+ try {
350
+ const endpointFile = path.join(directory, "endpoint.json");
351
+ await withFileLock(endpointFile, async () => {
352
+ if (JSON.parse(await readFile2(endpointFile, "utf8")).workerId === service.workerId)
353
+ await unlink(endpointFile);
354
+ });
355
+ } catch {
356
+ }
357
+ }
358
+ }
359
+ return 0;
360
+ } catch (error) {
361
+ deps.renderer.write(`automation: ${toErrorMessage(error)}
362
+ `);
363
+ return 1;
364
+ }
365
+ };
366
+ export {
367
+ automationCmd
368
+ };
369
+ //# sourceMappingURL=automation-MCDFCIMV.js.map