@algosuite/vo-mcp 0.2.0-beta.2 → 0.2.0-beta.21

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.
@@ -0,0 +1,2333 @@
1
+ import { createRequire as __cr } from 'module'; const require = __cr(import.meta.url);
2
+ var __defProp = Object.defineProperty;
3
+ var __getOwnPropNames = Object.getOwnPropertyNames;
4
+ var __esm = (fn, res, err) => function __init() {
5
+ if (err) throw err[0];
6
+ try {
7
+ return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res;
8
+ } catch (e) {
9
+ throw err = [e], e;
10
+ }
11
+ };
12
+ var __export = (target, all) => {
13
+ for (var name in all)
14
+ __defProp(target, name, { get: all[name], enumerable: true });
15
+ };
16
+
17
+ // src/runner/control-plane-auth-stub.mjs
18
+ var control_plane_auth_stub_exports = {};
19
+ __export(control_plane_auth_stub_exports, {
20
+ getFirebaseAuth: () => getFirebaseAuth
21
+ });
22
+ async function getFirebaseAuth() {
23
+ throw new Error(
24
+ "vo-mcp runner: no control-plane credential. Run `vo-mcp login` first \u2014 the runner authenticates with your stored vo_credential (or set VO_CONTROL_PLANE_ADMIN_TOKEN)."
25
+ );
26
+ }
27
+ var init_control_plane_auth_stub = __esm({
28
+ "src/runner/control-plane-auth-stub.mjs"() {
29
+ }
30
+ });
31
+
32
+ // src/runner-supervisor.mjs
33
+ import { spawn } from "node:child_process";
34
+ import { randomUUID as randomUUID3 } from "node:crypto";
35
+ import { createRequire } from "node:module";
36
+ import { fileURLToPath as fileURLToPath3 } from "node:url";
37
+ import { dirname as dirname4, join as join5 } from "node:path";
38
+
39
+ // ../../scripts/virtual-office/code-runner/control-plane-client.mjs
40
+ var cachedFirebaseToken = null;
41
+ async function resolveBearer(env) {
42
+ const adminToken = env.VO_CONTROL_PLANE_ADMIN_TOKEN;
43
+ if (adminToken) return adminToken;
44
+ if (cachedFirebaseToken) return cachedFirebaseToken;
45
+ const { getFirebaseAuth: getFirebaseAuth2 } = await Promise.resolve().then(() => (init_control_plane_auth_stub(), control_plane_auth_stub_exports));
46
+ const auth = await getFirebaseAuth2({ env });
47
+ if (!auth || !auth.idToken) {
48
+ throw new Error(
49
+ "no control-plane credential: set VO_CONTROL_PLANE_ADMIN_TOKEN, or SMOKE_EMAIL/SMOKE_PASSWORD/SMOKE_API_KEY"
50
+ );
51
+ }
52
+ cachedFirebaseToken = auth.idToken;
53
+ return cachedFirebaseToken;
54
+ }
55
+ function createControlPlaneClient({
56
+ baseUrl,
57
+ env = process.env,
58
+ fetchImpl = fetch,
59
+ heartbeatTimeoutMs = Math.min(
60
+ Math.max(Number(env.VO_CODE_RUNNER_HEARTBEAT_TIMEOUT_MS) || 15e3, 1e3),
61
+ 6e4
62
+ )
63
+ } = {}) {
64
+ const resolvedBaseUrl = baseUrl ?? env.VO_CONTROL_PLANE_URL ?? "";
65
+ if (!resolvedBaseUrl) {
66
+ throw new Error("VO_CONTROL_PLANE_URL is required for the code-runner daemon");
67
+ }
68
+ const root = resolvedBaseUrl.replace(/\/+$/, "");
69
+ async function req(method, path2, body, { timeoutMs } = {}) {
70
+ const bearer = await resolveBearer(env);
71
+ const controller = timeoutMs ? new AbortController() : null;
72
+ let timeoutId;
73
+ const request = Promise.resolve(fetchImpl(`${root}${path2}`, {
74
+ method,
75
+ headers: {
76
+ "content-type": "application/json",
77
+ authorization: `Bearer ${bearer}`
78
+ },
79
+ body: body === void 0 ? void 0 : JSON.stringify(body),
80
+ ...controller ? { signal: controller.signal } : {}
81
+ }));
82
+ if (!timeoutMs) return request;
83
+ const timeout = new Promise((_, reject) => {
84
+ timeoutId = setTimeout(() => {
85
+ controller.abort();
86
+ reject(new Error(`control-plane ${path2} timed out after ${timeoutMs}ms`));
87
+ }, timeoutMs);
88
+ });
89
+ try {
90
+ return await Promise.race([request, timeout]);
91
+ } finally {
92
+ clearTimeout(timeoutId);
93
+ }
94
+ }
95
+ return {
96
+ /**
97
+ * Claim the next pending task. Returns the task or null (empty queue).
98
+ * `repos` (optional `owner/name` list) and `operatorIds` (optional
99
+ * `operator_id` list) scope the claim so this daemon only picks up tasks it
100
+ * serves — the control-plane filters by both (logical AND), so another
101
+ * operator's task never lands on (or bills) this machine.
102
+ */
103
+ async claim(runnerId2, repos, operatorIds, session = {}) {
104
+ const body = { runner_id: runnerId2 };
105
+ if (Array.isArray(repos) && repos.length > 0) body.repos = repos;
106
+ if (Array.isArray(operatorIds) && operatorIds.length > 0) body.operator_ids = operatorIds;
107
+ if (session.runnerInstanceId) body.runner_instance_id = session.runnerInstanceId;
108
+ if (session.runnerInstanceId && session.reconcileStale) body.reconcile_stale = true;
109
+ if (session.defaultAgent) body.default_agent = session.defaultAgent;
110
+ if (Array.isArray(session.availableAgents)) {
111
+ body.available_agents = session.availableAgents.filter((entry) => entry?.installed === true && entry?.authenticated === true).map((entry) => entry.agent);
112
+ }
113
+ const res = await req("POST", "/api/v1/code-task/claim", body);
114
+ if (res.status === 401) {
115
+ cachedFirebaseToken = null;
116
+ throw new Error("claim unauthorized (401)");
117
+ }
118
+ if (!res.ok) throw new Error(`claim failed: HTTP ${res.status}`);
119
+ const json = await res.json();
120
+ return json && json.task ? json.task : null;
121
+ },
122
+ /**
123
+ * Enqueue a new code-task (used by the PR watcher to auto-dispatch a CI fix).
124
+ * Server derives operator/tenant from the daemon's authenticated principal.
125
+ * Returns the created task, or throws on a non-2xx response.
126
+ */
127
+ async enqueueCodeTask({ repo, prompt, max_budget_usd, max_turns }) {
128
+ const body = { repo, prompt };
129
+ if (typeof max_budget_usd === "number") body.max_budget_usd = max_budget_usd;
130
+ if (typeof max_turns === "number") body.max_turns = max_turns;
131
+ const res = await req("POST", "/api/v1/code-task", body);
132
+ if (res.status === 401) {
133
+ cachedFirebaseToken = null;
134
+ throw new Error("enqueue unauthorized (401)");
135
+ }
136
+ if (!res.ok) throw new Error(`enqueue failed: HTTP ${res.status}`);
137
+ const json = await res.json();
138
+ return json && json.task ? json.task : null;
139
+ },
140
+ /**
141
+ * Resume a failed/cancelled/max-turn partial code-task. The PR watcher uses
142
+ * this after the runner opens a partial draft PR and CI is no longer pending.
143
+ */
144
+ async resumeCodeTask(taskId) {
145
+ const res = await req("POST", `/api/v1/code-task/${encodeURIComponent(taskId)}/resume`, {});
146
+ if (res.status === 401) {
147
+ cachedFirebaseToken = null;
148
+ throw new Error("resume unauthorized (401)");
149
+ }
150
+ if (!res.ok) throw new Error(`resume failed: HTTP ${res.status}`);
151
+ const json = await res.json();
152
+ return json && json.task ? json.task : null;
153
+ },
154
+ /**
155
+ * Send a CI-green PR through the production verify-before-act merge route.
156
+ * The server inspects the current diff, applies deterministic blockers, runs
157
+ * consensus, records a receipt, and direct-merges only the inspected SHA.
158
+ */
159
+ async mergeVerifiedPr(prNumber, automationContext) {
160
+ const res = await req("POST", "/api/v1/admin/pr/merge", { prNumber, automationContext });
161
+ if (res.status === 401) {
162
+ cachedFirebaseToken = null;
163
+ throw new Error("gated merge unauthorized (401)");
164
+ }
165
+ const json = await res.json().catch(() => ({}));
166
+ if (res.ok && json?.ok === true) {
167
+ const result = json?.result && typeof json.result === "object" ? json.result : {};
168
+ const status = result.merged === true || result.status === "merged" ? "merged" : result.status === "auto-merge-enabled" || String(result.action || "").includes("auto-merge") ? "queued" : "accepted";
169
+ return {
170
+ status,
171
+ detail: typeof result.detail === "string" ? result.detail : null,
172
+ actionReceiptId: typeof json.action_receipt_id === "string" ? json.action_receipt_id : null
173
+ };
174
+ }
175
+ if (res.status === 503 && json?.error === "verify_unavailable") {
176
+ return { status: "retry", reason: json.reason || "verification unavailable" };
177
+ }
178
+ return {
179
+ status: "blocked",
180
+ reason: json?.reason || json?.message || json?.error || `HTTP ${res.status}`,
181
+ actionReceiptId: typeof json?.action_receipt_id === "string" ? json.action_receipt_id : null
182
+ };
183
+ },
184
+ /**
185
+ * Append progress / set terminal status. Returns
186
+ * { task } — applied
187
+ * { terminal: true } — task already terminal (operator cancelled): STOP
188
+ */
189
+ async postProgress(taskId, patch) {
190
+ const res = await req("PATCH", `/api/v1/code-task/${taskId}/progress`, patch);
191
+ if (res.status === 409) return { terminal: true };
192
+ if (res.status === 404) return { terminal: true, missing: true };
193
+ if (!res.ok) throw new Error(`progress failed: HTTP ${res.status}`);
194
+ const json = await res.json();
195
+ return { task: json && json.task };
196
+ },
197
+ async getTask(taskId) {
198
+ const res = await req("GET", `/api/v1/code-task/${taskId}`);
199
+ if (res.status === 404) return null;
200
+ if (!res.ok) throw new Error(`getTask failed: HTTP ${res.status}`);
201
+ const json = await res.json();
202
+ return json ? json.task : null;
203
+ },
204
+ async downloadTaskAttachment(taskId, attachmentId) {
205
+ const path2 = `/api/v1/code-task/${encodeURIComponent(taskId)}/attachment/${encodeURIComponent(attachmentId)}`;
206
+ const res = await req("GET", path2);
207
+ if (res.status === 401) cachedFirebaseToken = null;
208
+ if (!res.ok) throw new Error(`attachment download failed: HTTP ${res.status}`);
209
+ return Buffer.from(await res.arrayBuffer());
210
+ },
211
+ async getTaskKnowledgeContext(taskId, { query } = {}) {
212
+ const body = {};
213
+ if (typeof query === "string" && query.trim()) body.query = query;
214
+ const res = await req("POST", `/api/v1/code-task/${encodeURIComponent(taskId)}/knowledge-context`, body);
215
+ if (res.status === 401) {
216
+ cachedFirebaseToken = null;
217
+ throw new Error("knowledge-context unauthorized (401)");
218
+ }
219
+ if (res.status === 404) return null;
220
+ if (!res.ok) throw new Error(`knowledge-context failed: HTTP ${res.status}`);
221
+ return res.json();
222
+ },
223
+ /**
224
+ * Report this machine's rolling-7-day Claude Code token usage (the real
225
+ * weekly-capacity gauge) PLUS the operator's real Claude weekly % (when
226
+ * available). The daemon authenticates as admin, so the target `operatorId`
227
+ * is named explicitly. Best-effort; throws on a non-2xx so the caller can
228
+ * log + move on.
229
+ *
230
+ * `tokens` = { input_tokens, output_tokens, cache_creation_tokens, cache_read_tokens }.
231
+ * Optional: `claudeWeeklyPct` (number) + `claudeWeeklyResetsAt` (ISO string | null).
232
+ */
233
+ async postWeeklyTokens({ operatorId, runnerId: runnerId2, tokens, claudeWeeklyPct, claudeWeeklyResetsAt }) {
234
+ const body = {
235
+ operator_id: operatorId,
236
+ runner_id: runnerId2,
237
+ input_tokens: tokens.input_tokens,
238
+ output_tokens: tokens.output_tokens,
239
+ cache_creation_tokens: tokens.cache_creation_tokens,
240
+ cache_read_tokens: tokens.cache_read_tokens
241
+ };
242
+ if (typeof claudeWeeklyPct === "number") {
243
+ body.claude_weekly_pct = claudeWeeklyPct;
244
+ }
245
+ if (claudeWeeklyResetsAt !== void 0) {
246
+ body.claude_weekly_resets_at = claudeWeeklyResetsAt;
247
+ }
248
+ const res = await req("POST", "/api/v1/weekly-tokens", body);
249
+ if (res.status === 401) {
250
+ cachedFirebaseToken = null;
251
+ throw new Error("weekly-tokens unauthorized (401)");
252
+ }
253
+ if (!res.ok) throw new Error(`weekly-tokens failed: HTTP ${res.status}`);
254
+ return true;
255
+ },
256
+ /**
257
+ * Send a liveness heartbeat (M2). The control-plane upserts it under the
258
+ * authenticated operator so the web shows a TRUE "runner online" signal.
259
+ * Best-effort caller; throws on 401/non-ok so the daemon can log + retry.
260
+ */
261
+ async postHeartbeat({ runnerId: runnerId2, runnerInstanceId, operatorId, uptimeSec, activeTasks, maxConcurrency, effectiveConcurrency, measuredTaskSlots, measuredCpuSlots, measuredMemorySlots, version, daemonVersion, defaultAgent, supervisorInstanceId: supervisorInstanceId2, supervisorVersion: supervisorVersion2, supervisorCapabilities, servedRepos, servedOperators, availableAgents, accountUsage, availableLocalModels }) {
262
+ const body = { runner_id: runnerId2 };
263
+ if (runnerInstanceId) body.runner_instance_id = runnerInstanceId;
264
+ if (operatorId) body.operator_id = operatorId;
265
+ if (typeof uptimeSec === "number") body.uptime_sec = uptimeSec;
266
+ if (typeof activeTasks === "number") body.active_tasks = activeTasks;
267
+ if (typeof maxConcurrency === "number") body.max_concurrency = maxConcurrency;
268
+ if (typeof effectiveConcurrency === "number") body.effective_concurrency = effectiveConcurrency;
269
+ if (typeof measuredTaskSlots === "number") body.measured_task_slots = measuredTaskSlots;
270
+ if (typeof measuredCpuSlots === "number") body.measured_cpu_slots = measuredCpuSlots;
271
+ if (typeof measuredMemorySlots === "number") body.measured_memory_slots = measuredMemorySlots;
272
+ if (version) body.version = version;
273
+ if (daemonVersion) body.daemon_version = daemonVersion;
274
+ if (defaultAgent) body.default_agent = defaultAgent;
275
+ if (supervisorInstanceId2) body.supervisor_instance_id = supervisorInstanceId2;
276
+ if (supervisorVersion2) body.supervisor_version = supervisorVersion2;
277
+ if (Array.isArray(supervisorCapabilities) && supervisorCapabilities.length > 0) {
278
+ body.supervisor_capabilities = supervisorCapabilities;
279
+ }
280
+ if (Array.isArray(servedRepos) && servedRepos.length > 0) body.served_repos = servedRepos;
281
+ if (Array.isArray(servedOperators) && servedOperators.length > 0) {
282
+ body.served_operator_ids = servedOperators;
283
+ }
284
+ if (Array.isArray(availableAgents) && availableAgents.length > 0) {
285
+ body.available_agents = availableAgents;
286
+ }
287
+ if (Array.isArray(accountUsage) && accountUsage.length > 0) {
288
+ body.account_usage = accountUsage;
289
+ }
290
+ if (Array.isArray(availableLocalModels) && availableLocalModels.length > 0) {
291
+ body.available_local_models = availableLocalModels;
292
+ }
293
+ const res = await req("POST", "/api/v1/runner/heartbeat", body, {
294
+ timeoutMs: heartbeatTimeoutMs
295
+ });
296
+ if (res.status === 401) {
297
+ cachedFirebaseToken = null;
298
+ throw new Error("heartbeat unauthorized (401)");
299
+ }
300
+ if (!res.ok) throw new Error(`heartbeat failed: HTTP ${res.status}`);
301
+ return res.json();
302
+ },
303
+ async getRunnerStatus({ operatorId } = {}) {
304
+ const query = operatorId ? `?operator_id=${encodeURIComponent(operatorId)}` : "";
305
+ const res = await req("GET", `/api/v1/runner/status${query}`, void 0, {
306
+ timeoutMs: heartbeatTimeoutMs
307
+ });
308
+ if (res.status === 401) {
309
+ cachedFirebaseToken = null;
310
+ throw new Error("runner status unauthorized (401)");
311
+ }
312
+ if (!res.ok) throw new Error(`runner status failed: HTTP ${res.status}`);
313
+ const body = await res.json();
314
+ return Array.isArray(body?.runners) ? body.runners : [];
315
+ },
316
+ async pollRunnerControl({ runnerId: runnerId2, operatorId, supervisorInstanceId: supervisorInstanceId2, supervisorVersion: supervisorVersion2, capabilities }) {
317
+ const body = { runner_id: runnerId2 };
318
+ if (operatorId) body.operator_id = operatorId;
319
+ if (supervisorInstanceId2) body.supervisor_instance_id = supervisorInstanceId2;
320
+ if (supervisorVersion2) body.supervisor_version = supervisorVersion2;
321
+ if (Array.isArray(capabilities) && capabilities.length > 0) body.capabilities = capabilities;
322
+ const res = await req("POST", "/api/v1/runner/control/poll", body);
323
+ if (res.status === 401) {
324
+ cachedFirebaseToken = null;
325
+ throw new Error("runner control poll unauthorized (401)");
326
+ }
327
+ if (!res.ok) throw new Error(`runner control poll failed: HTTP ${res.status}`);
328
+ const json = await res.json();
329
+ const action = json?.action;
330
+ return action && typeof action.action_id === "string" && action.action_id ? { ...action, actionId: action.action_id } : null;
331
+ },
332
+ async completeRunnerControl(actionId, { runnerId: runnerId2, operatorId, supervisorInstanceId: supervisorInstanceId2, supervisorVersion: supervisorVersion2, capabilities, status, detail }) {
333
+ const body = { runner_id: runnerId2, status };
334
+ if (operatorId) body.operator_id = operatorId;
335
+ if (supervisorInstanceId2) body.supervisor_instance_id = supervisorInstanceId2;
336
+ if (supervisorVersion2) body.supervisor_version = supervisorVersion2;
337
+ if (Array.isArray(capabilities) && capabilities.length > 0) body.capabilities = capabilities;
338
+ if (detail) body.detail = detail;
339
+ const res = await req("POST", `/api/v1/runner/control/${encodeURIComponent(actionId)}/complete`, body);
340
+ if (res.status === 401) {
341
+ cachedFirebaseToken = null;
342
+ throw new Error("runner control completion unauthorized (401)");
343
+ }
344
+ if (!res.ok) throw new Error(`runner control completion failed: HTTP ${res.status}`);
345
+ const json = await res.json();
346
+ return json?.action || null;
347
+ },
348
+ /**
349
+ * Mint a short-lived (~1h), repo-scoped GitHub App installation token for
350
+ * THIS runner's operator (M3). The control-plane keys the mint on the
351
+ * authenticated operator (ctx.operator_id), so the token covers only that
352
+ * operator's installation.
353
+ *
354
+ * Returns { token, expiresAt } on success. In legacy/admin mode it returns
355
+ * null on a miss so the caller may use ambient `gh`. In scoped-operator mode
356
+ * callers pass `{ required: true }`, which fails closed instead of letting a
357
+ * missing/mis-scoped installation fall through to the runner machine's `gh`.
358
+ *
359
+ * The minted token is only usable for push + PR if the GitHub App grants
360
+ * BOTH `Contents: write` (git push) AND `Pull requests: write` (gh pr
361
+ * create) — see docs/vo/github-app-setup-2026-06-18.md. A token missing
362
+ * either scope fails at push (→ ambient fallback) or at `gh pr create`.
363
+ */
364
+ async getInstallationToken({ required = false } = {}) {
365
+ const fail = (reason) => {
366
+ if (required) throw new Error(`installation-token required: ${reason}`);
367
+ return null;
368
+ };
369
+ try {
370
+ const res = await req("POST", "/api/v1/github/installation-token", {});
371
+ if (!res.ok) return fail(`HTTP ${res.status}`);
372
+ const json = await res.json();
373
+ if (!json || !json.token) return fail("missing token");
374
+ return { token: json.token, expiresAt: json.expires_at || null };
375
+ } catch (err) {
376
+ if (required) throw err;
377
+ return null;
378
+ }
379
+ },
380
+ /**
381
+ * Read the operator's dispatch-mode config (Fast→Ultracode effort setting).
382
+ * Returns the mode string ('fast'|'standard'|'deep'|'ultra'|'ultracode'),
383
+ * defaulting to 'standard' on any error. Never throws — best-effort.
384
+ */
385
+ async getDispatchMode() {
386
+ try {
387
+ const res = await req("GET", "/api/v1/dispatch-mode-config");
388
+ if (!res.ok) return "standard";
389
+ const json = await res.json();
390
+ return json?.dispatchMode || "standard";
391
+ } catch {
392
+ return "standard";
393
+ }
394
+ }
395
+ };
396
+ }
397
+
398
+ // ../../scripts/virtual-office/code-runner/runner-host-maintenance.mjs
399
+ import { spawnSync } from "node:child_process";
400
+ import { existsSync } from "node:fs";
401
+ import { win32 } from "node:path";
402
+ var DEFAULT_RUNNER_PACKAGE = "@algosuite/vo-mcp@beta";
403
+ var PACKAGE_SPEC_RE = /^@algosuite\/vo-mcp@(beta|latest|\d+(?:\.\d+){0,2}(?:-[\w.-]+)?)$/u;
404
+ var MAX_DIAGNOSTIC_CHARS = 800;
405
+ var NPM_CLI_SUFFIX = `\\${win32.join("node_modules", "npm", "bin", "npm-cli.js").toLowerCase()}`;
406
+ function escapeRegExp(value) {
407
+ return value.replace(/[.*+?^${}()|[\]\\]/gu, "\\$&");
408
+ }
409
+ function sanitizeMaintenanceDiagnostic(raw, env = {}) {
410
+ let value = String(raw || "").replace(/\b(?:vocred|npm|gh[oprsu])_[A-Za-z0-9._-]+\b/gu, "[REDACTED]").replace(/\bBearer\s+\S+/giu, "Bearer [REDACTED]").replace(/\b(_?authToken|token|password|secret|credential)(\s*[=:]\s*)\S+/giu, "$1$2[REDACTED]");
411
+ for (const [name, secret] of Object.entries(env)) {
412
+ if (!/(?:TOKEN|KEY|SECRET|PASSWORD|CREDENTIAL)/iu.test(name)) continue;
413
+ const text = String(secret || "");
414
+ if (text.length < 4) continue;
415
+ value = value.replace(new RegExp(escapeRegExp(text), "gu"), "[REDACTED]");
416
+ }
417
+ value = value.replace(/\s+/gu, " ").trim();
418
+ if (value.length <= MAX_DIAGNOSTIC_CHARS) return value;
419
+ return `${value.slice(0, MAX_DIAGNOSTIC_CHARS - 1)}\u2026`;
420
+ }
421
+ function isNpmCliPath(value) {
422
+ return typeof value === "string" && win32.isAbsolute(value) && win32.normalize(value).toLowerCase().endsWith(NPM_CLI_SUFFIX);
423
+ }
424
+ function resolveNpmCli({
425
+ env = process.env,
426
+ execPath = process.execPath,
427
+ fileExists = existsSync
428
+ } = {}) {
429
+ const candidates = [];
430
+ if (isNpmCliPath(env.npm_execpath)) candidates.push(env.npm_execpath);
431
+ candidates.push(win32.join(win32.dirname(execPath), "node_modules", "npm", "bin", "npm-cli.js"));
432
+ const pathValue = env.PATH ?? env.Path ?? env.path ?? "";
433
+ for (const entry of pathValue.split(";")) {
434
+ const trimmed = entry.trim();
435
+ if (!win32.isAbsolute(trimmed)) continue;
436
+ candidates.push(win32.join(trimmed, "node_modules", "npm", "bin", "npm-cli.js"));
437
+ }
438
+ const seen = /* @__PURE__ */ new Set();
439
+ for (const candidate of candidates) {
440
+ const normalized = win32.normalize(candidate);
441
+ const key = normalized.toLowerCase();
442
+ if (seen.has(key) || !isNpmCliPath(normalized)) continue;
443
+ seen.add(key);
444
+ if (fileExists(normalized)) return normalized;
445
+ }
446
+ return null;
447
+ }
448
+ function buildMaintenanceCommand(kind, {
449
+ platform = process.platform,
450
+ packageSpec = DEFAULT_RUNNER_PACKAGE,
451
+ env = process.env,
452
+ execPath = process.execPath,
453
+ fileExists = existsSync
454
+ } = {}) {
455
+ if (!["update", "reinstall"].includes(kind)) return null;
456
+ if (!PACKAGE_SPEC_RE.test(packageSpec)) throw new Error("unsafe runner package spec");
457
+ const npmCli = platform === "win32" ? resolveNpmCli({ env, execPath, fileExists }) : null;
458
+ if (platform === "win32" && !npmCli) throw new Error("trusted npm CLI not found");
459
+ const command = platform === "win32" ? execPath : "npm";
460
+ const args = [...npmCli ? [npmCli] : [], "install", "-g", packageSpec];
461
+ if (kind === "reinstall") args.push("--force");
462
+ return { command, args };
463
+ }
464
+ function runHostMaintenance(kind, {
465
+ platform = process.platform,
466
+ packageSpec = DEFAULT_RUNNER_PACKAGE,
467
+ env = process.env,
468
+ execPath = process.execPath,
469
+ fileExists = existsSync,
470
+ spawn: spawn2 = spawnSync,
471
+ log = () => {
472
+ }
473
+ } = {}) {
474
+ if (kind === "reconnect") return { ok: true, status: 0, command: null, args: [] };
475
+ let command;
476
+ try {
477
+ command = buildMaintenanceCommand(kind, { platform, packageSpec, env, execPath, fileExists });
478
+ } catch (error) {
479
+ return {
480
+ ok: false,
481
+ status: 2,
482
+ command: null,
483
+ args: [],
484
+ detail: sanitizeMaintenanceDiagnostic(error instanceof Error ? error.message : String(error), env)
485
+ };
486
+ }
487
+ if (!command) return { ok: false, status: 2, command: null, args: [] };
488
+ log(`runner maintenance: ${command.command} ${command.args.join(" ")}`);
489
+ const result = spawn2(command.command, command.args, {
490
+ stdio: ["ignore", "pipe", "pipe"],
491
+ encoding: "utf8",
492
+ env,
493
+ shell: false,
494
+ windowsHide: true
495
+ });
496
+ const status = typeof result.status === "number" ? result.status : 1;
497
+ const detail = sanitizeMaintenanceDiagnostic(
498
+ [result.stderr, result.stdout, result.error?.message].filter(Boolean).join("\n"),
499
+ env
500
+ );
501
+ if (detail) log(`runner maintenance result: ${detail}`);
502
+ return { ok: status === 0, status, command: command.command, args: command.args, detail };
503
+ }
504
+
505
+ // src/runner/bundled-runtime-updater.mjs
506
+ import { createHash as createHash4, randomUUID as randomUUID2 } from "node:crypto";
507
+ import {
508
+ existsSync as existsSync4,
509
+ lstatSync as lstatSync3,
510
+ mkdirSync as mkdirSync2,
511
+ readFileSync as readFileSync4,
512
+ readdirSync as readdirSync3,
513
+ renameSync as renameSync2,
514
+ rmSync as rmSync2,
515
+ writeFileSync as writeFileSync2
516
+ } from "node:fs";
517
+ import { basename, isAbsolute as isAbsolute3, join as join3, relative as relative3, resolve as resolve4, sep as sep2 } from "node:path";
518
+ import { spawnSync as spawnSync2 } from "node:child_process";
519
+
520
+ // ../../scripts/virtual-office/runner-bootstrap/runtime-authorization.mjs
521
+ import { createHash as createHash2 } from "node:crypto";
522
+ import { readFileSync as readFileSync2 } from "node:fs";
523
+ import { dirname, posix, resolve as resolve2 } from "node:path";
524
+ import { fileURLToPath } from "node:url";
525
+
526
+ // ../../scripts/virtual-office/runner-bootstrap/runtime-staged-tree.mjs
527
+ import { execFileSync } from "node:child_process";
528
+ import { createHash } from "node:crypto";
529
+ import {
530
+ existsSync as existsSync2,
531
+ lstatSync,
532
+ readFileSync,
533
+ readdirSync
534
+ } from "node:fs";
535
+ import { isAbsolute, join, relative, resolve, sep, win32 as win322 } from "node:path";
536
+ var WINDOWS_REPARSE_ATTRIBUTE = 1024;
537
+ var RUNNER_LOCK_KEY = "node_modules/@algosuite/vo-mcp";
538
+ var INSTALLED_TREE_ALGORITHM = "algohq-node-modules-manifest-sha256-v1";
539
+ function canonical(value) {
540
+ if (Array.isArray(value)) return value.map(canonical);
541
+ if (value && typeof value === "object") {
542
+ return Object.fromEntries(Object.keys(value).sort().map((key) => [key, canonical(value[key])]));
543
+ }
544
+ return value;
545
+ }
546
+ function canonicalJson(value) {
547
+ return JSON.stringify(canonical(value));
548
+ }
549
+ function hasFileAttribute(value, bit) {
550
+ if (typeof value === "bigint") return (value & BigInt(bit)) !== 0n;
551
+ return Number.isSafeInteger(value) && (value & bit) !== 0;
552
+ }
553
+ function isReparseStat(stats) {
554
+ if (!stats || typeof stats !== "object") return false;
555
+ if (typeof stats.isSymbolicLink === "function" && stats.isSymbolicLink()) return true;
556
+ if (stats.reparseTag !== void 0 && stats.reparseTag !== null && stats.reparseTag !== 0) return true;
557
+ return ["fileAttributes", "fileAttribute", "attributes"].some((key) => hasFileAttribute(stats[key], WINDOWS_REPARSE_ATTRIBUTE));
558
+ }
559
+ function platformConstraintAllows(values, target) {
560
+ if (!Array.isArray(values) || values.length === 0) return true;
561
+ if (values.some((value) => value === `!${target}`)) return false;
562
+ const positive = values.filter((value) => typeof value === "string" && !value.startsWith("!"));
563
+ return positive.length === 0 || positive.includes(target);
564
+ }
565
+ function isOmittedOptionalPackage(entry, platform = { os: "win32", arch: "x64" }) {
566
+ return entry?.optional === true && (!platformConstraintAllows(entry.os, platform.os) || !platformConstraintAllows(entry.cpu, platform.arch));
567
+ }
568
+ function assertContained(root, candidate, label) {
569
+ const rel = relative(root, candidate);
570
+ if (rel === "" || !rel.startsWith(`..${sep}`) && rel !== ".." && !isAbsolute(rel)) return;
571
+ throw new Error(`staged runtime ${label} escapes the payload root`);
572
+ }
573
+ function normalizeReportedPath(root, candidate) {
574
+ const absolute = resolve(String(candidate || ""));
575
+ assertContained(root, absolute, "reparse point");
576
+ return absolute;
577
+ }
578
+ function listWindowsReparsePoints(root, {
579
+ execFile = execFileSync,
580
+ env = process.env,
581
+ platform = process.platform
582
+ } = {}) {
583
+ if (platform !== "win32") return [];
584
+ const systemRoot = String(env.SystemRoot || env.SYSTEMROOT || "");
585
+ if (!win322.isAbsolute(systemRoot) || win322.normalize(systemRoot) !== systemRoot) {
586
+ throw new Error("staged runtime trusted PowerShell root unavailable");
587
+ }
588
+ const system32 = win322.join(systemRoot, "System32");
589
+ const powershell = win322.join(system32, "WindowsPowerShell", "v1.0", "powershell.exe");
590
+ const script = [
591
+ '$ErrorActionPreference = "Stop"',
592
+ "$root = [IO.Path]::GetFullPath($env:ALGOHQ_REPARSE_ROOT)",
593
+ "$items = @((Get-Item -LiteralPath $root -Force)) + @(Get-ChildItem -LiteralPath $root -Force -Recurse)",
594
+ "foreach ($item in $items) {",
595
+ " if (($item.Attributes -band [IO.FileAttributes]::ReparsePoint) -ne 0) {",
596
+ " [Console]::Out.WriteLine($item.FullName)",
597
+ " }",
598
+ "}"
599
+ ].join("; ");
600
+ const output = execFile(powershell, [
601
+ "-NoLogo",
602
+ "-NoProfile",
603
+ "-NonInteractive",
604
+ "-Command",
605
+ script
606
+ ], {
607
+ cwd: system32,
608
+ encoding: "utf8",
609
+ env: { SystemRoot: systemRoot, ALGOHQ_REPARSE_ROOT: win322.resolve(root) },
610
+ windowsHide: true
611
+ });
612
+ return String(output || "").split(/\r?\n/u).filter(Boolean).map((item) => normalizeReportedPath(root, item));
613
+ }
614
+ function normalizedRunnerRecord(actual, expected) {
615
+ const normalized = structuredClone(actual);
616
+ if (normalized?.resolved === expected?.resolved) return normalized;
617
+ if (typeof normalized?.resolved !== "string" || !normalized.resolved.startsWith("file:")) {
618
+ return normalized;
619
+ }
620
+ const fileName = normalized.resolved.slice("file:".length).replaceAll("\\", "/").split("/").at(-1);
621
+ const expectedFileNames = /* @__PURE__ */ new Set([
622
+ String(expected.resolved || "").split("/").at(-1),
623
+ `algosuite-vo-mcp-${expected.version}.tgz`
624
+ ]);
625
+ if (expectedFileNames.has(fileName)) normalized.resolved = expected.resolved;
626
+ return normalized;
627
+ }
628
+ function validateLock(lock, authorization) {
629
+ if (!lock || typeof lock !== "object" || Array.isArray(lock)) {
630
+ throw new Error("staged runtime package-lock must be an object");
631
+ }
632
+ if (lock.lockfileVersion !== authorization.dependency_lock.source_lockfile_version) {
633
+ throw new Error("staged runtime package-lock version mismatch");
634
+ }
635
+ if (!lock.packages || typeof lock.packages !== "object" || Array.isArray(lock.packages)) {
636
+ throw new Error("staged runtime package-lock package map missing");
637
+ }
638
+ const expected = authorization.dependency_lock.packages;
639
+ const actual = Object.fromEntries(Object.entries(lock.packages).filter(([key]) => key !== ""));
640
+ const expectedKeys = Object.keys(expected).sort();
641
+ const actualKeys = Object.keys(actual).sort();
642
+ if (canonicalJson(actualKeys) !== canonicalJson(expectedKeys)) {
643
+ throw new Error("staged runtime package-lock package set mismatch");
644
+ }
645
+ for (const key of expectedKeys) {
646
+ const record = key === RUNNER_LOCK_KEY ? normalizedRunnerRecord(actual[key], expected[key]) : actual[key];
647
+ if (canonicalJson(record) !== canonicalJson(expected[key])) {
648
+ throw new Error(`staged runtime package-lock record mismatch: ${key}`);
649
+ }
650
+ }
651
+ return { actual, expected };
652
+ }
653
+ function checkPathKind(path2, expectedKind, fsOps, label) {
654
+ if (!fsOps.exists(path2)) throw new Error(`staged runtime ${label} missing`);
655
+ const stats = fsOps.lstat(path2);
656
+ if (isReparseStat(stats) || fsOps.isReparsePoint(path2, stats)) {
657
+ throw new Error(`staged runtime ${label} is a reparse point`);
658
+ }
659
+ if (expectedKind === "directory" && !stats.isDirectory()) {
660
+ throw new Error(`staged runtime ${label} is not a directory`);
661
+ }
662
+ if (expectedKind === "file" && !stats.isFile()) {
663
+ throw new Error(`staged runtime ${label} is not a regular file`);
664
+ }
665
+ if (expectedKind === "file" && Number(stats.nlink) > 1) {
666
+ throw new Error(`staged runtime ${label} is hardlinked`);
667
+ }
668
+ return stats;
669
+ }
670
+ function verifyInstalledPackages(payloadRoot, packageRecords, platform, fsOps) {
671
+ const omitted = [];
672
+ let installed = 0;
673
+ for (const [key, entry] of Object.entries(packageRecords)) {
674
+ const path2 = resolve(payloadRoot, key);
675
+ assertContained(payloadRoot, path2, "package path");
676
+ const shouldOmit = isOmittedOptionalPackage(entry, platform);
677
+ if (shouldOmit) {
678
+ omitted.push(key);
679
+ if (fsOps.exists(path2)) throw new Error(`staged runtime optional package should be omitted: ${key}`);
680
+ continue;
681
+ }
682
+ checkPathKind(path2, "directory", fsOps, `installed package ${key}`);
683
+ installed += 1;
684
+ }
685
+ return { installed, omitted: omitted.sort() };
686
+ }
687
+ function treeRecord(kind, path2, stats, fileHash = "") {
688
+ if (kind === "d") return `d ${path2}\r
689
+ `;
690
+ return `f ${path2} ${stats.size} ${fileHash}\r
691
+ `;
692
+ }
693
+ function computeInstalledTree(nodeModulesRoot, fsOps = {}) {
694
+ const ops = {
695
+ exists: existsSync2,
696
+ lstat: lstatSync,
697
+ readdir: (path2) => readdirSync(path2, { withFileTypes: true }),
698
+ readFile: readFileSync,
699
+ isReparsePoint: () => false,
700
+ listReparsePoints: listWindowsReparsePoints,
701
+ ...fsOps
702
+ };
703
+ const root = resolve(nodeModulesRoot);
704
+ checkPathKind(root, "directory", ops, "node_modules root");
705
+ const reported = ops.listReparsePoints(root);
706
+ if (!Array.isArray(reported)) throw new Error("staged runtime reparse probe returned an invalid result");
707
+ if (reported.length > 0) throw new Error("staged runtime tree contains a Windows reparse point");
708
+ let fileCount = 0;
709
+ let directoryCount = 1;
710
+ let byteCount = 0;
711
+ const entries = [];
712
+ function walk(absolute, relativePath) {
713
+ for (const entry of ops.readdir(absolute)) {
714
+ const childRelative = relativePath ? `${relativePath}/${entry.name}` : entry.name;
715
+ if (childRelative === ".package-lock.json") continue;
716
+ const child = resolve(absolute, entry.name);
717
+ assertContained(root, child, "tree entry");
718
+ const stats = ops.lstat(child);
719
+ if (isReparseStat(stats) || ops.isReparsePoint(child, stats)) {
720
+ throw new Error(`staged runtime tree contains a reparse point: ${childRelative}`);
721
+ }
722
+ if (stats.isDirectory()) {
723
+ directoryCount += 1;
724
+ entries.push({ kind: "d", path: childRelative, stats });
725
+ walk(child, childRelative);
726
+ } else if (stats.isFile()) {
727
+ if (Number(stats.nlink) > 1) {
728
+ throw new Error(`staged runtime tree contains a hardlinked file: ${childRelative}`);
729
+ }
730
+ fileCount += 1;
731
+ byteCount += Number(stats.size);
732
+ const digest = createHash("sha256").update(ops.readFile(child)).digest("hex");
733
+ entries.push({ kind: "f", path: childRelative, stats, digest });
734
+ } else {
735
+ throw new Error(`staged runtime tree contains a non-file entry: ${childRelative}`);
736
+ }
737
+ }
738
+ }
739
+ walk(root, "");
740
+ entries.sort((left, right) => Buffer.compare(Buffer.from(left.path), Buffer.from(right.path)));
741
+ const manifest = treeRecord("d", "", { size: 0 }) + entries.map((entry) => treeRecord(entry.kind, entry.path, entry.stats, entry.digest)).join("");
742
+ return {
743
+ algorithm: INSTALLED_TREE_ALGORITHM,
744
+ sha256: createHash("sha256").update(manifest, "utf8").digest("hex"),
745
+ file_count: fileCount,
746
+ directory_count: directoryCount,
747
+ byte_count: byteCount,
748
+ canonical_manifest_byte_count: Buffer.byteLength(manifest),
749
+ reparse_point_count: 0,
750
+ hardlinked_file_count: 0
751
+ };
752
+ }
753
+ function compareStagedRuntime({
754
+ payloadRoot,
755
+ nodeModulesRoot = join(payloadRoot, "node_modules"),
756
+ packageLockFile = join(payloadRoot, "package-lock.json"),
757
+ authorization,
758
+ fsOps = {}
759
+ }) {
760
+ const payload = resolve(payloadRoot);
761
+ const modules = resolve(nodeModulesRoot);
762
+ const lockFile = resolve(packageLockFile);
763
+ if (modules !== resolve(payload, "node_modules") || lockFile !== resolve(payload, "package-lock.json")) {
764
+ throw new Error("staged runtime paths do not identify one canonical payload");
765
+ }
766
+ const ops = {
767
+ exists: existsSync2,
768
+ lstat: lstatSync,
769
+ readFile: readFileSync,
770
+ isReparsePoint: () => false,
771
+ ...fsOps
772
+ };
773
+ checkPathKind(lockFile, "file", ops, "package-lock");
774
+ const lock = JSON.parse(ops.readFile(lockFile, "utf8"));
775
+ const { expected } = validateLock(lock, authorization);
776
+ const platform = { os: authorization.platform.os, arch: authorization.platform.arch };
777
+ const packages = verifyInstalledPackages(payload, expected, platform, ops);
778
+ const declaredOmitted = [...authorization.dependency_lock.windows_x64_omitted_optional_entries].sort();
779
+ if (canonicalJson(packages.omitted) !== canonicalJson(declaredOmitted)) {
780
+ throw new Error("staged runtime optional omission list mismatch");
781
+ }
782
+ if (packages.installed !== Object.keys(expected).length - packages.omitted.length) {
783
+ throw new Error("staged runtime installed package count mismatch");
784
+ }
785
+ const tree = computeInstalledTree(modules, fsOps);
786
+ if (canonicalJson(tree) !== canonicalJson(authorization.installed_tree)) {
787
+ throw new Error("staged runtime installed tree mismatch");
788
+ }
789
+ return { ok: true, packageCount: Object.keys(expected).length, ...packages, tree };
790
+ }
791
+
792
+ // ../../scripts/virtual-office/runner-bootstrap/runtime-authorization.mjs
793
+ var HERE = dirname(fileURLToPath(import.meta.url));
794
+ var BETA13_WINDOWS_X64_AUTHORIZATION = resolve2(
795
+ HERE,
796
+ "authorizations",
797
+ "vo-mcp-0.2.0-beta.13-win32-x64.json"
798
+ );
799
+ var EXPECTED = Object.freeze({
800
+ authorizationId: "vo-mcp-0.2.0-beta.13-win32-x64-v1",
801
+ packageName: "@algosuite/vo-mcp",
802
+ version: "0.2.0-beta.13",
803
+ registry: "https://registry.npmjs.org/",
804
+ tarballUrl: "https://registry.npmjs.org/@algosuite/vo-mcp/-/vo-mcp-0.2.0-beta.13.tgz",
805
+ integrity: "sha512-TQa5VaEFsaleHbAZZp+zS4u5U/0zQBIGOiPDGn9IqfZfdMltH2dY81nTftvu5EUABthE1xTCrdSzJjO9mzkNag==",
806
+ tarballSha256: "c1e39e8bb2df7f53f48e46e77ffb617452a01849469ff4757b4384a478c1cbff",
807
+ npmShasumSha1: "f076649b31294aa38deb7852f38a889e0d0fbe44",
808
+ gitHead: "ba00db90720416fa7474581eb823c6255221880f",
809
+ sourceLockSha256: "f8bbb5e81a0057ee2d60145580ec07f7e7055d999bdd038515c6c4e88af6cc92",
810
+ canonicalEntriesSha256: "7f6b2f93ccb93ad625ed244dc59ac556792e478178fc690ad2ca2c6f3a2325d2",
811
+ entryCount: 107,
812
+ optionalEntryCount: 13,
813
+ installedEntryCount: 96,
814
+ treeAlgorithm: "algohq-node-modules-manifest-sha256-v1",
815
+ treeSha256: "9c8b0749d7ae1e2c132ef08c5b5655673ae88432859c561806276c9eb18f4874",
816
+ treeFileCount: 3538,
817
+ treeDirectoryCount: 553,
818
+ treeByteCount: 20566445,
819
+ treeManifestByteCount: 412062
820
+ });
821
+ var SRI_RE = /^sha512-[A-Za-z0-9+/]{86}==$/u;
822
+ var SHA256_RE = /^[a-f0-9]{64}$/u;
823
+ function canonical2(value) {
824
+ if (Array.isArray(value)) return value.map(canonical2);
825
+ if (value && typeof value === "object") {
826
+ return Object.fromEntries(Object.keys(value).sort().map((key) => [key, canonical2(value[key])]));
827
+ }
828
+ return value;
829
+ }
830
+ function sha256Canonical(value) {
831
+ return createHash2("sha256").update(`${JSON.stringify(canonical2(value))}
832
+ `, "utf8").digest("hex");
833
+ }
834
+ function assertEqual(actual, expected, label) {
835
+ if (actual !== expected) throw new Error(`runtime authorization ${label} mismatch`);
836
+ }
837
+ function isOmittedOnWindowsX64(entry) {
838
+ return isOmittedOptionalPackage(entry, { os: "win32", arch: "x64" });
839
+ }
840
+ function validateRuntimeAuthorization(value) {
841
+ if (!value || typeof value !== "object" || Array.isArray(value)) {
842
+ throw new Error("runtime authorization must be an object");
843
+ }
844
+ assertEqual(value.schema_version, 1, "schema version");
845
+ assertEqual(value.authorization_id, EXPECTED.authorizationId, "id");
846
+ assertEqual(value.package?.name, EXPECTED.packageName, "package name");
847
+ assertEqual(value.package?.version, EXPECTED.version, "package version");
848
+ assertEqual(value.package?.registry, EXPECTED.registry, "registry");
849
+ assertEqual(value.package?.tarball_url, EXPECTED.tarballUrl, "tarball URL");
850
+ assertEqual(value.package?.sri_sha512, EXPECTED.integrity, "package integrity");
851
+ assertEqual(value.package?.tarball_sha256, EXPECTED.tarballSha256, "tarball sha256");
852
+ assertEqual(value.package?.npm_shasum_sha1, EXPECTED.npmShasumSha1, "npm shasum");
853
+ assertEqual(value.package?.git_head, EXPECTED.gitHead, "git head");
854
+ assertEqual(value.package?.packed_file_count, 25, "packed file count");
855
+ assertEqual(value.package?.packed_bytes, 846151, "packed byte count");
856
+ assertEqual(value.package?.unpacked_bytes, 3350387, "unpacked byte count");
857
+ assertEqual(value.platform?.os, "win32", "operating system");
858
+ assertEqual(value.platform?.arch, "x64", "architecture");
859
+ assertEqual(value.platform?.package_node_engine, ">=22.5.0", "package node engine");
860
+ if (!/^24\.15\.0$/u.test(String(value.platform?.authorization_builder_node || "")) || value.platform?.authorization_builder_npm !== "11.12.1") {
861
+ throw new Error("runtime authorization builder toolchain mismatch");
862
+ }
863
+ for (const [key, expected] of Object.entries({
864
+ ignore_scripts: true,
865
+ bin_links: false,
866
+ include_optional: true,
867
+ omit_dev: true,
868
+ audit: false,
869
+ fund: false,
870
+ reject_links_reparse_points: true,
871
+ reject_hardlinks: true,
872
+ remove_generated_node_modules_package_lock_before_tree_validation: true
873
+ })) assertEqual(value.install_contract?.[key], expected, `install contract ${key}`);
874
+ assertEqual(value.install_contract?.allowed_registry_prefix, EXPECTED.registry, "registry prefix");
875
+ const lock = value.dependency_lock;
876
+ if (!lock?.packages || typeof lock.packages !== "object" || Array.isArray(lock.packages)) {
877
+ throw new Error("runtime authorization dependency set missing");
878
+ }
879
+ assertEqual(lock.source_lockfile_version, 3, "lockfile version");
880
+ assertEqual(lock.source_lock_sha256, EXPECTED.sourceLockSha256, "source lock sha256");
881
+ assertEqual(lock.canonical_entries_sha256, EXPECTED.canonicalEntriesSha256, "entry-set sha256");
882
+ assertEqual(lock.entry_count, EXPECTED.entryCount, "entry count");
883
+ assertEqual(lock.integrity_entry_count, EXPECTED.entryCount, "integrity count");
884
+ assertEqual(lock.optional_entry_count, EXPECTED.optionalEntryCount, "optional count");
885
+ assertEqual(lock.windows_x64_installed_entry_count, EXPECTED.installedEntryCount, "installed count");
886
+ const entries = Object.entries(lock.packages);
887
+ assertEqual(entries.length, EXPECTED.entryCount, "package map count");
888
+ for (const [key, entry] of entries) {
889
+ if (!key.startsWith("node_modules/") || key.includes("\\") || posix.normalize(key) !== key || key.split("/").includes("..")) {
890
+ throw new Error(`runtime authorization has unsafe package path: ${key}`);
891
+ }
892
+ if (!entry || typeof entry !== "object" || entry.link === true) {
893
+ throw new Error(`runtime authorization contains a linked package: ${key}`);
894
+ }
895
+ if (!SRI_RE.test(String(entry.integrity || ""))) {
896
+ throw new Error(`runtime authorization package lacks sha512 integrity: ${key}`);
897
+ }
898
+ if (!String(entry.resolved || "").startsWith(EXPECTED.registry)) {
899
+ throw new Error(`runtime authorization package is outside the public registry: ${key}`);
900
+ }
901
+ if (entry.hasInstallScript === true) {
902
+ throw new Error(`runtime authorization package declares an install script: ${key}`);
903
+ }
904
+ }
905
+ const runner = lock.packages["node_modules/@algosuite/vo-mcp"];
906
+ assertEqual(runner?.version, EXPECTED.version, "runner dependency version");
907
+ assertEqual(runner?.integrity, EXPECTED.integrity, "runner dependency integrity");
908
+ assertEqual(sha256Canonical(lock.packages), EXPECTED.canonicalEntriesSha256, "computed entry-set sha256");
909
+ const omitted = entries.filter(([, entry]) => isOmittedOnWindowsX64(entry)).map(([key]) => key).sort();
910
+ const declaredOmitted = [...lock.windows_x64_omitted_optional_entries || []].sort();
911
+ assertEqual(JSON.stringify(declaredOmitted), JSON.stringify(omitted), "omitted optional entries");
912
+ assertEqual(entries.length - omitted.length, EXPECTED.installedEntryCount, "derived installed count");
913
+ const tree = value.installed_tree;
914
+ assertEqual(tree?.algorithm, EXPECTED.treeAlgorithm, "tree algorithm");
915
+ if (!SHA256_RE.test(String(tree?.sha256 || ""))) throw new Error("runtime authorization tree hash invalid");
916
+ assertEqual(tree.sha256, EXPECTED.treeSha256, "tree sha256");
917
+ assertEqual(tree.file_count, EXPECTED.treeFileCount, "tree file count");
918
+ assertEqual(tree.directory_count, EXPECTED.treeDirectoryCount, "tree directory count");
919
+ assertEqual(tree.byte_count, EXPECTED.treeByteCount, "tree byte count");
920
+ assertEqual(tree.canonical_manifest_byte_count, EXPECTED.treeManifestByteCount, "tree manifest byte count");
921
+ assertEqual(tree.reparse_point_count, 0, "tree reparse count");
922
+ assertEqual(tree.hardlinked_file_count, 0, "tree hardlink count");
923
+ return value;
924
+ }
925
+ function readRuntimeAuthorization(file = BETA13_WINDOWS_X64_AUTHORIZATION) {
926
+ return validateRuntimeAuthorization(JSON.parse(readFileSync2(file, "utf8")));
927
+ }
928
+ function validateStagedRuntimeAuthorization(options) {
929
+ const authorization = validateRuntimeAuthorization(options?.authorization || readRuntimeAuthorization());
930
+ return compareStagedRuntime({ ...options, authorization });
931
+ }
932
+ if (process.argv[1] && resolve2(process.argv[1]) === fileURLToPath(import.meta.url)) {
933
+ readRuntimeAuthorization(process.argv[2] ? resolve2(process.argv[2]) : void 0);
934
+ process.stdout.write("AlgoHQ runtime authorization valid\n");
935
+ }
936
+
937
+ // src/runner/bundled-runtime-store.mjs
938
+ import { createHash as createHash3, randomUUID } from "node:crypto";
939
+ import {
940
+ closeSync,
941
+ existsSync as existsSync3,
942
+ fsyncSync,
943
+ lstatSync as lstatSync2,
944
+ mkdirSync,
945
+ openSync,
946
+ readFileSync as readFileSync3,
947
+ readdirSync as readdirSync2,
948
+ realpathSync,
949
+ renameSync,
950
+ rmSync,
951
+ writeFileSync
952
+ } from "node:fs";
953
+ import { homedir } from "node:os";
954
+ import { dirname as dirname2, isAbsolute as isAbsolute2, join as join2, relative as relative2, resolve as resolve3 } from "node:path";
955
+ var SLOT_ID_RE = /^vo-mcp-[0-9A-Za-z._-]{1,96}$/u;
956
+ var ACTION_ID_RE = /^[0-9A-Za-z._-]{1,128}$/u;
957
+ var INTEGRITY_RE = /^sha512-[A-Za-z0-9+/]{86}==$/u;
958
+ var VERSION_RE = /^\d+\.\d+\.\d+(?:-[0-9A-Za-z.-]+)?$/u;
959
+ var UUID_RE = /^[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/iu;
960
+ var ENTRY_REL = join2("node_modules", "@algosuite", "vo-mcp", "bin", "vo-mcp");
961
+ var SUPERVISOR_REL = join2("node_modules", "@algosuite", "vo-mcp", "dist", "runner-supervisor.js");
962
+ var PACKAGE_REL = join2("node_modules", "@algosuite", "vo-mcp", "package.json");
963
+ var CREDENTIAL_HELPER_REL = join2("node_modules", "@algosuite", "vo-mcp", "dist", "supervisor-credential-helper.js");
964
+ var MANIFEST_FILE = "runtime-manifest.json";
965
+ function within(parent, candidate) {
966
+ const rel = relative2(resolve3(parent), resolve3(candidate));
967
+ return rel === "" || !rel.startsWith("..") && !isAbsolute2(rel);
968
+ }
969
+ var APP_IDENTIFIER = "ai.algosuite.vo-runner";
970
+ var RUNNER_RUNTIME_DIR = "runner-runtime";
971
+ function defaultRuntimeRoot({ platform = process.platform, env = process.env, home = homedir() } = {}) {
972
+ if (platform === "win32") {
973
+ const appData = String(env.APPDATA || "").trim();
974
+ return appData && isAbsolute2(appData) ? join2(appData, APP_IDENTIFIER, RUNNER_RUNTIME_DIR) : null;
975
+ }
976
+ if (!home) return null;
977
+ if (platform === "darwin") {
978
+ return join2(home, "Library", "Application Support", APP_IDENTIFIER, RUNNER_RUNTIME_DIR);
979
+ }
980
+ const xdg = String(env.XDG_CONFIG_HOME || "").trim();
981
+ const base = xdg && isAbsolute2(xdg) ? xdg : join2(home, ".config");
982
+ return join2(base, APP_IDENTIFIER, RUNNER_RUNTIME_DIR);
983
+ }
984
+ function runtimeRootFromEnv(env = process.env, { platform, home } = {}) {
985
+ const value = String(env.VO_RUNNER_RUNTIME_ROOT || "").trim();
986
+ if (value) return isAbsolute2(value) ? resolve3(value) : null;
987
+ const derived = defaultRuntimeRoot({ platform, env, home });
988
+ return derived ? resolve3(derived) : null;
989
+ }
990
+ function hashFileSha512(file) {
991
+ return `sha512-${createHash3("sha512").update(readFileSync3(file)).digest("base64")}`;
992
+ }
993
+ function hashRuntimeTree(root) {
994
+ const hasher = createHash3("sha512");
995
+ const files = [];
996
+ const visit = (directory, prefix = "") => {
997
+ const rootStat = lstatSync2(directory);
998
+ if (rootStat.isSymbolicLink()) throw new Error("runtime tree contains a link/reparse point");
999
+ if (!rootStat.isDirectory()) throw new Error("runtime tree root is not a directory");
1000
+ for (const name of readdirSync2(directory).sort((a, b) => Buffer.compare(Buffer.from(a), Buffer.from(b)))) {
1001
+ const absolute = join2(directory, name);
1002
+ const relativePath = prefix ? `${prefix}/${name}` : name;
1003
+ const stat = lstatSync2(absolute);
1004
+ if (stat.isSymbolicLink()) throw new Error("runtime tree contains a link/reparse point");
1005
+ if (stat.isDirectory()) visit(absolute, relativePath);
1006
+ else if (stat.isFile() && relativePath !== MANIFEST_FILE) files.push({ absolute, relativePath, size: stat.size });
1007
+ else if (!stat.isFile()) throw new Error("runtime tree contains a non-regular file");
1008
+ }
1009
+ };
1010
+ visit(root);
1011
+ files.sort((a, b) => Buffer.compare(Buffer.from(a.relativePath), Buffer.from(b.relativePath)));
1012
+ for (const file of files) {
1013
+ const pathBytes = Buffer.from(file.relativePath, "utf8");
1014
+ hasher.update(`${pathBytes.length}:`);
1015
+ hasher.update(pathBytes);
1016
+ hasher.update(`:${file.size}:`);
1017
+ hasher.update(readFileSync3(file.absolute));
1018
+ hasher.update("\n");
1019
+ }
1020
+ return `sha512-${hasher.digest("base64")}`;
1021
+ }
1022
+ function atomicWriteJson(file, value) {
1023
+ mkdirSync(dirname2(file), { recursive: true });
1024
+ const temp = join2(dirname2(file), `.${randomUUID()}.tmp`);
1025
+ const fd = openSync(temp, "wx", 384);
1026
+ try {
1027
+ writeFileSync(fd, `${JSON.stringify(value, null, 2)}
1028
+ `, "utf8");
1029
+ fsyncSync(fd);
1030
+ } finally {
1031
+ closeSync(fd);
1032
+ }
1033
+ try {
1034
+ renameSync(temp, file);
1035
+ if (process.platform !== "win32") {
1036
+ try {
1037
+ const parentFd = openSync(dirname2(file), "r");
1038
+ try {
1039
+ fsyncSync(parentFd);
1040
+ } finally {
1041
+ closeSync(parentFd);
1042
+ }
1043
+ } catch {
1044
+ }
1045
+ }
1046
+ } finally {
1047
+ rmSync(temp, { force: true });
1048
+ }
1049
+ }
1050
+ function readActivation(runtimeRoot) {
1051
+ const file = join2(runtimeRoot, "current.json");
1052
+ if (!existsSync3(file)) return null;
1053
+ try {
1054
+ const value = JSON.parse(readFileSync3(file, "utf8"));
1055
+ return value?.schema_version === 1 ? value : null;
1056
+ } catch {
1057
+ return null;
1058
+ }
1059
+ }
1060
+ function slotPaths(runtimeRoot, slotId) {
1061
+ if (!SLOT_ID_RE.test(slotId)) throw new Error("invalid runtime slot id");
1062
+ const slotRoot = join2(runtimeRoot, "slots", slotId);
1063
+ return {
1064
+ slotRoot,
1065
+ entry: join2(slotRoot, ENTRY_REL),
1066
+ supervisor: join2(slotRoot, SUPERVISOR_REL),
1067
+ packageJson: join2(slotRoot, PACKAGE_REL),
1068
+ credentialHelper: join2(slotRoot, CREDENTIAL_HELPER_REL),
1069
+ manifest: join2(slotRoot, MANIFEST_FILE)
1070
+ };
1071
+ }
1072
+ function validActive(active) {
1073
+ return active && SLOT_ID_RE.test(active.slot_id) && VERSION_RE.test(active.version) && INTEGRITY_RE.test(active.integrity) && INTEGRITY_RE.test(active.entry_sha512) && INTEGRITY_RE.test(active.supervisor_sha512) && INTEGRITY_RE.test(active.tree_sha512);
1074
+ }
1075
+ function validateSlot(runtimeRoot, active) {
1076
+ if (!validActive(active)) return { ok: false, detail: "invalid activation metadata" };
1077
+ const paths = slotPaths(runtimeRoot, active.slot_id);
1078
+ try {
1079
+ const manifest = JSON.parse(readFileSync3(paths.manifest, "utf8"));
1080
+ const pkg = JSON.parse(readFileSync3(paths.packageJson, "utf8"));
1081
+ const expected = {
1082
+ slot_id: active.slot_id,
1083
+ version: active.version,
1084
+ integrity: active.integrity,
1085
+ entry_sha512: active.entry_sha512,
1086
+ supervisor_sha512: active.supervisor_sha512,
1087
+ tree_sha512: active.tree_sha512
1088
+ };
1089
+ for (const [key, value] of Object.entries(expected)) {
1090
+ if (manifest?.[key] !== value) return { ok: false, detail: `manifest ${key} mismatch` };
1091
+ }
1092
+ if (manifest?.schema_version !== 1 || pkg?.name !== "@algosuite/vo-mcp" || pkg?.version !== active.version) {
1093
+ return { ok: false, detail: "package identity mismatch" };
1094
+ }
1095
+ if (lstatSync2(paths.entry).isSymbolicLink() || lstatSync2(paths.supervisor).isSymbolicLink() || lstatSync2(paths.credentialHelper).isSymbolicLink()) {
1096
+ return { ok: false, detail: "runtime entry cannot be a link" };
1097
+ }
1098
+ if (!within(paths.slotRoot, realpathSync(paths.entry)) || !within(paths.slotRoot, realpathSync(paths.supervisor)) || !within(paths.slotRoot, realpathSync(paths.credentialHelper))) {
1099
+ return { ok: false, detail: "runtime entry escaped its slot" };
1100
+ }
1101
+ if (hashFileSha512(paths.entry) !== active.entry_sha512) return { ok: false, detail: "entry hash mismatch" };
1102
+ if (hashFileSha512(paths.supervisor) !== active.supervisor_sha512) return { ok: false, detail: "supervisor hash mismatch" };
1103
+ if (hashRuntimeTree(paths.slotRoot) !== active.tree_sha512) return { ok: false, detail: "runtime tree hash mismatch" };
1104
+ return { ok: true, paths, manifest };
1105
+ } catch (error) {
1106
+ return { ok: false, detail: error instanceof Error ? error.message : String(error) };
1107
+ }
1108
+ }
1109
+ function journalActivation(runtimeRoot, actionId, state, detail = "") {
1110
+ if (!ACTION_ID_RE.test(actionId)) throw new Error("invalid runner action id");
1111
+ atomicWriteJson(join2(runtimeRoot, "transactions", `${actionId}.json`), {
1112
+ schema_version: 1,
1113
+ action_id: actionId,
1114
+ state,
1115
+ detail: String(detail).slice(0, 400),
1116
+ updated_at: (/* @__PURE__ */ new Date()).toISOString()
1117
+ });
1118
+ }
1119
+ function activateSlot(runtimeRoot, active, action) {
1120
+ if (!validActive(active)) throw new Error("cannot activate invalid runtime metadata");
1121
+ if (!ACTION_ID_RE.test(action.actionId)) throw new Error("invalid runner action id");
1122
+ if (!UUID_RE.test(String(action.supervisorInstanceId || ""))) throw new Error("invalid claiming supervisor instance id");
1123
+ const validated = validateSlot(runtimeRoot, active);
1124
+ if (!validated.ok) throw new Error(`cannot activate invalid runtime slot: ${validated.detail}`);
1125
+ const current = readActivation(runtimeRoot);
1126
+ if (current?.pending) throw new Error("another runtime activation is still pending");
1127
+ const pointer = {
1128
+ schema_version: 1,
1129
+ generation: randomUUID(),
1130
+ active,
1131
+ previous: validActive(current?.active) ? current.active : null,
1132
+ pending: {
1133
+ action_id: action.actionId,
1134
+ runner_id: String(action.runnerId || ""),
1135
+ operator_id: String(action.operatorId || ""),
1136
+ supervisor_instance_id: action.supervisorInstanceId,
1137
+ activated_at: (/* @__PURE__ */ new Date()).toISOString(),
1138
+ ack_attempts: 0
1139
+ }
1140
+ };
1141
+ journalActivation(runtimeRoot, action.actionId, "prepared", `${active.version} ${active.integrity}`);
1142
+ atomicWriteJson(join2(runtimeRoot, "current.json"), pointer);
1143
+ return pointer;
1144
+ }
1145
+ function activationSupervisorInstanceId(runtimeRoot, fallback) {
1146
+ const current = runtimeRoot ? readActivation(runtimeRoot) : null;
1147
+ const pending = String(current?.pending?.supervisor_instance_id || "");
1148
+ if (UUID_RE.test(pending)) return pending;
1149
+ return UUID_RE.test(String(fallback || "")) ? fallback : null;
1150
+ }
1151
+ function recordActivationRetry(runtimeRoot, pointer, detail) {
1152
+ const current = readActivation(runtimeRoot);
1153
+ if (current?.generation !== pointer?.generation || current?.pending?.action_id !== pointer?.pending?.action_id) {
1154
+ throw new Error("runtime activation generation changed before retry");
1155
+ }
1156
+ const attempts = Math.max(0, Number(current.pending.ack_attempts || 0)) + 1;
1157
+ const updated = {
1158
+ ...current,
1159
+ pending: { ...current.pending, ack_attempts: attempts, last_error: String(detail).slice(0, 240) }
1160
+ };
1161
+ atomicWriteJson(join2(runtimeRoot, "current.json"), updated);
1162
+ journalActivation(runtimeRoot, current.pending.action_id, "ack-retry", `attempt ${attempts}: ${detail}`);
1163
+ return updated;
1164
+ }
1165
+ function attestCurrentSupervisor({ runtimeRoot, selfPath: selfPath2, version }) {
1166
+ const pointer = readActivation(runtimeRoot);
1167
+ if (!pointer?.pending || !validActive(pointer.active)) return { ok: false, detail: "no pending activation" };
1168
+ const validated = validateSlot(runtimeRoot, pointer.active);
1169
+ if (!validated.ok) return validated;
1170
+ try {
1171
+ if (realpathSync(selfPath2) !== realpathSync(validated.paths.supervisor)) {
1172
+ return { ok: false, detail: "running supervisor is not the activated supervisor" };
1173
+ }
1174
+ } catch {
1175
+ return { ok: false, detail: "could not resolve running supervisor path" };
1176
+ }
1177
+ if (version !== pointer.active.version) return { ok: false, detail: "running supervisor version mismatch" };
1178
+ return { ok: true, pointer, active: pointer.active, paths: validated.paths };
1179
+ }
1180
+ function finalizeActivation(runtimeRoot, pointer) {
1181
+ const current = readActivation(runtimeRoot);
1182
+ if (current?.generation !== pointer?.generation || current?.pending?.action_id !== pointer?.pending?.action_id) {
1183
+ throw new Error("runtime activation generation changed before finalization");
1184
+ }
1185
+ journalActivation(runtimeRoot, pointer.pending.action_id, "attesting", `${pointer.active.version} verified`);
1186
+ atomicWriteJson(join2(runtimeRoot, "current.json"), {
1187
+ schema_version: 1,
1188
+ generation: pointer.generation,
1189
+ active: pointer.active,
1190
+ previous: pointer.previous || null,
1191
+ pending: null
1192
+ });
1193
+ try {
1194
+ journalActivation(runtimeRoot, pointer.pending.action_id, "attested", `${pointer.active.version} active`);
1195
+ } catch {
1196
+ }
1197
+ }
1198
+ function rollbackActivation(runtimeRoot, pointer, detail) {
1199
+ const current = readActivation(runtimeRoot);
1200
+ if (current?.generation !== pointer?.generation || current?.pending?.action_id !== pointer?.pending?.action_id) {
1201
+ throw new Error("runtime activation generation changed before rollback");
1202
+ }
1203
+ journalActivation(runtimeRoot, pointer.pending.action_id, "rolling-back", detail);
1204
+ const rolledBack = {
1205
+ schema_version: 1,
1206
+ generation: randomUUID(),
1207
+ active: validActive(pointer?.previous) ? pointer.previous : null,
1208
+ previous: null,
1209
+ pending: {
1210
+ ...pointer.pending,
1211
+ terminal_status: "failed",
1212
+ terminal_detail: String(detail).slice(0, 400),
1213
+ rolled_back_at: (/* @__PURE__ */ new Date()).toISOString()
1214
+ }
1215
+ };
1216
+ atomicWriteJson(join2(runtimeRoot, "current.json"), rolledBack);
1217
+ try {
1218
+ journalActivation(runtimeRoot, pointer.pending.action_id, "rolled-back", detail);
1219
+ } catch {
1220
+ }
1221
+ return rolledBack;
1222
+ }
1223
+ function acknowledgeActivationFailure(runtimeRoot, pointer) {
1224
+ const current = readActivation(runtimeRoot);
1225
+ if (current?.generation !== pointer?.generation || current?.pending?.action_id !== pointer?.pending?.action_id || current?.pending?.terminal_status !== "failed") {
1226
+ throw new Error("runtime rollback acknowledgement obligation changed");
1227
+ }
1228
+ atomicWriteJson(join2(runtimeRoot, "current.json"), { ...current, pending: null });
1229
+ try {
1230
+ journalActivation(runtimeRoot, pointer.pending.action_id, "failure-acknowledged", pointer.pending.terminal_detail);
1231
+ } catch {
1232
+ }
1233
+ }
1234
+
1235
+ // src/runner/bundled-runtime-updater.mjs
1236
+ var PACKAGE_NAME = "@algosuite/vo-mcp";
1237
+ var PACKAGE_SPEC_RE2 = /^@algosuite\/vo-mcp@\d+\.\d+\.\d+(?:-[0-9A-Za-z.-]+)?$/u;
1238
+ var INTEGRITY_RE2 = /^sha512-[A-Za-z0-9+/]{86}==$/u;
1239
+ var MAX_TARBALL_BYTES = 100 * 1024 * 1024;
1240
+ var PUBLIC_REGISTRY = "https://registry.npmjs.org/";
1241
+ function buildMinimalMaintenanceEnv(env = process.env, runtimeRoot = "") {
1242
+ const allowed = /* @__PURE__ */ new Set([
1243
+ "PATH",
1244
+ "Path",
1245
+ "path",
1246
+ "PATHEXT",
1247
+ "SystemRoot",
1248
+ "SYSTEMROOT",
1249
+ "WINDIR",
1250
+ "COMSPEC",
1251
+ "TEMP",
1252
+ "TMP",
1253
+ "TMPDIR",
1254
+ "HOME",
1255
+ "USERPROFILE",
1256
+ "APPDATA",
1257
+ "LOCALAPPDATA",
1258
+ "ProgramFiles",
1259
+ "ProgramFiles(x86)",
1260
+ "ProgramW6432",
1261
+ "LANG",
1262
+ "LC_ALL"
1263
+ ]);
1264
+ const clean = {};
1265
+ for (const [key, value] of Object.entries(env)) {
1266
+ if (allowed.has(key) && typeof value === "string") clean[key] = value;
1267
+ }
1268
+ return {
1269
+ ...clean,
1270
+ npm_config_ignore_scripts: "true",
1271
+ npm_config_bin_links: "false",
1272
+ npm_config_audit: "false",
1273
+ npm_config_fund: "false",
1274
+ npm_config_update_notifier: "false",
1275
+ npm_config_registry: PUBLIC_REGISTRY,
1276
+ ...runtimeRoot ? {
1277
+ npm_config_userconfig: join3(runtimeRoot, "maintenance", "user.npmrc"),
1278
+ npm_config_globalconfig: join3(runtimeRoot, "maintenance", "global.npmrc"),
1279
+ npm_config_cache: join3(runtimeRoot, "maintenance", "npm-cache")
1280
+ } : {}
1281
+ };
1282
+ }
1283
+ function defaultRun(command, args, options) {
1284
+ return spawnSync2(command, args, {
1285
+ cwd: options.cwd,
1286
+ encoding: "utf8",
1287
+ env: options.env,
1288
+ shell: false,
1289
+ windowsHide: true,
1290
+ stdio: ["ignore", "pipe", "pipe"],
1291
+ timeout: options.timeout ?? 12e4
1292
+ });
1293
+ }
1294
+ function commandRunner({ platform, execPath, env, fileExists, run }) {
1295
+ const npmCli = platform === "win32" ? resolveNpmCli({ env, execPath, fileExists }) : null;
1296
+ if (platform === "win32" && !npmCli) throw new Error("trusted npm CLI not found");
1297
+ const npmCommand = platform === "win32" ? execPath : "npm";
1298
+ const prefix = npmCli ? [npmCli] : [];
1299
+ return {
1300
+ npm(args, options) {
1301
+ return run(npmCommand, [...prefix, ...args], options);
1302
+ },
1303
+ node(args, options) {
1304
+ return run(execPath, args, options);
1305
+ }
1306
+ };
1307
+ }
1308
+ function parseJsonOutput(result, operation) {
1309
+ if (result?.status !== 0) {
1310
+ throw new Error(`${operation} failed: ${String(result?.stderr || result?.error?.message || `exit ${result?.status ?? 1}`)}`);
1311
+ }
1312
+ try {
1313
+ return JSON.parse(String(result.stdout || ""));
1314
+ } catch {
1315
+ throw new Error(`${operation} returned invalid JSON`);
1316
+ }
1317
+ }
1318
+ function tarballIntegrity(file) {
1319
+ return `sha512-${createHash4("sha512").update(readFileSync4(file)).digest("base64")}`;
1320
+ }
1321
+ function assertNoLinks(root) {
1322
+ const pending = [root];
1323
+ while (pending.length) {
1324
+ const current = pending.pop();
1325
+ const stat = lstatSync3(current);
1326
+ if (stat.isSymbolicLink()) throw new Error("installed runtime contains a link/reparse point");
1327
+ if (!stat.isDirectory()) continue;
1328
+ for (const entry of readdirSync3(current)) pending.push(join3(current, entry));
1329
+ }
1330
+ }
1331
+ function validateDependencyLock(payloadRoot, expected) {
1332
+ const lock = JSON.parse(readFileSync4(join3(payloadRoot, "package-lock.json"), "utf8"));
1333
+ if (Number(lock.lockfileVersion) < 3 || !lock.packages || typeof lock.packages !== "object") {
1334
+ throw new Error("runtime dependency lock is missing or unsupported");
1335
+ }
1336
+ let foundPackage = false;
1337
+ for (const [key, item] of Object.entries(lock.packages)) {
1338
+ if (!key) continue;
1339
+ if (item?.link === true) throw new Error(`runtime dependency lock contains link: ${key}`);
1340
+ const isRunner = key.replaceAll("\\", "/").endsWith("node_modules/@algosuite/vo-mcp");
1341
+ if (isRunner) {
1342
+ foundPackage = item.version === expected.version && item.integrity === expected.integrity;
1343
+ continue;
1344
+ }
1345
+ if (!INTEGRITY_RE2.test(String(item?.integrity || ""))) throw new Error(`dependency lacks sha512 integrity: ${key}`);
1346
+ if (!String(item?.resolved || "").startsWith("https://registry.npmjs.org/")) {
1347
+ throw new Error(`dependency is not registry-pinned: ${key}`);
1348
+ }
1349
+ }
1350
+ if (!foundPackage) throw new Error("installed runner package does not match registry integrity");
1351
+ }
1352
+ function writeAuthorizedInstallSeed(payloadRoot, tarball, runtimeAuthorization) {
1353
+ const authorization = validateRuntimeAuthorization(runtimeAuthorization);
1354
+ const stagingRoot = resolve4(payloadRoot, "..", "..");
1355
+ const tarballFromStaging = relative3(stagingRoot, resolve4(tarball));
1356
+ if (!tarballFromStaging || tarballFromStaging === ".." || tarballFromStaging.startsWith(`..${sep2}`) || isAbsolute3(tarballFromStaging)) {
1357
+ throw new Error("authorized runtime tarball escaped staging root");
1358
+ }
1359
+ const relativeTarball = relative3(payloadRoot, resolve4(tarball)).replaceAll("\\", "/");
1360
+ if (!relativeTarball || isAbsolute3(relativeTarball) || relativeTarball.includes("\n") || relativeTarball.includes("\r")) {
1361
+ throw new Error("authorized runtime tarball path invalid");
1362
+ }
1363
+ const fileSpec = `file:${relativeTarball}`;
1364
+ const packageRecord = {
1365
+ name: "algohq-runner-runtime",
1366
+ version: "0.0.0",
1367
+ private: true,
1368
+ dependencies: { [PACKAGE_NAME]: fileSpec }
1369
+ };
1370
+ const packages = structuredClone(authorization.dependency_lock.packages);
1371
+ packages[`node_modules/${PACKAGE_NAME}`].resolved = fileSpec;
1372
+ const lock = {
1373
+ name: packageRecord.name,
1374
+ version: packageRecord.version,
1375
+ lockfileVersion: authorization.dependency_lock.source_lockfile_version,
1376
+ requires: true,
1377
+ packages: { "": packageRecord, ...packages }
1378
+ };
1379
+ writeFileSync2(join3(payloadRoot, "package.json"), `${JSON.stringify(packageRecord)}
1380
+ `, { mode: 384 });
1381
+ writeFileSync2(join3(payloadRoot, "package-lock.json"), `${JSON.stringify(lock)}
1382
+ `, { mode: 384 });
1383
+ return { fileSpec, lock };
1384
+ }
1385
+ function buildActive(slotId, metadata, paths) {
1386
+ return {
1387
+ slot_id: slotId,
1388
+ version: metadata.version,
1389
+ integrity: metadata.integrity,
1390
+ entry_sha512: hashFileSha512(paths.entry),
1391
+ supervisor_sha512: hashFileSha512(paths.supervisor),
1392
+ tree_sha512: hashRuntimeTree(paths.slotRoot ?? paths.payloadRoot)
1393
+ };
1394
+ }
1395
+ function installSlot({
1396
+ runtimeRoot,
1397
+ metadata,
1398
+ tarball,
1399
+ runner,
1400
+ npmEnv,
1401
+ runOptions,
1402
+ force,
1403
+ runtimeAuthorization
1404
+ }) {
1405
+ const digest = createHash4("sha256").update(metadata.integrity).digest("hex").slice(0, 16);
1406
+ const suffix = force ? `${digest}-${randomUUID2().slice(0, 8)}` : digest;
1407
+ const slotId = `vo-mcp-${metadata.version}-${suffix}`;
1408
+ const finalPaths = slotPaths(runtimeRoot, slotId);
1409
+ if (!force && existsSync4(finalPaths.slotRoot)) {
1410
+ const manifest = JSON.parse(readFileSync4(finalPaths.manifest, "utf8"));
1411
+ const active = buildActive(slotId, metadata, finalPaths);
1412
+ const validated = validateSlot(runtimeRoot, active);
1413
+ if (validated.ok && manifest.integrity === metadata.integrity) {
1414
+ if (runtimeAuthorization) {
1415
+ validateStagedRuntimeAuthorization({
1416
+ payloadRoot: finalPaths.slotRoot,
1417
+ authorization: runtimeAuthorization
1418
+ });
1419
+ }
1420
+ return { active, created: false };
1421
+ }
1422
+ }
1423
+ const staging = join3(runtimeRoot, "staging", randomUUID2());
1424
+ const payload = join3(staging, "payload");
1425
+ let installedSlot = false;
1426
+ try {
1427
+ mkdirSync2(payload, { recursive: true });
1428
+ let installArgs;
1429
+ if (runtimeAuthorization) {
1430
+ writeAuthorizedInstallSeed(payload, tarball, runtimeAuthorization);
1431
+ installArgs = [
1432
+ "ci",
1433
+ "--ignore-scripts",
1434
+ "--no-bin-links",
1435
+ "--no-audit",
1436
+ "--no-fund",
1437
+ `--registry=${PUBLIC_REGISTRY}`
1438
+ ];
1439
+ } else {
1440
+ writeFileSync2(join3(payload, "package.json"), `${JSON.stringify({
1441
+ name: "algohq-runner-runtime",
1442
+ version: "0.0.0",
1443
+ private: true
1444
+ })}
1445
+ `);
1446
+ installArgs = [
1447
+ "install",
1448
+ "--ignore-scripts",
1449
+ "--no-bin-links",
1450
+ "--no-audit",
1451
+ "--no-fund",
1452
+ "--package-lock=true",
1453
+ "--save-exact",
1454
+ `--registry=${PUBLIC_REGISTRY}`,
1455
+ tarball
1456
+ ];
1457
+ }
1458
+ const install = runner.npm(
1459
+ installArgs,
1460
+ { ...runOptions, cwd: payload, env: npmEnv, timeout: 18e4 }
1461
+ );
1462
+ if (install.status !== 0) throw new Error(`npm install failed: ${install.stderr || install.error?.message || install.status}`);
1463
+ assertNoLinks(payload);
1464
+ validateDependencyLock(payload, metadata);
1465
+ if (runtimeAuthorization) {
1466
+ validateStagedRuntimeAuthorization({ payloadRoot: payload, authorization: runtimeAuthorization });
1467
+ }
1468
+ const stagedPaths = {
1469
+ entry: join3(payload, "node_modules", "@algosuite", "vo-mcp", "bin", "vo-mcp"),
1470
+ supervisor: join3(payload, "node_modules", "@algosuite", "vo-mcp", "dist", "runner-supervisor.js"),
1471
+ packageJson: join3(payload, "node_modules", "@algosuite", "vo-mcp", "package.json"),
1472
+ credentialHelper: join3(payload, "node_modules", "@algosuite", "vo-mcp", "dist", "supervisor-credential-helper.js"),
1473
+ slotRoot: payload
1474
+ };
1475
+ const pkg = JSON.parse(readFileSync4(stagedPaths.packageJson, "utf8"));
1476
+ if (pkg.name !== PACKAGE_NAME || pkg.version !== metadata.version) throw new Error("installed package identity mismatch");
1477
+ if (!lstatSync3(stagedPaths.credentialHelper).isFile()) throw new Error("installed credential helper is missing");
1478
+ const smoke = runner.node([stagedPaths.entry, "runner", "--version"], { ...runOptions, cwd: payload, env: npmEnv, timeout: 3e4 });
1479
+ if (smoke.status !== 0 || String(smoke.stdout || "").trim() !== `vo-mcp runner ${metadata.version}`) {
1480
+ throw new Error("bundled runtime smoke check failed");
1481
+ }
1482
+ const active = buildActive(slotId, metadata, stagedPaths);
1483
+ atomicWriteJson(join3(payload, "runtime-manifest.json"), { schema_version: 1, ...active });
1484
+ mkdirSync2(join3(runtimeRoot, "slots"), { recursive: true });
1485
+ if (existsSync4(finalPaths.slotRoot)) throw new Error("immutable runtime slot already exists");
1486
+ renameSync2(payload, finalPaths.slotRoot);
1487
+ installedSlot = true;
1488
+ const validated = validateSlot(runtimeRoot, active);
1489
+ if (!validated.ok) throw new Error(`staged runtime validation failed: ${validated.detail}`);
1490
+ return { active, created: true };
1491
+ } catch (error) {
1492
+ if (installedSlot) rmSync2(finalPaths.slotRoot, { recursive: true, force: true });
1493
+ throw error;
1494
+ } finally {
1495
+ rmSync2(staging, { recursive: true, force: true });
1496
+ }
1497
+ }
1498
+ function stageBundledRuntimeSlot(options) {
1499
+ const {
1500
+ runtimeRoot,
1501
+ packageSpec,
1502
+ expectedVersion,
1503
+ expectedIntegrity,
1504
+ platform = process.platform,
1505
+ execPath = process.execPath,
1506
+ env = process.env,
1507
+ fileExists = existsSync4,
1508
+ run = defaultRun,
1509
+ force = false,
1510
+ runtimeAuthorization = null
1511
+ } = options;
1512
+ if (!runtimeRoot || !isAbsolute3(runtimeRoot)) return { ok: false, status: 2, detail: "bundled runtime root unavailable" };
1513
+ if (!expectedVersion || !PACKAGE_SPEC_RE2.test(`${PACKAGE_NAME}@${expectedVersion}`)) {
1514
+ return { ok: false, status: 2, detail: "invalid expected runner version" };
1515
+ }
1516
+ if (!expectedIntegrity || !INTEGRITY_RE2.test(expectedIntegrity)) {
1517
+ return { ok: false, status: 2, detail: "invalid expected runner integrity" };
1518
+ }
1519
+ const exactSpec = `${PACKAGE_NAME}@${expectedVersion}`;
1520
+ if (packageSpec !== exactSpec) return { ok: false, status: 2, detail: "runner package spec does not match authorized version" };
1521
+ const resolvedRoot = resolve4(runtimeRoot);
1522
+ const npmEnv = buildMinimalMaintenanceEnv(env, resolvedRoot);
1523
+ const runOptions = { env: npmEnv, cwd: resolvedRoot };
1524
+ let tarDir = null;
1525
+ try {
1526
+ mkdirSync2(resolvedRoot, { recursive: true });
1527
+ mkdirSync2(join3(resolvedRoot, "maintenance"), { recursive: true });
1528
+ writeFileSync2(npmEnv.npm_config_userconfig, "", { mode: 384 });
1529
+ writeFileSync2(npmEnv.npm_config_globalconfig, "", { mode: 384 });
1530
+ const runner = commandRunner({ platform, execPath, env: npmEnv, fileExists, run });
1531
+ const metadata = { version: expectedVersion, integrity: expectedIntegrity };
1532
+ tarDir = join3(resolvedRoot, "staging", randomUUID2());
1533
+ mkdirSync2(tarDir, { recursive: true });
1534
+ const packed = parseJsonOutput(runner.npm([
1535
+ "pack",
1536
+ exactSpec,
1537
+ "--ignore-scripts",
1538
+ "--json",
1539
+ "--pack-destination",
1540
+ tarDir,
1541
+ `--registry=${PUBLIC_REGISTRY}`
1542
+ ], runOptions), "npm pack");
1543
+ const record = Array.isArray(packed) ? packed[0] : packed;
1544
+ const tarball = join3(tarDir, basename(String(record?.filename || "")));
1545
+ if (!existsSync4(tarball) || !basename(tarball).endsWith(".tgz")) throw new Error("npm pack returned no tarball");
1546
+ if (lstatSync3(tarball).size > MAX_TARBALL_BYTES) throw new Error("runner package tarball exceeds size limit");
1547
+ if (record.integrity !== metadata.integrity || tarballIntegrity(tarball) !== metadata.integrity) {
1548
+ throw new Error("runner package sha512 integrity mismatch");
1549
+ }
1550
+ const installed = installSlot({
1551
+ runtimeRoot: resolvedRoot,
1552
+ metadata,
1553
+ tarball,
1554
+ runner,
1555
+ npmEnv,
1556
+ runOptions,
1557
+ force,
1558
+ runtimeAuthorization
1559
+ });
1560
+ return { ok: true, status: 0, ...installed };
1561
+ } catch (error) {
1562
+ return { ok: false, status: 1, detail: sanitizeMaintenanceDiagnostic(error instanceof Error ? error.message : String(error), env) };
1563
+ } finally {
1564
+ if (tarDir) rmSync2(tarDir, { recursive: true, force: true });
1565
+ }
1566
+ }
1567
+ function stageAndActivateBundledUpdate(options) {
1568
+ const staged = stageBundledRuntimeSlot(options);
1569
+ if (!staged.ok) return staged;
1570
+ try {
1571
+ activateSlot(resolve4(options.runtimeRoot), staged.active, options.action);
1572
+ return { ...staged, handoff: true };
1573
+ } catch (error) {
1574
+ return {
1575
+ ok: false,
1576
+ status: 1,
1577
+ detail: sanitizeMaintenanceDiagnostic(error instanceof Error ? error.message : String(error), options.env ?? process.env)
1578
+ };
1579
+ }
1580
+ }
1581
+
1582
+ // ../../scripts/virtual-office/code-runner/legacy-orphan-sweep.mjs
1583
+ import { spawnSync as spawnSync4 } from "node:child_process";
1584
+
1585
+ // ../../scripts/virtual-office/code-runner/orphan-agent-reaper.mjs
1586
+ import { spawnSync as spawnSync3 } from "node:child_process";
1587
+ import { existsSync as existsSync5, mkdirSync as mkdirSync3, readdirSync as readdirSync4, readFileSync as readFileSync5, rmSync as rmSync3, writeFileSync as writeFileSync3 } from "node:fs";
1588
+ import os from "node:os";
1589
+ import path from "node:path";
1590
+ function windowsSystemRoot(env = process.env) {
1591
+ return env.SystemRoot || env.WINDIR || "C:\\Windows";
1592
+ }
1593
+ function windowsPowershellExe(env = process.env) {
1594
+ return path.join(windowsSystemRoot(env), "System32", "WindowsPowerShell", "v1.0", "powershell.exe");
1595
+ }
1596
+ function killProcessTree(pid, { platform = process.platform, spawn: spawn2 = spawnSync3, env = process.env } = {}) {
1597
+ if (!Number.isInteger(pid) || pid <= 0) return false;
1598
+ if (platform === "win32") {
1599
+ const taskkill = path.join(windowsSystemRoot(env), "System32", "taskkill.exe");
1600
+ const r = spawn2(taskkill, ["/PID", String(pid), "/T", "/F"], { windowsHide: true, stdio: "ignore", timeout: 15e3 });
1601
+ return !r.error && r.status === 0;
1602
+ }
1603
+ try {
1604
+ process.kill(-pid, "SIGKILL");
1605
+ return true;
1606
+ } catch {
1607
+ try {
1608
+ process.kill(pid, "SIGKILL");
1609
+ return true;
1610
+ } catch {
1611
+ return false;
1612
+ }
1613
+ }
1614
+ }
1615
+
1616
+ // ../../scripts/virtual-office/code-runner/legacy-orphan-sweep.mjs
1617
+ var MAX_LEGACY_KILLS = 50;
1618
+ var SWEEP_RECENCY_BUFFER_MS = 5e3;
1619
+ var SIGNATURES = [
1620
+ {
1621
+ signature: "claude-headless",
1622
+ // claude-args.mjs always emits `-p --output-format stream-json --verbose`.
1623
+ test: (cl) => /(?:^|[\\/"\s])claude(?:\.exe|\.cmd|\.ps1)?(?:"|\s)/iu.test(cl) && /--output-format[\s"=]+stream-json/iu.test(cl)
1624
+ },
1625
+ {
1626
+ signature: "codex-headless",
1627
+ // openai-compatible-runner always emits `exec --json`.
1628
+ test: (cl) => /(?:^|[\\/"\s])codex(?:\.exe|\.cmd|\.ps1)?(?:"|\s)/iu.test(cl) && /\bexec\b/u.test(cl) && /--json\b/u.test(cl)
1629
+ }
1630
+ ];
1631
+ function matchAgentSignature(commandLine) {
1632
+ if (typeof commandLine !== "string" || !commandLine) return null;
1633
+ for (const { signature, test } of SIGNATURES) {
1634
+ if (test(commandLine)) return signature;
1635
+ }
1636
+ return null;
1637
+ }
1638
+ function selectLegacyOrphans({ processes, cutoffMs, protectedPids = /* @__PURE__ */ new Set() }) {
1639
+ const byPid = /* @__PURE__ */ new Map();
1640
+ for (const proc of processes) {
1641
+ if (Number.isInteger(proc?.pid) && proc.pid > 0) byPid.set(proc.pid, proc);
1642
+ }
1643
+ const kills = [];
1644
+ for (const proc of byPid.values()) {
1645
+ if (protectedPids.has(proc.pid)) continue;
1646
+ if (!(Number.isFinite(proc.creationMs) && proc.creationMs < cutoffMs)) continue;
1647
+ const signature = matchAgentSignature(proc.commandLine);
1648
+ if (!signature) continue;
1649
+ const parent = Number.isInteger(proc.ppid) && proc.ppid > 0 ? byPid.get(proc.ppid) : void 0;
1650
+ const parentDead = !parent || Number.isFinite(parent.creationMs) && parent.creationMs > proc.creationMs;
1651
+ if (!parentDead) continue;
1652
+ kills.push({ pid: proc.pid, creationMs: proc.creationMs, signature, commandLine: proc.commandLine });
1653
+ }
1654
+ kills.sort((a, b) => a.creationMs - b.creationMs);
1655
+ return { kills: kills.slice(0, MAX_LEGACY_KILLS) };
1656
+ }
1657
+ function parsePosixSweepLine(line, nowMs) {
1658
+ const match = /^\s*(\d+)\s+(\d+)\s+(\d+)\s+(.+)$/u.exec(line ?? "");
1659
+ if (!match) return null;
1660
+ const pid = Number(match[1]);
1661
+ if (!Number.isInteger(pid) || pid <= 0) return null;
1662
+ return {
1663
+ pid,
1664
+ ppid: Number(match[2]),
1665
+ creationMs: nowMs - Number(match[3]) * 1e3,
1666
+ commandLine: match[4]
1667
+ };
1668
+ }
1669
+ function listProcessesForSweep({ platform = process.platform, spawn: spawn2 = spawnSync4, nowMs = Date.now(), env = process.env, warn = console.warn } = {}) {
1670
+ const rows = [];
1671
+ if (platform === "win32") {
1672
+ const ps = "Get-CimInstance Win32_Process | Where-Object { $_.CreationDate } | ForEach-Object { @{ p = $_.ProcessId; pp = $_.ParentProcessId; c = (([DateTimeOffset]$_.CreationDate.ToUniversalTime()).ToUnixTimeMilliseconds()); cl = [string]$_.CommandLine } | ConvertTo-Json -Compress }";
1673
+ const result2 = spawn2(windowsPowershellExe(env), ["-NoProfile", "-NonInteractive", "-Command", ps], {
1674
+ windowsHide: true,
1675
+ encoding: "utf8",
1676
+ timeout: 3e4,
1677
+ maxBuffer: 64 * 1024 * 1024
1678
+ });
1679
+ if (result2.error || result2.status !== 0) {
1680
+ const cause = result2.error ? result2.error.message : `powershell exit ${result2.status}`;
1681
+ warn(`[orphan-sweep] process enumeration failed (${cause}); sweeping nothing this cycle`);
1682
+ throw new Error(`process enumeration failed (${cause}); swept nothing`);
1683
+ }
1684
+ for (const line of String(result2.stdout ?? "").split(/\r?\n/u)) {
1685
+ if (!line.trim()) continue;
1686
+ try {
1687
+ const parsed = JSON.parse(line);
1688
+ const pid = Number(parsed?.p);
1689
+ if (!Number.isInteger(pid) || pid <= 0) continue;
1690
+ rows.push({
1691
+ pid,
1692
+ ppid: Number(parsed.pp),
1693
+ creationMs: Number(parsed.c),
1694
+ commandLine: typeof parsed.cl === "string" ? parsed.cl : ""
1695
+ });
1696
+ } catch {
1697
+ }
1698
+ }
1699
+ return rows;
1700
+ }
1701
+ const result = spawn2("ps", ["-eo", "pid=,ppid=,etimes=,args="], { encoding: "utf8", timeout: 3e4, maxBuffer: 64 * 1024 * 1024 });
1702
+ if (result.error || result.status !== 0) {
1703
+ const cause = result.error ? result.error.message : `ps exit ${result.status}`;
1704
+ warn(`[orphan-sweep] process enumeration failed (${cause}); sweeping nothing this cycle`);
1705
+ throw new Error(`process enumeration failed (${cause}); swept nothing`);
1706
+ }
1707
+ for (const line of String(result.stdout ?? "").split("\n")) {
1708
+ const row = parsePosixSweepLine(line, nowMs);
1709
+ if (row) rows.push(row);
1710
+ }
1711
+ return rows;
1712
+ }
1713
+ function runLegacyOrphanSweep({
1714
+ nowMs = Date.now(),
1715
+ protectedPids = [process.pid],
1716
+ listProcesses = listProcessesForSweep,
1717
+ killTree = killProcessTree,
1718
+ log = () => {
1719
+ }
1720
+ } = {}) {
1721
+ try {
1722
+ const processes = listProcesses({ nowMs });
1723
+ const { kills } = selectLegacyOrphans({
1724
+ processes,
1725
+ cutoffMs: nowMs - SWEEP_RECENCY_BUFFER_MS,
1726
+ protectedPids: new Set(protectedPids)
1727
+ });
1728
+ const killed = [];
1729
+ for (const kill of kills) {
1730
+ const done = killTree(kill.pid);
1731
+ log(`legacy-orphan-sweep ${done ? "killed" : "FAILED to kill"} pid=${kill.pid} sig=${kill.signature} cmd=${String(kill.commandLine).slice(0, 200)}`);
1732
+ if (done) killed.push({ pid: kill.pid, signature: kill.signature });
1733
+ }
1734
+ const failed2 = kills.length - killed.length;
1735
+ const detail = kills.length === 0 ? `no orphaned agent processes matched the sweep criteria (${processes.length} scanned)` : `purged ${killed.length} of ${kills.length} orphaned agent process tree(s)${failed2 > 0 ? ` (${failed2} kill(s) failed)` : ""}: ${killed.map((k) => `${k.pid}:${k.signature}`).join(", ").slice(0, 700)}`;
1736
+ return { ok: true, status: 0, detail, killed };
1737
+ } catch (error) {
1738
+ return { ok: false, status: 1, detail: `legacy sweep error: ${error instanceof Error ? error.message : String(error)}`.slice(0, 500), killed: [] };
1739
+ }
1740
+ }
1741
+
1742
+ // src/runner/supervisor-activation.mjs
1743
+ var MAX_ACK_ATTEMPTS = 3;
1744
+ var delay = (ms) => new Promise((resolve5) => setTimeout(resolve5, ms));
1745
+ async function waitForAuthoritativeRunnerHeartbeat({
1746
+ client,
1747
+ runnerId: runnerId2,
1748
+ operatorId,
1749
+ runnerInstanceId,
1750
+ excludeRunnerInstanceId,
1751
+ daemonVersion,
1752
+ supervisorIdentity,
1753
+ timeoutMs = 6e4,
1754
+ pollMs = 1e3
1755
+ }) {
1756
+ if (!runnerInstanceId && !excludeRunnerInstanceId) throw new Error("runner instance identity proof unavailable");
1757
+ const deadline = Date.now() + timeoutMs;
1758
+ while (Date.now() < deadline) {
1759
+ try {
1760
+ const runners = await client.getRunnerStatus({ ...operatorId ? { operatorId } : {} });
1761
+ const runner = runners.find((item) => item?.runner_id === runnerId2 && item.status === "online" && (runnerInstanceId ? item.runner_meta?.runner_instance_id === runnerInstanceId : Boolean(item.runner_meta?.runner_instance_id) && item.runner_meta.runner_instance_id !== excludeRunnerInstanceId) && item.runner_meta?.daemon_version === daemonVersion && item.runner_meta?.supervisor_instance_id === supervisorIdentity.supervisorInstanceId && item.runner_meta?.supervisor_version === supervisorIdentity.supervisorVersion && supervisorIdentity.capabilities.every((value) => item.runner_meta?.supervisor_capabilities?.includes(value)) && Array.isArray(item.runner_meta?.available_agents) && item.runner_meta.available_agents.some((agent) => agent.agent === item.runner_meta.default_agent && agent.installed === true && agent.authenticated === true));
1762
+ if (runner) return runner;
1763
+ } catch {
1764
+ }
1765
+ await delay(pollMs);
1766
+ }
1767
+ throw new Error("authoritative child heartbeat attestation timed out");
1768
+ }
1769
+ async function finishPendingActivation({
1770
+ client,
1771
+ child,
1772
+ runtimeRoot,
1773
+ operatorId,
1774
+ runnerId: runnerId2,
1775
+ selfPath: selfPath2,
1776
+ packageVersion: packageVersion2,
1777
+ supervisorIdentity,
1778
+ waitForLocalRunner: waitForLocalRunner2,
1779
+ localStatus: localStatus2,
1780
+ waitForCloudRunner = waitForAuthoritativeRunnerHeartbeat,
1781
+ cloudTimeoutMs,
1782
+ cloudPollMs,
1783
+ stopChild: stopChild2,
1784
+ launchPreviousChild,
1785
+ log = () => {
1786
+ }
1787
+ }) {
1788
+ const pointer = runtimeRoot ? readActivation(runtimeRoot) : null;
1789
+ if (!pointer?.pending) return true;
1790
+ const runnerIdForAction = pointer.pending.runner_id || runnerId2;
1791
+ const operatorIdForAction = pointer.pending.operator_id || operatorId;
1792
+ if (pointer.pending.terminal_status === "failed") {
1793
+ try {
1794
+ await client.completeRunnerControl(pointer.pending.action_id, {
1795
+ runnerId: runnerIdForAction,
1796
+ ...operatorIdForAction ? { operatorId: operatorIdForAction } : {},
1797
+ ...supervisorIdentity,
1798
+ status: "failed",
1799
+ detail: pointer.pending.terminal_detail
1800
+ });
1801
+ acknowledgeActivationFailure(runtimeRoot, pointer);
1802
+ return true;
1803
+ } catch (error) {
1804
+ log(`activation failure acknowledgement remains pending: ${error instanceof Error ? error.message : String(error)}`);
1805
+ await stopChild2(child);
1806
+ return false;
1807
+ }
1808
+ }
1809
+ let attestation;
1810
+ try {
1811
+ attestation = attestCurrentSupervisor({ runtimeRoot, selfPath: selfPath2, version: packageVersion2 });
1812
+ if (!attestation.ok) throw new Error(attestation.detail);
1813
+ if (!await waitForLocalRunner2(child)) throw new Error("activated runner did not become locally ready");
1814
+ } catch (error) {
1815
+ let detail = `activation attestation failed: ${error instanceof Error ? error.message : String(error)}`.slice(0, 400);
1816
+ let rolledBack = null;
1817
+ try {
1818
+ rolledBack = rollbackActivation(runtimeRoot, pointer, detail);
1819
+ } catch (rollbackError) {
1820
+ detail = `${detail}; rollback pending: ${rollbackError instanceof Error ? rollbackError.message : String(rollbackError)}`.slice(0, 400);
1821
+ }
1822
+ try {
1823
+ await client.completeRunnerControl(pointer.pending.action_id, {
1824
+ runnerId: runnerIdForAction,
1825
+ ...operatorIdForAction ? { operatorId: operatorIdForAction } : {},
1826
+ ...supervisorIdentity,
1827
+ status: "failed",
1828
+ detail
1829
+ });
1830
+ if (rolledBack) acknowledgeActivationFailure(runtimeRoot, rolledBack);
1831
+ } catch {
1832
+ } finally {
1833
+ await stopChild2(child);
1834
+ }
1835
+ return false;
1836
+ }
1837
+ let activatedRunnerInstanceId = null;
1838
+ try {
1839
+ const status = await localStatus2();
1840
+ activatedRunnerInstanceId = status?.runnerInstanceId || null;
1841
+ await waitForCloudRunner({
1842
+ client,
1843
+ runnerId: runnerIdForAction,
1844
+ operatorId: operatorIdForAction,
1845
+ runnerInstanceId: status?.runnerInstanceId,
1846
+ daemonVersion: `vo-mcp/${attestation.active.version}`,
1847
+ supervisorIdentity,
1848
+ timeoutMs: cloudTimeoutMs,
1849
+ pollMs: cloudPollMs
1850
+ });
1851
+ await client.completeRunnerControl(pointer.pending.action_id, {
1852
+ runnerId: runnerIdForAction,
1853
+ ...operatorIdForAction ? { operatorId: operatorIdForAction } : {},
1854
+ ...supervisorIdentity,
1855
+ status: "succeeded",
1856
+ detail: `attested new supervisor ${attestation.active.version} ${attestation.active.integrity}`
1857
+ });
1858
+ finalizeActivation(runtimeRoot, attestation.pointer);
1859
+ return true;
1860
+ } catch (error) {
1861
+ const failure = `activation acknowledgement failed: ${error instanceof Error ? error.message : String(error)}`.slice(0, 400);
1862
+ let retry;
1863
+ try {
1864
+ retry = recordActivationRetry(runtimeRoot, attestation.pointer, failure);
1865
+ } catch (retryError) {
1866
+ log(`activation retry could not be recorded: ${retryError instanceof Error ? retryError.message : String(retryError)}`);
1867
+ await stopChild2(child);
1868
+ return false;
1869
+ }
1870
+ if (retry.pending.ack_attempts < MAX_ACK_ATTEMPTS) {
1871
+ log(`activation acknowledgement pending (${retry.pending.ack_attempts}/${MAX_ACK_ATTEMPTS})`);
1872
+ await stopChild2(child);
1873
+ return false;
1874
+ }
1875
+ const previous = validateSlot(runtimeRoot, retry.previous);
1876
+ let detail = `${failure}; retry limit reached; previous runtime restored`.slice(0, 400);
1877
+ let rollbackChild = null;
1878
+ let rolledBack = null;
1879
+ try {
1880
+ if (!previous.ok) throw new Error(`previous runtime invalid: ${previous.detail}`, { cause: error });
1881
+ rolledBack = rollbackActivation(runtimeRoot, retry, detail);
1882
+ await stopChild2(child);
1883
+ rollbackChild = launchPreviousChild(previous.paths.entry);
1884
+ if (!await waitForLocalRunner2(rollbackChild)) throw new Error("previous runner did not become locally ready", { cause: error });
1885
+ const status = await localStatus2();
1886
+ await waitForCloudRunner({
1887
+ client,
1888
+ runnerId: runnerIdForAction,
1889
+ operatorId: operatorIdForAction,
1890
+ runnerInstanceId: status?.runnerInstanceId,
1891
+ excludeRunnerInstanceId: status?.runnerInstanceId ? void 0 : activatedRunnerInstanceId,
1892
+ daemonVersion: `vo-mcp/${retry.previous.version}`,
1893
+ supervisorIdentity,
1894
+ timeoutMs: cloudTimeoutMs,
1895
+ pollMs: cloudPollMs
1896
+ });
1897
+ } catch (rollbackError) {
1898
+ detail = `${detail}; rollback proof failed: ${rollbackError instanceof Error ? rollbackError.message : String(rollbackError)}`.slice(0, 400);
1899
+ }
1900
+ try {
1901
+ await client.completeRunnerControl(retry.pending.action_id, {
1902
+ runnerId: runnerIdForAction,
1903
+ ...operatorIdForAction ? { operatorId: operatorIdForAction } : {},
1904
+ ...supervisorIdentity,
1905
+ status: "failed",
1906
+ detail
1907
+ });
1908
+ if (rolledBack) acknowledgeActivationFailure(runtimeRoot, rolledBack);
1909
+ } catch {
1910
+ }
1911
+ if (rollbackChild) await stopChild2(rollbackChild);
1912
+ else await stopChild2(child);
1913
+ return false;
1914
+ }
1915
+ }
1916
+
1917
+ // src/runner/supervisor-child-env.mjs
1918
+ import { hostname as systemHostname } from "node:os";
1919
+
1920
+ // src/runner-readiness.mjs
1921
+ function failed({ paired = false, operatorId = null, tenantId = null, githubReady = null, error, message }) {
1922
+ return { ok: false, paired, operatorId, tenantId, githubReady, error, message };
1923
+ }
1924
+ async function responseBody(response) {
1925
+ try {
1926
+ const value = await response.json();
1927
+ return value && typeof value === "object" ? value : {};
1928
+ } catch {
1929
+ return {};
1930
+ }
1931
+ }
1932
+ function serverMessage(body, fallback) {
1933
+ return typeof body.message === "string" && body.message.trim() ? body.message.trim() : fallback;
1934
+ }
1935
+ async function fetchWithTimeout(fetchImpl, url, init, timeoutMs) {
1936
+ const controller = new AbortController();
1937
+ const timer = setTimeout(() => controller.abort(), timeoutMs);
1938
+ try {
1939
+ return await fetchImpl(url, { ...init, signal: controller.signal });
1940
+ } finally {
1941
+ clearTimeout(timer);
1942
+ }
1943
+ }
1944
+ async function probeRunnerReadiness({
1945
+ controlPlaneUrl,
1946
+ token,
1947
+ fetchImpl = fetch,
1948
+ requireGithub = false,
1949
+ timeoutMs = 1e4
1950
+ }) {
1951
+ const base = controlPlaneUrl.replace(/\/+$/u, "");
1952
+ const headers = { authorization: `Bearer ${token}` };
1953
+ let identityResponse;
1954
+ try {
1955
+ identityResponse = await fetchWithTimeout(
1956
+ fetchImpl,
1957
+ `${base}/api/v1/auth/me`,
1958
+ { headers },
1959
+ timeoutMs
1960
+ );
1961
+ } catch (error) {
1962
+ const detail = error instanceof Error ? error.message : String(error);
1963
+ return failed({
1964
+ error: "control_plane_unreachable",
1965
+ message: `AlgoHQ could not be reached: ${detail}`
1966
+ });
1967
+ }
1968
+ const identity = await responseBody(identityResponse);
1969
+ if (!identityResponse.ok) {
1970
+ return failed({
1971
+ error: "credential_rejected",
1972
+ message: "The saved pairing is expired or revoked. Pair this computer again."
1973
+ });
1974
+ }
1975
+ if (identity.provisioned !== true || identity.role !== "operator" || typeof identity.operator_id !== "string" || !identity.operator_id.trim() || typeof identity.tenant_id !== "string" || !identity.tenant_id.trim()) {
1976
+ return failed({
1977
+ error: "operator_identity_missing",
1978
+ message: "The pairing credential is valid but has no operator identity. Pair this computer again."
1979
+ });
1980
+ }
1981
+ const operatorId = identity.operator_id.trim();
1982
+ const tenantId = identity.tenant_id.trim();
1983
+ if (!requireGithub) {
1984
+ return {
1985
+ ok: true,
1986
+ paired: true,
1987
+ operatorId,
1988
+ tenantId,
1989
+ githubReady: null,
1990
+ error: null,
1991
+ message: "Paired to AlgoHQ."
1992
+ };
1993
+ }
1994
+ let githubResponse;
1995
+ try {
1996
+ githubResponse = await fetchWithTimeout(
1997
+ fetchImpl,
1998
+ `${base}/api/v1/github/installation-token`,
1999
+ {
2000
+ method: "POST",
2001
+ headers: { ...headers, "content-type": "application/json" },
2002
+ body: "{}"
2003
+ },
2004
+ timeoutMs
2005
+ );
2006
+ } catch (error) {
2007
+ const detail = error instanceof Error ? error.message : String(error);
2008
+ return failed({
2009
+ paired: true,
2010
+ operatorId,
2011
+ tenantId,
2012
+ githubReady: false,
2013
+ error: "github_preflight_unreachable",
2014
+ message: `GitHub publication readiness could not be checked: ${detail}`
2015
+ });
2016
+ }
2017
+ const github = await responseBody(githubResponse);
2018
+ if (!githubResponse.ok || typeof github.token !== "string" || !github.token) {
2019
+ const error = typeof github.error === "string" && github.error ? github.error : "github_not_ready";
2020
+ return failed({
2021
+ paired: true,
2022
+ operatorId,
2023
+ tenantId,
2024
+ githubReady: false,
2025
+ error,
2026
+ message: serverMessage(github, `GitHub publication preflight failed (HTTP ${githubResponse.status}).`)
2027
+ });
2028
+ }
2029
+ return {
2030
+ ok: true,
2031
+ paired: true,
2032
+ operatorId,
2033
+ tenantId,
2034
+ githubReady: true,
2035
+ error: null,
2036
+ message: "Paired and ready to publish through the Algosuite GitHub App."
2037
+ };
2038
+ }
2039
+ function pairedOperatorScope(readiness) {
2040
+ return readiness?.ok === true && readiness?.paired === true && typeof readiness.operatorId === "string" && readiness.operatorId.trim() ? readiness.operatorId.trim() : null;
2041
+ }
2042
+
2043
+ // src/runner/supervisor-child-env.mjs
2044
+ function resolveSupervisorRunnerId(env = {}, hostname = systemHostname) {
2045
+ const explicit = String(env.VO_CODE_RUNNER_ID || "").trim();
2046
+ return explicit || `vo-code-runner-${hostname()}`;
2047
+ }
2048
+ function buildSupervisorChildEnv({
2049
+ baseEnv = {},
2050
+ controlPlaneUrl,
2051
+ explicitAdminToken = null,
2052
+ pairedOperatorId = null
2053
+ } = {}) {
2054
+ const childEnv = {
2055
+ ...baseEnv,
2056
+ VO_CONTROL_PLANE_URL: controlPlaneUrl
2057
+ };
2058
+ const adminToken = typeof explicitAdminToken === "string" ? explicitAdminToken.trim() : "";
2059
+ const operatorId = typeof pairedOperatorId === "string" ? pairedOperatorId.trim() : "";
2060
+ if (adminToken) childEnv.VO_CONTROL_PLANE_ADMIN_TOKEN = adminToken;
2061
+ else delete childEnv.VO_CONTROL_PLANE_ADMIN_TOKEN;
2062
+ if (operatorId) childEnv.VO_CODE_RUNNER_OPERATOR_IDS = operatorId;
2063
+ return childEnv;
2064
+ }
2065
+ async function prepareSupervisorAuth({
2066
+ baseEnv = {},
2067
+ storedCredential = null,
2068
+ controlPlaneUrl,
2069
+ probeReadiness = probeRunnerReadiness
2070
+ } = {}) {
2071
+ const explicitAdminToken = baseEnv.VO_CONTROL_PLANE_ADMIN_TOKEN?.trim();
2072
+ const token = explicitAdminToken || storedCredential?.vo_credential;
2073
+ if (!token) throw new Error("runner is not paired; run `vo-mcp pair` once on this host");
2074
+ let operatorId = String(baseEnv.VO_CODE_RUNNER_OPERATOR_IDS || "").split(/[\s,]+/u).filter(Boolean)[0] || void 0;
2075
+ if (!explicitAdminToken) {
2076
+ const readiness = await probeReadiness({ controlPlaneUrl, token, requireGithub: true });
2077
+ if (!readiness.ok) throw new Error(`runner readiness failed: ${readiness.message}`);
2078
+ operatorId = pairedOperatorScope(readiness) || void 0;
2079
+ if (!operatorId) throw new Error("runner readiness failed: paired operator scope is missing");
2080
+ }
2081
+ const childEnv = buildSupervisorChildEnv({
2082
+ baseEnv,
2083
+ controlPlaneUrl,
2084
+ explicitAdminToken,
2085
+ pairedOperatorId: explicitAdminToken ? null : operatorId
2086
+ });
2087
+ const clientEnv = {
2088
+ ...baseEnv,
2089
+ VO_CONTROL_PLANE_ADMIN_TOKEN: token,
2090
+ VO_CONTROL_PLANE_URL: controlPlaneUrl,
2091
+ ...operatorId ? { VO_CODE_RUNNER_OPERATOR_IDS: operatorId } : {}
2092
+ };
2093
+ return { childEnv, clientEnv, operatorId };
2094
+ }
2095
+
2096
+ // src/runner/supervisor-credential-reader.mjs
2097
+ import { spawnSync as spawnSync5 } from "node:child_process";
2098
+ import { existsSync as existsSync6 } from "node:fs";
2099
+ import { dirname as dirname3, join as join4 } from "node:path";
2100
+ import { fileURLToPath as fileURLToPath2 } from "node:url";
2101
+ function defaultCredentialHelperPath(metaUrl = import.meta.url) {
2102
+ const moduleDir = dirname3(fileURLToPath2(metaUrl));
2103
+ const bundled = join4(moduleDir, "supervisor-credential-helper.js");
2104
+ const source = join4(moduleDir, "..", "supervisor-credential-helper.mjs");
2105
+ return existsSync6(source) ? source : bundled;
2106
+ }
2107
+ function readStoredCredentialIsolated({
2108
+ spawn: spawn2 = spawnSync5,
2109
+ execPath = process.execPath,
2110
+ helperPath = defaultCredentialHelperPath(),
2111
+ helperArgs = [],
2112
+ env = process.env
2113
+ } = {}) {
2114
+ const result = spawn2(execPath, [helperPath, ...helperArgs], {
2115
+ env,
2116
+ encoding: "utf8",
2117
+ stdio: ["ignore", "pipe", "pipe"],
2118
+ shell: false,
2119
+ windowsHide: true,
2120
+ timeout: 1e4
2121
+ });
2122
+ if (result.status !== 0) {
2123
+ throw new Error("runner credential helper could not read the paired credential");
2124
+ }
2125
+ try {
2126
+ const parsed = JSON.parse(String(result.stdout || ""));
2127
+ return parsed && typeof parsed === "object" ? parsed : null;
2128
+ } catch {
2129
+ throw new Error("runner credential helper returned an invalid credential");
2130
+ }
2131
+ }
2132
+
2133
+ // src/runner-supervisor.mjs
2134
+ var DEFAULT_CONTROL_PLANE_URL = "https://vo-control-plane-bzjphrajaq-uc.a.run.app";
2135
+ var POLL_MS = 5e3;
2136
+ var CHILD_START_MS = 1500;
2137
+ var SUPERVISOR_CAPABILITIES = ["bundled-runtime-slots-v1", "legacy-orphan-purge-v1"];
2138
+ var UUID_RE2 = /^[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/iu;
2139
+ var selfPath = fileURLToPath3(import.meta.url);
2140
+ var childEntry = join5(dirname4(selfPath), "runner-cli.js");
2141
+ var sleep = (ms) => new Promise((resolve5) => setTimeout(resolve5, ms));
2142
+ var runnerId = resolveSupervisorRunnerId(process.env);
2143
+ function packageVersion() {
2144
+ try {
2145
+ return createRequire(import.meta.url)("../package.json").version || "unknown";
2146
+ } catch {
2147
+ return "unknown";
2148
+ }
2149
+ }
2150
+ var requestedSupervisorInstanceId = UUID_RE2.test(String(process.env.VO_RUNNER_SUPERVISOR_INSTANCE_ID || "")) ? process.env.VO_RUNNER_SUPERVISOR_INSTANCE_ID : randomUUID3();
2151
+ var supervisorInstanceId = activationSupervisorInstanceId(
2152
+ runtimeRootFromEnv(process.env),
2153
+ requestedSupervisorInstanceId
2154
+ );
2155
+ var supervisorVersion = packageVersion();
2156
+ var supervisorControlIdentity = {
2157
+ supervisorInstanceId,
2158
+ supervisorVersion,
2159
+ capabilities: SUPERVISOR_CAPABILITIES
2160
+ };
2161
+ function spawnChild(childEnv) {
2162
+ return spawn(process.execPath, [childEntry], {
2163
+ env: childEnv,
2164
+ stdio: "inherit",
2165
+ windowsHide: true
2166
+ });
2167
+ }
2168
+ function spawnPreviousChild(entry, childEnv) {
2169
+ return spawn(process.execPath, [entry, "runner"], {
2170
+ env: childEnv,
2171
+ stdio: "inherit",
2172
+ windowsHide: true
2173
+ });
2174
+ }
2175
+ async function localStatus() {
2176
+ try {
2177
+ const port = Number(process.env.VO_CODE_RUNNER_CONTROL_PORT || 7787);
2178
+ const response = await fetch(`http://127.0.0.1:${port}/status`, { signal: AbortSignal.timeout(800) });
2179
+ if (!response.ok) return null;
2180
+ const body = await response.json();
2181
+ return body?.ok === true ? body : null;
2182
+ } catch {
2183
+ return null;
2184
+ }
2185
+ }
2186
+ async function waitForLocalRunner(child) {
2187
+ const deadline = Date.now() + 15e3;
2188
+ while (Date.now() < deadline) {
2189
+ if (child.exitCode !== null) return false;
2190
+ const status = await localStatus();
2191
+ if (status?.running === true && Number(status.pid) === Number(child.pid)) return true;
2192
+ await sleep(500);
2193
+ }
2194
+ return false;
2195
+ }
2196
+ async function stopChild(child) {
2197
+ if (!child || child.exitCode !== null) return;
2198
+ child.kill(process.platform === "win32" ? void 0 : "SIGTERM");
2199
+ await Promise.race([
2200
+ new Promise((resolve5) => child.once("exit", resolve5)),
2201
+ sleep(15e3)
2202
+ ]);
2203
+ if (child.exitCode === null) child.kill("SIGKILL");
2204
+ }
2205
+ async function main() {
2206
+ const stored = readStoredCredentialIsolated();
2207
+ const controlPlaneUrl = process.env.VO_CONTROL_PLANE_URL || DEFAULT_CONTROL_PLANE_URL;
2208
+ const { childEnv, clientEnv, operatorId } = await prepareSupervisorAuth({
2209
+ baseEnv: process.env,
2210
+ storedCredential: stored,
2211
+ controlPlaneUrl
2212
+ });
2213
+ Object.assign(childEnv, {
2214
+ VO_RUNNER_SUPERVISOR_INSTANCE_ID: supervisorInstanceId,
2215
+ VO_RUNNER_SUPERVISOR_VERSION: supervisorVersion,
2216
+ VO_RUNNER_SUPERVISOR_CAPABILITIES: SUPERVISOR_CAPABILITIES.join(",")
2217
+ });
2218
+ const client = createControlPlaneClient({ baseUrl: controlPlaneUrl, env: clientEnv });
2219
+ const runtimeRoot = runtimeRootFromEnv(process.env);
2220
+ let child = null;
2221
+ let stopping = false;
2222
+ let handling = false;
2223
+ const respawn = () => {
2224
+ if (!stopping && !handling && (!child || child.exitCode !== null)) child = launchChild();
2225
+ };
2226
+ const launchChild = () => {
2227
+ const next = spawnChild(childEnv);
2228
+ next.on("exit", () => setTimeout(respawn, 2e3));
2229
+ return next;
2230
+ };
2231
+ child = launchChild();
2232
+ if (!await finishPendingActivation({
2233
+ client,
2234
+ child,
2235
+ runtimeRoot,
2236
+ operatorId,
2237
+ runnerId,
2238
+ selfPath,
2239
+ packageVersion: supervisorVersion,
2240
+ supervisorIdentity: supervisorControlIdentity,
2241
+ waitForLocalRunner,
2242
+ localStatus,
2243
+ stopChild,
2244
+ launchPreviousChild: (entry) => spawnPreviousChild(entry, childEnv),
2245
+ log: (message) => console.error(`[vo-runner supervisor] ${message}`)
2246
+ })) {
2247
+ stopping = true;
2248
+ return;
2249
+ }
2250
+ const shutdown = async () => {
2251
+ if (stopping) return;
2252
+ stopping = true;
2253
+ await stopChild(child);
2254
+ };
2255
+ process.on("SIGINT", () => {
2256
+ void shutdown().finally(() => process.exit(0));
2257
+ });
2258
+ process.on("SIGTERM", () => {
2259
+ void shutdown().finally(() => process.exit(0));
2260
+ });
2261
+ while (!stopping) {
2262
+ try {
2263
+ const action = await client.pollRunnerControl({
2264
+ runnerId,
2265
+ ...operatorId ? { operatorId } : {},
2266
+ ...supervisorControlIdentity
2267
+ });
2268
+ if (!action) {
2269
+ await sleep(POLL_MS);
2270
+ continue;
2271
+ }
2272
+ handling = true;
2273
+ const beforeStop = await localStatus();
2274
+ if (beforeStop && Number(beforeStop.activeTasks || 0) > 0) {
2275
+ await client.completeRunnerControl(action.actionId, {
2276
+ runnerId,
2277
+ ...operatorId ? { operatorId } : {},
2278
+ ...supervisorControlIdentity,
2279
+ status: "failed",
2280
+ detail: `deferred safely: ${beforeStop.activeTasks} active task(s); retry when the runner is idle`
2281
+ });
2282
+ handling = false;
2283
+ continue;
2284
+ }
2285
+ await stopChild(child);
2286
+ const bundledAction = action.kind === "update" || action.kind === "reinstall";
2287
+ const result = bundledAction ? stageAndActivateBundledUpdate({
2288
+ runtimeRoot,
2289
+ packageSpec: `@algosuite/vo-mcp@${action.desired_package_version}`,
2290
+ expectedVersion: action.desired_package_version,
2291
+ expectedIntegrity: action.desired_package_integrity,
2292
+ action: {
2293
+ actionId: action.actionId,
2294
+ runnerId,
2295
+ operatorId: operatorId || "",
2296
+ supervisorInstanceId
2297
+ },
2298
+ env: process.env,
2299
+ force: action.kind === "reinstall"
2300
+ }) : action.kind === "purge-orphans" ? runLegacyOrphanSweep({
2301
+ protectedPids: [process.pid],
2302
+ log: (message) => console.warn(`[vo-runner supervisor] ${message}`)
2303
+ }) : runHostMaintenance(action.kind, {
2304
+ env: clientEnv,
2305
+ log: (message) => console.warn(`[vo-runner supervisor] ${message}`)
2306
+ });
2307
+ if (result.ok && result.handoff) {
2308
+ console.warn(`[vo-runner supervisor] activated ${result.active.version}; exiting for new-process attestation`);
2309
+ return;
2310
+ }
2311
+ child = launchChild();
2312
+ await sleep(CHILD_START_MS);
2313
+ if (child.exitCode !== null || !await waitForLocalRunner(child)) result.ok = false;
2314
+ await client.completeRunnerControl(action.actionId, {
2315
+ runnerId,
2316
+ ...operatorId ? { operatorId } : {},
2317
+ ...supervisorControlIdentity,
2318
+ status: result.ok ? "succeeded" : "failed",
2319
+ detail: result.ok ? result.detail ? `${result.detail}; runner ${packageVersion()} reconnected`.slice(0, 1e3) : `runner ${packageVersion()} reconnected` : `maintenance exited ${result.status}${result.detail ? `: ${result.detail}` : ""}`
2320
+ });
2321
+ handling = false;
2322
+ } catch (error) {
2323
+ console.error(`[vo-runner supervisor] ${error instanceof Error ? error.message : String(error)}`);
2324
+ handling = false;
2325
+ await sleep(POLL_MS);
2326
+ }
2327
+ }
2328
+ }
2329
+ main().catch((error) => {
2330
+ console.error(`[vo-runner supervisor] fatal: ${error instanceof Error ? error.message : String(error)}`);
2331
+ process.exitCode = 1;
2332
+ });
2333
+ //# sourceMappingURL=runner-supervisor.js.map