@mycompbox/compbox-cli 1.13.0
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/.tsbuildinfo +1 -0
- package/commands/configure.d.ts +24 -0
- package/commands/configure.d.ts.map +1 -0
- package/commands/configure.js +170 -0
- package/commands/configure.js.map +1 -0
- package/commands/doctor.d.ts +136 -0
- package/commands/doctor.d.ts.map +1 -0
- package/commands/doctor.js +606 -0
- package/commands/doctor.js.map +1 -0
- package/commands/install.d.ts +3 -0
- package/commands/install.d.ts.map +1 -0
- package/commands/install.js +88 -0
- package/commands/install.js.map +1 -0
- package/commands/list.d.ts +3 -0
- package/commands/list.d.ts.map +1 -0
- package/commands/list.js +141 -0
- package/commands/list.js.map +1 -0
- package/commands/login.d.ts +15 -0
- package/commands/login.d.ts.map +1 -0
- package/commands/login.js +102 -0
- package/commands/login.js.map +1 -0
- package/commands/publish.d.ts +3 -0
- package/commands/publish.d.ts.map +1 -0
- package/commands/publish.js +102 -0
- package/commands/publish.js.map +1 -0
- package/commands/run.d.ts +76 -0
- package/commands/run.d.ts.map +1 -0
- package/commands/run.js +417 -0
- package/commands/run.js.map +1 -0
- package/commands/service.d.ts +29 -0
- package/commands/service.d.ts.map +1 -0
- package/commands/service.js +142 -0
- package/commands/service.js.map +1 -0
- package/commands/update.d.ts +3 -0
- package/commands/update.d.ts.map +1 -0
- package/commands/update.js +141 -0
- package/commands/update.js.map +1 -0
- package/index.d.ts +3 -0
- package/index.d.ts.map +1 -0
- package/index.js +8508 -0
- package/index.js.map +1 -0
- package/package.json +30 -0
- package/utils/agent-invoker.d.ts +393 -0
- package/utils/agent-invoker.d.ts.map +1 -0
- package/utils/agent-invoker.js +1632 -0
- package/utils/agent-invoker.js.map +1 -0
- package/utils/api-client.d.ts +54 -0
- package/utils/api-client.d.ts.map +1 -0
- package/utils/api-client.js +214 -0
- package/utils/api-client.js.map +1 -0
- package/utils/auth-client.d.ts +61 -0
- package/utils/auth-client.d.ts.map +1 -0
- package/utils/auth-client.js +135 -0
- package/utils/auth-client.js.map +1 -0
- package/utils/claude-runtime.d.ts +116 -0
- package/utils/claude-runtime.d.ts.map +1 -0
- package/utils/claude-runtime.js +291 -0
- package/utils/claude-runtime.js.map +1 -0
- package/utils/config-store.d.ts +128 -0
- package/utils/config-store.d.ts.map +1 -0
- package/utils/config-store.js +137 -0
- package/utils/config-store.js.map +1 -0
- package/utils/file-manager.d.ts +32 -0
- package/utils/file-manager.d.ts.map +1 -0
- package/utils/file-manager.js +208 -0
- package/utils/file-manager.js.map +1 -0
- package/utils/keychain.d.ts +46 -0
- package/utils/keychain.d.ts.map +1 -0
- package/utils/keychain.js +167 -0
- package/utils/keychain.js.map +1 -0
- package/utils/loopback.d.ts +45 -0
- package/utils/loopback.d.ts.map +1 -0
- package/utils/loopback.js +95 -0
- package/utils/loopback.js.map +1 -0
- package/utils/mailbox-client.d.ts +98 -0
- package/utils/mailbox-client.d.ts.map +1 -0
- package/utils/mailbox-client.js +178 -0
- package/utils/mailbox-client.js.map +1 -0
- package/utils/mcp-config-writer.d.ts +133 -0
- package/utils/mcp-config-writer.d.ts.map +1 -0
- package/utils/mcp-config-writer.js +184 -0
- package/utils/mcp-config-writer.js.map +1 -0
- package/utils/pack-bundler.d.ts +30 -0
- package/utils/pack-bundler.d.ts.map +1 -0
- package/utils/pack-bundler.js +141 -0
- package/utils/pack-bundler.js.map +1 -0
- package/utils/prompt.d.ts +31 -0
- package/utils/prompt.d.ts.map +1 -0
- package/utils/prompt.js +86 -0
- package/utils/prompt.js.map +1 -0
- package/utils/table-formatter.d.ts +42 -0
- package/utils/table-formatter.d.ts.map +1 -0
- package/utils/table-formatter.js +125 -0
- package/utils/table-formatter.js.map +1 -0
- package/utils/version-resolver.d.ts +31 -0
- package/utils/version-resolver.d.ts.map +1 -0
- package/utils/version-resolver.js +75 -0
- package/utils/version-resolver.js.map +1 -0
package/commands/run.js
ADDED
|
@@ -0,0 +1,417 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `compbox run` — the Agent Runner daemon (ENG-E23 / ENG-122).
|
|
3
|
+
*
|
|
4
|
+
* Long-polls central for the next job assigned to this machine, claims it,
|
|
5
|
+
* and dispatches by type. This command IS the process a service supervisor
|
|
6
|
+
* (launchd / systemd via `brew services`, or a Windows Service) keeps alive —
|
|
7
|
+
* see the ENG-122 distribution decision (Homebrew tap + PowerShell installer,
|
|
8
|
+
* no hand-rolled installer).
|
|
9
|
+
*
|
|
10
|
+
* Scope note (ENG-122-1/2/4, honestly reflecting what's implemented): the
|
|
11
|
+
* poll/claim/dispatch/heartbeat/reconnect mechanics are complete. Real agent
|
|
12
|
+
* invocation is implemented for `job.type: epic_request` under BOTH
|
|
13
|
+
* `agentMode: api-key` (Anthropic Messages API directly) and
|
|
14
|
+
* `agentMode: claude-subscription` (shells out to the local `claude` CLI,
|
|
15
|
+
* using whichever account is logged in — personal Pro/Max or a Team seat) —
|
|
16
|
+
* see agent-invoker.ts. `start_execution` jobs still fall back to the honest
|
|
17
|
+
* "not implemented" completion below rather than faking anything — nobody
|
|
18
|
+
* is ever left staring at a silent "awaiting_input" forever; the browser's
|
|
19
|
+
* ENG-123-2 failure banner surfaces the reason.
|
|
20
|
+
*/
|
|
21
|
+
import { Command } from 'commander';
|
|
22
|
+
import chalk from 'chalk';
|
|
23
|
+
import { readConfig, applyPxpipeEnv, resolveProjectDir, updateConfig, } from '../utils/config-store.js';
|
|
24
|
+
import { START_EXECUTION_KINDS } from '@mycompbox/shared';
|
|
25
|
+
import { retrieveSecret, MACHINE_TOKEN_ACCOUNT, AI_KEY_ACCOUNT, } from '../utils/keychain.js';
|
|
26
|
+
import { MailboxClient, MailboxClientError, } from '../utils/mailbox-client.js';
|
|
27
|
+
import { invokeEpicRequestViaApi, invokeEpicRequestViaSubscription, invokeStartExecutionPlan, invokeStartExecutionOrchestrate, } from '../utils/agent-invoker.js';
|
|
28
|
+
import { probeAgentRuntime, } from '../utils/claude-runtime.js';
|
|
29
|
+
// Long-poll wait requested per next-job call (the server clamps to its own max).
|
|
30
|
+
// Kept comfortably below the HTTP client timeout (see mailbox-client.ts): on
|
|
31
|
+
// Cloud Run the round-trip for a held connection intermittently added enough
|
|
32
|
+
// overhead that a 25s poll exceeded a 35s client timeout, so every idle poll
|
|
33
|
+
// errored and backed off (up to 30s) — leaving the daemon asleep when a job
|
|
34
|
+
// arrived, which read as a slow/stuck start. A shorter poll returns well within
|
|
35
|
+
// the (now wider) client timeout with margin to spare.
|
|
36
|
+
const POLL_WAIT_MS = 15_000;
|
|
37
|
+
/** Backoff schedule on transient errors (network/5xx): 1s, 2s, 4s, ... capped. */
|
|
38
|
+
const BACKOFF_BASE_MS = 1_000;
|
|
39
|
+
const BACKOFF_MAX_MS = 30_000;
|
|
40
|
+
/**
|
|
41
|
+
* Cadence for the explicit agent-runtime heartbeat (ENG-136). The runtime status
|
|
42
|
+
* rides ONLY on this POST (next-job is a GET and cannot carry it), so it is sent
|
|
43
|
+
* on its own clock — independent of the ~25s job-poll — every this-many ms.
|
|
44
|
+
*/
|
|
45
|
+
const HEARTBEAT_INTERVAL_MS = 60_000;
|
|
46
|
+
/**
|
|
47
|
+
* Whether the agent-runtime heartbeat is due. Driven SOLELY by when the runtime
|
|
48
|
+
* status was last *sent* (`lastRuntimeSentMs`), never by next-job activity — this
|
|
49
|
+
* is the invariant whose violation (gating on a timer that next-job reset every
|
|
50
|
+
* ~25s) previously starved the 60s heartbeat so the status was never transmitted
|
|
51
|
+
* and machines showed "agent runtime not found" despite being able to run jobs.
|
|
52
|
+
* `lastRuntimeSentMs === 0` (nothing sent yet) is always due.
|
|
53
|
+
*/
|
|
54
|
+
export function runtimeHeartbeatDue(nowMs, lastRuntimeSentMs, intervalMs = HEARTBEAT_INTERVAL_MS) {
|
|
55
|
+
return nowMs - lastRuntimeSentMs >= intervalMs;
|
|
56
|
+
}
|
|
57
|
+
function sleep(ms) {
|
|
58
|
+
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
59
|
+
}
|
|
60
|
+
function backoffDelay(consecutiveErrors) {
|
|
61
|
+
return Math.min(BACKOFF_MAX_MS, BACKOFF_BASE_MS * 2 ** (consecutiveErrors - 1));
|
|
62
|
+
}
|
|
63
|
+
/**
|
|
64
|
+
* Runs the agent-runtime probe (ENG-136), swallowing ANY error: a probe must
|
|
65
|
+
* never stop the daemon loop or block job dispatch. `probeAgentRuntime` is
|
|
66
|
+
* contractually non-rejecting, but we defend against it anyway — on a thrown
|
|
67
|
+
* error we log a gray warning and return `previous` (the last known-good probe,
|
|
68
|
+
* or `undefined` at startup) so the daemon keeps reporting the most recent
|
|
69
|
+
* status rather than dropping it.
|
|
70
|
+
*
|
|
71
|
+
* Extracted (and exported) as a pure-ish, dependency-injectable unit so the
|
|
72
|
+
* error-swallowing behavior is testable without spawning a real `claude`.
|
|
73
|
+
*/
|
|
74
|
+
export async function safeProbeAgentRuntime(probe, previous, log = (m) => { console.warn(chalk.gray(m)); }) {
|
|
75
|
+
try {
|
|
76
|
+
return await probe();
|
|
77
|
+
}
|
|
78
|
+
catch (error) {
|
|
79
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
80
|
+
log(` Agent runtime probe failed (continuing to poll): ${message}`);
|
|
81
|
+
return previous;
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
/**
|
|
85
|
+
* One idle-heartbeat cycle (ENG-136): refresh the cached runtime probe (errors
|
|
86
|
+
* swallowed by {@link safeProbeAgentRuntime}) and send an explicit heartbeat
|
|
87
|
+
* carrying the latest known runtime status. The heartbeat POST body is the SOLE
|
|
88
|
+
* ingestion channel for runtime status — next-job is a GET and cannot carry it
|
|
89
|
+
* (architect correction H3).
|
|
90
|
+
*
|
|
91
|
+
* Heartbeat failure is best-effort (swallowed) — the next successful next-job
|
|
92
|
+
* call heartbeats server-side anyway. Returns `{ runtime, sent }` so the caller
|
|
93
|
+
* updates its cache regardless, but only advances its heartbeat clock when the
|
|
94
|
+
* ping actually landed.
|
|
95
|
+
*/
|
|
96
|
+
export async function runHeartbeatCycle(deps) {
|
|
97
|
+
const runtime = await safeProbeAgentRuntime(deps.probe, deps.previous, deps.log);
|
|
98
|
+
try {
|
|
99
|
+
await deps.client.heartbeat(deps.machineToken, runtime);
|
|
100
|
+
return { runtime, sent: true };
|
|
101
|
+
}
|
|
102
|
+
catch {
|
|
103
|
+
// Best-effort — the next successful next-job call will heartbeat anyway.
|
|
104
|
+
return { runtime, sent: false };
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
/**
|
|
108
|
+
* Parse a claimed job's `payload` into a {@link StartExecutionPayload}, or null
|
|
109
|
+
* if malformed. The mailbox delivers `payload` as `unknown` — already an object
|
|
110
|
+
* in the common case (the API deserializes the stored JSON string, matching how
|
|
111
|
+
* `parseSeedPayload` treats it), but tolerant of a raw JSON string too.
|
|
112
|
+
*/
|
|
113
|
+
export function parseStartExecutionPayload(payload) {
|
|
114
|
+
let parsed = payload;
|
|
115
|
+
if (typeof payload === 'string') {
|
|
116
|
+
try {
|
|
117
|
+
parsed = JSON.parse(payload);
|
|
118
|
+
}
|
|
119
|
+
catch {
|
|
120
|
+
return null;
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
if (typeof parsed !== 'object' || parsed === null)
|
|
124
|
+
return null;
|
|
125
|
+
const p = parsed;
|
|
126
|
+
if (!START_EXECUTION_KINDS.includes(p.kind))
|
|
127
|
+
return null;
|
|
128
|
+
if (p.kind === 'plan' && typeof p.epicRequestId === 'string' && p.epicRequestId.length > 0) {
|
|
129
|
+
return { kind: 'plan', epicRequestId: p.epicRequestId };
|
|
130
|
+
}
|
|
131
|
+
if (p.kind === 'orchestrate' && typeof p.epicId === 'string' && p.epicId.length > 0) {
|
|
132
|
+
return { kind: 'orchestrate', epicId: p.epicId };
|
|
133
|
+
}
|
|
134
|
+
return null;
|
|
135
|
+
}
|
|
136
|
+
/**
|
|
137
|
+
* Dispatch a `start_execution` job (ENG-164 / ENG-165). Routes on the payload
|
|
138
|
+
* `kind`:
|
|
139
|
+
* - `plan` → run the local @planner headless against the Epic Request.
|
|
140
|
+
* - `orchestrate` → run the local @orchestrator headless against the epic
|
|
141
|
+
* (ENG-165), halt-and-reporting a HARD-gate needs-review
|
|
142
|
+
* outcome rather than hanging.
|
|
143
|
+
* A malformed payload or a job missing prerequisites also fails cleanly.
|
|
144
|
+
*/
|
|
145
|
+
async function handleStartExecution(ctx, job) {
|
|
146
|
+
const payload = parseStartExecutionPayload(job.payload);
|
|
147
|
+
if (!payload) {
|
|
148
|
+
await completeWithFailure(ctx, job, 'The start_execution job payload was missing or malformed (expected {kind:"plan",epicRequestId} or {kind:"orchestrate",epicId}).');
|
|
149
|
+
return;
|
|
150
|
+
}
|
|
151
|
+
if (payload.kind === 'plan') {
|
|
152
|
+
const userApiToken = ctx.userApiToken;
|
|
153
|
+
if (!userApiToken) {
|
|
154
|
+
await completeWithFailure(ctx, job, 'This device is not paired with a user API token, so the planner cannot run or record its epic. Run `compbox configure` to re-pair.');
|
|
155
|
+
return;
|
|
156
|
+
}
|
|
157
|
+
console.log(chalk.gray(' Running the planner headless (start_execution / plan)...'));
|
|
158
|
+
await runInvokerAndComplete(ctx, job, () => invokeStartExecutionPlan({
|
|
159
|
+
mailboxClient: ctx.client,
|
|
160
|
+
machineToken: ctx.machineToken,
|
|
161
|
+
jobId: job.id,
|
|
162
|
+
apiBaseUrl: ctx.apiBaseUrl,
|
|
163
|
+
userApiToken,
|
|
164
|
+
epicRequestId: payload.epicRequestId,
|
|
165
|
+
projectDir: ctx.projectDir,
|
|
166
|
+
}, {
|
|
167
|
+
// H4: keep this machine online during the long planner run — the poll
|
|
168
|
+
// loop (its own heartbeat) is blocked for the whole job.
|
|
169
|
+
heartbeat: () => ctx.client.heartbeat(ctx.machineToken).catch(() => undefined),
|
|
170
|
+
}));
|
|
171
|
+
return;
|
|
172
|
+
}
|
|
173
|
+
// kind === 'orchestrate' (ENG-165): run the local @orchestrator headless
|
|
174
|
+
// against the existing epic, reusing ENG-164's repo resolution + spawn profile.
|
|
175
|
+
const userApiToken = ctx.userApiToken;
|
|
176
|
+
if (!userApiToken) {
|
|
177
|
+
await completeWithFailure(ctx, job, 'This device is not paired with a user API token, so the orchestrator cannot run. Run `compbox configure` to re-pair.');
|
|
178
|
+
return;
|
|
179
|
+
}
|
|
180
|
+
console.log(chalk.gray(' Running the orchestrator headless (start_execution / orchestrate)...'));
|
|
181
|
+
await runInvokerAndComplete(ctx, job, () => invokeStartExecutionOrchestrate({
|
|
182
|
+
mailboxClient: ctx.client,
|
|
183
|
+
machineToken: ctx.machineToken,
|
|
184
|
+
jobId: job.id,
|
|
185
|
+
epicId: payload.epicId,
|
|
186
|
+
projectDir: ctx.projectDir,
|
|
187
|
+
}, {
|
|
188
|
+
// H4: keep this machine online during the long orchestrator run — the
|
|
189
|
+
// poll loop (its own heartbeat) is blocked for the whole job.
|
|
190
|
+
heartbeat: () => ctx.client.heartbeat(ctx.machineToken).catch(() => undefined),
|
|
191
|
+
}));
|
|
192
|
+
}
|
|
193
|
+
/** Runs an agent invoker and closes out the job with its result, logging either way. */
|
|
194
|
+
async function runInvokerAndComplete(ctx, job, invoke) {
|
|
195
|
+
try {
|
|
196
|
+
const outcome = await invoke();
|
|
197
|
+
await ctx.client.completeJob(ctx.machineToken, job.id, outcome.status, outcome.result);
|
|
198
|
+
console.log(outcome.status === 'done'
|
|
199
|
+
? chalk.green(` Job ${job.id} completed: ${JSON.stringify(outcome.result)}`)
|
|
200
|
+
: chalk.yellow(` Job ${job.id} failed: ${JSON.stringify(outcome.result)}`));
|
|
201
|
+
}
|
|
202
|
+
catch (error) {
|
|
203
|
+
const message = error instanceof MailboxClientError ? error.message : 'complete-job failed';
|
|
204
|
+
console.error(chalk.red(` Failed to close out job ${job.id}: ${message}`));
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
/** Completes a job as failed with an explanatory `error` result, logging either way. */
|
|
208
|
+
async function completeWithFailure(ctx, job, error) {
|
|
209
|
+
try {
|
|
210
|
+
await ctx.client.completeJob(ctx.machineToken, job.id, 'failed', { error });
|
|
211
|
+
console.log(chalk.gray(` Marked ${job.id} as failed with an explanatory result.`));
|
|
212
|
+
}
|
|
213
|
+
catch (closeError) {
|
|
214
|
+
const message = closeError instanceof MailboxClientError ? closeError.message : 'complete-job failed';
|
|
215
|
+
console.error(chalk.red(` Failed to close out job ${job.id}: ${message}`));
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
/**
|
|
219
|
+
* Handles a claimed job: dispatches to the real agent-invoker when the
|
|
220
|
+
* job/mode combination is implemented, otherwise completes it as failed with
|
|
221
|
+
* a clear, honest reason rather than leaving it claimed/running forever or
|
|
222
|
+
* faking an interview.
|
|
223
|
+
*/
|
|
224
|
+
async function handleJob(ctx, job) {
|
|
225
|
+
console.log(chalk.cyan(`\n→ Claimed job ${job.id} (type: ${job.type}, status: ${job.status})`));
|
|
226
|
+
const userApiToken = ctx.userApiToken;
|
|
227
|
+
const isEpicRequest = job.type === 'epic_request' && !!userApiToken;
|
|
228
|
+
if (isEpicRequest && userApiToken) {
|
|
229
|
+
if (ctx.aiMode === 'api-key') {
|
|
230
|
+
const { value: anthropicApiKey } = await retrieveSecret(AI_KEY_ACCOUNT);
|
|
231
|
+
if (anthropicApiKey) {
|
|
232
|
+
console.log(chalk.gray(' Running the request-formulator interview (api mode)...'));
|
|
233
|
+
await runInvokerAndComplete(ctx, job, () => invokeEpicRequestViaApi(job, {
|
|
234
|
+
mailboxClient: ctx.client,
|
|
235
|
+
machineToken: ctx.machineToken,
|
|
236
|
+
apiBaseUrl: ctx.apiBaseUrl,
|
|
237
|
+
userApiToken,
|
|
238
|
+
anthropicApiKey,
|
|
239
|
+
}));
|
|
240
|
+
return;
|
|
241
|
+
}
|
|
242
|
+
}
|
|
243
|
+
else if (ctx.aiMode === 'claude-subscription') {
|
|
244
|
+
console.log(chalk.gray(' Running the request-formulator interview (claude-subscription mode, local `claude` CLI)...'));
|
|
245
|
+
await runInvokerAndComplete(ctx, job, () => invokeEpicRequestViaSubscription(job, {
|
|
246
|
+
mailboxClient: ctx.client,
|
|
247
|
+
machineToken: ctx.machineToken,
|
|
248
|
+
apiBaseUrl: ctx.apiBaseUrl,
|
|
249
|
+
userApiToken,
|
|
250
|
+
}));
|
|
251
|
+
return;
|
|
252
|
+
}
|
|
253
|
+
}
|
|
254
|
+
// start_execution (ENG-164): route on the payload `kind` (plan/orchestrate).
|
|
255
|
+
// This replaces the old catch-all not-implemented fallback for this job type.
|
|
256
|
+
if (job.type === 'start_execution') {
|
|
257
|
+
await handleStartExecution(ctx, job);
|
|
258
|
+
return;
|
|
259
|
+
}
|
|
260
|
+
console.log(chalk.yellow(` ${job.type}/${ctx.aiMode ?? 'unset'} is not implemented yet in this daemon build (ENG-122-2).`));
|
|
261
|
+
await completeWithFailure(ctx, job, `Agent invocation for job type "${job.type}" with agentMode "${ctx.aiMode ?? 'unset'}" ` +
|
|
262
|
+
"isn't implemented yet in this daemon build. This job was claimed but could not be run.");
|
|
263
|
+
}
|
|
264
|
+
export const runCommand = new Command('run')
|
|
265
|
+
.description('Start the Agent Runner daemon — long-polls central for jobs assigned to this machine')
|
|
266
|
+
.option('--once', 'Run a single poll cycle and exit (testing)')
|
|
267
|
+
.option('--wait-ms <ms>', `Long-poll wait per request (default ${String(POLL_WAIT_MS)})`)
|
|
268
|
+
.option('--project-dir <path>', 'CompBox project repo the planner runs in for start_execution jobs (persisted; also settable via COMPBOX_PROJECT_DIR)')
|
|
269
|
+
.action(async (options) => {
|
|
270
|
+
// ENG-164 (H2): a `--project-dir` flag both configures THIS run and is
|
|
271
|
+
// persisted so future runs (and the background service) inherit it — a real
|
|
272
|
+
// SET path, not merely a read path. Resolution order is applied below.
|
|
273
|
+
if (options.projectDir && options.projectDir.trim().length > 0) {
|
|
274
|
+
updateConfig({ projectDir: options.projectDir.trim() });
|
|
275
|
+
}
|
|
276
|
+
const config = readConfig();
|
|
277
|
+
// pxpipe (ENG-E26): promote the persisted enable flag so the daemon routes
|
|
278
|
+
// through pxpipe even as a background service (no shell env inherited).
|
|
279
|
+
applyPxpipeEnv(config);
|
|
280
|
+
if (!config.apiBaseUrl) {
|
|
281
|
+
console.error(chalk.red('\nNot configured. Run `compbox login` first.'));
|
|
282
|
+
process.exit(1);
|
|
283
|
+
return;
|
|
284
|
+
}
|
|
285
|
+
if (!config.machineId) {
|
|
286
|
+
console.error(chalk.red('\nThis device is not paired. Run `compbox configure` first — it pairs the device and mints a machine token.'));
|
|
287
|
+
process.exit(1);
|
|
288
|
+
return;
|
|
289
|
+
}
|
|
290
|
+
if (config.aiMode === 'claude-subscription' && config.agentModeAcknowledged !== true) {
|
|
291
|
+
console.error(chalk.red('\nClaude Code CLI subscription mode requires an entitlement acknowledgment. Run `compbox configure` to confirm it.'));
|
|
292
|
+
process.exit(1);
|
|
293
|
+
return;
|
|
294
|
+
}
|
|
295
|
+
const { value: machineToken } = await retrieveSecret(MACHINE_TOKEN_ACCOUNT);
|
|
296
|
+
if (!machineToken) {
|
|
297
|
+
console.error(chalk.red('\nMachine token not found on this device. Run `compbox configure` again to re-pair.'));
|
|
298
|
+
process.exit(1);
|
|
299
|
+
return;
|
|
300
|
+
}
|
|
301
|
+
const waitMs = options.waitMs ? Number.parseInt(options.waitMs, 10) : POLL_WAIT_MS;
|
|
302
|
+
const client = new MailboxClient(config.apiBaseUrl);
|
|
303
|
+
// ENG-164 (H2): resolve the project repo once (flag → env → persisted config).
|
|
304
|
+
const projectDir = resolveProjectDir({ flag: options.projectDir, config });
|
|
305
|
+
const jobContext = {
|
|
306
|
+
client,
|
|
307
|
+
machineToken,
|
|
308
|
+
apiBaseUrl: config.apiBaseUrl,
|
|
309
|
+
aiMode: config.aiMode,
|
|
310
|
+
userApiToken: config.apiToken,
|
|
311
|
+
projectDir,
|
|
312
|
+
};
|
|
313
|
+
console.log(chalk.bold('\nCompBox Agent Runner\n'));
|
|
314
|
+
console.log(chalk.gray(` Central: ${config.apiBaseUrl}`));
|
|
315
|
+
console.log(chalk.gray(` Machine: ${config.machineName ?? config.machineId}`));
|
|
316
|
+
console.log(chalk.gray(` AI mode: ${config.aiMode ?? 'unset'}`));
|
|
317
|
+
console.log(chalk.gray(` Project: ${projectDir ?? '(unset — start_execution/plan jobs will fail until set)'}`));
|
|
318
|
+
console.log(chalk.gray(` Polling for jobs (long-poll, ~${String(Math.round(waitMs / 1000))}s per request)...\n`));
|
|
319
|
+
// Agent-runtime probe (ENG-136): probe the local `claude` runtime ONCE at
|
|
320
|
+
// startup and cache it; it is refreshed on the heartbeat cadence below and
|
|
321
|
+
// rides along in each explicit heartbeat POST so central can surface
|
|
322
|
+
// whether this machine can actually run subscription-mode jobs. Reuses
|
|
323
|
+
// ENG-135's bounded, always-kill, never-rejecting probe.
|
|
324
|
+
const runtimeProbe = () => probeAgentRuntime();
|
|
325
|
+
let agentRuntime = await safeProbeAgentRuntime(runtimeProbe, undefined);
|
|
326
|
+
if (agentRuntime) {
|
|
327
|
+
console.log(chalk.gray(` Agent runtime: ${agentRuntime.status} (${agentRuntime.detail})\n`));
|
|
328
|
+
}
|
|
329
|
+
// The agent-runtime status rides ONLY on the explicit heartbeat POST body
|
|
330
|
+
// (next-job is a GET and cannot carry it — ENG-136 H3). Send it ONCE at
|
|
331
|
+
// startup so the machine's readiness appears within seconds, and track its
|
|
332
|
+
// OWN cadence clock (`lastRuntimeSentAt`) below — one that next-job must NOT
|
|
333
|
+
// reset. Otherwise the ~25s job-poll perpetually resets the timer and the
|
|
334
|
+
// 60s runtime heartbeat never fires: the probed status is computed but never
|
|
335
|
+
// transmitted, central stays `unknown`, and the web shows "agent runtime not
|
|
336
|
+
// found" for a machine that can actually run jobs. (Regression fix.)
|
|
337
|
+
let lastRuntimeSentAt = 0;
|
|
338
|
+
if (agentRuntime) {
|
|
339
|
+
try {
|
|
340
|
+
await client.heartbeat(machineToken, agentRuntime);
|
|
341
|
+
lastRuntimeSentAt = Date.now();
|
|
342
|
+
}
|
|
343
|
+
catch {
|
|
344
|
+
// Best-effort — the loop below re-sends on its own cadence.
|
|
345
|
+
}
|
|
346
|
+
}
|
|
347
|
+
let running = true;
|
|
348
|
+
// Registering a SIGINT/SIGTERM handler disables Node's default
|
|
349
|
+
// terminate-on-signal behavior — the process becomes OUR responsibility
|
|
350
|
+
// to exit. Without a real process.exit() here, Ctrl+C appears to do
|
|
351
|
+
// nothing: `running` flips false, but the loop is typically blocked
|
|
352
|
+
// inside the in-flight long-poll (up to ~waitMs) and even once that
|
|
353
|
+
// resolves, lingering handles (axios's keep-alive connection pool) can
|
|
354
|
+
// keep the event loop alive indefinitely. Verified live: without this,
|
|
355
|
+
// Ctrl+C did not stop the daemon. A hard exit is the right tradeoff for
|
|
356
|
+
// a manual test/dev daemon — it may abandon an in-progress job's final
|
|
357
|
+
// completion call, which is acceptable here but would need a graceful
|
|
358
|
+
// drain (abort the in-flight request, let the current job finish) for
|
|
359
|
+
// a production-hardened version.
|
|
360
|
+
const shutdown = (signal) => {
|
|
361
|
+
if (!running)
|
|
362
|
+
return;
|
|
363
|
+
running = false;
|
|
364
|
+
console.log(chalk.gray(`\nReceived ${signal}, shutting down...`));
|
|
365
|
+
process.exit(0);
|
|
366
|
+
};
|
|
367
|
+
process.on('SIGINT', () => { shutdown('SIGINT'); });
|
|
368
|
+
process.on('SIGTERM', () => { shutdown('SIGTERM'); });
|
|
369
|
+
let consecutiveErrors = 0;
|
|
370
|
+
do {
|
|
371
|
+
let job = null;
|
|
372
|
+
try {
|
|
373
|
+
job = await client.nextJob(machineToken, waitMs);
|
|
374
|
+
consecutiveErrors = 0;
|
|
375
|
+
// next-job refreshes lastSeenAt server-side (machine-auth); the runtime
|
|
376
|
+
// status is sent separately below on its OWN cadence (do not couple it
|
|
377
|
+
// to next-job, or the runtime heartbeat is starved — see startup note).
|
|
378
|
+
}
|
|
379
|
+
catch (error) {
|
|
380
|
+
consecutiveErrors += 1;
|
|
381
|
+
const message = error instanceof MailboxClientError ? error.message : 'next-job request failed';
|
|
382
|
+
const delay = backoffDelay(consecutiveErrors);
|
|
383
|
+
console.error(chalk.red(`\n✖ next-job failed (attempt ${String(consecutiveErrors)}): ${message}`));
|
|
384
|
+
console.error(chalk.gray(` Reconnecting in ${String(Math.round(delay / 1000))}s...`));
|
|
385
|
+
if (options.once)
|
|
386
|
+
break;
|
|
387
|
+
await sleep(delay);
|
|
388
|
+
continue;
|
|
389
|
+
}
|
|
390
|
+
if (job) {
|
|
391
|
+
await handleJob(jobContext, job);
|
|
392
|
+
}
|
|
393
|
+
// Agent-runtime heartbeat (ENG-136): refresh the cached probe and re-send
|
|
394
|
+
// the status on its OWN cadence, independent of next-job. `lastRuntimeSentAt`
|
|
395
|
+
// is advanced ONLY when a runtime heartbeat is actually sent (never by
|
|
396
|
+
// next-job), so this fires every HEARTBEAT_INTERVAL_MS even while the ~25s
|
|
397
|
+
// job-poll runs continuously. The heartbeat POST body is the sole ingestion
|
|
398
|
+
// channel for runtime status (next-job is a GET and cannot carry it).
|
|
399
|
+
if (runtimeHeartbeatDue(Date.now(), lastRuntimeSentAt)) {
|
|
400
|
+
const cycle = await runHeartbeatCycle({
|
|
401
|
+
client,
|
|
402
|
+
machineToken,
|
|
403
|
+
probe: runtimeProbe,
|
|
404
|
+
previous: agentRuntime,
|
|
405
|
+
});
|
|
406
|
+
agentRuntime = cycle.runtime;
|
|
407
|
+
if (cycle.sent) {
|
|
408
|
+
lastRuntimeSentAt = Date.now();
|
|
409
|
+
}
|
|
410
|
+
}
|
|
411
|
+
if (options.once)
|
|
412
|
+
break;
|
|
413
|
+
// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition -- `running` is mutated asynchronously by the SIGINT/SIGTERM handlers above; ESLint's control-flow analysis can't see that.
|
|
414
|
+
} while (running);
|
|
415
|
+
console.log(chalk.gray('Agent Runner stopped.'));
|
|
416
|
+
});
|
|
417
|
+
//# sourceMappingURL=run.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"run.js","sourceRoot":"","sources":["../../src/commands/run.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `compbox service install|uninstall|start|stop` — Windows Service adapter
|
|
3
|
+
* (ENG-122-3), the ONE platform without a Homebrew equivalent.
|
|
4
|
+
*
|
|
5
|
+
* On macOS/Linux, `brew services start compbox` handles this for us (see the
|
|
6
|
+
* ENG-122 distribution decision) — this command intentionally does nothing
|
|
7
|
+
* there and tells the user to use `brew services` instead.
|
|
8
|
+
*
|
|
9
|
+
* Implementation: wraps the built-in `sc.exe` (Service Control Manager CLI,
|
|
10
|
+
* present on every Windows install — no extra native dependency). This is a
|
|
11
|
+
* reasonable first pass, not a fully SCM-compliant service: a plain Node
|
|
12
|
+
* process registered this way runs under the SCM and restarts with the
|
|
13
|
+
* machine (`start= auto`), but does not itself handle SERVICE_CONTROL codes
|
|
14
|
+
* (pause/continue, custom stop reasons, etc.) the way a proper service host
|
|
15
|
+
* would. `compbox run`'s existing SIGINT/SIGTERM handling covers ordinary
|
|
16
|
+
* stop/restart. A more robust future upgrade would wrap the process with
|
|
17
|
+
* NSSM or the `node-windows` library for full SCM compliance — deferred.
|
|
18
|
+
*
|
|
19
|
+
* Validated on Windows via CI (ENG-167, GitHub `windows-latest`, run
|
|
20
|
+
* 29947646845): `service install` registers CompBoxAgentRunner with
|
|
21
|
+
* START_TYPE=AUTO_START and a correctly-quoted binPath (node exe + script each
|
|
22
|
+
* quoted, so install paths with spaces are safe), and `uninstall` removes it.
|
|
23
|
+
* NOT yet proven: the daemon *staying* RUNNING (needs a login/configure'd
|
|
24
|
+
* machine — without config `compbox run` exits immediately) and a literal
|
|
25
|
+
* reboot (CI runners are ephemeral; AUTO_START is the proxy for boot-persistence).
|
|
26
|
+
*/
|
|
27
|
+
import { Command } from 'commander';
|
|
28
|
+
export declare const serviceCommand: Command;
|
|
29
|
+
//# sourceMappingURL=service.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"service.d.ts","sourceRoot":"","sources":["../../src/commands/service.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AAEH,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AA4BpC,eAAO,MAAM,cAAc,SAE1B,CAAC"}
|
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `compbox service install|uninstall|start|stop` — Windows Service adapter
|
|
3
|
+
* (ENG-122-3), the ONE platform without a Homebrew equivalent.
|
|
4
|
+
*
|
|
5
|
+
* On macOS/Linux, `brew services start compbox` handles this for us (see the
|
|
6
|
+
* ENG-122 distribution decision) — this command intentionally does nothing
|
|
7
|
+
* there and tells the user to use `brew services` instead.
|
|
8
|
+
*
|
|
9
|
+
* Implementation: wraps the built-in `sc.exe` (Service Control Manager CLI,
|
|
10
|
+
* present on every Windows install — no extra native dependency). This is a
|
|
11
|
+
* reasonable first pass, not a fully SCM-compliant service: a plain Node
|
|
12
|
+
* process registered this way runs under the SCM and restarts with the
|
|
13
|
+
* machine (`start= auto`), but does not itself handle SERVICE_CONTROL codes
|
|
14
|
+
* (pause/continue, custom stop reasons, etc.) the way a proper service host
|
|
15
|
+
* would. `compbox run`'s existing SIGINT/SIGTERM handling covers ordinary
|
|
16
|
+
* stop/restart. A more robust future upgrade would wrap the process with
|
|
17
|
+
* NSSM or the `node-windows` library for full SCM compliance — deferred.
|
|
18
|
+
*
|
|
19
|
+
* Validated on Windows via CI (ENG-167, GitHub `windows-latest`, run
|
|
20
|
+
* 29947646845): `service install` registers CompBoxAgentRunner with
|
|
21
|
+
* START_TYPE=AUTO_START and a correctly-quoted binPath (node exe + script each
|
|
22
|
+
* quoted, so install paths with spaces are safe), and `uninstall` removes it.
|
|
23
|
+
* NOT yet proven: the daemon *staying* RUNNING (needs a login/configure'd
|
|
24
|
+
* machine — without config `compbox run` exits immediately) and a literal
|
|
25
|
+
* reboot (CI runners are ephemeral; AUTO_START is the proxy for boot-persistence).
|
|
26
|
+
*/
|
|
27
|
+
import { Command } from 'commander';
|
|
28
|
+
import chalk from 'chalk';
|
|
29
|
+
import { execFile } from 'node:child_process';
|
|
30
|
+
import { promisify } from 'node:util';
|
|
31
|
+
const execFileAsync = promisify(execFile);
|
|
32
|
+
const SERVICE_NAME = 'CompBoxAgentRunner';
|
|
33
|
+
const SERVICE_DISPLAY_NAME = 'CompBox Agent Runner';
|
|
34
|
+
function requireWindows() {
|
|
35
|
+
if (process.platform !== 'win32') {
|
|
36
|
+
console.error(chalk.red(`\n'compbox service' is Windows-only. On macOS/Linux, use Homebrew instead:\n` +
|
|
37
|
+
` brew services start compbox\n` +
|
|
38
|
+
` brew services stop compbox`));
|
|
39
|
+
return false;
|
|
40
|
+
}
|
|
41
|
+
return true;
|
|
42
|
+
}
|
|
43
|
+
async function runSc(args) {
|
|
44
|
+
return execFileAsync('sc.exe', args);
|
|
45
|
+
}
|
|
46
|
+
export const serviceCommand = new Command('service').description('Manage the Agent Runner as a Windows Service (macOS/Linux: use `brew services` instead)');
|
|
47
|
+
serviceCommand
|
|
48
|
+
.command('install')
|
|
49
|
+
.description('Register the Agent Runner as an always-on Windows Service (auto-start on boot)')
|
|
50
|
+
.action(async () => {
|
|
51
|
+
if (!requireWindows()) {
|
|
52
|
+
process.exit(1);
|
|
53
|
+
return;
|
|
54
|
+
}
|
|
55
|
+
const nodeExe = process.execPath;
|
|
56
|
+
const scriptPath = process.argv[1] ?? '';
|
|
57
|
+
const binPath = `"${nodeExe}" "${scriptPath}" run`;
|
|
58
|
+
try {
|
|
59
|
+
// sc.exe requires each `option=` and its value as SEPARATE argv tokens
|
|
60
|
+
// (`binPath=` then the value, `start=` then `auto`), NOT one mashed token
|
|
61
|
+
// like `binPath= <value>` or `start= auto`. Passed via execFile (no shell),
|
|
62
|
+
// a mashed token makes sc.exe reject the whole command. Node quotes the
|
|
63
|
+
// space-containing binPath value automatically on the Windows command line.
|
|
64
|
+
await runSc([
|
|
65
|
+
'create',
|
|
66
|
+
SERVICE_NAME,
|
|
67
|
+
'binPath=', binPath,
|
|
68
|
+
'start=', 'auto',
|
|
69
|
+
'DisplayName=', SERVICE_DISPLAY_NAME,
|
|
70
|
+
]);
|
|
71
|
+
console.log(chalk.green(`\n✔ Service "${SERVICE_DISPLAY_NAME}" registered (starts automatically on boot).`));
|
|
72
|
+
console.log(chalk.gray(` Start it now with: compbox service start`));
|
|
73
|
+
}
|
|
74
|
+
catch (error) {
|
|
75
|
+
const message = error instanceof Error ? error.message : 'sc.exe create failed';
|
|
76
|
+
const stderr = error.stderr?.trim();
|
|
77
|
+
console.error(chalk.red(`\nFailed to install the service: ${message}`));
|
|
78
|
+
if (stderr)
|
|
79
|
+
console.error(chalk.red(` sc.exe: ${stderr}`));
|
|
80
|
+
console.error(chalk.gray(' If this is a permissions error, run the terminal as Administrator.'));
|
|
81
|
+
process.exit(1);
|
|
82
|
+
}
|
|
83
|
+
});
|
|
84
|
+
serviceCommand
|
|
85
|
+
.command('uninstall')
|
|
86
|
+
.description('Stop and remove the Windows Service')
|
|
87
|
+
.action(async () => {
|
|
88
|
+
if (!requireWindows()) {
|
|
89
|
+
process.exit(1);
|
|
90
|
+
return;
|
|
91
|
+
}
|
|
92
|
+
try {
|
|
93
|
+
await runSc(['stop', SERVICE_NAME]).catch(() => {
|
|
94
|
+
// Already stopped — not an error.
|
|
95
|
+
});
|
|
96
|
+
await runSc(['delete', SERVICE_NAME]);
|
|
97
|
+
console.log(chalk.green(`\n✔ Service "${SERVICE_DISPLAY_NAME}" removed.`));
|
|
98
|
+
}
|
|
99
|
+
catch (error) {
|
|
100
|
+
const message = error instanceof Error ? error.message : 'sc.exe delete failed';
|
|
101
|
+
console.error(chalk.red(`\nFailed to remove the service: ${message}`));
|
|
102
|
+
console.error(chalk.gray(' Try running this terminal as Administrator.'));
|
|
103
|
+
process.exit(1);
|
|
104
|
+
}
|
|
105
|
+
});
|
|
106
|
+
serviceCommand
|
|
107
|
+
.command('start')
|
|
108
|
+
.description('Start the registered Windows Service')
|
|
109
|
+
.action(async () => {
|
|
110
|
+
if (!requireWindows()) {
|
|
111
|
+
process.exit(1);
|
|
112
|
+
return;
|
|
113
|
+
}
|
|
114
|
+
try {
|
|
115
|
+
await runSc(['start', SERVICE_NAME]);
|
|
116
|
+
console.log(chalk.green(`\n✔ Service "${SERVICE_DISPLAY_NAME}" started.`));
|
|
117
|
+
}
|
|
118
|
+
catch (error) {
|
|
119
|
+
const message = error instanceof Error ? error.message : 'sc.exe start failed';
|
|
120
|
+
console.error(chalk.red(`\nFailed to start the service: ${message}`));
|
|
121
|
+
process.exit(1);
|
|
122
|
+
}
|
|
123
|
+
});
|
|
124
|
+
serviceCommand
|
|
125
|
+
.command('stop')
|
|
126
|
+
.description('Stop the running Windows Service')
|
|
127
|
+
.action(async () => {
|
|
128
|
+
if (!requireWindows()) {
|
|
129
|
+
process.exit(1);
|
|
130
|
+
return;
|
|
131
|
+
}
|
|
132
|
+
try {
|
|
133
|
+
await runSc(['stop', SERVICE_NAME]);
|
|
134
|
+
console.log(chalk.green(`\n✔ Service "${SERVICE_DISPLAY_NAME}" stopped.`));
|
|
135
|
+
}
|
|
136
|
+
catch (error) {
|
|
137
|
+
const message = error instanceof Error ? error.message : 'sc.exe stop failed';
|
|
138
|
+
console.error(chalk.red(`\nFailed to stop the service: ${message}`));
|
|
139
|
+
process.exit(1);
|
|
140
|
+
}
|
|
141
|
+
});
|
|
142
|
+
//# sourceMappingURL=service.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"service.js","sourceRoot":"","sources":["../../src/commands/service.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"update.d.ts","sourceRoot":"","sources":["../../src/commands/update.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAcpC,eAAO,MAAM,aAAa,SAmKtB,CAAC"}
|