@schoolai/shipyard 3.22.0 → 3.23.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 (58) hide show
  1. package/dist/{auth-SRHTZ2JL.js → auth-OHZTVIQU.js} +3 -3
  2. package/dist/capability-detector-worker.js +8 -8
  3. package/dist/{chunk-TFWDJCUJ.js → chunk-2UE3TNY7.js} +2 -2
  4. package/dist/{chunk-WFBU4LXL.js → chunk-2YH743ND.js} +2 -2
  5. package/dist/{chunk-XEBCOG2U.js → chunk-7E6VXTEO.js} +4 -4
  6. package/dist/{chunk-MRD6526D.js → chunk-CA5PKTNJ.js} +2 -2
  7. package/dist/{chunk-JGFN3QI6.js → chunk-CGNWVGXL.js} +4 -4
  8. package/dist/{chunk-ABFHBWBF.js → chunk-HQTCVJRK.js} +2 -2
  9. package/dist/{chunk-TSJOSKN3.js → chunk-JD4WV327.js} +2 -2
  10. package/dist/{chunk-DE3IJ4E4.js → chunk-K7EXPNAD.js} +6 -6
  11. package/dist/{chunk-WPH74RMT.js → chunk-LRBG3X5U.js} +3 -3
  12. package/dist/{chunk-VOC55DTY.js → chunk-OCYT65KO.js} +7 -7
  13. package/dist/{chunk-6RJG24XI.js → chunk-ROEOOZOW.js} +2 -2
  14. package/dist/{chunk-UTL5PF43.js → chunk-SXXAWBW7.js} +5 -3
  15. package/dist/{chunk-UTL5PF43.js.map → chunk-SXXAWBW7.js.map} +1 -1
  16. package/dist/{chunk-TUQTEWK3.js → chunk-VSRWL5SP.js} +2 -2
  17. package/dist/{chunk-WCWHY4C3.js → chunk-W3EMVBWP.js} +4 -4
  18. package/dist/{chunk-VPJRM77V.js → chunk-Y4RRW6MA.js} +4 -4
  19. package/dist/cursor-runner.js +3 -3
  20. package/dist/electron-utility.js +6 -6
  21. package/dist/{git-repo-3OG3TPCC.js → git-repo-IVN2RQNY.js} +4 -4
  22. package/dist/index.js +10 -10
  23. package/dist/{logger-R3DVMU7P.js → logger-I6HU4VSX.js} +3 -3
  24. package/dist/{login-TKO7FQXH.js → login-WL5WEEGJ.js} +6 -6
  25. package/dist/{logout-23UJSTCS.js → logout-EZQ4UBLH.js} +5 -5
  26. package/dist/{mcp-servers-2QITAK3X.js → mcp-servers-52C67MDD.js} +4 -4
  27. package/dist/{plan-backfill-D6FJZ65E.js → plan-backfill-EF6KR6BE.js} +4 -4
  28. package/dist/{roi-E27VSQ7N.js → roi-GUUSTWXN.js} +3 -3
  29. package/dist/{serve-5D77VQ6J.js → serve-J3MG3WHK.js} +147 -56
  30. package/dist/{serve-5D77VQ6J.js.map → serve-J3MG3WHK.js.map} +1 -1
  31. package/dist/{skills-N4FXCXYQ.js → skills-XBREPCFU.js} +2 -2
  32. package/dist/{start-VQSOEMXG.js → start-TCI35UML.js} +11 -11
  33. package/dist/start-TCI35UML.js.map +1 -0
  34. package/package.json +1 -1
  35. package/dist/start-VQSOEMXG.js.map +0 -1
  36. /package/dist/{auth-SRHTZ2JL.js.map → auth-OHZTVIQU.js.map} +0 -0
  37. /package/dist/{chunk-TFWDJCUJ.js.map → chunk-2UE3TNY7.js.map} +0 -0
  38. /package/dist/{chunk-WFBU4LXL.js.map → chunk-2YH743ND.js.map} +0 -0
  39. /package/dist/{chunk-XEBCOG2U.js.map → chunk-7E6VXTEO.js.map} +0 -0
  40. /package/dist/{chunk-MRD6526D.js.map → chunk-CA5PKTNJ.js.map} +0 -0
  41. /package/dist/{chunk-JGFN3QI6.js.map → chunk-CGNWVGXL.js.map} +0 -0
  42. /package/dist/{chunk-ABFHBWBF.js.map → chunk-HQTCVJRK.js.map} +0 -0
  43. /package/dist/{chunk-TSJOSKN3.js.map → chunk-JD4WV327.js.map} +0 -0
  44. /package/dist/{chunk-DE3IJ4E4.js.map → chunk-K7EXPNAD.js.map} +0 -0
  45. /package/dist/{chunk-WPH74RMT.js.map → chunk-LRBG3X5U.js.map} +0 -0
  46. /package/dist/{chunk-VOC55DTY.js.map → chunk-OCYT65KO.js.map} +0 -0
  47. /package/dist/{chunk-6RJG24XI.js.map → chunk-ROEOOZOW.js.map} +0 -0
  48. /package/dist/{chunk-TUQTEWK3.js.map → chunk-VSRWL5SP.js.map} +0 -0
  49. /package/dist/{chunk-WCWHY4C3.js.map → chunk-W3EMVBWP.js.map} +0 -0
  50. /package/dist/{chunk-VPJRM77V.js.map → chunk-Y4RRW6MA.js.map} +0 -0
  51. /package/dist/{git-repo-3OG3TPCC.js.map → git-repo-IVN2RQNY.js.map} +0 -0
  52. /package/dist/{logger-R3DVMU7P.js.map → logger-I6HU4VSX.js.map} +0 -0
  53. /package/dist/{login-TKO7FQXH.js.map → login-WL5WEEGJ.js.map} +0 -0
  54. /package/dist/{logout-23UJSTCS.js.map → logout-EZQ4UBLH.js.map} +0 -0
  55. /package/dist/{mcp-servers-2QITAK3X.js.map → mcp-servers-52C67MDD.js.map} +0 -0
  56. /package/dist/{plan-backfill-D6FJZ65E.js.map → plan-backfill-EF6KR6BE.js.map} +0 -0
  57. /package/dist/{roi-E27VSQ7N.js.map → roi-GUUSTWXN.js.map} +0 -0
  58. /package/dist/{skills-N4FXCXYQ.js.map → skills-XBREPCFU.js.map} +0 -0
@@ -5,8 +5,8 @@ import {
5
5
  loadAuthToken,
6
6
  readConfig,
7
7
  writeConfig
8
- } from "./chunk-TFWDJCUJ.js";
9
- import "./chunk-UTL5PF43.js";
8
+ } from "./chunk-2UE3TNY7.js";
9
+ import "./chunk-SXXAWBW7.js";
10
10
  import "./chunk-JCEWPG3R.js";
11
11
  import "./chunk-2H7UOFLK.js";
12
12
  export {
@@ -16,4 +16,4 @@ export {
16
16
  readConfig,
17
17
  writeConfig
18
18
  };
19
- //# sourceMappingURL=auth-SRHTZ2JL.js.map
19
+ //# sourceMappingURL=auth-OHZTVIQU.js.map
@@ -3,19 +3,19 @@ import {
3
3
  detectCapabilities,
4
4
  refreshMcpServers,
5
5
  registerBuiltInProfiles
6
- } from "./chunk-VOC55DTY.js";
7
- import "./chunk-TUQTEWK3.js";
6
+ } from "./chunk-OCYT65KO.js";
7
+ import "./chunk-VSRWL5SP.js";
8
8
  import {
9
9
  getRepoMetadata
10
- } from "./chunk-DE3IJ4E4.js";
10
+ } from "./chunk-K7EXPNAD.js";
11
11
  import "./chunk-4T2OQAVL.js";
12
12
  import {
13
13
  MCPTokenStore
14
- } from "./chunk-JGFN3QI6.js";
14
+ } from "./chunk-CGNWVGXL.js";
15
15
  import "./chunk-RR6V6SNM.js";
16
- import "./chunk-6RJG24XI.js";
16
+ import "./chunk-ROEOOZOW.js";
17
17
  import "./chunk-ZFKJAYAN.js";
18
- import "./chunk-ABFHBWBF.js";
18
+ import "./chunk-HQTCVJRK.js";
19
19
  import "./chunk-QI5ERXFK.js";
20
20
  import "./chunk-UESTOND6.js";
21
21
  import "./chunk-EHQITHQX.js";
@@ -23,8 +23,8 @@ import "./chunk-X3MULCV5.js";
23
23
  import {
24
24
  flushLogger,
25
25
  logger
26
- } from "./chunk-MRD6526D.js";
27
- import "./chunk-UTL5PF43.js";
26
+ } from "./chunk-CA5PKTNJ.js";
27
+ import "./chunk-SXXAWBW7.js";
28
28
  import "./chunk-JCEWPG3R.js";
29
29
  import "./chunk-2H7UOFLK.js";
30
30
 
@@ -1,7 +1,7 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
3
  getShipyardHome
4
- } from "./chunk-UTL5PF43.js";
4
+ } from "./chunk-SXXAWBW7.js";
5
5
  import {
6
6
  external_exports
7
7
  } from "./chunk-JCEWPG3R.js";
@@ -90,4 +90,4 @@ export {
90
90
  deleteConfig,
91
91
  loadAuthToken
92
92
  };
93
- //# sourceMappingURL=chunk-TFWDJCUJ.js.map
93
+ //# sourceMappingURL=chunk-2UE3TNY7.js.map
@@ -1,7 +1,7 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
3
  getLogFilePath
4
- } from "./chunk-MRD6526D.js";
4
+ } from "./chunk-CA5PKTNJ.js";
5
5
 
6
6
  // src/shared/commands/output.ts
7
7
  import { appendFileSync } from "fs";
@@ -40,4 +40,4 @@ export {
40
40
  print,
41
41
  printError
42
42
  };
43
- //# sourceMappingURL=chunk-WFBU4LXL.js.map
43
+ //# sourceMappingURL=chunk-2YH743ND.js.map
@@ -1,14 +1,14 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
3
  getDaemonVersion
4
- } from "./chunk-WCWHY4C3.js";
4
+ } from "./chunk-W3EMVBWP.js";
5
5
  import {
6
6
  WorkerCommandCodec,
7
7
  WorkerReplyCodec
8
8
  } from "./chunk-WW4UGZBY.js";
9
9
  import {
10
10
  buildNodeSpawnEnv
11
- } from "./chunk-TUQTEWK3.js";
11
+ } from "./chunk-VSRWL5SP.js";
12
12
  import {
13
13
  createServiceSupervisor
14
14
  } from "./chunk-ZFKJAYAN.js";
@@ -19,7 +19,7 @@ import {
19
19
  getShipyardHome,
20
20
  resolveNodeExecPath,
21
21
  validateEnv
22
- } from "./chunk-UTL5PF43.js";
22
+ } from "./chunk-SXXAWBW7.js";
23
23
 
24
24
  // src/services/metrics/metrics-collector.ts
25
25
  import { readFileSync } from "fs";
@@ -1116,4 +1116,4 @@ export {
1116
1116
  shutdownFileWatcherGuard,
1117
1117
  guardedSubscribe
1118
1118
  };
1119
- //# sourceMappingURL=chunk-XEBCOG2U.js.map
1119
+ //# sourceMappingURL=chunk-7E6VXTEO.js.map
@@ -2,7 +2,7 @@
2
2
  import {
3
3
  getShipyardHome,
4
4
  validateEnv
5
- } from "./chunk-UTL5PF43.js";
5
+ } from "./chunk-SXXAWBW7.js";
6
6
 
7
7
  // src/shared/logger.ts
8
8
  import { mkdirSync } from "fs";
@@ -145,4 +145,4 @@ export {
145
145
  createChildLogger,
146
146
  flushLogger
147
147
  };
148
- //# sourceMappingURL=chunk-MRD6526D.js.map
148
+ //# sourceMappingURL=chunk-CA5PKTNJ.js.map
@@ -5,10 +5,10 @@ import {
5
5
  } from "./chunk-RR6V6SNM.js";
6
6
  import {
7
7
  logger
8
- } from "./chunk-MRD6526D.js";
8
+ } from "./chunk-CA5PKTNJ.js";
9
9
  import {
10
10
  isClaudeCodeKeychainReadEnabled
11
- } from "./chunk-UTL5PF43.js";
11
+ } from "./chunk-SXXAWBW7.js";
12
12
  import {
13
13
  external_exports
14
14
  } from "./chunk-JCEWPG3R.js";
@@ -1558,7 +1558,7 @@ async function detectMCPServers(environments, tokenStore, log, lastKnown, prefer
1558
1558
  lastKnown
1559
1559
  );
1560
1560
  } catch (err) {
1561
- const { logger: logger2 } = await import("./logger-R3DVMU7P.js");
1561
+ const { logger: logger2 } = await import("./logger-I6HU4VSX.js");
1562
1562
  if (lastKnown && lastKnown.length > 0) {
1563
1563
  logger2.warn(
1564
1564
  { err, lastKnownCount: lastKnown.length },
@@ -1763,4 +1763,4 @@ export {
1763
1763
  resolveAuthStatuses,
1764
1764
  detectMCPServers
1765
1765
  };
1766
- //# sourceMappingURL=chunk-JGFN3QI6.js.map
1766
+ //# sourceMappingURL=chunk-CGNWVGXL.js.map
@@ -8,7 +8,7 @@ import {
8
8
  } from "./chunk-X3MULCV5.js";
9
9
  import {
10
10
  logger
11
- } from "./chunk-MRD6526D.js";
11
+ } from "./chunk-CA5PKTNJ.js";
12
12
 
13
13
  // src/services/asset-resolver.ts
14
14
  var ASSET_PATH_META_KEY = "shipyard.assetPath";
@@ -588,4 +588,4 @@ export {
588
588
  toRecord,
589
589
  buildCursorUserPrompt
590
590
  };
591
- //# sourceMappingURL=chunk-ABFHBWBF.js.map
591
+ //# sourceMappingURL=chunk-HQTCVJRK.js.map
@@ -5,7 +5,7 @@ import {
5
5
  import {
6
6
  getShipyardHome,
7
7
  validateEnv
8
- } from "./chunk-UTL5PF43.js";
8
+ } from "./chunk-SXXAWBW7.js";
9
9
 
10
10
  // src/services/bootstrap/native-crash-report.ts
11
11
  import { mkdirSync } from "fs";
@@ -59,4 +59,4 @@ function setOptionalFlag(report, flag) {
59
59
  export {
60
60
  enableNativeCrashReports
61
61
  };
62
- //# sourceMappingURL=chunk-TSJOSKN3.js.map
62
+ //# sourceMappingURL=chunk-JD4WV327.js.map
@@ -7,10 +7,10 @@ import {
7
7
  } from "./chunk-RR6V6SNM.js";
8
8
  import {
9
9
  logger
10
- } from "./chunk-MRD6526D.js";
10
+ } from "./chunk-CA5PKTNJ.js";
11
11
  import {
12
12
  getShipyardHome
13
- } from "./chunk-UTL5PF43.js";
13
+ } from "./chunk-SXXAWBW7.js";
14
14
  import {
15
15
  external_exports
16
16
  } from "./chunk-JCEWPG3R.js";
@@ -261,7 +261,7 @@ async function refreshRepoPathsInBackground(log) {
261
261
  const startGen = cacheGeneration;
262
262
  const start = Date.now();
263
263
  try {
264
- const { findGitRepos: findGitRepos2, discoverWorktrees: discoverWorktrees2 } = await import("./git-repo-3OG3TPCC.js");
264
+ const { findGitRepos: findGitRepos2, discoverWorktrees: discoverWorktrees2 } = await import("./git-repo-IVN2RQNY.js");
265
265
  const { homedir: homedir2 } = await import("os");
266
266
  const repos = await findGitRepos2(homedir2());
267
267
  const worktrees = await discoverWorktrees2(repos);
@@ -653,7 +653,7 @@ async function runDetectEnvironments(lastKnown) {
653
653
  repoPaths = await findGitRepos(homedir2());
654
654
  } catch (err) {
655
655
  if (lastKnown && lastKnown.length > 0) {
656
- const { logger: logger2 } = await import("./logger-R3DVMU7P.js");
656
+ const { logger: logger2 } = await import("./logger-I6HU4VSX.js");
657
657
  logger2.warn(
658
658
  { err, lastKnownCount: lastKnown.length },
659
659
  "detectEnvironments findGitRepos threw \u2014 preserving lastKnown"
@@ -663,7 +663,7 @@ async function runDetectEnvironments(lastKnown) {
663
663
  return [];
664
664
  }
665
665
  if (repoPaths.length === 0 && lastKnown && lastKnown.length > 0) {
666
- const { logger: logger2 } = await import("./logger-R3DVMU7P.js");
666
+ const { logger: logger2 } = await import("./logger-I6HU4VSX.js");
667
667
  logger2.warn(
668
668
  { lastKnownCount: lastKnown.length },
669
669
  "detectEnvironments walk empty but lastKnown non-empty \u2014 preserving lastKnown"
@@ -796,4 +796,4 @@ export {
796
796
  discoverWorktrees,
797
797
  _testing
798
798
  };
799
- //# sourceMappingURL=chunk-DE3IJ4E4.js.map
799
+ //# sourceMappingURL=chunk-K7EXPNAD.js.map
@@ -37,11 +37,11 @@ import {
37
37
  } from "./chunk-X3MULCV5.js";
38
38
  import {
39
39
  logger
40
- } from "./chunk-MRD6526D.js";
40
+ } from "./chunk-CA5PKTNJ.js";
41
41
  import {
42
42
  getPlanBackfillMode,
43
43
  getShipyardHome
44
- } from "./chunk-UTL5PF43.js";
44
+ } from "./chunk-SXXAWBW7.js";
45
45
  import {
46
46
  external_exports
47
47
  } from "./chunk-JCEWPG3R.js";
@@ -34783,4 +34783,4 @@ export {
34783
34783
  runPlanBackfill,
34784
34784
  buildTaskStateStore
34785
34785
  };
34786
- //# sourceMappingURL=chunk-WPH74RMT.js.map
34786
+ //# sourceMappingURL=chunk-LRBG3X5U.js.map
@@ -10,7 +10,7 @@ import {
10
10
  run,
11
11
  runWithTimeout,
12
12
  truncateDiff
13
- } from "./chunk-DE3IJ4E4.js";
13
+ } from "./chunk-K7EXPNAD.js";
14
14
  import {
15
15
  gitExecSafe
16
16
  } from "./chunk-4T2OQAVL.js";
@@ -19,13 +19,13 @@ import {
19
19
  CODEX_RUNTIME_ID,
20
20
  CURSOR_RUNTIME_ID,
21
21
  detectMCPServers
22
- } from "./chunk-JGFN3QI6.js";
22
+ } from "./chunk-CGNWVGXL.js";
23
23
  import {
24
24
  isEnoent
25
25
  } from "./chunk-RR6V6SNM.js";
26
26
  import {
27
27
  detectSkills
28
- } from "./chunk-6RJG24XI.js";
28
+ } from "./chunk-ROEOOZOW.js";
29
29
  import {
30
30
  createOneShotPool
31
31
  } from "./chunk-ZFKJAYAN.js";
@@ -38,7 +38,7 @@ import {
38
38
  formatTaskFeedback,
39
39
  toRecord,
40
40
  toSdkContent
41
- } from "./chunk-ABFHBWBF.js";
41
+ } from "./chunk-HQTCVJRK.js";
42
42
  import {
43
43
  AGENT_IDS,
44
44
  AGENT_SYSTEM_CLAUDE_CODE,
@@ -60,12 +60,12 @@ import {
60
60
  } from "./chunk-UESTOND6.js";
61
61
  import {
62
62
  logger
63
- } from "./chunk-MRD6526D.js";
63
+ } from "./chunk-CA5PKTNJ.js";
64
64
  import {
65
65
  getShipyardHome,
66
66
  resolveNodeExecPath,
67
67
  validateEnv
68
- } from "./chunk-UTL5PF43.js";
68
+ } from "./chunk-SXXAWBW7.js";
69
69
  import {
70
70
  external_exports
71
71
  } from "./chunk-JCEWPG3R.js";
@@ -8295,4 +8295,4 @@ export {
8295
8295
  refreshInstalledAgents,
8296
8296
  refreshCodexAuthSlice
8297
8297
  };
8298
- //# sourceMappingURL=chunk-VOC55DTY.js.map
8298
+ //# sourceMappingURL=chunk-OCYT65KO.js.map
@@ -244,7 +244,7 @@ async function detectSkills(environments, lastKnown, options) {
244
244
  try {
245
245
  return await detectSkillsInner(environments, resolvedMirror, options?.activeRepoRoot);
246
246
  } catch (err) {
247
- const { logger } = await import("./logger-R3DVMU7P.js");
247
+ const { logger } = await import("./logger-I6HU4VSX.js");
248
248
  if (lastKnown && lastKnown.length > 0) {
249
249
  logger.warn(
250
250
  { err, lastKnownCount: lastKnown.length },
@@ -428,4 +428,4 @@ export {
428
428
  scopeEnvironmentsToActiveRepo,
429
429
  _resolveBucketForTesting
430
430
  };
431
- //# sourceMappingURL=chunk-6RJG24XI.js.map
431
+ //# sourceMappingURL=chunk-ROEOOZOW.js.map
@@ -224,11 +224,13 @@ var EnvSchema = external_exports.object({
224
224
  SHIPYARD_FILE_WATCHER_MAX: external_exports.coerce.number().int().positive().optional(),
225
225
  /**
226
226
  * Override the daemon's own --max-old-space-size (MB). When absent, the
227
- * value is auto-derived from os.totalmem() × 25 %, clamped to [2048, 4096].
227
+ * value is auto-derived from os.totalmem() × 25 %, clamped to [2048, 8192].
228
228
  * An explicit value is honored as-is (clamped to 65536 to catch typos) and
229
229
  * passed to --max-old-space-size at BOTH spawn sites: the outer CLI
230
230
  * supervisor (start-supervisor.ts) and the Electron utility-process fork
231
- * (daemon-host.ts). See apps/daemon/src/shared/daemon-heap-limit.ts.
231
+ * (daemon-host.ts). See apps/daemon/src/shared/daemon-heap-limit.ts. The
232
+ * Electron path also honors a `daemon.maxHeapGb` config.json setting below
233
+ * this env var in precedence — see daemon-heap-limit-config.ts.
232
234
  */
233
235
  SHIPYARD_DAEMON_MAX_OLD_SPACE_MB: external_exports.coerce.number().int().positive().optional()
234
236
  });
@@ -258,4 +260,4 @@ export {
258
260
  validateEnv,
259
261
  getStallProfilerConfig
260
262
  };
261
- //# sourceMappingURL=chunk-UTL5PF43.js.map
263
+ //# sourceMappingURL=chunk-SXXAWBW7.js.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/shared/env.ts"],"sourcesContent":["import { homedir } from 'node:os';\nimport { join } from 'node:path';\nimport { z } from 'zod';\n\n/**\n * Enable fine-grained tool streaming so MCP tool inputs stream token-by-token\n * via input_json_delta events instead of being buffered as complete JSON.\n * Set early so the subprocess inherits it.\n */\nprocess.env.CLAUDE_CODE_ENABLE_FINE_GRAINED_TOOL_STREAMING = '1';\n\n/**\n * Enable the Task system (TaskCreate/TaskGet/TaskList/TaskUpdate) which provides\n * structured task management with dependency tracking (blocks/blockedBy).\n * Replaces TodoWrite — when enabled, CC only has TaskCreate (not TodoWrite).\n */\nprocess.env.CLAUDE_CODE_ENABLE_TASKS = '1';\n\n/**\n * Bootstrap exception: reads `process.env` directly because it feeds the\n * `SHIPYARD_DATA_DIR` schema transform (via `shipyardDirName`). Routing it\n * through `validateEnv()` would recurse (validateEnv → parse → transform →\n * shipyardDirName → isDevMode → validateEnv).\n */\nexport function isDevMode(): boolean {\n return process.env.SHIPYARD_DEV === '1' || process.env.SHIPYARD_DEV === 'true';\n}\n\n/**\n * When enabled, the agent runs with only Shipyard builtins (system prompt,\n * comment + visualize MCP servers) and no user/plugin MCP servers.\n * Useful for testing the harness in isolation without external interference.\n *\n * Set `SHIPYARD_VANILLA_AGENT=1` to activate.\n */\nexport function isVanillaAgentMode(): boolean {\n return validateEnv().SHIPYARD_VANILLA_AGENT;\n}\n\n/**\n * Reuse Claude Code's macOS Keychain item (\"Claude Code-credentials\") as a\n * source for MCP connector OAuth clients/tokens.\n *\n * Default OFF. The daemon is a different binary than Claude Code, so reading\n * that Keychain item triggers a macOS password prompt in a GUI session (or a\n * silent denial when headless) — unacceptable in the connector status path,\n * which runs on every capability refresh. Shipyard's own token store is the\n * source of truth, mirroring how local MCPs work (no prompt). Opt back into\n * the legacy import behavior with `SHIPYARD_MCP_REUSE_CC_KEYCHAIN=1`.\n */\nexport function isClaudeCodeKeychainReadEnabled(): boolean {\n return validateEnv().SHIPYARD_MCP_REUSE_CC_KEYCHAIN;\n}\n\n/**\n * Reuse Codex's macOS Keychain item (\"Codex MCP Credentials\") as a source for\n * per-MCP-connector OAuth clients/tokens (Codex DCRs its own atomic\n * client_id+token pairs, useful for borrowing on connectors Claude Code hasn't\n * registered with).\n *\n * Default OFF for the same reason as {@link isClaudeCodeKeychainReadEnabled}:\n * the daemon is a different binary than Codex, so reading that Keychain item\n * triggers a macOS password prompt in a GUI session (or a silent denial when\n * headless) — unacceptable in the connector status path. The\n * `~/.codex/.credentials.json` file fallback is read regardless. Opt in with\n * `SHIPYARD_MCP_REUSE_CODEX_KEYCHAIN=1`.\n */\nexport function isCodexKeychainReadEnabled(): boolean {\n return validateEnv().SHIPYARD_MCP_REUSE_CODEX_KEYCHAIN;\n}\n\nfunction shipyardDirName(): string {\n return isDevMode() ? '.shipyard-dev' : '.shipyard';\n}\n\n/**\n * The signaling worker is a RENDEZVOUS point: a browser and this daemon only\n * meet if they hit the SAME worker. This MUST equal the web build's\n * `VITE_SESSION_SERVER_URL` (.github/workflows/ci.yml + publish-npm.yml) — all\n * live clients (web next+stable, daemons all channels) point here. Do not make\n * this per-channel; a staging worker is a CI/E2E fixture only. See AGENTS.md\n * Invariant #22 (the #4186 \"install daemon\" incident).\n */\nconst DEFAULT_PRODUCTION_SIGNALING_URL = 'https://shipyard-session-server.jacob-191.workers.dev';\nconst DEFAULT_PRODUCTION_WEB_URL = 'https://shipyard.computer';\n\n/**\n * Bootstrap exception: reads `process.env` directly (not `validateEnv()`)\n * because it computes the electron-utility startup-breadcrumb path BEFORE\n * `validateEnv()` runs — the breadcrumb must still resolve when validateEnv\n * itself throws (see apps/daemon/src/electron-utility.ts).\n */\nexport function getShipyardHome(): string {\n if (process.env.SHIPYARD_HOME) return process.env.SHIPYARD_HOME;\n return join(homedir(), shipyardDirName());\n}\n\n/**\n * The executable to fork JS subprocesses with (cursor-runner, the TS language\n * server, the cursor-hook shim).\n *\n * `child_process.fork`/`spawn` default to `process.execPath`. Inside an\n * Electron *utility* process (how the packaged daemon runs) that does NOT point\n * at the main app binary — it resolves to a per-platform helper executable\n * (e.g. `…/Frameworks/Shipyard Helper.app/Contents/MacOS/Shipyard Helper`) that\n * may not exist on disk in the shipped bundle. Forking it ENOENTs, which (with\n * the cursor-runner pre-warm running at boot) crash-looped the daemon on\n * 2026-06-01 (Dylan's log).\n *\n * The Electron *main* process has a guaranteed-valid `process.execPath` (the\n * main app binary, which also honors `ELECTRON_RUN_AS_NODE=1`) and forwards it\n * as `SHIPYARD_NODE_EXEC_PATH` (see apps/electron/src/main/daemon-host.ts). The\n * CLI daemon and dev leave it unset and fall back to `process.execPath`, which\n * is already correct there. Declared in `EnvSchema` so a malformed value fails\n * at boot. Re-exported from `spawn-as-node.ts` for call-site ergonomics.\n */\nexport function resolveNodeExecPath(): string {\n return validateEnv().SHIPYARD_NODE_EXEC_PATH || process.execPath;\n}\n\n/**\n * Resolved one-time plan-backfill mode (boot). Unset/default ⇒ `auto`: the\n * daemon rebuilds missing plan CRDTs write-once on boot, gated only by the\n * marker's `completed` flag (a no-op on every later boot). The env var is an\n * OPTIONAL OVERRIDE, not the gate: `dry-run` forces a read-only fleet-telemetry\n * pass that writes nothing; `off` disables the scan entirely (escape hatch);\n * `write` forces a write pass that bypasses the completion gate (manual re-run /\n * testing).\n */\nexport function getPlanBackfillMode(): 'off' | 'auto' | 'dry-run' | 'write' {\n return validateEnv().SHIPYARD_PLAN_BACKFILL_MODE;\n}\n\nexport const EnvSchema = z.object({\n ANTHROPIC_API_KEY: z.string().min(1).optional(),\n SHIPYARD_DEV: z\n .enum(['1', 'true', '0', 'false', ''])\n .optional()\n .transform((v) => v === '1' || v === 'true'),\n SHIPYARD_DAEMON_CHILD: z\n .enum(['1', 'true', '0', 'false', ''])\n .optional()\n .transform((v) => v === '1' || v === 'true'),\n SHIPYARD_DATA_DIR: z\n .string()\n .optional()\n .transform((v) => v ?? `~/${shipyardDirName()}/data`),\n LOG_LEVEL: z.enum(['debug', 'info', 'warn', 'error']).default('info'),\n SHIPYARD_SIGNALING_URL: z.string().url().optional(),\n SHIPYARD_PRODUCTION_SIGNALING_URL: z.string().url().default(DEFAULT_PRODUCTION_SIGNALING_URL),\n SHIPYARD_METRICS_WORKER_URL: z\n .string()\n .url()\n .default('https://shipyard-metrics.jacob-191.workers.dev'),\n SHIPYARD_TELEMETRY: z\n .enum(['1', 'true', '0', 'false', ''])\n .optional()\n .transform((v) => v !== '0' && v !== 'false'),\n SHIPYARD_USER_TOKEN: z.string().optional(),\n /**\n * Worker URL for the `shipyard roi` report command (distinct from\n * SHIPYARD_METRICS_WORKER_URL, which is the telemetry ingestion endpoint).\n * Falls back to the `--worker-url` CLI flag when unset.\n */\n SHIPYARD_METRICS_URL: z.string().url().optional(),\n SHIPYARD_USER_ID: z.string().optional(),\n SHIPYARD_MACHINE_ID: z.string().optional(),\n SHIPYARD_MACHINE_NAME: z.string().optional(),\n SHIPYARD_USER_DISPLAY_NAME: z.string().optional(),\n SHIPYARD_HOME: z.string().optional(),\n SHIPYARD_WORKTREE_NAME: z.string().optional(),\n /**\n * Electron utility transport mode. Absent for the CLI daemon; set by\n * apps/electron/src/main/daemon-host.ts when the daemon runs as an Electron\n * utility process.\n */\n SHIPYARD_TRANSPORT: z.enum(['electron-port']).optional(),\n /**\n * Build artifact hosting this daemon. Electron disables the daemon's npm\n * self-updater because electron-updater owns app replacement.\n */\n SHIPYARD_ARTIFACT: z.enum(['electron']).optional(),\n SHIPYARD_VANILLA_AGENT: z\n .enum(['1', 'true', '0', 'false', ''])\n .optional()\n .transform((v) => v === '1' || v === 'true'),\n /**\n * One-time plan-CRDT backfill mode (auto-on-boot). Empty/absent → `auto`:\n * every daemon rebuilds its missing plan CRDTs write-once on boot, gated ONLY\n * by the `~/.shipyard/data/plan-backfill-v1.json` marker's `completed` flag, so\n * it runs once per machine and is a no-op on every subsequent boot — no manual\n * action required. The env var is an OPTIONAL OVERRIDE, not the gate:\n * - `dry-run` → force a READ-ONLY pass that emits `plan_backfill_dryrun` fleet\n * telemetry and writes nothing (testing / observability).\n * - `off` → disable the scan entirely (escape hatch).\n * - `write` → force a write pass that bypasses the completion gate (manual\n * re-run / testing); still per-task idempotent via the marker.\n */\n SHIPYARD_PLAN_BACKFILL_MODE: z\n .enum(['off', 'auto', 'dry-run', 'write', ''])\n .optional()\n .transform((v) => (v === undefined || v === '' ? 'auto' : v)),\n /**\n * Opt in to reusing Claude Code's macOS Keychain credentials for MCP OAuth.\n * Default OFF — see {@link isClaudeCodeKeychainReadEnabled} for why (prompt\n * avoidance). Validated here so a typo fails at boot.\n */\n SHIPYARD_MCP_REUSE_CC_KEYCHAIN: z\n .enum(['1', 'true', '0', 'false', ''])\n .optional()\n .transform((v) => v === '1' || v === 'true'),\n /**\n * Opt in to reusing Codex's macOS Keychain credentials (\"Codex MCP\n * Credentials\") for MCP OAuth. Default OFF — see\n * {@link isCodexKeychainReadEnabled} for why (prompt avoidance). Validated\n * here so a typo fails at boot.\n */\n SHIPYARD_MCP_REUSE_CODEX_KEYCHAIN: z\n .enum(['1', 'true', '0', 'false', ''])\n .optional()\n .transform((v) => v === '1' || v === 'true'),\n SHIPYARD_STALL_PROFILER_ENABLED: z\n .enum(['1', 'true', '0', 'false', ''])\n .optional()\n .transform((v) => v !== '0' && v !== 'false'),\n /**\n * Master kill-switch for native-crash evidence capture: the supervisor exit\n * record, `process.report` fatal-error diagnostic reports, and the supervisor\n * stderr tail. Default ON. Telemetry of the distilled summary is separately\n * gated by `SHIPYARD_TELEMETRY`; this only controls the local on-disk capture.\n */\n SHIPYARD_CRASH_EVIDENCE: z\n .enum(['1', 'true', '0', 'false', ''])\n .optional()\n .transform((v) => v !== '0' && v !== 'false'),\n /**\n * Event-loop stall threshold (ms). Once `histogram.max` crosses this,\n * the profiler starts a CPU profile. History: raised 250ms → 5000ms in\n * May 2026 because the capture itself amplified the stalls it measured\n * (V8 inspector `post()` consumed ~493ms of an 800ms capture window);\n * lowered 5000ms → 1000ms in Jun 2026 (#4484) because the dominant fleet\n * stall volume is 1–10s jank that 5s never profiled. Amplification is now\n * bounded by the profiler's own guards instead of the threshold: the 5-min\n * SHIPYARD_STALL_PROFILER_MIN_INTERVAL_MS rate limit between captures, a\n * capture-in-progress flag, and the 250ms sample window — worst case 12\n * captures/hour (~3s of profiling per hour).\n * Override via `SHIPYARD_STALL_PROFILER_THRESHOLD_MS`.\n */\n SHIPYARD_STALL_PROFILER_THRESHOLD_MS: z.coerce.number().int().positive().default(1_000),\n /**\n * How long to sample the V8 CPU profile after a stall is detected.\n * Default 250ms — long enough for the post-stall queue drain and the\n * cascading sibling stall (if any) to land in the same profile, but\n * short enough that the capture window does not itself amplify the\n * stall on the same event loop. Lowered from 1000 → 250 in May 2026\n * after profile evidence showed `#capture` consumed ~1s of inclusive\n * time inside a 3.5s stall window — the profiler was contributing\n * to the stalls it was trying to measure once #3204 dropped the\n * threshold from 500ms to 250ms.\n */\n SHIPYARD_STALL_PROFILER_PROFILE_DURATION_MS: z.coerce.number().int().positive().default(250),\n /**\n * Cascading-stall suppression window. After one capture, subsequent\n * stalls within this window log `stall_profile_suppressed` instead of\n * starting a new profile. Raised from 30s to 300s (5min) in May 2026\n * to prevent capture storms — at 30s a recurring stall every 60s would\n * trigger 2 captures/min, each adding hundreds of ms of inspector overhead.\n */\n SHIPYARD_STALL_PROFILER_MIN_INTERVAL_MS: z.coerce.number().int().nonnegative().default(300_000),\n /**\n * Supervisor wedge detection: if the daemon child sends no IPC heartbeats\n * for this many ms, the supervisor declares the event loop wedged, sends\n * SIGUSR2 to capture a CPU profile (the inspector runs off the JS loop),\n * waits 2s, then SIGKILLs and respawns via the normal abnormal-exit flow.\n * Generous default — 30s tolerates GC pauses and other legitimate sync\n * blocks well outside human-noticeable territory.\n */\n SHIPYARD_WEDGE_TIMEOUT_MS: z.coerce.number().int().positive().default(30_000),\n /**\n * Daemon child IPC heartbeat interval. Must be smaller than\n * SHIPYARD_WEDGE_TIMEOUT_MS by a comfortable margin so a single missed\n * heartbeat doesn't trip the wedge detector.\n */\n SHIPYARD_HEARTBEAT_INTERVAL_MS: z.coerce.number().int().positive().default(2_000),\n /**\n * When set to `'electron-safe-storage'`, the vault-key-manager wraps the\n * master key via Electron's safeStorage API (macOS Keychain on macOS,\n * Secret Service on Linux). Set by daemon-host.ts in the Electron main\n * process. When absent, the daemon falls back to the existing AES-256-GCM\n * file-based path (CLI daemon path — unchanged).\n */\n SHIPYARD_VAULT_BACKEND: z.enum(['electron-safe-storage']).optional(),\n /**\n * Absolute path to a Node-capable executable for forking JS subprocesses\n * (cursor-runner, the TS language server, the cursor-hook shim). Set by\n * apps/electron/src/main/daemon-host.ts to the Electron MAIN process's\n * `process.execPath` (the main app binary), because the daemon utility\n * process's own `process.execPath` resolves to a helper executable that may\n * not exist in the packaged bundle. Absent for the CLI daemon, which falls\n * back to its own `process.execPath`. See `resolveNodeExecPath()`.\n */\n SHIPYARD_NODE_EXEC_PATH: z.string().optional(),\n /**\n * TTL (ms) for the per-repo worktree list cache. Browser polls at 4-7/sec;\n * this collapses redundant git subprocess invocations to 1 per TTL window.\n * Set to 0 to disable caching (useful for tests that need fresh results).\n */\n SHIPYARD_WORKTREE_LIST_TTL_MS: z.coerce.number().int().min(0).default(30_000),\n /**\n * Override for the web app URL the daemon advertises. Absent → derived from\n * the signaling URL (see `getWebAppUrl`). A malformed value fails at boot\n * rather than propagating into URL concatenation.\n */\n SHIPYARD_WEB_URL: z.string().url().optional(),\n SHIPYARD_PRODUCTION_WEB_URL: z.string().url().default(DEFAULT_PRODUCTION_WEB_URL),\n /**\n * Absolute path override for the Claude Code binary. When set and the file\n * exists, the daemon spawns it instead of the SDK-bundled native binary.\n * See `resolveClaudeCodePath` / `resolveClaudeBinaryPath`.\n */\n CLAUDE_CODE_PATH: z.string().optional(),\n /**\n * Suppress auto-opening the verification URL in the browser during the\n * CLI device-login flow. Default OFF (the browser opens).\n */\n SHIPYARD_NO_OPEN: z\n .enum(['1', 'true', '0', 'false', ''])\n .optional()\n .transform((v) => v === '1' || v === 'true'),\n /**\n * Kill-switch for Loro CRDT recovery on startup. Default OFF (recovery runs).\n */\n SHIPYARD_DISABLE_LORO_RECOVERY: z\n .enum(['1', 'true', '0', 'false', ''])\n .optional()\n .transform((v) => v === '1' || v === 'true'),\n /**\n * Harness HTTP MCP server (Codex's endpoint). Default ON — Codex ships with\n * every daemon and needs it. `SHIPYARD_HARNESS_HTTP=0` is the kill-switch for\n * Codex-disabled deployments.\n */\n SHIPYARD_HARNESS_HTTP: z\n .enum(['1', 'true', '0', 'false', ''])\n .optional()\n .transform((v) => v !== '0' && v !== 'false'),\n /**\n * Use the shared `codex app-server` engine (single token owner — fixes the\n * 401-storm). Default ON. Set to `0`/`false` for the legacy per-process path.\n */\n SHIPYARD_CODEX_SHARED_ENGINE: z\n .enum(['1', 'true', '0', 'false', ''])\n .optional()\n .transform((v) => v !== '0' && v !== 'false'),\n /**\n * Browser-metrics sampling cadence. Kept as a raw string here — clamp /\n * disable (`0`) / invalid-fallback semantics + logging live in\n * `parseBrowserMetricsInterval` (services/metrics/browser-metrics-config.ts),\n * which consumes this validated value.\n */\n SHIPYARD_BROWSER_METRICS_INTERVAL_MS: z.string().optional(),\n /**\n * Max concurrent file watchers before the guard sheds new subscriptions.\n * Absent → default (450). An invalid value fails at boot.\n */\n SHIPYARD_FILE_WATCHER_MAX: z.coerce.number().int().positive().optional(),\n /**\n * Override the daemon's own --max-old-space-size (MB). When absent, the\n * value is auto-derived from os.totalmem() × 25 %, clamped to [2048, 4096].\n * An explicit value is honored as-is (clamped to 65536 to catch typos) and\n * passed to --max-old-space-size at BOTH spawn sites: the outer CLI\n * supervisor (start-supervisor.ts) and the Electron utility-process fork\n * (daemon-host.ts). See apps/daemon/src/shared/daemon-heap-limit.ts.\n */\n SHIPYARD_DAEMON_MAX_OLD_SPACE_MB: z.coerce.number().int().positive().optional(),\n});\n\nexport type Env = z.infer<typeof EnvSchema>;\n\nexport function validateEnv(): Env {\n return EnvSchema.parse(process.env);\n}\n\n/**\n * Resolved profiler tuning derived from the validated env. Re-parses the\n * schema so a typo in one of the *_MS vars (e.g. `THRESHOLD_MS=abc`) fails\n * here too — boot already failed in `validateEnv()` (start.ts / serve.ts\n * call it), this second parse is cheap and keeps the wiring side free of\n * `process.env.X` reads at the call site.\n */\nexport interface StallProfilerConfig {\n enabled: boolean;\n thresholdMs: number;\n captureMs: number;\n rateLimitMs: number;\n}\n\nexport function getStallProfilerConfig(): StallProfilerConfig {\n const env = validateEnv();\n\n /**\n * Default OFF in packaged Electron when `SHIPYARD_STALL_PROFILER_ENABLED`\n * is unset. Rationale: the V8 inspector `post()` blocks the event loop\n * during capture — in a 2,959ms stall, `node:inspector:117 post` consumed\n * 493.5ms of 800ms captured, i.e. the profiler was profiling itself.\n * Trade: lose production stall visibility in packaged builds in exchange\n * for not amplifying stalls for users. Dev sessions and CLI daemon keep\n * profiling enabled by default. Override with `SHIPYARD_STALL_PROFILER_ENABLED=1`.\n *\n * `env.SHIPYARD_STALL_PROFILER_ENABLED` is `true` when the env var is\n * absent (the Zod transform treats missing/empty as truthy). We detect\n * \"explicitly set\" vs \"defaulted\" by reading `process.env` directly here\n * — the schema transform already validated it, so this read is safe.\n */\n const explicitlySet =\n process.env.SHIPYARD_STALL_PROFILER_ENABLED !== undefined &&\n process.env.SHIPYARD_STALL_PROFILER_ENABLED !== '';\n\n const enabled = explicitlySet\n ? env.SHIPYARD_STALL_PROFILER_ENABLED\n : env.SHIPYARD_ARTIFACT !== 'electron';\n\n return {\n enabled,\n thresholdMs: env.SHIPYARD_STALL_PROFILER_THRESHOLD_MS,\n captureMs: env.SHIPYARD_STALL_PROFILER_PROFILE_DURATION_MS,\n rateLimitMs: env.SHIPYARD_STALL_PROFILER_MIN_INTERVAL_MS,\n };\n}\n"],"mappings":"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","names":[]}
1
+ {"version":3,"sources":["../src/shared/env.ts"],"sourcesContent":["import { homedir } from 'node:os';\nimport { join } from 'node:path';\nimport { z } from 'zod';\n\n/**\n * Enable fine-grained tool streaming so MCP tool inputs stream token-by-token\n * via input_json_delta events instead of being buffered as complete JSON.\n * Set early so the subprocess inherits it.\n */\nprocess.env.CLAUDE_CODE_ENABLE_FINE_GRAINED_TOOL_STREAMING = '1';\n\n/**\n * Enable the Task system (TaskCreate/TaskGet/TaskList/TaskUpdate) which provides\n * structured task management with dependency tracking (blocks/blockedBy).\n * Replaces TodoWrite — when enabled, CC only has TaskCreate (not TodoWrite).\n */\nprocess.env.CLAUDE_CODE_ENABLE_TASKS = '1';\n\n/**\n * Bootstrap exception: reads `process.env` directly because it feeds the\n * `SHIPYARD_DATA_DIR` schema transform (via `shipyardDirName`). Routing it\n * through `validateEnv()` would recurse (validateEnv → parse → transform →\n * shipyardDirName → isDevMode → validateEnv).\n */\nexport function isDevMode(): boolean {\n return process.env.SHIPYARD_DEV === '1' || process.env.SHIPYARD_DEV === 'true';\n}\n\n/**\n * When enabled, the agent runs with only Shipyard builtins (system prompt,\n * comment + visualize MCP servers) and no user/plugin MCP servers.\n * Useful for testing the harness in isolation without external interference.\n *\n * Set `SHIPYARD_VANILLA_AGENT=1` to activate.\n */\nexport function isVanillaAgentMode(): boolean {\n return validateEnv().SHIPYARD_VANILLA_AGENT;\n}\n\n/**\n * Reuse Claude Code's macOS Keychain item (\"Claude Code-credentials\") as a\n * source for MCP connector OAuth clients/tokens.\n *\n * Default OFF. The daemon is a different binary than Claude Code, so reading\n * that Keychain item triggers a macOS password prompt in a GUI session (or a\n * silent denial when headless) — unacceptable in the connector status path,\n * which runs on every capability refresh. Shipyard's own token store is the\n * source of truth, mirroring how local MCPs work (no prompt). Opt back into\n * the legacy import behavior with `SHIPYARD_MCP_REUSE_CC_KEYCHAIN=1`.\n */\nexport function isClaudeCodeKeychainReadEnabled(): boolean {\n return validateEnv().SHIPYARD_MCP_REUSE_CC_KEYCHAIN;\n}\n\n/**\n * Reuse Codex's macOS Keychain item (\"Codex MCP Credentials\") as a source for\n * per-MCP-connector OAuth clients/tokens (Codex DCRs its own atomic\n * client_id+token pairs, useful for borrowing on connectors Claude Code hasn't\n * registered with).\n *\n * Default OFF for the same reason as {@link isClaudeCodeKeychainReadEnabled}:\n * the daemon is a different binary than Codex, so reading that Keychain item\n * triggers a macOS password prompt in a GUI session (or a silent denial when\n * headless) — unacceptable in the connector status path. The\n * `~/.codex/.credentials.json` file fallback is read regardless. Opt in with\n * `SHIPYARD_MCP_REUSE_CODEX_KEYCHAIN=1`.\n */\nexport function isCodexKeychainReadEnabled(): boolean {\n return validateEnv().SHIPYARD_MCP_REUSE_CODEX_KEYCHAIN;\n}\n\nfunction shipyardDirName(): string {\n return isDevMode() ? '.shipyard-dev' : '.shipyard';\n}\n\n/**\n * The signaling worker is a RENDEZVOUS point: a browser and this daemon only\n * meet if they hit the SAME worker. This MUST equal the web build's\n * `VITE_SESSION_SERVER_URL` (.github/workflows/ci.yml + publish-npm.yml) — all\n * live clients (web next+stable, daemons all channels) point here. Do not make\n * this per-channel; a staging worker is a CI/E2E fixture only. See AGENTS.md\n * Invariant #22 (the #4186 \"install daemon\" incident).\n */\nconst DEFAULT_PRODUCTION_SIGNALING_URL = 'https://shipyard-session-server.jacob-191.workers.dev';\nconst DEFAULT_PRODUCTION_WEB_URL = 'https://shipyard.computer';\n\n/**\n * Bootstrap exception: reads `process.env` directly (not `validateEnv()`)\n * because it computes the electron-utility startup-breadcrumb path BEFORE\n * `validateEnv()` runs — the breadcrumb must still resolve when validateEnv\n * itself throws (see apps/daemon/src/electron-utility.ts).\n */\nexport function getShipyardHome(): string {\n if (process.env.SHIPYARD_HOME) return process.env.SHIPYARD_HOME;\n return join(homedir(), shipyardDirName());\n}\n\n/**\n * The executable to fork JS subprocesses with (cursor-runner, the TS language\n * server, the cursor-hook shim).\n *\n * `child_process.fork`/`spawn` default to `process.execPath`. Inside an\n * Electron *utility* process (how the packaged daemon runs) that does NOT point\n * at the main app binary — it resolves to a per-platform helper executable\n * (e.g. `…/Frameworks/Shipyard Helper.app/Contents/MacOS/Shipyard Helper`) that\n * may not exist on disk in the shipped bundle. Forking it ENOENTs, which (with\n * the cursor-runner pre-warm running at boot) crash-looped the daemon on\n * 2026-06-01 (Dylan's log).\n *\n * The Electron *main* process has a guaranteed-valid `process.execPath` (the\n * main app binary, which also honors `ELECTRON_RUN_AS_NODE=1`) and forwards it\n * as `SHIPYARD_NODE_EXEC_PATH` (see apps/electron/src/main/daemon-host.ts). The\n * CLI daemon and dev leave it unset and fall back to `process.execPath`, which\n * is already correct there. Declared in `EnvSchema` so a malformed value fails\n * at boot. Re-exported from `spawn-as-node.ts` for call-site ergonomics.\n */\nexport function resolveNodeExecPath(): string {\n return validateEnv().SHIPYARD_NODE_EXEC_PATH || process.execPath;\n}\n\n/**\n * Resolved one-time plan-backfill mode (boot). Unset/default ⇒ `auto`: the\n * daemon rebuilds missing plan CRDTs write-once on boot, gated only by the\n * marker's `completed` flag (a no-op on every later boot). The env var is an\n * OPTIONAL OVERRIDE, not the gate: `dry-run` forces a read-only fleet-telemetry\n * pass that writes nothing; `off` disables the scan entirely (escape hatch);\n * `write` forces a write pass that bypasses the completion gate (manual re-run /\n * testing).\n */\nexport function getPlanBackfillMode(): 'off' | 'auto' | 'dry-run' | 'write' {\n return validateEnv().SHIPYARD_PLAN_BACKFILL_MODE;\n}\n\nexport const EnvSchema = z.object({\n ANTHROPIC_API_KEY: z.string().min(1).optional(),\n SHIPYARD_DEV: z\n .enum(['1', 'true', '0', 'false', ''])\n .optional()\n .transform((v) => v === '1' || v === 'true'),\n SHIPYARD_DAEMON_CHILD: z\n .enum(['1', 'true', '0', 'false', ''])\n .optional()\n .transform((v) => v === '1' || v === 'true'),\n SHIPYARD_DATA_DIR: z\n .string()\n .optional()\n .transform((v) => v ?? `~/${shipyardDirName()}/data`),\n LOG_LEVEL: z.enum(['debug', 'info', 'warn', 'error']).default('info'),\n SHIPYARD_SIGNALING_URL: z.string().url().optional(),\n SHIPYARD_PRODUCTION_SIGNALING_URL: z.string().url().default(DEFAULT_PRODUCTION_SIGNALING_URL),\n SHIPYARD_METRICS_WORKER_URL: z\n .string()\n .url()\n .default('https://shipyard-metrics.jacob-191.workers.dev'),\n SHIPYARD_TELEMETRY: z\n .enum(['1', 'true', '0', 'false', ''])\n .optional()\n .transform((v) => v !== '0' && v !== 'false'),\n SHIPYARD_USER_TOKEN: z.string().optional(),\n /**\n * Worker URL for the `shipyard roi` report command (distinct from\n * SHIPYARD_METRICS_WORKER_URL, which is the telemetry ingestion endpoint).\n * Falls back to the `--worker-url` CLI flag when unset.\n */\n SHIPYARD_METRICS_URL: z.string().url().optional(),\n SHIPYARD_USER_ID: z.string().optional(),\n SHIPYARD_MACHINE_ID: z.string().optional(),\n SHIPYARD_MACHINE_NAME: z.string().optional(),\n SHIPYARD_USER_DISPLAY_NAME: z.string().optional(),\n SHIPYARD_HOME: z.string().optional(),\n SHIPYARD_WORKTREE_NAME: z.string().optional(),\n /**\n * Electron utility transport mode. Absent for the CLI daemon; set by\n * apps/electron/src/main/daemon-host.ts when the daemon runs as an Electron\n * utility process.\n */\n SHIPYARD_TRANSPORT: z.enum(['electron-port']).optional(),\n /**\n * Build artifact hosting this daemon. Electron disables the daemon's npm\n * self-updater because electron-updater owns app replacement.\n */\n SHIPYARD_ARTIFACT: z.enum(['electron']).optional(),\n SHIPYARD_VANILLA_AGENT: z\n .enum(['1', 'true', '0', 'false', ''])\n .optional()\n .transform((v) => v === '1' || v === 'true'),\n /**\n * One-time plan-CRDT backfill mode (auto-on-boot). Empty/absent → `auto`:\n * every daemon rebuilds its missing plan CRDTs write-once on boot, gated ONLY\n * by the `~/.shipyard/data/plan-backfill-v1.json` marker's `completed` flag, so\n * it runs once per machine and is a no-op on every subsequent boot — no manual\n * action required. The env var is an OPTIONAL OVERRIDE, not the gate:\n * - `dry-run` → force a READ-ONLY pass that emits `plan_backfill_dryrun` fleet\n * telemetry and writes nothing (testing / observability).\n * - `off` → disable the scan entirely (escape hatch).\n * - `write` → force a write pass that bypasses the completion gate (manual\n * re-run / testing); still per-task idempotent via the marker.\n */\n SHIPYARD_PLAN_BACKFILL_MODE: z\n .enum(['off', 'auto', 'dry-run', 'write', ''])\n .optional()\n .transform((v) => (v === undefined || v === '' ? 'auto' : v)),\n /**\n * Opt in to reusing Claude Code's macOS Keychain credentials for MCP OAuth.\n * Default OFF — see {@link isClaudeCodeKeychainReadEnabled} for why (prompt\n * avoidance). Validated here so a typo fails at boot.\n */\n SHIPYARD_MCP_REUSE_CC_KEYCHAIN: z\n .enum(['1', 'true', '0', 'false', ''])\n .optional()\n .transform((v) => v === '1' || v === 'true'),\n /**\n * Opt in to reusing Codex's macOS Keychain credentials (\"Codex MCP\n * Credentials\") for MCP OAuth. Default OFF — see\n * {@link isCodexKeychainReadEnabled} for why (prompt avoidance). Validated\n * here so a typo fails at boot.\n */\n SHIPYARD_MCP_REUSE_CODEX_KEYCHAIN: z\n .enum(['1', 'true', '0', 'false', ''])\n .optional()\n .transform((v) => v === '1' || v === 'true'),\n SHIPYARD_STALL_PROFILER_ENABLED: z\n .enum(['1', 'true', '0', 'false', ''])\n .optional()\n .transform((v) => v !== '0' && v !== 'false'),\n /**\n * Master kill-switch for native-crash evidence capture: the supervisor exit\n * record, `process.report` fatal-error diagnostic reports, and the supervisor\n * stderr tail. Default ON. Telemetry of the distilled summary is separately\n * gated by `SHIPYARD_TELEMETRY`; this only controls the local on-disk capture.\n */\n SHIPYARD_CRASH_EVIDENCE: z\n .enum(['1', 'true', '0', 'false', ''])\n .optional()\n .transform((v) => v !== '0' && v !== 'false'),\n /**\n * Event-loop stall threshold (ms). Once `histogram.max` crosses this,\n * the profiler starts a CPU profile. History: raised 250ms → 5000ms in\n * May 2026 because the capture itself amplified the stalls it measured\n * (V8 inspector `post()` consumed ~493ms of an 800ms capture window);\n * lowered 5000ms → 1000ms in Jun 2026 (#4484) because the dominant fleet\n * stall volume is 1–10s jank that 5s never profiled. Amplification is now\n * bounded by the profiler's own guards instead of the threshold: the 5-min\n * SHIPYARD_STALL_PROFILER_MIN_INTERVAL_MS rate limit between captures, a\n * capture-in-progress flag, and the 250ms sample window — worst case 12\n * captures/hour (~3s of profiling per hour).\n * Override via `SHIPYARD_STALL_PROFILER_THRESHOLD_MS`.\n */\n SHIPYARD_STALL_PROFILER_THRESHOLD_MS: z.coerce.number().int().positive().default(1_000),\n /**\n * How long to sample the V8 CPU profile after a stall is detected.\n * Default 250ms — long enough for the post-stall queue drain and the\n * cascading sibling stall (if any) to land in the same profile, but\n * short enough that the capture window does not itself amplify the\n * stall on the same event loop. Lowered from 1000 → 250 in May 2026\n * after profile evidence showed `#capture` consumed ~1s of inclusive\n * time inside a 3.5s stall window — the profiler was contributing\n * to the stalls it was trying to measure once #3204 dropped the\n * threshold from 500ms to 250ms.\n */\n SHIPYARD_STALL_PROFILER_PROFILE_DURATION_MS: z.coerce.number().int().positive().default(250),\n /**\n * Cascading-stall suppression window. After one capture, subsequent\n * stalls within this window log `stall_profile_suppressed` instead of\n * starting a new profile. Raised from 30s to 300s (5min) in May 2026\n * to prevent capture storms — at 30s a recurring stall every 60s would\n * trigger 2 captures/min, each adding hundreds of ms of inspector overhead.\n */\n SHIPYARD_STALL_PROFILER_MIN_INTERVAL_MS: z.coerce.number().int().nonnegative().default(300_000),\n /**\n * Supervisor wedge detection: if the daemon child sends no IPC heartbeats\n * for this many ms, the supervisor declares the event loop wedged, sends\n * SIGUSR2 to capture a CPU profile (the inspector runs off the JS loop),\n * waits 2s, then SIGKILLs and respawns via the normal abnormal-exit flow.\n * Generous default — 30s tolerates GC pauses and other legitimate sync\n * blocks well outside human-noticeable territory.\n */\n SHIPYARD_WEDGE_TIMEOUT_MS: z.coerce.number().int().positive().default(30_000),\n /**\n * Daemon child IPC heartbeat interval. Must be smaller than\n * SHIPYARD_WEDGE_TIMEOUT_MS by a comfortable margin so a single missed\n * heartbeat doesn't trip the wedge detector.\n */\n SHIPYARD_HEARTBEAT_INTERVAL_MS: z.coerce.number().int().positive().default(2_000),\n /**\n * When set to `'electron-safe-storage'`, the vault-key-manager wraps the\n * master key via Electron's safeStorage API (macOS Keychain on macOS,\n * Secret Service on Linux). Set by daemon-host.ts in the Electron main\n * process. When absent, the daemon falls back to the existing AES-256-GCM\n * file-based path (CLI daemon path — unchanged).\n */\n SHIPYARD_VAULT_BACKEND: z.enum(['electron-safe-storage']).optional(),\n /**\n * Absolute path to a Node-capable executable for forking JS subprocesses\n * (cursor-runner, the TS language server, the cursor-hook shim). Set by\n * apps/electron/src/main/daemon-host.ts to the Electron MAIN process's\n * `process.execPath` (the main app binary), because the daemon utility\n * process's own `process.execPath` resolves to a helper executable that may\n * not exist in the packaged bundle. Absent for the CLI daemon, which falls\n * back to its own `process.execPath`. See `resolveNodeExecPath()`.\n */\n SHIPYARD_NODE_EXEC_PATH: z.string().optional(),\n /**\n * TTL (ms) for the per-repo worktree list cache. Browser polls at 4-7/sec;\n * this collapses redundant git subprocess invocations to 1 per TTL window.\n * Set to 0 to disable caching (useful for tests that need fresh results).\n */\n SHIPYARD_WORKTREE_LIST_TTL_MS: z.coerce.number().int().min(0).default(30_000),\n /**\n * Override for the web app URL the daemon advertises. Absent → derived from\n * the signaling URL (see `getWebAppUrl`). A malformed value fails at boot\n * rather than propagating into URL concatenation.\n */\n SHIPYARD_WEB_URL: z.string().url().optional(),\n SHIPYARD_PRODUCTION_WEB_URL: z.string().url().default(DEFAULT_PRODUCTION_WEB_URL),\n /**\n * Absolute path override for the Claude Code binary. When set and the file\n * exists, the daemon spawns it instead of the SDK-bundled native binary.\n * See `resolveClaudeCodePath` / `resolveClaudeBinaryPath`.\n */\n CLAUDE_CODE_PATH: z.string().optional(),\n /**\n * Suppress auto-opening the verification URL in the browser during the\n * CLI device-login flow. Default OFF (the browser opens).\n */\n SHIPYARD_NO_OPEN: z\n .enum(['1', 'true', '0', 'false', ''])\n .optional()\n .transform((v) => v === '1' || v === 'true'),\n /**\n * Kill-switch for Loro CRDT recovery on startup. Default OFF (recovery runs).\n */\n SHIPYARD_DISABLE_LORO_RECOVERY: z\n .enum(['1', 'true', '0', 'false', ''])\n .optional()\n .transform((v) => v === '1' || v === 'true'),\n /**\n * Harness HTTP MCP server (Codex's endpoint). Default ON — Codex ships with\n * every daemon and needs it. `SHIPYARD_HARNESS_HTTP=0` is the kill-switch for\n * Codex-disabled deployments.\n */\n SHIPYARD_HARNESS_HTTP: z\n .enum(['1', 'true', '0', 'false', ''])\n .optional()\n .transform((v) => v !== '0' && v !== 'false'),\n /**\n * Use the shared `codex app-server` engine (single token owner — fixes the\n * 401-storm). Default ON. Set to `0`/`false` for the legacy per-process path.\n */\n SHIPYARD_CODEX_SHARED_ENGINE: z\n .enum(['1', 'true', '0', 'false', ''])\n .optional()\n .transform((v) => v !== '0' && v !== 'false'),\n /**\n * Browser-metrics sampling cadence. Kept as a raw string here — clamp /\n * disable (`0`) / invalid-fallback semantics + logging live in\n * `parseBrowserMetricsInterval` (services/metrics/browser-metrics-config.ts),\n * which consumes this validated value.\n */\n SHIPYARD_BROWSER_METRICS_INTERVAL_MS: z.string().optional(),\n /**\n * Max concurrent file watchers before the guard sheds new subscriptions.\n * Absent → default (450). An invalid value fails at boot.\n */\n SHIPYARD_FILE_WATCHER_MAX: z.coerce.number().int().positive().optional(),\n /**\n * Override the daemon's own --max-old-space-size (MB). When absent, the\n * value is auto-derived from os.totalmem() × 25 %, clamped to [2048, 8192].\n * An explicit value is honored as-is (clamped to 65536 to catch typos) and\n * passed to --max-old-space-size at BOTH spawn sites: the outer CLI\n * supervisor (start-supervisor.ts) and the Electron utility-process fork\n * (daemon-host.ts). See apps/daemon/src/shared/daemon-heap-limit.ts. The\n * Electron path also honors a `daemon.maxHeapGb` config.json setting below\n * this env var in precedence — see daemon-heap-limit-config.ts.\n */\n SHIPYARD_DAEMON_MAX_OLD_SPACE_MB: z.coerce.number().int().positive().optional(),\n});\n\nexport type Env = z.infer<typeof EnvSchema>;\n\nexport function validateEnv(): Env {\n return EnvSchema.parse(process.env);\n}\n\n/**\n * Resolved profiler tuning derived from the validated env. Re-parses the\n * schema so a typo in one of the *_MS vars (e.g. `THRESHOLD_MS=abc`) fails\n * here too — boot already failed in `validateEnv()` (start.ts / serve.ts\n * call it), this second parse is cheap and keeps the wiring side free of\n * `process.env.X` reads at the call site.\n */\nexport interface StallProfilerConfig {\n enabled: boolean;\n thresholdMs: number;\n captureMs: number;\n rateLimitMs: number;\n}\n\nexport function getStallProfilerConfig(): StallProfilerConfig {\n const env = validateEnv();\n\n /**\n * Default OFF in packaged Electron when `SHIPYARD_STALL_PROFILER_ENABLED`\n * is unset. Rationale: the V8 inspector `post()` blocks the event loop\n * during capture — in a 2,959ms stall, `node:inspector:117 post` consumed\n * 493.5ms of 800ms captured, i.e. the profiler was profiling itself.\n * Trade: lose production stall visibility in packaged builds in exchange\n * for not amplifying stalls for users. Dev sessions and CLI daemon keep\n * profiling enabled by default. Override with `SHIPYARD_STALL_PROFILER_ENABLED=1`.\n *\n * `env.SHIPYARD_STALL_PROFILER_ENABLED` is `true` when the env var is\n * absent (the Zod transform treats missing/empty as truthy). We detect\n * \"explicitly set\" vs \"defaulted\" by reading `process.env` directly here\n * — the schema transform already validated it, so this read is safe.\n */\n const explicitlySet =\n process.env.SHIPYARD_STALL_PROFILER_ENABLED !== undefined &&\n process.env.SHIPYARD_STALL_PROFILER_ENABLED !== '';\n\n const enabled = explicitlySet\n ? env.SHIPYARD_STALL_PROFILER_ENABLED\n : env.SHIPYARD_ARTIFACT !== 'electron';\n\n return {\n enabled,\n thresholdMs: env.SHIPYARD_STALL_PROFILER_THRESHOLD_MS,\n captureMs: env.SHIPYARD_STALL_PROFILER_PROFILE_DURATION_MS,\n rateLimitMs: env.SHIPYARD_STALL_PROFILER_MIN_INTERVAL_MS,\n };\n}\n"],"mappings":"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","names":[]}
@@ -1,7 +1,7 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
3
  resolveNodeExecPath
4
- } from "./chunk-UTL5PF43.js";
4
+ } from "./chunk-SXXAWBW7.js";
5
5
 
6
6
  // src/shared/spawn-as-node.ts
7
7
  var isElectronOverride = null;
@@ -34,4 +34,4 @@ export {
34
34
  buildNodeSpawnEnv,
35
35
  rewriteElectronNodeScriptSpawn
36
36
  };
37
- //# sourceMappingURL=chunk-TUQTEWK3.js.map
37
+ //# sourceMappingURL=chunk-VSRWL5SP.js.map
@@ -1,7 +1,7 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
3
  validateEnv
4
- } from "./chunk-UTL5PF43.js";
4
+ } from "./chunk-SXXAWBW7.js";
5
5
  import {
6
6
  external_exports
7
7
  } from "./chunk-JCEWPG3R.js";
@@ -18,8 +18,8 @@ function getDaemonVersion() {
18
18
  return cached;
19
19
  }
20
20
  function readDaemonVersion() {
21
- if ("3.22.0".length > 0) {
22
- return "3.22.0";
21
+ if ("3.23.0".length > 0) {
22
+ return "3.23.0";
23
23
  }
24
24
  try {
25
25
  const here = dirname(fileURLToPath(import.meta.url));
@@ -48,4 +48,4 @@ function readDaemonVersion() {
48
48
  export {
49
49
  getDaemonVersion
50
50
  };
51
- //# sourceMappingURL=chunk-WCWHY4C3.js.map
51
+ //# sourceMappingURL=chunk-W3EMVBWP.js.map
@@ -2,13 +2,13 @@
2
2
  import {
3
3
  print,
4
4
  printError
5
- } from "./chunk-WFBU4LXL.js";
5
+ } from "./chunk-2YH743ND.js";
6
6
  import {
7
7
  getConfigPath,
8
8
  loadAuthToken,
9
9
  readConfig,
10
10
  writeConfig
11
- } from "./chunk-TFWDJCUJ.js";
11
+ } from "./chunk-2UE3TNY7.js";
12
12
  import {
13
13
  DeviceExchangeCodeResponseSchema,
14
14
  DevicePollPendingSchema,
@@ -19,7 +19,7 @@ import {
19
19
  import {
20
20
  isDevMode,
21
21
  validateEnv
22
- } from "./chunk-UTL5PF43.js";
22
+ } from "./chunk-SXXAWBW7.js";
23
23
  import {
24
24
  external_exports
25
25
  } from "./chunk-JCEWPG3R.js";
@@ -223,4 +223,4 @@ export {
223
223
  ensureAuthenticated,
224
224
  loginCommand
225
225
  };
226
- //# sourceMappingURL=chunk-VPJRM77V.js.map
226
+ //# sourceMappingURL=chunk-Y4RRW6MA.js.map
@@ -4,7 +4,7 @@ import {
4
4
  } from "./chunk-CKQL7TWQ.js";
5
5
  import {
6
6
  buildCursorUserPrompt
7
- } from "./chunk-ABFHBWBF.js";
7
+ } from "./chunk-HQTCVJRK.js";
8
8
  import {
9
9
  ContentBlockSchema,
10
10
  HARNESS_SERVER_NAME
@@ -12,8 +12,8 @@ import {
12
12
  import "./chunk-UESTOND6.js";
13
13
  import "./chunk-EHQITHQX.js";
14
14
  import "./chunk-X3MULCV5.js";
15
- import "./chunk-MRD6526D.js";
16
- import "./chunk-UTL5PF43.js";
15
+ import "./chunk-CA5PKTNJ.js";
16
+ import "./chunk-SXXAWBW7.js";
17
17
  import "./chunk-JCEWPG3R.js";
18
18
  import "./chunk-2H7UOFLK.js";
19
19
 
@@ -1,21 +1,21 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
3
  getDaemonVersion
4
- } from "./chunk-WCWHY4C3.js";
4
+ } from "./chunk-W3EMVBWP.js";
5
5
  import {
6
6
  enableNativeCrashReports
7
- } from "./chunk-TSJOSKN3.js";
7
+ } from "./chunk-JD4WV327.js";
8
8
  import {
9
9
  installWasmPanicBuffer
10
10
  } from "./chunk-5OTUGRW3.js";
11
11
  import "./chunk-UNGAIUEX.js";
12
12
  import {
13
13
  logger
14
- } from "./chunk-MRD6526D.js";
14
+ } from "./chunk-CA5PKTNJ.js";
15
15
  import {
16
16
  getShipyardHome,
17
17
  validateEnv
18
- } from "./chunk-UTL5PF43.js";
18
+ } from "./chunk-SXXAWBW7.js";
19
19
  import "./chunk-JCEWPG3R.js";
20
20
  import "./chunk-2H7UOFLK.js";
21
21
 
@@ -40,7 +40,7 @@ function isVersionQuery(data) {
40
40
  }
41
41
  async function loadAuthFromConfig(env) {
42
42
  if (env.SHIPYARD_USER_TOKEN) return;
43
- const { loadAuthToken } = await import("./auth-SRHTZ2JL.js");
43
+ const { loadAuthToken } = await import("./auth-OHZTVIQU.js");
44
44
  const auth = await loadAuthToken();
45
45
  if (auth.status === "ok") {
46
46
  env.SHIPYARD_USER_TOKEN = auth.token;
@@ -71,7 +71,7 @@ async function main() {
71
71
  await loadAuthFromConfig(env);
72
72
  crumb("post-auth-load");
73
73
  crumb("pre-serve-import");
74
- const { serve } = await import("./serve-5D77VQ6J.js");
74
+ const { serve } = await import("./serve-J3MG3WHK.js");
75
75
  crumb("post-serve-import");
76
76
  const portQueue = [];
77
77
  let acceptor = null;
@@ -14,12 +14,12 @@ import {
14
14
  isGhAvailable,
15
15
  isGitRepo,
16
16
  parseOwnerRepo
17
- } from "./chunk-DE3IJ4E4.js";
17
+ } from "./chunk-K7EXPNAD.js";
18
18
  import "./chunk-4T2OQAVL.js";
19
19
  import "./chunk-RR6V6SNM.js";
20
20
  import "./chunk-ZFKJAYAN.js";
21
- import "./chunk-MRD6526D.js";
22
- import "./chunk-UTL5PF43.js";
21
+ import "./chunk-CA5PKTNJ.js";
22
+ import "./chunk-SXXAWBW7.js";
23
23
  import "./chunk-JCEWPG3R.js";
24
24
  import "./chunk-2H7UOFLK.js";
25
25
  export {
@@ -38,4 +38,4 @@ export {
38
38
  isGitRepo,
39
39
  parseOwnerRepo
40
40
  };
41
- //# sourceMappingURL=git-repo-3OG3TPCC.js.map
41
+ //# sourceMappingURL=git-repo-IVN2RQNY.js.map