@schoolai/shipyard 3.7.0 → 3.8.0-rc.20260529.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (104) hide show
  1. package/dist/{auth-SS7LV5XK.js → auth-EXHO3AG5.js} +4 -4
  2. package/dist/capability-detector-worker.js +142 -0
  3. package/dist/capability-detector-worker.js.map +1 -0
  4. package/dist/{chunk-DKMDBOFU.js → chunk-2CNIEBKO.js} +21 -11
  5. package/dist/chunk-2CNIEBKO.js.map +1 -0
  6. package/dist/chunk-4T2OQAVL.js +51 -0
  7. package/dist/chunk-4T2OQAVL.js.map +1 -0
  8. package/dist/chunk-5ER6ZHA2.js +46 -0
  9. package/dist/chunk-5ER6ZHA2.js.map +1 -0
  10. package/dist/chunk-7LSEE26O.js +24227 -0
  11. package/dist/chunk-7LSEE26O.js.map +1 -0
  12. package/dist/{chunk-7AHRFPAL.js → chunk-7YOU7MBN.js} +183 -17
  13. package/dist/chunk-7YOU7MBN.js.map +1 -0
  14. package/dist/chunk-CMGJGK6R.js +382 -0
  15. package/dist/chunk-CMGJGK6R.js.map +1 -0
  16. package/dist/{chunk-VPMN47TL.js → chunk-CNR7O5YH.js} +1 -2
  17. package/dist/{chunk-2J3WSIAF.js → chunk-EF2DAODF.js} +18 -3
  18. package/dist/chunk-EF2DAODF.js.map +1 -0
  19. package/dist/chunk-HQ43PHOH.js +1203 -0
  20. package/dist/chunk-HQ43PHOH.js.map +1 -0
  21. package/dist/chunk-KITSAHTX.js +134 -0
  22. package/dist/chunk-KITSAHTX.js.map +1 -0
  23. package/dist/chunk-LESHN5J5.js +6898 -0
  24. package/dist/chunk-LESHN5J5.js.map +1 -0
  25. package/dist/{chunk-LW2MS4T5.js → chunk-LMJFHKRD.js} +15 -12
  26. package/dist/chunk-LMJFHKRD.js.map +1 -0
  27. package/dist/{chunk-SNYEQHUK.js → chunk-NACJENDW.js} +14 -21
  28. package/dist/chunk-NACJENDW.js.map +1 -0
  29. package/dist/{chunk-IISLTKYY.js → chunk-TU63KZFW.js} +2 -2
  30. package/dist/chunk-TX6DK4PK.js +186 -0
  31. package/dist/chunk-TX6DK4PK.js.map +1 -0
  32. package/dist/chunk-UQVXWOPT.js +48 -0
  33. package/dist/chunk-UQVXWOPT.js.map +1 -0
  34. package/dist/{chunk-3MNPDCO5.js → chunk-WBB4XHLH.js} +139 -140
  35. package/dist/chunk-WBB4XHLH.js.map +1 -0
  36. package/dist/chunk-X3MULCV5.js +11 -0
  37. package/dist/chunk-X3MULCV5.js.map +1 -0
  38. package/dist/chunk-YZ3Z3ZYI.js +787 -0
  39. package/dist/chunk-YZ3Z3ZYI.js.map +1 -0
  40. package/dist/{chunk-2UN5AR7V.js → chunk-ZAOPND5G.js} +2 -2
  41. package/dist/chunk-ZFKJAYAN.js +542 -0
  42. package/dist/chunk-ZFKJAYAN.js.map +1 -0
  43. package/dist/cursor-hook-shim.js +316 -0
  44. package/dist/cursor-hook-shim.js.map +1 -0
  45. package/dist/cursor-runner.js +358 -0
  46. package/dist/cursor-runner.js.map +1 -0
  47. package/dist/electron-utility.js +111 -0
  48. package/dist/electron-utility.js.map +1 -0
  49. package/dist/git-pool-V73Q53NX.js +18 -0
  50. package/dist/{git-repo-VRT57DGC.js → git-repo-TN3VZXQV.js} +9 -6
  51. package/dist/index.js +12 -12
  52. package/dist/index.js.map +1 -1
  53. package/dist/{logger-GQCSLSZH.js → logger-QHPTO22N.js} +4 -4
  54. package/dist/login-Q7SZI7JJ.js +20 -0
  55. package/dist/{logout-VUNCW5B2.js → logout-O4AVMO5S.js} +6 -6
  56. package/dist/mcp-servers-F64M5T4I.js +24 -0
  57. package/dist/{roi-Y3MX5UW4.js → roi-EYDLPOCS.js} +5 -5
  58. package/dist/rss-worker.js +159 -0
  59. package/dist/rss-worker.js.map +1 -0
  60. package/dist/{serve-O53FNK64.js → serve-6A7RJWEF.js} +89862 -102999
  61. package/dist/{serve-O53FNK64.js.map → serve-6A7RJWEF.js.map} +1 -1
  62. package/dist/skills-ZHEPSBHW.js +11 -0
  63. package/dist/{start-IDFDHRD6.js → start-YGYYIK53.js} +229 -27
  64. package/dist/start-YGYYIK53.js.map +1 -0
  65. package/dist/vault-crypto-BKDOA65F.js +13 -0
  66. package/dist/vault-crypto-BKDOA65F.js.map +1 -0
  67. package/dist/worker.js +6 -3
  68. package/dist/worker.js.map +1 -1
  69. package/package.json +17 -10
  70. package/dist/chunk-2J3WSIAF.js.map +0 -1
  71. package/dist/chunk-3MNPDCO5.js.map +0 -1
  72. package/dist/chunk-66OBOZ3X.js +0 -79
  73. package/dist/chunk-66OBOZ3X.js.map +0 -1
  74. package/dist/chunk-7AHRFPAL.js.map +0 -1
  75. package/dist/chunk-DKMDBOFU.js.map +0 -1
  76. package/dist/chunk-L2WQMPWS.js +0 -666
  77. package/dist/chunk-L2WQMPWS.js.map +0 -1
  78. package/dist/chunk-LW2MS4T5.js.map +0 -1
  79. package/dist/chunk-PI77CUEP.js +0 -49
  80. package/dist/chunk-PI77CUEP.js.map +0 -1
  81. package/dist/chunk-RXI4637N.js +0 -395
  82. package/dist/chunk-RXI4637N.js.map +0 -1
  83. package/dist/chunk-SNYEQHUK.js.map +0 -1
  84. package/dist/chunk-VBPHGPBR.js +0 -126
  85. package/dist/chunk-VBPHGPBR.js.map +0 -1
  86. package/dist/index.d.ts +0 -2
  87. package/dist/login-L4BBPUYO.js +0 -20
  88. package/dist/mcp-servers-MXS5VAWI.js +0 -18
  89. package/dist/shell-V36EX2IJ.js +0 -27
  90. package/dist/skills-GPGRNV4R.js +0 -9
  91. package/dist/start-IDFDHRD6.js.map +0 -1
  92. package/dist/worker.d.ts +0 -49
  93. /package/dist/{auth-SS7LV5XK.js.map → auth-EXHO3AG5.js.map} +0 -0
  94. /package/dist/{chunk-VPMN47TL.js.map → chunk-CNR7O5YH.js.map} +0 -0
  95. /package/dist/{chunk-IISLTKYY.js.map → chunk-TU63KZFW.js.map} +0 -0
  96. /package/dist/{chunk-2UN5AR7V.js.map → chunk-ZAOPND5G.js.map} +0 -0
  97. /package/dist/{git-repo-VRT57DGC.js.map → git-pool-V73Q53NX.js.map} +0 -0
  98. /package/dist/{logger-GQCSLSZH.js.map → git-repo-TN3VZXQV.js.map} +0 -0
  99. /package/dist/{login-L4BBPUYO.js.map → logger-QHPTO22N.js.map} +0 -0
  100. /package/dist/{mcp-servers-MXS5VAWI.js.map → login-Q7SZI7JJ.js.map} +0 -0
  101. /package/dist/{logout-VUNCW5B2.js.map → logout-O4AVMO5S.js.map} +0 -0
  102. /package/dist/{shell-V36EX2IJ.js.map → mcp-servers-F64M5T4I.js.map} +0 -0
  103. /package/dist/{roi-Y3MX5UW4.js.map → roi-EYDLPOCS.js.map} +0 -0
  104. /package/dist/{skills-GPGRNV4R.js.map → skills-ZHEPSBHW.js.map} +0 -0
@@ -0,0 +1,11 @@
1
+ #!/usr/bin/env node
2
+ import {
3
+ _resolveBucketForTesting,
4
+ detectSkills
5
+ } from "./chunk-CMGJGK6R.js";
6
+ import "./chunk-2H7UOFLK.js";
7
+ export {
8
+ _resolveBucketForTesting,
9
+ detectSkills
10
+ };
11
+ //# sourceMappingURL=skills-ZHEPSBHW.js.map
@@ -3,27 +3,31 @@ import {
3
3
  createMetricsCollector,
4
4
  decideAction,
5
5
  makeInitialAttemptState
6
- } from "./chunk-3MNPDCO5.js";
7
- import {
8
- assertNever
9
- } from "./chunk-SNYEQHUK.js";
6
+ } from "./chunk-WBB4XHLH.js";
10
7
  import {
11
8
  ensureAuthenticated,
12
9
  getSignalingUrl
13
- } from "./chunk-LW2MS4T5.js";
14
- import "./chunk-7AHRFPAL.js";
15
- import "./chunk-EHQITHQX.js";
10
+ } from "./chunk-LMJFHKRD.js";
16
11
  import {
17
12
  print
18
- } from "./chunk-IISLTKYY.js";
19
- import "./chunk-2UN5AR7V.js";
13
+ } from "./chunk-TU63KZFW.js";
20
14
  import {
21
15
  loadAuthToken
22
- } from "./chunk-2J3WSIAF.js";
16
+ } from "./chunk-EF2DAODF.js";
17
+ import "./chunk-NACJENDW.js";
18
+ import "./chunk-ZFKJAYAN.js";
19
+ import "./chunk-7YOU7MBN.js";
20
+ import "./chunk-EHQITHQX.js";
21
+ import {
22
+ assertNever
23
+ } from "./chunk-X3MULCV5.js";
24
+ import {
25
+ logger
26
+ } from "./chunk-ZAOPND5G.js";
23
27
  import {
24
28
  validateEnv
25
- } from "./chunk-PI77CUEP.js";
26
- import "./chunk-VPMN47TL.js";
29
+ } from "./chunk-KITSAHTX.js";
30
+ import "./chunk-CNR7O5YH.js";
27
31
  import "./chunk-2H7UOFLK.js";
28
32
 
29
33
  // src/shared/commands/start.ts
@@ -33,12 +37,25 @@ import { fork as childProcessFork } from "child_process";
33
37
  var ABNORMAL_SIGNALS = /* @__PURE__ */ new Set(["SIGABRT", "SIGSEGV", "SIGBUS", "SIGKILL"]);
34
38
  var SHUTDOWN_GRACEFUL_MS = 5e3;
35
39
  var SHUTDOWN_SIGTERM_MS = 2e3;
40
+ var DEFAULT_WEDGE_TIMEOUT_MS = 3e4;
41
+ var WEDGE_SIGUSR2_GRACE_MS = 2e3;
42
+ var WEDGE_CHECK_INTERVAL_MS = 5e3;
43
+ function isHeartbeatPayload(msg) {
44
+ if (msg === null || typeof msg !== "object") return false;
45
+ if (Reflect.get(msg, "type") !== "heartbeat") return false;
46
+ const tick = Reflect.get(msg, "tick");
47
+ const lag = Reflect.get(msg, "eventLoopLagMs");
48
+ return typeof tick === "number" && typeof lag === "number";
49
+ }
36
50
  function runStartSupervisor(deps) {
37
51
  let child = null;
38
52
  let restartState = makeInitialAttemptState();
39
53
  let restartTimer = null;
40
54
  let circuitLogged = false;
41
55
  let shutdownInitiated = false;
56
+ let wedgeCheckTimer = null;
57
+ const wedgeTimeoutMs = deps.wedgeTimeoutMs ?? DEFAULT_WEDGE_TIMEOUT_MS;
58
+ const wedgeEnabled = wedgeTimeoutMs > 0;
42
59
  let resolveDone = () => {
43
60
  };
44
61
  const done = new Promise((resolve) => {
@@ -52,10 +69,22 @@ function runStartSupervisor(deps) {
52
69
  env: { ...process.env, SHIPYARD_DAEMON_CHILD: "1" },
53
70
  stdio: "inherit"
54
71
  });
72
+ const now = deps.now();
55
73
  const holder = {
56
74
  process: proc,
57
- spawnedAt: deps.now(),
58
- exited: false
75
+ spawnedAt: now,
76
+ exited: false,
77
+ /**
78
+ * Seed lastHeartbeatAt = spawnedAt so the wedge check has a sensible
79
+ * baseline. The first real heartbeat overwrites this; until then, the
80
+ * wedge timeout effectively gives the child wedgeTimeoutMs to come
81
+ * alive and emit its first heartbeat (plus eventual SDK / capability
82
+ * detect work). That's intentional — the alternative (subtract some
83
+ * "boot grace" constant) is more code for no extra signal.
84
+ */
85
+ lastHeartbeatAt: now,
86
+ lastHeartbeatLagMs: 0,
87
+ wedgeGraceStartedAt: null
59
88
  };
60
89
  child = holder;
61
90
  deps.log({
@@ -75,6 +104,13 @@ function runStartSupervisor(deps) {
75
104
  err: err instanceof Error ? err.message : String(err)
76
105
  });
77
106
  });
107
+ proc.on("message", (msg) => {
108
+ if (child !== holder) return;
109
+ if (!isHeartbeatPayload(msg)) return;
110
+ holder.lastHeartbeatAt = deps.now();
111
+ holder.lastHeartbeatLagMs = msg.eventLoopLagMs;
112
+ holder.wedgeGraceStartedAt = null;
113
+ });
78
114
  }
79
115
  function handleChildExit(holder, code, signal) {
80
116
  holder.exited = true;
@@ -169,6 +205,10 @@ function runStartSupervisor(deps) {
169
205
  deps.clearTimeout(restartTimer);
170
206
  restartTimer = null;
171
207
  }
208
+ if (wedgeCheckTimer !== null) {
209
+ deps.clearTimeout(wedgeCheckTimer);
210
+ wedgeCheckTimer = null;
211
+ }
172
212
  const holder = child;
173
213
  if (!holder) {
174
214
  deps.log({ event: "daemon_supervisor_shutdown_no_child" });
@@ -214,9 +254,76 @@ function runStartSupervisor(deps) {
214
254
  resolveDone({ exitCode: 1, reason: "graceful" });
215
255
  }
216
256
  }
257
+ function scheduleWedgeCheck() {
258
+ if (!wedgeEnabled) return;
259
+ if (shutdownInitiated) return;
260
+ wedgeCheckTimer = deps.setTimeout(() => {
261
+ wedgeCheckTimer = null;
262
+ runWedgeCheck();
263
+ scheduleWedgeCheck();
264
+ }, WEDGE_CHECK_INTERVAL_MS);
265
+ }
266
+ function runWedgeCheck() {
267
+ if (shutdownInitiated) return;
268
+ const holder = child;
269
+ if (!holder || holder.exited) return;
270
+ const now = deps.now();
271
+ if (holder.wedgeGraceStartedAt !== null) {
272
+ maybeFinishWedgeGrace(holder, now);
273
+ return;
274
+ }
275
+ const silentMs = now - holder.lastHeartbeatAt;
276
+ if (silentMs <= wedgeTimeoutMs) return;
277
+ declareWedge(holder, now, silentMs);
278
+ }
279
+ function maybeFinishWedgeGrace(holder, now) {
280
+ const graceStart = holder.wedgeGraceStartedAt;
281
+ if (graceStart === null) return;
282
+ const graceElapsed = now - graceStart;
283
+ if (graceElapsed < WEDGE_SIGUSR2_GRACE_MS) return;
284
+ deps.log({
285
+ event: "daemon_wedge_sigkill",
286
+ pid: holder.process.pid ?? null,
287
+ graceElapsedMs: graceElapsed
288
+ });
289
+ captureMetric("daemon_wedge_sigkill", {
290
+ pid: holder.process.pid ?? null,
291
+ graceElapsedMs: graceElapsed
292
+ });
293
+ safeKill(holder, "SIGKILL");
294
+ }
295
+ function declareWedge(holder, now, silentMs) {
296
+ deps.log({
297
+ event: "daemon_wedge_detected",
298
+ pid: holder.process.pid ?? null,
299
+ lastHeartbeatAt: holder.lastHeartbeatAt,
300
+ silentMs,
301
+ lastKnownLagMs: holder.lastHeartbeatLagMs,
302
+ wedgeTimeoutMs
303
+ });
304
+ captureMetric("daemon_wedge_detected", {
305
+ pid: holder.process.pid ?? null,
306
+ silentMs,
307
+ lastKnownLagMs: holder.lastHeartbeatLagMs
308
+ });
309
+ holder.wedgeGraceStartedAt = now;
310
+ safeKill(holder, "SIGUSR2");
311
+ }
312
+ function safeKill(holder, signal) {
313
+ try {
314
+ holder.process.kill(signal);
315
+ } catch (err) {
316
+ deps.log({
317
+ event: "daemon_supervisor_kill_failed",
318
+ signal,
319
+ err: err instanceof Error ? err.message : String(err)
320
+ });
321
+ }
322
+ }
217
323
  deps.onSignal("SIGTERM", forwardShutdown);
218
324
  deps.onSignal("SIGINT", forwardShutdown);
219
325
  spawnChild({ isRespawn: false });
326
+ scheduleWedgeCheck();
220
327
  return { done };
221
328
  }
222
329
  function isAbnormalExit(code, signal) {
@@ -225,9 +332,66 @@ function isAbnormalExit(code, signal) {
225
332
  return false;
226
333
  }
227
334
 
335
+ // src/shared/commands/supervisor-process-guards.ts
336
+ function installSupervisorProcessGuards(deps) {
337
+ deps.onUncaughtException((err, origin) => {
338
+ deps.log({
339
+ event: "supervisor_uncaught_exception",
340
+ err: serializeSupervisorProcessError(err),
341
+ origin
342
+ });
343
+ });
344
+ deps.onUnhandledRejection((reason) => {
345
+ deps.log({
346
+ event: "supervisor_unhandled_rejection",
347
+ reason: serializeSupervisorProcessError(reason)
348
+ });
349
+ });
350
+ }
351
+ function serializeSupervisorProcessError(value, depth = 0) {
352
+ if (depth >= 3) {
353
+ return {
354
+ kind: "non_error",
355
+ name: typeof value,
356
+ message: "[Max serialization depth reached]"
357
+ };
358
+ }
359
+ if (value instanceof Error) {
360
+ const serialized = {
361
+ kind: "error",
362
+ name: value.name || "Error",
363
+ message: value.message
364
+ };
365
+ if (value.stack) {
366
+ serialized.stack = value.stack;
367
+ }
368
+ const cause = value.cause;
369
+ if (cause !== void 0) {
370
+ serialized.cause = serializeSupervisorProcessError(cause, depth + 1);
371
+ }
372
+ return serialized;
373
+ }
374
+ return {
375
+ kind: "non_error",
376
+ name: typeof value,
377
+ message: stringifyUnknown(value)
378
+ };
379
+ }
380
+ function stringifyUnknown(value) {
381
+ if (typeof value === "string") return value;
382
+ try {
383
+ const json = JSON.stringify(value);
384
+ if (json !== void 0) return json;
385
+ } catch {
386
+ return String(value);
387
+ }
388
+ return String(value);
389
+ }
390
+
228
391
  // src/shared/commands/start.ts
229
392
  async function startCommand() {
230
- if (process.env.SHIPYARD_DAEMON_CHILD === "1") {
393
+ const env = validateEnv();
394
+ if (env.SHIPYARD_DAEMON_CHILD) {
231
395
  return runDaemonChild();
232
396
  }
233
397
  return runSupervisor();
@@ -243,27 +407,31 @@ async function runDaemonChild() {
243
407
  env.SHIPYARD_USER_ID = authResult.userId;
244
408
  env.SHIPYARD_USER_DISPLAY_NAME = authResult.displayName;
245
409
  env.SHIPYARD_SIGNALING_URL = authResult.signalingUrl;
246
- const { serve } = await import("./serve-O53FNK64.js");
410
+ const { serve } = await import("./serve-6A7RJWEF.js");
247
411
  return serve({ isDev: env.SHIPYARD_DEV, autoOpenBrowser: !authResult.deviceFlowRan });
248
412
  }
249
413
  async function runSupervisor() {
414
+ installSupervisorGuardsOnce();
250
415
  const childModulePath = process.argv[1];
251
416
  if (!childModulePath) {
252
417
  throw new Error("shipyard start: cannot determine childModulePath from process.argv");
253
418
  }
254
- const auth = await loadAuthToken();
255
- const METRICS_WORKER_URL = process.env.SHIPYARD_METRICS_WORKER_URL || "https://shipyard-metrics.jacob-191.workers.dev";
256
- const metrics = auth.status === "ok" ? createMetricsCollector(
257
- METRICS_WORKER_URL,
258
- auth.token,
259
- process.env.SHIPYARD_TELEMETRY !== "0"
260
- ) : void 0;
419
+ const env = validateEnv();
420
+ const metrics = await createSupervisorMetrics(env);
261
421
  const { done } = runStartSupervisor({
262
422
  fork: childProcessFork,
263
423
  childModulePath,
264
424
  childArgs: process.argv.slice(2),
265
- log: (entry) => process.stderr.write(`${JSON.stringify(entry)}
266
- `),
425
+ /**
426
+ * Route supervisor events (daemon_wedge_detected, daemon_supervisor_*,
427
+ * etc.) through the same pino logger the daemon child uses so they land
428
+ * in `~/.shipyard/logs/daemon.log` instead of going to stderr-only.
429
+ * Stderr is preserved (pino's `pino/file destination:1` target writes to
430
+ * it in production) so terminal output is unchanged. Before this, the
431
+ * wedge / sigkill / respawn events were invisible in the log file —
432
+ * confirmed via grep showing 0 matches across all rotation files.
433
+ */
434
+ log: (entry) => logger.info({ component: "supervisor", ...entry }, `supervisor: ${entry.event}`),
267
435
  metrics,
268
436
  now: () => Date.now(),
269
437
  setTimeout: (fn, ms) => setTimeout(fn, ms),
@@ -273,13 +441,47 @@ async function runSupervisor() {
273
441
  },
274
442
  onSignal: (sig, handler) => {
275
443
  process.on(sig, handler);
276
- }
444
+ },
445
+ wedgeTimeoutMs: env.SHIPYARD_WEDGE_TIMEOUT_MS
277
446
  });
278
447
  const result = await done;
279
448
  metrics?.dispose();
280
449
  process.exit(result.exitCode);
281
450
  }
451
+ var supervisorGuardsInstalled = false;
452
+ function installSupervisorGuardsOnce() {
453
+ if (supervisorGuardsInstalled) return;
454
+ supervisorGuardsInstalled = true;
455
+ installSupervisorProcessGuards({
456
+ log: logSupervisorProcessGuard,
457
+ onUncaughtException: (handler) => {
458
+ process.on("uncaughtException", handler);
459
+ },
460
+ onUnhandledRejection: (handler) => {
461
+ process.on("unhandledRejection", handler);
462
+ }
463
+ });
464
+ }
465
+ function logSupervisorProcessGuard(entry) {
466
+ logger.error({ component: "supervisor", ...entry }, `supervisor: ${entry.event}`);
467
+ }
468
+ async function createSupervisorMetrics(env) {
469
+ try {
470
+ const auth = await loadAuthToken();
471
+ return auth.status === "ok" ? createMetricsCollector(env.SHIPYARD_METRICS_WORKER_URL, auth.token, env.SHIPYARD_TELEMETRY) : void 0;
472
+ } catch (err) {
473
+ logger.error(
474
+ {
475
+ component: "supervisor",
476
+ event: "supervisor_metrics_setup_failed",
477
+ err: serializeSupervisorProcessError(err)
478
+ },
479
+ "supervisor: supervisor_metrics_setup_failed"
480
+ );
481
+ return void 0;
482
+ }
483
+ }
282
484
  export {
283
485
  startCommand
284
486
  };
285
- //# sourceMappingURL=start-IDFDHRD6.js.map
487
+ //# sourceMappingURL=start-YGYYIK53.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/shared/commands/start.ts","../src/shared/commands/start-supervisor.ts","../src/shared/commands/supervisor-process-guards.ts"],"sourcesContent":["import { fork as childProcessFork } from 'node:child_process';\nimport { loadAuthToken } from '../../services/bootstrap/auth.js';\nimport { createMetricsCollector } from '../../services/metrics/metrics-collector.js';\nimport { type Env, validateEnv } from '../env.js';\nimport { logger } from '../logger.js';\nimport { ensureAuthenticated, getSignalingUrl } from './login.js';\nimport { print } from './output.js';\nimport { runStartSupervisor } from './start-supervisor.js';\nimport {\n installSupervisorProcessGuards,\n type SupervisorProcessGuardEvent,\n serializeSupervisorProcessError,\n} from './supervisor-process-guards.js';\n\n/**\n * `shipyard start` -- the primary entry point for running the daemon.\n *\n * Two-layer process model (Commit 3 of the watcher-worker subprocess\n * rollout): the parent CLI process forks itself and supervises a daemon\n * child. The branch is gated on `SHIPYARD_DAEMON_CHILD=1` in the env so the\n * forked child re-enters this same function and runs the existing\n * `serve()` path verbatim. The parent monitors the child's exit and\n * respawns on abnormal exits (SIGABRT/SIGSEGV/SIGBUS/SIGKILL or non-zero\n * exit code), backed by `decideAction`'s pure backoff/circuit core\n * (third use site after `file-watcher-guard.ts`'s per-path circuit and\n * the watcher-worker supervisor's restart circuit).\n */\nexport async function startCommand(): Promise<void> {\n const env = validateEnv();\n if (env.SHIPYARD_DAEMON_CHILD) {\n return runDaemonChild();\n }\n return runSupervisor();\n}\n\nasync function runDaemonChild(): Promise<void> {\n const env = validateEnv();\n const signalingUrl = getSignalingUrl();\n\n const codeIdx = process.argv.indexOf('--code');\n const webCode = codeIdx !== -1 ? process.argv[codeIdx + 1] : undefined;\n\n print('Starting Shipyard daemon...\\n');\n\n const authResult = await ensureAuthenticated({ signalingUrl, webCode });\n\n env.SHIPYARD_USER_TOKEN = authResult.token;\n env.SHIPYARD_USER_ID = authResult.userId;\n env.SHIPYARD_USER_DISPLAY_NAME = authResult.displayName;\n env.SHIPYARD_SIGNALING_URL = authResult.signalingUrl;\n\n const { serve } = await import('../../services/serve.js');\n return serve({ isDev: env.SHIPYARD_DEV, autoOpenBrowser: !authResult.deviceFlowRan });\n}\n\nasync function runSupervisor(): Promise<void> {\n installSupervisorGuardsOnce();\n\n /**\n * Re-fork the same entrypoint that started us. `process.argv[1]` is the\n * Node script (or bundled binary) currently executing; the forked child\n * picks up `SHIPYARD_DAEMON_CHILD=1` and takes the `runDaemonChild()`\n * branch above.\n */\n const childModulePath = process.argv[1];\n if (!childModulePath) {\n throw new Error('shipyard start: cannot determine childModulePath from process.argv');\n }\n\n /**\n * Build the supervisor's own metrics collector. The daemon child constructs\n * a separate one in `serve.ts` once it's authenticated. We use the existing\n * cached auth token here without running the device flow — if the user has\n * never logged in, metrics is just `undefined` (the supervisor still runs).\n * This keeps option (a) of Commit 4's spec: supervisor parent gets its own\n * collector at startup, daemon child gets its own as today.\n */\n const env = validateEnv();\n const metrics = await createSupervisorMetrics(env);\n\n /**\n * Read the wedge timeout from the validated env so the supervisor reuses\n * the same Zod-typed default as the daemon child. The daemon child runs in\n * a separate process and revalidates env itself; the only shared contract\n * is the env var name.\n */\n const { done } = runStartSupervisor({\n fork: childProcessFork,\n childModulePath,\n childArgs: process.argv.slice(2),\n /**\n * Route supervisor events (daemon_wedge_detected, daemon_supervisor_*,\n * etc.) through the same pino logger the daemon child uses so they land\n * in `~/.shipyard/logs/daemon.log` instead of going to stderr-only.\n * Stderr is preserved (pino's `pino/file destination:1` target writes to\n * it in production) so terminal output is unchanged. Before this, the\n * wedge / sigkill / respawn events were invisible in the log file —\n * confirmed via grep showing 0 matches across all rotation files.\n */\n log: (entry) =>\n logger.info({ component: 'supervisor', ...entry }, `supervisor: ${entry.event}`),\n metrics,\n now: () => Date.now(),\n setTimeout: (fn, ms) => setTimeout(fn, ms),\n clearTimeout: (timer) => {\n if (timer === null || timer === undefined) return;\n clearTimeout(timer as never);\n },\n onSignal: (sig, handler) => {\n process.on(sig, handler);\n },\n wedgeTimeoutMs: env.SHIPYARD_WEDGE_TIMEOUT_MS,\n });\n\n const result = await done;\n /** Flush any pending metrics events before the supervisor exits. */\n metrics?.dispose();\n /**\n * Propagate the daemon child's exit code (or `1` for circuit-open) to\n * shell/CI. Exiting here also stops any non-trivial Node teardown that\n * the supervisor's signal handlers might otherwise block.\n */\n process.exit(result.exitCode);\n}\n\nlet supervisorGuardsInstalled = false;\n\nfunction installSupervisorGuardsOnce(): void {\n if (supervisorGuardsInstalled) return;\n supervisorGuardsInstalled = true;\n\n installSupervisorProcessGuards({\n log: logSupervisorProcessGuard,\n onUncaughtException: (handler) => {\n process.on('uncaughtException', handler);\n },\n onUnhandledRejection: (handler) => {\n process.on('unhandledRejection', handler);\n },\n });\n}\n\nfunction logSupervisorProcessGuard(entry: SupervisorProcessGuardEvent): void {\n logger.error({ component: 'supervisor', ...entry }, `supervisor: ${entry.event}`);\n}\n\nasync function createSupervisorMetrics(env: Env) {\n try {\n const auth = await loadAuthToken();\n return auth.status === 'ok'\n ? createMetricsCollector(env.SHIPYARD_METRICS_WORKER_URL, auth.token, env.SHIPYARD_TELEMETRY)\n : undefined;\n } catch (err) {\n logger.error(\n {\n component: 'supervisor',\n event: 'supervisor_metrics_setup_failed',\n err: serializeSupervisorProcessError(err),\n },\n 'supervisor: supervisor_metrics_setup_failed'\n );\n return undefined;\n }\n}\n","import type { ChildProcess } from 'node:child_process';\nimport { assertNever } from '../assert-never.js';\nimport { type AttemptState, decideAction, makeInitialAttemptState } from '../file-watcher-guard.js';\n\n/**\n * Mirrors the shape of `MetricsCapture` from\n * `apps/daemon/src/services/metrics/metrics-collector.ts`. Declared inline\n * (not imported) so the supervisor module stays in `apps/daemon/src/shared/`\n * with no upward dependency on `services/metrics/`. Production wiring in\n * `start.ts` constructs a `MetricsCollector` and passes it through.\n */\nexport interface StartSupervisorMetrics {\n capture(eventType: string, properties: Record<string, unknown>): void;\n}\n\n/**\n * Outer CLI supervisor: the parent `shipyard start` process forks itself and\n * supervises the daemon child. On abnormal exit (SIGABRT/SIGSEGV/SIGBUS/SIGKILL\n * or non-zero exit code) it applies the same backoff + circuit semantics as\n * the watcher worker supervisor (third use of `decideAction`'s pure core) and\n * respawns. On graceful exit it propagates the exit code.\n *\n * On supervisor SIGTERM/SIGINT it forwards `{cmd:'shutdown'}` over the IPC\n * channel that `child_process.fork` sets up automatically, then escalates\n * SIGTERM -> SIGKILL on the same shape as the watcher-worker supervisor.\n */\n\nexport interface StartSupervisorDeps {\n fork: typeof import('node:child_process').fork;\n /** Path to the daemon binary entrypoint (typically `process.argv[1]`). */\n childModulePath: string;\n /** Forwarded to the child verbatim (typically `process.argv.slice(2)`). */\n childArgs: string[];\n log: (entry: { event: string; [key: string]: unknown }) => void;\n /**\n * Optional metrics capture. Production wiring constructs a\n * `MetricsCollector` from `loadAuthToken()` config (the supervisor parent\n * runs before the daemon child constructs its own collector). When the\n * user has no saved auth token, the supervisor still runs — metrics is\n * just `undefined` and supervision events still surface via `log`.\n */\n metrics?: StartSupervisorMetrics;\n now: () => number;\n setTimeout: (fn: () => void, ms: number) => unknown;\n clearTimeout: (timer: unknown) => void;\n /**\n * Process signal subscription. Tests inject a stub so SIGTERM/SIGINT can be\n * driven without touching the real `process` global.\n */\n onSignal: (signal: NodeJS.Signals, handler: () => void) => void;\n /**\n * Wedge detection: declare the child wedged if no IPC heartbeat arrives\n * for this long. Default 30_000 (matches `SHIPYARD_WEDGE_TIMEOUT_MS`'s\n * Zod default). Pass 0 to disable wedge detection entirely (useful for\n * tests that don't exercise wedge semantics).\n */\n wedgeTimeoutMs?: number;\n}\n\nexport type StartSupervisorReason = 'graceful' | 'circuit_open';\n\nexport interface StartSupervisorResult {\n /**\n * Resolves when the child exits cleanly OR the circuit opens. Callers can\n * await this from the entrypoint to keep the supervisor process alive\n * until the supervised daemon settles.\n */\n done: Promise<{ exitCode: number; reason: StartSupervisorReason }>;\n}\n\n/** Empirically: SIGSEGV -> 139, SIGBUS -> 138, SIGABRT -> 134 on POSIX. */\nconst ABNORMAL_SIGNALS = new Set<NodeJS.Signals>(['SIGABRT', 'SIGSEGV', 'SIGBUS', 'SIGKILL']);\n\nconst SHUTDOWN_GRACEFUL_MS = 5_000;\nconst SHUTDOWN_SIGTERM_MS = 2_000;\nconst DEFAULT_WEDGE_TIMEOUT_MS = 30_000;\n/**\n * Grace window between SIGUSR2 (capture profile) and SIGKILL (force exit).\n * The child's SIGUSR2 handler does a best-effort 500ms CPU profile capture;\n * 2s leaves enough headroom for the inspector to dump-and-flush, while\n * keeping the wedged daemon from blocking the user any longer than needed.\n */\nconst WEDGE_SIGUSR2_GRACE_MS = 2_000;\n/**\n * How often the supervisor checks for wedge. Frequent enough to catch within\n * ~1 check interval after the timeout expires, infrequent enough that the\n * check itself isn't a measurable cost. The supervisor's loop has almost no\n * work to do — this is the only periodic timer in the parent process.\n */\nconst WEDGE_CHECK_INTERVAL_MS = 5_000;\n\ninterface HeartbeatPayload {\n type: 'heartbeat';\n tick: number;\n eventLoopLagMs: number;\n}\n\nfunction isHeartbeatPayload(msg: unknown): msg is HeartbeatPayload {\n if (msg === null || typeof msg !== 'object') return false;\n if (Reflect.get(msg, 'type') !== 'heartbeat') return false;\n const tick = Reflect.get(msg, 'tick');\n const lag = Reflect.get(msg, 'eventLoopLagMs');\n return typeof tick === 'number' && typeof lag === 'number';\n}\n\ninterface ChildHolder {\n process: ChildProcess;\n spawnedAt: number;\n exited: boolean;\n lastHeartbeatAt: number;\n lastHeartbeatLagMs: number;\n /** null = no wedge currently in flight. Set when SIGUSR2 was sent. */\n wedgeGraceStartedAt: number | null;\n}\n\nexport function runStartSupervisor(deps: StartSupervisorDeps): StartSupervisorResult {\n let child: ChildHolder | null = null;\n let restartState: AttemptState = makeInitialAttemptState();\n let restartTimer: unknown = null;\n let circuitLogged = false;\n let shutdownInitiated = false;\n let wedgeCheckTimer: unknown = null;\n\n const wedgeTimeoutMs = deps.wedgeTimeoutMs ?? DEFAULT_WEDGE_TIMEOUT_MS;\n const wedgeEnabled = wedgeTimeoutMs > 0;\n\n let resolveDone: (value: { exitCode: number; reason: StartSupervisorReason }) => void = () => {};\n const done = new Promise<{ exitCode: number; reason: StartSupervisorReason }>((resolve) => {\n resolveDone = resolve;\n });\n\n function captureMetric(eventType: string, properties: Record<string, unknown>): void {\n deps.metrics?.capture(eventType, properties);\n }\n\n function spawnChild(opts: { isRespawn: boolean }): void {\n const proc = deps.fork(deps.childModulePath, deps.childArgs, {\n env: { ...process.env, SHIPYARD_DAEMON_CHILD: '1' },\n stdio: 'inherit',\n });\n const now = deps.now();\n const holder: ChildHolder = {\n process: proc,\n spawnedAt: now,\n exited: false,\n /**\n * Seed lastHeartbeatAt = spawnedAt so the wedge check has a sensible\n * baseline. The first real heartbeat overwrites this; until then, the\n * wedge timeout effectively gives the child wedgeTimeoutMs to come\n * alive and emit its first heartbeat (plus eventual SDK / capability\n * detect work). That's intentional — the alternative (subtract some\n * \"boot grace\" constant) is more code for no extra signal.\n */\n lastHeartbeatAt: now,\n lastHeartbeatLagMs: 0,\n wedgeGraceStartedAt: null,\n };\n child = holder;\n deps.log({\n event: 'daemon_supervisor_started',\n pid: proc.pid ?? null,\n deathCount: restartState.failureCount,\n });\n\n if (opts.isRespawn) {\n emitRespawn(proc.pid ?? null);\n }\n\n proc.on('exit', (code, signal) => {\n handleChildExit(holder, code, signal);\n });\n proc.on('error', (err) => {\n deps.log({\n event: 'daemon_supervisor_child_error',\n err: err instanceof Error ? err.message : String(err),\n });\n });\n proc.on('message', (msg) => {\n /**\n * Drop stale messages from previous children. `proc.on('message')` is\n * attached to this `proc`, but the holder may have been retired (e.g.\n * concurrent kill + respawn race) before the message lands; ignore\n * anything that no longer belongs to the live `child`.\n */\n if (child !== holder) return;\n if (!isHeartbeatPayload(msg)) return;\n holder.lastHeartbeatAt = deps.now();\n holder.lastHeartbeatLagMs = msg.eventLoopLagMs;\n /**\n * If a wedge was just declared but the child recovered before the\n * SIGKILL window closed, clear the grace state so we don't kill a\n * now-healthy child. The grace window is only 2s; in practice this is\n * rare but cheap to be defensive about.\n */\n holder.wedgeGraceStartedAt = null;\n });\n }\n\n function handleChildExit(\n holder: ChildHolder,\n code: number | null,\n signal: NodeJS.Signals | null\n ): void {\n holder.exited = true;\n /** Drop stale exits — only the live child drives state. */\n if (child !== holder) return;\n child = null;\n\n const uptimeMs = deps.now() - holder.spawnedAt;\n\n if (shutdownInitiated) {\n deps.log({ event: 'daemon_supervisor_child_exited_during_shutdown', code, signal, uptimeMs });\n resolveDone({ exitCode: code ?? 0, reason: 'graceful' });\n return;\n }\n\n if (!isAbnormalExit(code, signal)) {\n deps.log({ event: 'daemon_supervisor_child_exited_clean', code, signal, uptimeMs });\n resolveDone({ exitCode: code ?? 0, reason: 'graceful' });\n return;\n }\n\n handleAbnormalChildExit(holder, code, signal, uptimeMs);\n }\n\n function handleAbnormalChildExit(\n holder: ChildHolder,\n code: number | null,\n signal: NodeJS.Signals | null,\n uptimeMs: number\n ): void {\n deps.log({ event: 'daemon_supervisor_child_died', code, signal, uptimeMs });\n\n /**\n * Reuse the guard's pure restart core: 'subscribe_failure' drives the\n * same backoff + circuit logic per-process. Different state instance,\n * same primitive (third use site after guard's per-path circuit and the\n * watcher-worker supervisor's restart circuit).\n */\n const failureDecision = decideAction(restartState, { kind: 'subscribe_failure' }, deps.now());\n restartState = failureDecision.state;\n emitFailureSignals(failureDecision.signals, holder);\n\n if (restartState.circuitOpenedAt !== null) {\n /**\n * Per the spec for this layer: rather than half-open probe like the\n * watcher worker (which lives inside the daemon), the CLI supervisor\n * surfaces the failure to the user so they can investigate. The\n * supervisor terminates rather than continuing to thrash.\n */\n resolveDone({ exitCode: 1, reason: 'circuit_open' });\n return;\n }\n\n scheduleRespawn();\n }\n\n function emitFailureSignals(\n signals: ReturnType<typeof decideAction>['signals'],\n holder: ChildHolder\n ): void {\n for (const sig of signals) {\n if (sig.kind === 'circuit_opened' && !circuitLogged) {\n circuitLogged = true;\n const windowMs = deps.now() - holder.spawnedAt;\n deps.log({\n event: 'daemon_supervisor_circuit_open',\n deathCount: restartState.failureCount,\n windowMs,\n });\n captureMetric('daemon_supervisor_circuit_open', {\n deathCount: restartState.failureCount,\n windowMs,\n });\n }\n }\n }\n\n function scheduleRespawn(): void {\n if (shutdownInitiated) return;\n if (restartState.circuitOpenedAt !== null) return;\n\n const requestDecision = decideAction(\n restartState,\n { kind: 'request_subscribe', reason: 'rescan', activeCount: 0 },\n deps.now()\n );\n restartState = requestDecision.state;\n deps.log({\n event: 'daemon_supervisor_respawn_scheduled',\n deathCount: restartState.failureCount,\n });\n\n switch (requestDecision.action.kind) {\n case 'wait': {\n restartTimer = deps.setTimeout(() => {\n restartTimer = null;\n if (shutdownInitiated) return;\n spawnChild({ isRespawn: true });\n }, requestDecision.action.ms);\n return;\n }\n case 'subscribe':\n case 'evict_and_subscribe':\n /**\n * `evict_and_subscribe` only fires when activeCount >= MAX_ACTIVE_WATCHERS;\n * we always pass activeCount: 0 so the pure core never returns it here.\n * Treating both as \"respawn now\" keeps the switch exhaustive without\n * coupling this layer to guard-internals.\n */\n spawnChild({ isRespawn: true });\n return;\n case 'reject_stub':\n /** Circuit-open path is handled by the caller before scheduleRespawn. */\n return;\n case 'noop':\n /** decideRequest never returns 'noop'. */\n return;\n default:\n assertNever(requestDecision.action);\n }\n }\n\n /**\n * Emit the `daemon_supervisor_respawned` log + metric. The metric carries\n * `newPid` (post-fork) plus `deathCount`. Called inline in `spawnChild` for\n * respawns; the initial spawn does not emit this event.\n */\n function emitRespawn(newPid: number | null): void {\n deps.log({ event: 'daemon_supervisor_respawned', deathCount: restartState.failureCount });\n captureMetric('daemon_supervisor_respawned', {\n newPid,\n deathCount: restartState.failureCount,\n });\n }\n\n function forwardShutdown(): void {\n if (shutdownInitiated) return;\n shutdownInitiated = true;\n\n if (restartTimer !== null) {\n deps.clearTimeout(restartTimer);\n restartTimer = null;\n }\n if (wedgeCheckTimer !== null) {\n deps.clearTimeout(wedgeCheckTimer);\n wedgeCheckTimer = null;\n }\n\n const holder = child;\n if (!holder) {\n /**\n * Either we never spawned, the previous child already exited, or the\n * circuit opened. Resolve with code 0 so the supervisor process exits.\n */\n deps.log({ event: 'daemon_supervisor_shutdown_no_child' });\n resolveDone({ exitCode: 0, reason: 'graceful' });\n return;\n }\n\n deps.log({ event: 'daemon_supervisor_shutdown_requested' });\n\n /**\n * Cooperative shutdown via the IPC channel that `fork()` sets up. The\n * child should listen for `process.on('message', ...)` and tear down\n * gracefully on `{cmd:'shutdown'}`.\n */\n try {\n holder.process.send?.({ cmd: 'shutdown' });\n } catch (err) {\n deps.log({\n event: 'daemon_supervisor_shutdown_send_failed',\n err: err instanceof Error ? err.message : String(err),\n });\n }\n\n /** SIGTERM after grace, SIGKILL after that. */\n deps.setTimeout(() => onSigtermDeadline(holder), SHUTDOWN_GRACEFUL_MS);\n }\n\n function onSigtermDeadline(holder: ChildHolder): void {\n if (holder.exited) return;\n deps.log({ event: 'daemon_supervisor_shutdown_sigterm' });\n try {\n holder.process.kill('SIGTERM');\n } catch (err) {\n deps.log({\n event: 'daemon_supervisor_kill_failed',\n signal: 'SIGTERM',\n err: err instanceof Error ? err.message : String(err),\n });\n }\n deps.setTimeout(() => onSigkillDeadline(holder), SHUTDOWN_SIGTERM_MS);\n }\n\n function onSigkillDeadline(holder: ChildHolder): void {\n if (holder.exited) return;\n deps.log({ event: 'daemon_supervisor_shutdown_sigkill' });\n try {\n holder.process.kill('SIGKILL');\n } catch (err) {\n deps.log({\n event: 'daemon_supervisor_kill_failed',\n signal: 'SIGKILL',\n err: err instanceof Error ? err.message : String(err),\n });\n /**\n * Safety net: SIGKILL throwing AND on-exit not firing is a paranoid\n * combination (zombie process, EPERM on a child we own), but if it\n * happens the `done` promise hangs forever and so does whoever\n * awaits it. Resolve here so the supervisor parent can exit.\n */\n resolveDone({ exitCode: 1, reason: 'graceful' });\n }\n }\n\n /**\n * Wedge detection loop. Runs in the SUPERVISOR PROCESS, which has almost\n * no work to do — it's a separate Node process from the daemon, so its\n * event loop is not subject to whatever's blocking the daemon child's.\n *\n * The check is a recurring `setTimeout` (not `setInterval`) so we only\n * need one timer-shape dep and so the test harness can drive it via\n * `advanceBy`. The chain self-cancels when `shutdownInitiated` flips true\n * or the function returns without rescheduling.\n */\n function scheduleWedgeCheck(): void {\n if (!wedgeEnabled) return;\n if (shutdownInitiated) return;\n wedgeCheckTimer = deps.setTimeout(() => {\n wedgeCheckTimer = null;\n runWedgeCheck();\n scheduleWedgeCheck();\n }, WEDGE_CHECK_INTERVAL_MS);\n }\n\n function runWedgeCheck(): void {\n if (shutdownInitiated) return;\n const holder = child;\n if (!holder || holder.exited) return;\n\n const now = deps.now();\n if (holder.wedgeGraceStartedAt !== null) {\n maybeFinishWedgeGrace(holder, now);\n return;\n }\n const silentMs = now - holder.lastHeartbeatAt;\n if (silentMs <= wedgeTimeoutMs) return;\n declareWedge(holder, now, silentMs);\n }\n\n /**\n * SIGUSR2 already sent; if the grace window has elapsed without a fresh\n * heartbeat clearing `wedgeGraceStartedAt`, escalate to SIGKILL. The OS\n * kill triggers Node's `exit` event with `signal: 'SIGKILL'`, which goes\n * through `handleChildExit` -> `handleAbnormalChildExit` -> the existing\n * `decideAction` respawn flow. Nothing else to do here.\n */\n function maybeFinishWedgeGrace(holder: ChildHolder, now: number): void {\n const graceStart = holder.wedgeGraceStartedAt;\n if (graceStart === null) return;\n const graceElapsed = now - graceStart;\n if (graceElapsed < WEDGE_SIGUSR2_GRACE_MS) return;\n\n deps.log({\n event: 'daemon_wedge_sigkill',\n pid: holder.process.pid ?? null,\n graceElapsedMs: graceElapsed,\n });\n captureMetric('daemon_wedge_sigkill', {\n pid: holder.process.pid ?? null,\n graceElapsedMs: graceElapsed,\n });\n safeKill(holder, 'SIGKILL');\n }\n\n /**\n * First wedge detection for this child. Log + send SIGUSR2 (to give the\n * daemon a chance to dump a CPU profile via the off-thread inspector)\n * and start the grace window.\n */\n function declareWedge(holder: ChildHolder, now: number, silentMs: number): void {\n deps.log({\n event: 'daemon_wedge_detected',\n pid: holder.process.pid ?? null,\n lastHeartbeatAt: holder.lastHeartbeatAt,\n silentMs,\n lastKnownLagMs: holder.lastHeartbeatLagMs,\n wedgeTimeoutMs,\n });\n captureMetric('daemon_wedge_detected', {\n pid: holder.process.pid ?? null,\n silentMs,\n lastKnownLagMs: holder.lastHeartbeatLagMs,\n });\n\n holder.wedgeGraceStartedAt = now;\n safeKill(holder, 'SIGUSR2');\n }\n\n function safeKill(holder: ChildHolder, signal: NodeJS.Signals): void {\n try {\n holder.process.kill(signal);\n } catch (err) {\n deps.log({\n event: 'daemon_supervisor_kill_failed',\n signal,\n err: err instanceof Error ? err.message : String(err),\n });\n }\n }\n\n /** Wire signal forwarding via injected hook so tests can drive deterministically. */\n deps.onSignal('SIGTERM', forwardShutdown);\n deps.onSignal('SIGINT', forwardShutdown);\n\n /** Spawn the initial child immediately. */\n spawnChild({ isRespawn: false });\n scheduleWedgeCheck();\n\n return { done };\n}\n\nfunction isAbnormalExit(code: number | null, signal: NodeJS.Signals | null): boolean {\n if (signal !== null && ABNORMAL_SIGNALS.has(signal)) return true;\n if (code !== null && code !== 0) return true;\n return false;\n}\n","export type SupervisorProcessGuardEvent =\n | {\n event: 'supervisor_uncaught_exception';\n err: SerializedSupervisorProcessError;\n origin: string;\n }\n | {\n event: 'supervisor_unhandled_rejection';\n reason: SerializedSupervisorProcessError;\n };\n\nexport interface SerializedSupervisorProcessError {\n kind: 'error' | 'non_error';\n name: string;\n message: string;\n stack?: string;\n cause?: SerializedSupervisorProcessError;\n}\n\nexport interface SupervisorProcessGuardDeps {\n log: (entry: SupervisorProcessGuardEvent) => void;\n onUncaughtException: (\n handler: (err: Error, origin: NodeJS.UncaughtExceptionOrigin) => void\n ) => void;\n onUnhandledRejection: (handler: (reason: unknown, promise: Promise<unknown>) => void) => void;\n}\n\nexport function installSupervisorProcessGuards(deps: SupervisorProcessGuardDeps): void {\n deps.onUncaughtException((err, origin) => {\n deps.log({\n event: 'supervisor_uncaught_exception',\n err: serializeSupervisorProcessError(err),\n origin,\n });\n });\n\n deps.onUnhandledRejection((reason) => {\n deps.log({\n event: 'supervisor_unhandled_rejection',\n reason: serializeSupervisorProcessError(reason),\n });\n });\n}\n\nexport function serializeSupervisorProcessError(\n value: unknown,\n depth = 0\n): SerializedSupervisorProcessError {\n if (depth >= 3) {\n return {\n kind: 'non_error',\n name: typeof value,\n message: '[Max serialization depth reached]',\n };\n }\n\n if (value instanceof Error) {\n const serialized: SerializedSupervisorProcessError = {\n kind: 'error',\n name: value.name || 'Error',\n message: value.message,\n };\n if (value.stack) {\n serialized.stack = value.stack;\n }\n const cause = value.cause;\n if (cause !== undefined) {\n serialized.cause = serializeSupervisorProcessError(cause, depth + 1);\n }\n return serialized;\n }\n\n return {\n kind: 'non_error',\n name: typeof value,\n message: stringifyUnknown(value),\n };\n}\n\nfunction stringifyUnknown(value: unknown): string {\n if (typeof value === 'string') return value;\n\n try {\n const json = JSON.stringify(value);\n if (json !== undefined) return json;\n } catch {\n return String(value);\n }\n\n return String(value);\n}\n"],"mappings":"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","names":[]}
@@ -0,0 +1,13 @@
1
+ #!/usr/bin/env node
2
+ import {
3
+ decryptCredential,
4
+ encryptCredential,
5
+ generateVaultKey
6
+ } from "./chunk-5ER6ZHA2.js";
7
+ import "./chunk-2H7UOFLK.js";
8
+ export {
9
+ decryptCredential,
10
+ encryptCredential,
11
+ generateVaultKey
12
+ };
13
+ //# sourceMappingURL=vault-crypto-BKDOA65F.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":[],"sourcesContent":[],"mappings":"","names":[]}
package/dist/worker.js CHANGED
@@ -1,10 +1,13 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
- assertNever,
4
3
  decodeLine,
5
4
  encodeLine
6
- } from "./chunk-SNYEQHUK.js";
7
- import "./chunk-VPMN47TL.js";
5
+ } from "./chunk-NACJENDW.js";
6
+ import "./chunk-ZFKJAYAN.js";
7
+ import {
8
+ assertNever
9
+ } from "./chunk-X3MULCV5.js";
10
+ import "./chunk-CNR7O5YH.js";
8
11
  import "./chunk-2H7UOFLK.js";
9
12
 
10
13
  // src/services/watcher-worker/worker.ts
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/services/watcher-worker/worker.ts"],"sourcesContent":["import { assertNever } from '../../shared/assert-never.js';\nimport {\n decodeLine,\n encodeLine,\n type WorkerCommand,\n type WorkerParcelEvent,\n type WorkerReply,\n type WorkerSubscribeOptions,\n} from './worker-protocol.js';\n\n/**\n * Watcher worker entry point — the only place in the daemon that imports\n * `@parcel/watcher` (post-Commit-4). The worker is intentionally minimal:\n * subscribe/unsubscribe/shutdown over stdio, no business logic, no state\n * beyond a Map<id, AsyncSubscription>.\n *\n * Crashes are expected (parcel/watcher SIGABRTs on FSEvents NULL-deref); we\n * keep the blast surface as small as possible so the parent's supervisor\n * can replay the subscription map onto a fresh worker.\n */\n\ninterface ParcelEventLike {\n type: 'create' | 'update' | 'delete';\n path: string;\n}\n\ninterface ParcelAsyncSubscriptionLike {\n unsubscribe(): Promise<void>;\n}\n\nexport type WorkerSubscribeFn = (\n path: string,\n fn: (err: Error | null, events: ParcelEventLike[]) => unknown,\n opts?: WorkerSubscribeOptions\n) => Promise<ParcelAsyncSubscriptionLike>;\n\nexport interface RunWorkerDeps {\n subscribe: WorkerSubscribeFn;\n stdin: NodeJS.ReadableStream;\n stdout: NodeJS.WritableStream;\n stderr: NodeJS.WritableStream;\n}\n\ninterface ActiveSubscription {\n handle: ParcelAsyncSubscriptionLike;\n}\n\nexport async function runWorker(deps: RunWorkerDeps): Promise<void> {\n const subs = new Map<string, ActiveSubscription>();\n let shuttingDown = false;\n let resolveDone: () => void;\n const done = new Promise<void>((resolve) => {\n resolveDone = resolve;\n });\n\n function logStderr(\n level: 'info' | 'warn' | 'error',\n event: string,\n extra?: Record<string, unknown>\n ): void {\n const payload = extra ? { level, event, ...extra } : { level, event };\n deps.stderr.write(`${JSON.stringify(payload)}\\n`);\n }\n\n function writeReply(reply: WorkerReply): void {\n deps.stdout.write(`${encodeLine(reply)}\\n`);\n }\n\n /** Coerce a parcel/watcher event into the protocol's strict shape. */\n function toWireEvents(events: ParcelEventLike[]): WorkerParcelEvent[] {\n return events.map((e) => ({ type: e.type, path: e.path }));\n }\n\n async function handleSubscribe(\n id: string,\n path: string,\n opts: WorkerSubscribeOptions\n ): Promise<void> {\n const callback = (err: Error | null, events: ParcelEventLike[]) => {\n if (err !== null) {\n logStderr('warn', 'watcher_callback_error', {\n id,\n path,\n err: err.message,\n });\n return;\n }\n /**\n * Drop events for ids that have been unsubscribed but whose underlying\n * watcher hasn't fully torn down yet.\n */\n if (!subs.has(id)) return;\n writeReply({ type: 'events', id, events: toWireEvents(events) });\n };\n\n try {\n const handle = await deps.subscribe(path, callback, opts);\n /** If the worker began shutdown while subscribe was in flight, undo. */\n if (shuttingDown) {\n await handle.unsubscribe().catch(() => {});\n return;\n }\n subs.set(id, { handle });\n writeReply({ type: 'subscribed', id });\n } catch (err) {\n const message = err instanceof Error ? err.message : String(err);\n writeReply({ type: 'subscribe_failed', id, error: message });\n }\n }\n\n async function handleUnsubscribe(id: string): Promise<void> {\n const entry = subs.get(id);\n subs.delete(id);\n if (entry) {\n try {\n await entry.handle.unsubscribe();\n } catch (err) {\n logStderr('warn', 'watcher_unsubscribe_failed', {\n id,\n err: err instanceof Error ? err.message : String(err),\n });\n }\n }\n writeReply({ type: 'unsubscribed', id });\n }\n\n async function handleShutdown(): Promise<void> {\n if (shuttingDown) return;\n shuttingDown = true;\n const entries = Array.from(subs.values());\n subs.clear();\n await Promise.all(\n entries.map(async (entry) => {\n try {\n await entry.handle.unsubscribe();\n } catch (err) {\n logStderr('warn', 'watcher_unsubscribe_failed_on_shutdown', {\n err: err instanceof Error ? err.message : String(err),\n });\n }\n })\n );\n resolveDone();\n }\n\n function dispatch(cmd: WorkerCommand): void {\n switch (cmd.cmd) {\n case 'subscribe':\n void handleSubscribe(cmd.id, cmd.path, cmd.opts);\n return;\n case 'unsubscribe':\n void handleUnsubscribe(cmd.id);\n return;\n case 'shutdown':\n void handleShutdown();\n return;\n default:\n assertNever(cmd);\n }\n }\n\n let buffer = '';\n deps.stdin.on('data', (chunk: Buffer | string) => {\n buffer += typeof chunk === 'string' ? chunk : chunk.toString('utf8');\n let nl = buffer.indexOf('\\n');\n while (nl !== -1) {\n const line = buffer.slice(0, nl);\n buffer = buffer.slice(nl + 1);\n nl = buffer.indexOf('\\n');\n if (line.length === 0) continue;\n const decoded = decodeLine(line);\n if (decoded === null) {\n logStderr('warn', 'worker_decode_failed', { line });\n continue;\n }\n /** Workers should never see Reply messages; ignore-and-log if they do. */\n if ('type' in decoded) {\n logStderr('warn', 'worker_received_unexpected_reply', { type: decoded.type });\n continue;\n }\n dispatch(decoded);\n }\n });\n\n /** Parent died → unsubscribe everything and exit. */\n deps.stdin.on('end', () => {\n void handleShutdown();\n });\n deps.stdin.on('close', () => {\n void handleShutdown();\n });\n\n return done;\n}\n\n/**\n * Default entry: when this file is run directly via `fork()` from the\n * supervisor, import `@parcel/watcher` and wire it up to runWorker. Tests\n * import `runWorker` directly and skip this branch entirely.\n */\nasync function main(): Promise<void> {\n const parcelWatcher = await import('@parcel/watcher');\n await runWorker({\n subscribe: (path, fn, opts) => parcelWatcher.subscribe(path, fn, toParcelOptions(opts)),\n stdin: process.stdin,\n stdout: process.stdout,\n stderr: process.stderr,\n });\n}\n\n/**\n * Translate the protocol's options shape to `@parcel/watcher`'s. The protocol\n * accepts `backend: 'default'` to mean \"use the platform default\" — parcel's\n * `BackendType` does not include 'default' and instead expects the field to\n * be omitted entirely.\n */\nfunction toParcelOptions(\n opts: WorkerSubscribeOptions | undefined\n): { ignore?: string[]; backend?: 'brute-force' | 'watchman' } | undefined {\n if (!opts) return undefined;\n const out: { ignore?: string[]; backend?: 'brute-force' | 'watchman' } = {};\n if (opts.ignore) out.ignore = opts.ignore;\n if (opts.backend && opts.backend !== 'default') out.backend = opts.backend;\n return out;\n}\n\nconst entryUrl = process.argv[1] ? `file://${process.argv[1]}` : null;\nif (entryUrl !== null && import.meta.url === entryUrl) {\n void main();\n}\n"],"mappings":"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","names":[]}
1
+ {"version":3,"sources":["../src/services/watcher-worker/worker.ts"],"sourcesContent":["import { assertNever } from '../../shared/assert-never.js';\nimport {\n decodeLine,\n encodeLine,\n type WorkerCommand,\n type WorkerParcelEvent,\n type WorkerReply,\n type WorkerSubscribeOptions,\n} from './worker-protocol.js';\n\n/**\n * Watcher worker entry point — the only place in the daemon that imports\n * `@parcel/watcher` (post-Commit-4). The worker is intentionally minimal:\n * subscribe/unsubscribe/shutdown over stdio, no business logic, no state\n * beyond a Map<id, AsyncSubscription>.\n *\n * Crashes are expected (parcel/watcher SIGABRTs on FSEvents NULL-deref); we\n * keep the blast surface as small as possible so the parent's supervisor\n * can replay the subscription map onto a fresh worker.\n */\n\ninterface ParcelEventLike {\n type: 'create' | 'update' | 'delete';\n path: string;\n}\n\ninterface ParcelAsyncSubscriptionLike {\n unsubscribe(): Promise<void>;\n}\n\nexport type WorkerSubscribeFn = (\n path: string,\n fn: (err: Error | null, events: ParcelEventLike[]) => unknown,\n opts?: WorkerSubscribeOptions\n) => Promise<ParcelAsyncSubscriptionLike>;\n\nexport interface RunWorkerDeps {\n subscribe: WorkerSubscribeFn;\n stdin: NodeJS.ReadableStream;\n stdout: NodeJS.WritableStream;\n stderr: NodeJS.WritableStream;\n}\n\ninterface ActiveSubscription {\n handle: ParcelAsyncSubscriptionLike;\n}\n\nexport async function runWorker(deps: RunWorkerDeps): Promise<void> {\n const subs = new Map<string, ActiveSubscription>();\n let shuttingDown = false;\n let resolveDone: () => void;\n const done = new Promise<void>((resolve) => {\n resolveDone = resolve;\n });\n\n function logStderr(\n level: 'info' | 'warn' | 'error',\n event: string,\n extra?: Record<string, unknown>\n ): void {\n const payload = extra ? { level, event, ...extra } : { level, event };\n deps.stderr.write(`${JSON.stringify(payload)}\\n`);\n }\n\n function writeReply(reply: WorkerReply): void {\n deps.stdout.write(`${encodeLine(reply)}\\n`);\n }\n\n /** Coerce a parcel/watcher event into the protocol's strict shape. */\n function toWireEvents(events: ParcelEventLike[]): WorkerParcelEvent[] {\n return events.map((e) => ({ type: e.type, path: e.path }));\n }\n\n async function handleSubscribe(\n id: string,\n path: string,\n opts: WorkerSubscribeOptions\n ): Promise<void> {\n const callback = (err: Error | null, events: ParcelEventLike[]) => {\n if (err !== null) {\n logStderr('warn', 'watcher_callback_error', {\n id,\n path,\n err: err.message,\n });\n return;\n }\n /**\n * Drop events for ids that have been unsubscribed but whose underlying\n * watcher hasn't fully torn down yet.\n */\n if (!subs.has(id)) return;\n writeReply({ type: 'events', id, events: toWireEvents(events) });\n };\n\n try {\n const handle = await deps.subscribe(path, callback, opts);\n /** If the worker began shutdown while subscribe was in flight, undo. */\n if (shuttingDown) {\n await handle.unsubscribe().catch(() => {});\n return;\n }\n subs.set(id, { handle });\n writeReply({ type: 'subscribed', id });\n } catch (err) {\n const message = err instanceof Error ? err.message : String(err);\n writeReply({ type: 'subscribe_failed', id, error: message });\n }\n }\n\n async function handleUnsubscribe(id: string): Promise<void> {\n const entry = subs.get(id);\n subs.delete(id);\n if (entry) {\n try {\n await entry.handle.unsubscribe();\n } catch (err) {\n logStderr('warn', 'watcher_unsubscribe_failed', {\n id,\n err: err instanceof Error ? err.message : String(err),\n });\n }\n }\n writeReply({ type: 'unsubscribed', id });\n }\n\n async function handleShutdown(): Promise<void> {\n if (shuttingDown) return;\n shuttingDown = true;\n const entries = Array.from(subs.values());\n subs.clear();\n await Promise.all(\n entries.map(async (entry) => {\n try {\n await entry.handle.unsubscribe();\n } catch (err) {\n logStderr('warn', 'watcher_unsubscribe_failed_on_shutdown', {\n err: err instanceof Error ? err.message : String(err),\n });\n }\n })\n );\n resolveDone();\n }\n\n function dispatch(cmd: WorkerCommand): void {\n switch (cmd.cmd) {\n case 'subscribe':\n void handleSubscribe(cmd.id, cmd.path, cmd.opts);\n return;\n case 'unsubscribe':\n void handleUnsubscribe(cmd.id);\n return;\n case 'shutdown':\n void handleShutdown();\n return;\n default:\n assertNever(cmd);\n }\n }\n\n let buffer = '';\n deps.stdin.on('data', (chunk: Buffer | string) => {\n buffer += typeof chunk === 'string' ? chunk : chunk.toString('utf8');\n let nl = buffer.indexOf('\\n');\n while (nl !== -1) {\n const line = buffer.slice(0, nl);\n buffer = buffer.slice(nl + 1);\n nl = buffer.indexOf('\\n');\n if (line.length === 0) continue;\n const decoded = decodeLine(line);\n if (decoded === null) {\n logStderr('warn', 'worker_decode_failed', { line });\n continue;\n }\n /** Workers should never see Reply messages; ignore-and-log if they do. */\n if ('type' in decoded) {\n logStderr('warn', 'worker_received_unexpected_reply', { type: decoded.type });\n continue;\n }\n dispatch(decoded);\n }\n });\n\n /** Parent died → unsubscribe everything and exit. */\n deps.stdin.on('end', () => {\n void handleShutdown();\n });\n deps.stdin.on('close', () => {\n void handleShutdown();\n });\n\n return done;\n}\n\n/**\n * Default entry: when this file is run directly via `fork()` from the\n * supervisor, import `@parcel/watcher` and wire it up to runWorker. Tests\n * import `runWorker` directly and skip this branch entirely.\n */\nasync function main(): Promise<void> {\n const parcelWatcher = await import('@parcel/watcher');\n await runWorker({\n subscribe: (path, fn, opts) => parcelWatcher.subscribe(path, fn, toParcelOptions(opts)),\n stdin: process.stdin,\n stdout: process.stdout,\n stderr: process.stderr,\n });\n}\n\n/**\n * Translate the protocol's options shape to `@parcel/watcher`'s. The protocol\n * accepts `backend: 'default'` to mean \"use the platform default\" — parcel's\n * `BackendType` does not include 'default' and instead expects the field to\n * be omitted entirely.\n */\nfunction toParcelOptions(\n opts: WorkerSubscribeOptions | undefined\n): { ignore?: string[]; backend?: 'brute-force' | 'watchman' } | undefined {\n if (!opts) return undefined;\n const out: { ignore?: string[]; backend?: 'brute-force' | 'watchman' } = {};\n if (opts.ignore) out.ignore = opts.ignore;\n if (opts.backend && opts.backend !== 'default') out.backend = opts.backend;\n return out;\n}\n\nconst entryUrl = process.argv[1] ? `file://${process.argv[1]}` : null;\nif (entryUrl !== null && import.meta.url === entryUrl) {\n void main();\n}\n"],"mappings":"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","names":[]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@schoolai/shipyard",
3
- "version": "3.7.0",
3
+ "version": "3.8.0-rc.20260529.0",
4
4
  "description": "Shipyard daemon - Claude Agent SDK + Loro CRDT sync",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -19,25 +19,32 @@
19
19
  "postinstall": "node -e \"const fs=require('fs'),path=require('path');const d=path.join(__dirname,'node_modules','node-pty','prebuilds');if(fs.existsSync(d))for(const a of fs.readdirSync(d)){const h=path.join(d,a,'spawn-helper');try{fs.chmodSync(h,0o755)}catch{}}\""
20
20
  },
21
21
  "dependencies": {
22
- "@anthropic-ai/claude-agent-sdk": "0.2.120",
22
+ "@anthropic-ai/claude-agent-sdk": "0.3.153",
23
+ "@cursor/sdk": "1.0.13",
23
24
  "@linear/sdk": "82.1.0",
24
25
  "@modelcontextprotocol/sdk": "^1.29.0",
26
+ "@openai/codex": "0.133.0",
27
+ "@parcel/watcher": "^2.5.6",
28
+ "happy-dom": "^20.5.0",
25
29
  "loro-crdt": "1.12.1",
30
+ "mermaid": "^11.12.3",
31
+ "modern-screenshot": "^4.6.8",
26
32
  "node-datachannel": "^0.32.1",
27
33
  "node-pty": "^1.0.0",
28
- "@parcel/watcher": "^2.5.6",
34
+ "open": "^11.0.0",
29
35
  "pino": "^9.6.0",
30
- "mermaid": "^11.12.3",
31
- "jsdom": "^26.1.0",
32
36
  "pino-roll": "^2.1.0",
33
- "modern-screenshot": "^4.6.8",
34
- "typescript": "^5.9.3",
37
+ "toml": "^4.1.1",
38
+ "typescript": "^6.0.3",
35
39
  "typescript-language-server": "^5.1.3",
36
- "ws": "^8.18.0",
37
- "open": "^11.0.0"
40
+ "ws": "^8.18.0"
38
41
  },
39
42
  "optionalDependencies": {
40
- "node-llama-cpp": "^3.16.2",
43
+ "@cursor/sdk-darwin-arm64": "1.0.13",
44
+ "@cursor/sdk-darwin-x64": "1.0.13",
45
+ "@cursor/sdk-linux-arm64": "1.0.13",
46
+ "@cursor/sdk-linux-x64": "1.0.13",
47
+ "@cursor/sdk-win32-x64": "1.0.13",
41
48
  "pino-pretty": "^13.0.0"
42
49
  },
43
50
  "bundledDependencies": [
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/services/bootstrap/auth.ts"],"sourcesContent":["import { mkdir, readFile, unlink, writeFile } from 'node:fs/promises';\nimport { dirname, join } from 'node:path';\nimport { z } from 'zod';\nimport { getShipyardHome } from '../../shared/env.js';\n\nconst ShipyardConfigSchema = z.object({\n auth: z.object({\n token: z.string(),\n userId: z.string(),\n displayName: z.string(),\n providers: z.array(z.string()),\n expiresAt: z.number(),\n signalingUrl: z.string(),\n }),\n});\n\nexport type ShipyardConfig = z.infer<typeof ShipyardConfigSchema>;\n\nexport function getConfigPath(): string {\n return join(getShipyardHome(), 'config.json');\n}\n\nexport async function readConfig(): Promise<ShipyardConfig | null> {\n try {\n const raw = await readFile(getConfigPath(), 'utf-8');\n return ShipyardConfigSchema.parse(JSON.parse(raw));\n } catch {\n return null;\n }\n}\n\nexport async function writeConfig(config: ShipyardConfig): Promise<void> {\n await mkdir(dirname(getConfigPath()), { recursive: true, mode: 0o700 });\n await writeFile(getConfigPath(), `${JSON.stringify(config, null, 2)}\\n`, { mode: 0o600 });\n}\n\nexport async function deleteConfig(): Promise<boolean> {\n try {\n await unlink(getConfigPath());\n return true;\n } catch {\n return false;\n }\n}\n\nexport type AuthResult =\n | { status: 'ok'; token: string; userId: string; displayName: string; signalingUrl?: string }\n | { status: 'expired' }\n | { status: 'missing' };\n\nexport interface AuthEnvOverrides {\n SHIPYARD_USER_TOKEN?: string;\n SHIPYARD_USER_ID?: string;\n SHIPYARD_USER_DISPLAY_NAME?: string;\n SHIPYARD_SIGNALING_URL?: string;\n}\n\n/**\n * Load auth token from validated env (CI override) or config file.\n */\nexport async function loadAuthToken(envOverrides?: AuthEnvOverrides): Promise<AuthResult> {\n const envToken = envOverrides?.SHIPYARD_USER_TOKEN;\n if (envToken) {\n return {\n status: 'ok',\n token: envToken,\n userId: envOverrides?.SHIPYARD_USER_ID ?? '',\n displayName: envOverrides?.SHIPYARD_USER_DISPLAY_NAME ?? '',\n signalingUrl: envOverrides?.SHIPYARD_SIGNALING_URL,\n };\n }\n\n const config = await readConfig();\n if (!config?.auth?.token) return { status: 'missing' };\n\n if (config.auth.expiresAt < Date.now()) {\n return { status: 'expired' };\n }\n\n return {\n status: 'ok',\n token: config.auth.token,\n userId: config.auth.userId,\n displayName: config.auth.displayName,\n signalingUrl: config.auth.signalingUrl,\n };\n}\n"],"mappings":";;;;;;;;;AAAA,SAAS,OAAO,UAAU,QAAQ,iBAAiB;AACnD,SAAS,SAAS,YAAY;AAI9B,IAAM,uBAAuB,iBAAE,OAAO;AAAA,EACpC,MAAM,iBAAE,OAAO;AAAA,IACb,OAAO,iBAAE,OAAO;AAAA,IAChB,QAAQ,iBAAE,OAAO;AAAA,IACjB,aAAa,iBAAE,OAAO;AAAA,IACtB,WAAW,iBAAE,MAAM,iBAAE,OAAO,CAAC;AAAA,IAC7B,WAAW,iBAAE,OAAO;AAAA,IACpB,cAAc,iBAAE,OAAO;AAAA,EACzB,CAAC;AACH,CAAC;AAIM,SAAS,gBAAwB;AACtC,SAAO,KAAK,gBAAgB,GAAG,aAAa;AAC9C;AAEA,eAAsB,aAA6C;AACjE,MAAI;AACF,UAAM,MAAM,MAAM,SAAS,cAAc,GAAG,OAAO;AACnD,WAAO,qBAAqB,MAAM,KAAK,MAAM,GAAG,CAAC;AAAA,EACnD,QAAQ;AACN,WAAO;AAAA,EACT;AACF;AAEA,eAAsB,YAAY,QAAuC;AACvE,QAAM,MAAM,QAAQ,cAAc,CAAC,GAAG,EAAE,WAAW,MAAM,MAAM,IAAM,CAAC;AACtE,QAAM,UAAU,cAAc,GAAG,GAAG,KAAK,UAAU,QAAQ,MAAM,CAAC,CAAC;AAAA,GAAM,EAAE,MAAM,IAAM,CAAC;AAC1F;AAEA,eAAsB,eAAiC;AACrD,MAAI;AACF,UAAM,OAAO,cAAc,CAAC;AAC5B,WAAO;AAAA,EACT,QAAQ;AACN,WAAO;AAAA,EACT;AACF;AAiBA,eAAsB,cAAc,cAAsD;AACxF,QAAM,WAAW,cAAc;AAC/B,MAAI,UAAU;AACZ,WAAO;AAAA,MACL,QAAQ;AAAA,MACR,OAAO;AAAA,MACP,QAAQ,cAAc,oBAAoB;AAAA,MAC1C,aAAa,cAAc,8BAA8B;AAAA,MACzD,cAAc,cAAc;AAAA,IAC9B;AAAA,EACF;AAEA,QAAM,SAAS,MAAM,WAAW;AAChC,MAAI,CAAC,QAAQ,MAAM,MAAO,QAAO,EAAE,QAAQ,UAAU;AAErD,MAAI,OAAO,KAAK,YAAY,KAAK,IAAI,GAAG;AACtC,WAAO,EAAE,QAAQ,UAAU;AAAA,EAC7B;AAEA,SAAO;AAAA,IACL,QAAQ;AAAA,IACR,OAAO,OAAO,KAAK;AAAA,IACnB,QAAQ,OAAO,KAAK;AAAA,IACpB,aAAa,OAAO,KAAK;AAAA,IACzB,cAAc,OAAO,KAAK;AAAA,EAC5B;AACF;","names":[]}