@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.
- package/data/providers.json +8 -177
- package/dist/{acp-M4565YUW.js → acp-EDGUWU7N.js} +4 -7
- package/dist/{audit-46YK5DSC.js → audit-KCBTHUQI.js} +1 -1
- package/dist/{auth-TE55JF4Z.js → auth-SUHKFFO4.js} +136 -59
- package/dist/auth-menu/github-copilot-oauth.d.ts +1 -35
- package/dist/auth-menu/index-input.d.ts +3 -0
- package/dist/auth-menu/panel-flow-bridge.d.ts +29 -0
- package/dist/auth-menu/panel-service.d.ts +3 -34
- package/dist/auth-menu/provider-auth-login.d.ts +1 -1
- package/dist/auto-discover-providers-QIL34K3U.js +7 -0
- package/dist/automation-MCDFCIMV.js +369 -0
- package/dist/{bench-GTLPM22B.js → bench-MMZHFHDL.js} +152 -43
- package/dist/boot/brain-decision-log.d.ts +1 -1
- package/dist/boot/system-prompt-builder.d.ts +1 -1
- package/dist/boot/system-prompt-menu.d.ts +11 -2
- package/dist/boot/tui-coordinator-setup.d.ts +2 -0
- package/dist/boot/tui-startup-output.d.ts +11 -0
- package/dist/{boot-auto-thin-6Q56FY3F.js → boot-auto-thin-K2DHHYIW.js} +3 -3
- package/dist/{chronicle-UTFETHP6.js → chronicle-PBJS7BN6.js} +5 -3
- package/dist/{chunk-EPR42SWM.js → chunk-2EZ2DHOW.js} +60 -7
- package/dist/{chunk-7IQWQCBH.js → chunk-3LEFXLAW.js} +39 -39
- package/dist/{chunk-WSTVGC2C.js → chunk-4BX2YDTQ.js} +13 -6
- package/dist/{chunk-SFBFYIOW.js → chunk-4MDRHMX4.js} +3 -2
- package/dist/{chunk-RGFWGUAB.js → chunk-4UWCMBPG.js} +159 -3
- package/dist/{chunk-RDGGWMTI.js → chunk-65VNCAF6.js} +2 -22
- package/dist/{chunk-WQZW3RDP.js → chunk-7BNBD4PE.js} +2 -2
- package/dist/chunk-CFHWIFXL.js +123 -0
- package/dist/{chunk-RZ4ZDRYA.js → chunk-DIOODAYM.js} +2 -2
- package/dist/{chunk-L2RVEQWO.js → chunk-HHRYIDOW.js} +2 -2
- package/dist/chunk-LIZXJHZQ.js +61 -0
- package/dist/{chunk-YHYINL47.js → chunk-LKFKO4JW.js} +2 -2
- package/dist/{chunk-Q4INH2DG.js → chunk-MPQCHBDJ.js} +2 -1
- package/dist/{chunk-2ERX3RPN.js → chunk-P3GRLV47.js} +30 -17
- package/dist/{chunk-ZMX5HRLI.js → chunk-P3Q3KMFH.js} +6 -4
- package/dist/{chunk-KOEO4CJG.js → chunk-PGNERAZL.js} +4 -4
- package/dist/{chunk-ENRYW73H.js → chunk-RQ4UBKYH.js} +81 -10
- package/dist/{chunk-X756ZS72.js → chunk-SUDNPYWD.js} +2 -2
- package/dist/{chunk-LDCGZJTC.js → chunk-TWSQTQKR.js} +12 -4
- package/dist/chunk-TXAFIERF.js +37 -0
- package/dist/{chunk-UZ2TMHNH.js → chunk-UKP7H2U4.js} +10 -2
- package/dist/{chunk-5JYDKG5B.js → chunk-WHEYHDIE.js} +7 -2
- package/dist/{chunk-LBPLTTZ6.js → chunk-WTJDSTYD.js} +4 -4
- package/dist/{chunk-L5TD7UIG.js → chunk-WXJH4F6K.js} +7 -7
- package/dist/{chunk-IS2PPBQV.js → chunk-X4LW5CZM.js} +8 -4
- package/dist/{chunk-BBHWW6LR.js → chunk-YL4KCHZG.js} +18 -6
- package/dist/{chunk-LUMETJFO.js → chunk-ZYACRPLN.js} +2 -2
- package/dist/{cli-context-O2SHBDFH.js → cli-context-ITHGP2CE.js} +91 -84
- package/dist/cli-context.d.ts +3 -1
- package/dist/cli-infrastructure.d.ts +33 -0
- package/dist/{cli-main-BKG4ZVLN.js → cli-main-TQGLKB6D.js} +3783 -2753
- package/dist/config-doctor.d.ts +1 -1
- package/dist/{config-transfer-RUWLDCNK.js → config-transfer-UE37X4B5.js} +2 -2
- package/dist/{execution-THXWTHYT.js → execution-C2IECWF7.js} +5271 -5118
- package/dist/execution-chimera-handlers.d.ts +6 -0
- package/dist/execution-session-callbacks.d.ts +36 -0
- package/dist/execution-webui.d.ts +3 -0
- package/dist/execution.d.ts +1 -1
- package/dist/fleet/host-acp-runner.d.ts +15 -0
- package/dist/fleet/host-budget.d.ts +13 -0
- package/dist/fleet/host-context.d.ts +5 -0
- package/dist/fleet/host-explore-companion.d.ts +7 -16
- package/dist/fleet/host.d.ts +2 -0
- package/dist/fleet/status-broadcast.d.ts +1 -1
- package/dist/{hq-44F4OQH2.js → hq-KBFDKTDD.js} +13 -7
- package/dist/hq-publisher.d.ts +4 -1
- package/dist/hq-server/sage-sync-send.d.ts +5 -0
- package/dist/hq-server/server-lifecycle.d.ts +1 -1
- package/dist/hq-server/snapshot.d.ts +1 -1
- package/dist/{hq-server-UIB24LU6.js → hq-server-AVNKZ2PV.js} +2 -2
- package/dist/{hq-service-BMMJAHRT.js → hq-service-FGB7BY2D.js} +3 -3
- package/dist/hq-ttl-N4DAIAVX.js +7 -0
- package/dist/index.js +126 -10
- package/dist/{mailbox-serve-GNGXEQ2R.js → mailbox-serve-S536N66F.js} +4 -3
- package/dist/{modeldiag-W7XAE625.js → modeldiag-PQILD7TN.js} +26 -8
- package/dist/{per-subcommand-help-QDAQASPZ.js → per-subcommand-help-VKPV3OEY.js} +2 -2
- package/dist/{plugin-usage-CIGOOTX3.js → plugin-usage-3TZGROK7.js} +3 -3
- package/dist/{plugins-HQTXUD6A.js → plugins-ITH4SLO6.js} +4 -4
- package/dist/pre-launch/project-check.d.ts +2 -8
- package/dist/pre-launch.d.ts +1 -1
- package/dist/{project-manifest-7NSFVBM3.js → project-manifest-FTFDCCTZ.js} +2 -2
- package/dist/{project-picker-SVBXMPKX.js → project-picker-NZYJKQQC.js} +3 -3
- package/dist/provider-cloud-config.d.ts +4 -0
- package/dist/provider-config-utils.d.ts +1 -1
- package/dist/{providers-models-5Z2PJTEW.js → providers-models-W57I3QOA.js} +3 -3
- package/dist/{remote-GB5VXCCT.js → remote-C74BCNND.js} +5 -5
- package/dist/{rewind-JA3ZLCNM.js → rewind-7AULJS7F.js} +7 -5
- package/dist/sage-FFKUOCQL.js +475 -0
- package/dist/sage-connect.d.ts +62 -0
- package/dist/sage-hq-sync.d.ts +8 -0
- package/dist/sage-sync-ZMTHB477.js +69 -0
- package/dist/sandbox-TYGD6FI2.js +131 -0
- package/dist/services/sdd/project-context.d.ts +1 -1
- package/dist/{sessions-config-XHLXJ5C3.js → sessions-config-VE26SB56.js} +19 -8
- package/dist/short-circuit-flags-6LB3EGF7.js +10 -0
- package/dist/slash-commands/openai-quota.d.ts +12 -6
- package/dist/slash-commands/sandbox.d.ts +5 -0
- package/dist/slash-commands/scout-stats.d.ts +46 -0
- package/dist/slash-commands/sdd-inspect-commands.d.ts +9 -0
- package/dist/slash-commands/sdd-session-commands.d.ts +19 -0
- package/dist/slash-commands/sdd.d.ts +1 -1
- package/dist/slash-commands/zai-plan.d.ts +26 -0
- package/dist/{standalone-auto-update-TOTRGCSA.js → standalone-auto-update-VQC4GYCM.js} +3 -3
- package/dist/{standalone-update-LZO3MNLH.js → standalone-update-HZHDZZOR.js} +2 -2
- package/dist/subcommands/handlers/automation-credentials.d.ts +8 -0
- package/dist/subcommands/handlers/automation.d.ts +3 -0
- package/dist/subcommands/handlers/bench-experiments.d.ts +5 -0
- package/dist/subcommands/handlers/helpers.d.ts +1 -1
- package/dist/subcommands/handlers/sage-sync.d.ts +4 -0
- package/dist/subcommands/handlers/sage.d.ts +5 -0
- package/dist/subcommands/handlers/sandbox.d.ts +5 -0
- package/dist/{subcommands-SRRB3RAS.js → subcommands-QO7VYTSS.js} +2 -2
- package/dist/{tools-skills-GH24UCBY.js → tools-skills-33AI6K6S.js} +2 -2
- package/dist/tui-provider-quota-menu.d.ts +5 -0
- package/dist/{typesafe-WA5O3KVS.js → typesafe-NHC322W5.js} +27 -8
- package/dist/{typesafe-topic-replay-ZILIU3WS.js → typesafe-topic-replay-DB2AYL7V.js} +2 -2
- package/dist/{typesafe-triage-replay-FGAW7QM7.js → typesafe-triage-replay-27NDAZ3V.js} +2 -2
- package/dist/{update-TDZX4IL2.js → update-J5WHAFPE.js} +3 -3
- package/dist/{update-check-J5ZVKS73.js → update-check-VLVT4IV2.js} +2 -2
- package/dist/{version-help-XJJQ7YSH.js → version-help-ATOKGMTK.js} +2 -2
- package/dist/webui-server/prefs-seeding.d.ts +1 -0
- package/dist/webui-server/route-contexts.d.ts +1 -0
- package/dist/{webui-server-GCXIO3JY.js → webui-server-GQOVLQS3.js} +79 -40
- package/dist/webui-session-retirement.d.ts +10 -0
- package/dist/wiring/command-host-state.d.ts +2 -0
- package/dist/wiring/director-announcement.d.ts +3 -1
- package/dist/wiring/session-establishment.d.ts +10 -8
- package/dist/wiring/session-runtime.d.ts +1 -1
- package/dist/wiring/session.d.ts +3 -0
- package/package.json +30 -29
- package/dist/auto-discover-providers-DXR25GTG.js +0 -7
- package/dist/chunk-3ZTLPNEQ.js +0 -58
- package/dist/chunk-D5GB5MHA.js +0 -87
- package/dist/chunk-SIGUWXAA.js +0 -43
- package/dist/hq-ttl-VJR65OBJ.js +0 -7
- 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
|