@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
@@ -1,27 +1,27 @@
1
1
  #!/usr/bin/env node
2
- import {
3
- DeviceExchangeCodeResponseSchema,
4
- DevicePollPendingSchema,
5
- DevicePollResponseSchema,
6
- DeviceStartResponseSchema,
7
- ROUTES
8
- } from "./chunk-7AHRFPAL.js";
9
2
  import {
10
3
  print,
11
4
  printError
12
- } from "./chunk-IISLTKYY.js";
5
+ } from "./chunk-TU63KZFW.js";
13
6
  import {
14
7
  getConfigPath,
15
8
  loadAuthToken,
16
9
  readConfig,
17
10
  writeConfig
18
- } from "./chunk-2J3WSIAF.js";
11
+ } from "./chunk-EF2DAODF.js";
12
+ import {
13
+ DeviceExchangeCodeResponseSchema,
14
+ DevicePollPendingSchema,
15
+ DevicePollResponseSchema,
16
+ DeviceStartResponseSchema,
17
+ ROUTES
18
+ } from "./chunk-7YOU7MBN.js";
19
19
  import {
20
20
  isDevMode
21
- } from "./chunk-PI77CUEP.js";
21
+ } from "./chunk-KITSAHTX.js";
22
22
  import {
23
23
  external_exports
24
- } from "./chunk-VPMN47TL.js";
24
+ } from "./chunk-CNR7O5YH.js";
25
25
 
26
26
  // src/shared/commands/login.ts
27
27
  var DEFAULT_SIGNALING_URL = "https://shipyard-session-server.jacob-191.workers.dev";
@@ -36,6 +36,7 @@ async function ensureAuthenticated(opts) {
36
36
  token: existing.token,
37
37
  userId: existing.userId,
38
38
  displayName: existing.displayName,
39
+ avatarUrl: existing.avatarUrl,
39
40
  signalingUrl: existing.signalingUrl ?? opts.signalingUrl,
40
41
  deviceFlowRan: false
41
42
  };
@@ -168,6 +169,7 @@ async function handleSuccessfulPoll(pollRes, signalingUrl) {
168
169
  token: pollData.token,
169
170
  userId: pollData.user.id,
170
171
  displayName: pollData.user.displayName,
172
+ avatarUrl: pollData.user.avatarUrl,
171
173
  providers: pollData.user.providers,
172
174
  expiresAt,
173
175
  signalingUrl
@@ -181,6 +183,7 @@ async function handleSuccessfulPoll(pollRes, signalingUrl) {
181
183
  token: pollData.token,
182
184
  userId: pollData.user.id,
183
185
  displayName: pollData.user.displayName,
186
+ avatarUrl: pollData.user.avatarUrl,
184
187
  signalingUrl,
185
188
  deviceFlowRan: true
186
189
  };
@@ -219,4 +222,4 @@ export {
219
222
  ensureAuthenticated,
220
223
  loginCommand
221
224
  };
222
- //# sourceMappingURL=chunk-LW2MS4T5.js.map
225
+ //# sourceMappingURL=chunk-LMJFHKRD.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/shared/commands/login.ts"],"sourcesContent":["import {\n DeviceExchangeCodeResponseSchema,\n DevicePollPendingSchema,\n DevicePollResponseSchema,\n DeviceStartResponseSchema,\n ROUTES,\n} from '@shipyard/session';\nimport { z } from 'zod';\nimport {\n getConfigPath,\n loadAuthToken,\n readConfig,\n type ShipyardConfig,\n writeConfig,\n} from '../../services/bootstrap/auth.js';\nimport { isDevMode } from '../env.js';\nimport { print, printError } from './output.js';\n\nconst DEFAULT_SIGNALING_URL = 'https://shipyard-session-server.jacob-191.workers.dev';\nconst DEFAULT_DEV_SIGNALING_URL = 'http://localhost:4444';\n\nexport function getSignalingUrl(): string {\n return (\n process.env.SHIPYARD_SIGNALING_URL ??\n (isDevMode() ? DEFAULT_DEV_SIGNALING_URL : DEFAULT_SIGNALING_URL)\n );\n}\n\nexport interface AuthConfig {\n token: string;\n userId: string;\n displayName: string;\n avatarUrl: string | null;\n signalingUrl: string;\n}\n\nexport interface AuthResult extends AuthConfig {\n deviceFlowRan: boolean;\n}\n\n/**\n * Ensure the user is authenticated, running the device flow interactively if needed.\n * Returns the auth config on success, or calls process.exit(1) on failure.\n */\nexport async function ensureAuthenticated(opts: {\n signalingUrl: string;\n webCode?: string;\n}): Promise<AuthResult> {\n const existing = await loadAuthToken();\n\n if (existing.status === 'ok') {\n return {\n token: existing.token,\n userId: existing.userId,\n displayName: existing.displayName,\n avatarUrl: existing.avatarUrl,\n signalingUrl: existing.signalingUrl ?? opts.signalingUrl,\n deviceFlowRan: false,\n };\n }\n\n if (existing.status === 'expired') {\n print(' Token expired. Starting re-authentication...\\n');\n }\n\n if (opts.webCode) {\n try {\n return await runWebCodeFlow(opts.signalingUrl, opts.webCode);\n } catch {\n print(' Web code expired or invalid. Starting standard login...\\n');\n }\n }\n\n return runDeviceFlow(opts.signalingUrl);\n}\n\nasync function runDeviceFlow(signalingUrl: string): Promise<AuthResult> {\n const startData = await startDeviceFlow(signalingUrl);\n\n print(` Open this URL in your browser:\\n`);\n print(` ${startData.verificationUri}\\n`);\n print(` Your code: ${startData.userCode}\\n`);\n print(' Waiting for authorization...');\n\n if (!process.env.SHIPYARD_NO_OPEN) {\n import('open').then(({ default: open }) => open(startData.verificationUri)).catch(() => {});\n }\n\n const interval = (startData.interval ?? 5) * 1000;\n const deadline = Date.now() + startData.expiresIn * 1000;\n\n while (Date.now() < deadline) {\n await new Promise((resolve) => setTimeout(resolve, interval));\n\n try {\n const pollRes = await fetch(`${signalingUrl}${ROUTES.AUTH_DEVICE_POLL}`, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({ deviceCode: startData.deviceCode }),\n });\n\n if (pollRes.ok) {\n const result = await handleSuccessfulPoll(pollRes, signalingUrl);\n return result;\n }\n\n await handlePollError(pollRes, interval);\n } catch (err: unknown) {\n printError('Device flow poll failed', err instanceof Error ? err : undefined);\n }\n }\n\n printError('\\n Authorization timed out. Run `shipyard login` again.');\n process.exit(1);\n}\n\nasync function runWebCodeFlow(signalingUrl: string, userCode: string): Promise<AuthResult> {\n print(' Authenticating via web app code...\\n');\n\n let deviceCode: string | undefined;\n for (let attempt = 0; attempt < 3; attempt++) {\n if (attempt > 0) await new Promise((r) => setTimeout(r, 1000));\n\n const exchangeRes = await fetch(`${signalingUrl}${ROUTES.AUTH_DEVICE_EXCHANGE_CODE}`, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({ userCode }),\n });\n\n if (exchangeRes.ok) {\n const parsed = DeviceExchangeCodeResponseSchema.parse(await exchangeRes.json());\n deviceCode = parsed.deviceCode;\n break;\n }\n\n if (attempt === 2) {\n throw new Error(`Exchange failed after 3 attempts: ${exchangeRes.status}`);\n }\n }\n\n if (!deviceCode) throw new Error('Exchange failed: no device code');\n\n const pollRes = await fetch(`${signalingUrl}${ROUTES.AUTH_DEVICE_POLL}`, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({ deviceCode }),\n });\n\n if (!pollRes.ok) {\n throw new Error(`Poll failed: ${pollRes.status}`);\n }\n\n const result = await handleSuccessfulPoll(pollRes, signalingUrl);\n print(' Authenticated via web app\\n');\n return result;\n}\n\nexport async function loginCommand(options: { check?: boolean }): Promise<void> {\n if (options.check) {\n return checkLogin();\n }\n\n const signalingUrl = getSignalingUrl();\n await runDeviceFlow(signalingUrl);\n}\n\nasync function startDeviceFlow(signalingUrl: string) {\n print('Starting device authorization...\\n');\n\n let startRes: Response;\n try {\n startRes = await fetch(`${signalingUrl}${ROUTES.AUTH_DEVICE_START}`, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n });\n } catch (err) {\n printError(\n `Could not reach signaling server at ${signalingUrl}`,\n err instanceof Error ? err : undefined\n );\n printError('Check your internet connection or set SHIPYARD_SIGNALING_URL.');\n process.exit(1);\n }\n\n if (!startRes.ok) {\n printError(`Failed to start device flow: ${startRes.status}`);\n process.exit(1);\n }\n\n return DeviceStartResponseSchema.parse(await startRes.json());\n}\n\nfunction extractTokenExpiry(token: string): number {\n const fallback = Date.now() + 30 * 24 * 60 * 60 * 1000;\n try {\n const payloadB64 = token.split('.')[1];\n if (!payloadB64) return fallback;\n const payload: unknown = JSON.parse(Buffer.from(payloadB64, 'base64url').toString());\n const exp = z.object({ exp: z.number() }).safeParse(payload);\n return exp.success ? exp.data.exp * 1000 : fallback;\n } catch {\n return fallback;\n }\n}\n\nasync function handleSuccessfulPoll(pollRes: Response, signalingUrl: string): Promise<AuthResult> {\n const pollData = DevicePollResponseSchema.parse(await pollRes.json());\n const expiresAt = extractTokenExpiry(pollData.token);\n\n const config: ShipyardConfig = {\n auth: {\n token: pollData.token,\n userId: pollData.user.id,\n displayName: pollData.user.displayName,\n avatarUrl: pollData.user.avatarUrl,\n providers: pollData.user.providers,\n expiresAt,\n signalingUrl,\n },\n };\n\n await writeConfig(config);\n\n print(`\\n Logged in as ${pollData.user.displayName} (${pollData.user.providers.join(', ')})`);\n print(` Token saved to ${getConfigPath()}`);\n\n return {\n token: pollData.token,\n userId: pollData.user.id,\n displayName: pollData.user.displayName,\n avatarUrl: pollData.user.avatarUrl,\n signalingUrl,\n deviceFlowRan: true,\n };\n}\n\nasync function handlePollError(pollRes: Response, interval: number): Promise<void> {\n try {\n const errorData = DevicePollPendingSchema.parse(await pollRes.json());\n\n if (errorData.error === 'expired_token') {\n printError('\\n Authorization expired. Run `shipyard login` again.');\n process.exit(1);\n }\n\n if (errorData.error === 'slow_down') {\n await new Promise((resolve) => setTimeout(resolve, interval));\n }\n } catch (err: unknown) {\n printError('Failed to parse poll error response', err instanceof Error ? err : undefined);\n }\n}\n\nasync function checkLogin(): Promise<void> {\n const config = await readConfig();\n\n if (!config?.auth?.token) {\n print('Not logged in. Run `shipyard login` to authenticate.');\n process.exit(1);\n }\n\n if (config.auth.expiresAt < Date.now()) {\n print('Token expired. Run `shipyard login` to re-authenticate.');\n process.exit(1);\n }\n\n const daysLeft = Math.ceil((config.auth.expiresAt - Date.now()) / (24 * 60 * 60 * 1000));\n print(`Logged in as ${config.auth.displayName} (${config.auth.providers.join(', ')})`);\n print(`Token expires in ${daysLeft} days`);\n}\n"],"mappings":"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","names":[]}
@@ -1,12 +1,10 @@
1
1
  #!/usr/bin/env node
2
+ import {
3
+ createJsonLineCodec
4
+ } from "./chunk-ZFKJAYAN.js";
2
5
  import {
3
6
  external_exports
4
- } from "./chunk-VPMN47TL.js";
5
-
6
- // src/shared/assert-never.ts
7
- function assertNever(value) {
8
- throw new Error(`Unexpected value: ${JSON.stringify(value)}`);
9
- }
7
+ } from "./chunk-CNR7O5YH.js";
10
8
 
11
9
  // src/services/watcher-worker/worker-protocol.ts
12
10
  var SubscribeOptionsSchema = external_exports.object({
@@ -28,6 +26,7 @@ var CommandSchema = external_exports.discriminatedUnion("cmd", [
28
26
  external_exports.object({ cmd: external_exports.literal("unsubscribe"), id: external_exports.string() }),
29
27
  external_exports.object({ cmd: external_exports.literal("shutdown") })
30
28
  ]);
29
+ var WorkerCommandCodec = createJsonLineCodec(CommandSchema);
31
30
  var ParcelEventSchema = external_exports.object({
32
31
  type: external_exports.enum(["create", "update", "delete"]),
33
32
  path: external_exports.string()
@@ -38,27 +37,21 @@ var ReplySchema = external_exports.discriminatedUnion("type", [
38
37
  external_exports.object({ type: external_exports.literal("unsubscribed"), id: external_exports.string() }),
39
38
  external_exports.object({ type: external_exports.literal("events"), id: external_exports.string(), events: external_exports.array(ParcelEventSchema) })
40
39
  ]);
40
+ var WorkerReplyCodec = createJsonLineCodec(ReplySchema);
41
+ var WorkerMessageSchema = external_exports.union([CommandSchema, ReplySchema]);
42
+ var WorkerMessageCodec = createJsonLineCodec(WorkerMessageSchema);
41
43
  function encodeLine(msg) {
42
- return JSON.stringify(msg);
44
+ return WorkerMessageCodec.encode(msg);
43
45
  }
44
46
  function decodeLine(line) {
45
- let parsed;
46
- try {
47
- parsed = JSON.parse(line);
48
- } catch {
49
- return null;
50
- }
51
- if (parsed === null || typeof parsed !== "object") return null;
52
- const asCmd = CommandSchema.safeParse(parsed);
53
- if (asCmd.success) return asCmd.data;
54
- const asReply = ReplySchema.safeParse(parsed);
55
- if (asReply.success) return asReply.data;
56
- return null;
47
+ const decoded = WorkerMessageCodec.decode(line);
48
+ return decoded.ok ? decoded.message : null;
57
49
  }
58
50
 
59
51
  export {
60
- assertNever,
52
+ WorkerCommandCodec,
53
+ WorkerReplyCodec,
61
54
  encodeLine,
62
55
  decodeLine
63
56
  };
64
- //# sourceMappingURL=chunk-SNYEQHUK.js.map
57
+ //# sourceMappingURL=chunk-NACJENDW.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/services/watcher-worker/worker-protocol.ts"],"sourcesContent":["import { createJsonLineCodec } from '@shipyard/local-runtime';\nimport { z } from 'zod';\n\n/**\n * IPC protocol between the daemon (parent) and the watcher worker subprocess.\n *\n * Wire format: line-delimited JSON over the worker's stdio. Each line is\n * exactly one Command (parent → worker) or one Reply (worker → parent), with\n * no embedded literal newlines — JSON encoding escapes newlines inside string\n * values (e.g. a path containing `\\n`) so the line protocol stays intact.\n *\n * Stdout is the IPC channel; structured worker logs go to stderr only.\n */\n\nexport const SubscribeOptionsSchema = z.object({\n ignore: z.array(z.string()).optional(),\n /**\n * Constrained to the subset the daemon actually uses. `@parcel/watcher`\n * supports more backends but they are platform-specific and we never set\n * them — accepting the wider union here would just invite bugs.\n */\n backend: z.enum(['default', 'brute-force', 'watchman']).optional(),\n});\nexport type WorkerSubscribeOptions = z.infer<typeof SubscribeOptionsSchema>;\n\nexport const CommandSchema = z.discriminatedUnion('cmd', [\n z.object({\n cmd: z.literal('subscribe'),\n id: z.string(),\n path: z.string(),\n opts: SubscribeOptionsSchema,\n }),\n z.object({ cmd: z.literal('unsubscribe'), id: z.string() }),\n z.object({ cmd: z.literal('shutdown') }),\n]);\nexport type WorkerCommand = z.infer<typeof CommandSchema>;\nexport const WorkerCommandCodec = createJsonLineCodec(CommandSchema);\n\nconst ParcelEventSchema = z.object({\n type: z.enum(['create', 'update', 'delete']),\n path: z.string(),\n});\nexport type WorkerParcelEvent = z.infer<typeof ParcelEventSchema>;\n\nexport const ReplySchema = z.discriminatedUnion('type', [\n z.object({ type: z.literal('subscribed'), id: z.string() }),\n z.object({ type: z.literal('subscribe_failed'), id: z.string(), error: z.string() }),\n z.object({ type: z.literal('unsubscribed'), id: z.string() }),\n z.object({ type: z.literal('events'), id: z.string(), events: z.array(ParcelEventSchema) }),\n]);\nexport type WorkerReply = z.infer<typeof ReplySchema>;\nexport const WorkerReplyCodec = createJsonLineCodec(ReplySchema);\n\nconst WorkerMessageSchema = z.union([CommandSchema, ReplySchema]);\nconst WorkerMessageCodec = createJsonLineCodec(WorkerMessageSchema);\n\nexport function encodeLine(msg: WorkerCommand | WorkerReply): string {\n return WorkerMessageCodec.encode(msg);\n}\n\n/**\n * Decode a single line. Returns `null` for any malformed or\n * schema-rejected input — the worker logs and continues, the supervisor\n * drops the bad line. Throwing here would let one corrupt line crash an\n * otherwise-healthy peer, which is the opposite of what process supervision\n * is for.\n */\nexport function decodeLine(line: string): WorkerCommand | WorkerReply | null {\n const decoded = WorkerMessageCodec.decode(line);\n return decoded.ok ? decoded.message : null;\n}\n"],"mappings":";;;;;;;;;AAcO,IAAM,yBAAyB,iBAAE,OAAO;AAAA,EAC7C,QAAQ,iBAAE,MAAM,iBAAE,OAAO,CAAC,EAAE,SAAS;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAMrC,SAAS,iBAAE,KAAK,CAAC,WAAW,eAAe,UAAU,CAAC,EAAE,SAAS;AACnE,CAAC;AAGM,IAAM,gBAAgB,iBAAE,mBAAmB,OAAO;AAAA,EACvD,iBAAE,OAAO;AAAA,IACP,KAAK,iBAAE,QAAQ,WAAW;AAAA,IAC1B,IAAI,iBAAE,OAAO;AAAA,IACb,MAAM,iBAAE,OAAO;AAAA,IACf,MAAM;AAAA,EACR,CAAC;AAAA,EACD,iBAAE,OAAO,EAAE,KAAK,iBAAE,QAAQ,aAAa,GAAG,IAAI,iBAAE,OAAO,EAAE,CAAC;AAAA,EAC1D,iBAAE,OAAO,EAAE,KAAK,iBAAE,QAAQ,UAAU,EAAE,CAAC;AACzC,CAAC;AAEM,IAAM,qBAAqB,oBAAoB,aAAa;AAEnE,IAAM,oBAAoB,iBAAE,OAAO;AAAA,EACjC,MAAM,iBAAE,KAAK,CAAC,UAAU,UAAU,QAAQ,CAAC;AAAA,EAC3C,MAAM,iBAAE,OAAO;AACjB,CAAC;AAGM,IAAM,cAAc,iBAAE,mBAAmB,QAAQ;AAAA,EACtD,iBAAE,OAAO,EAAE,MAAM,iBAAE,QAAQ,YAAY,GAAG,IAAI,iBAAE,OAAO,EAAE,CAAC;AAAA,EAC1D,iBAAE,OAAO,EAAE,MAAM,iBAAE,QAAQ,kBAAkB,GAAG,IAAI,iBAAE,OAAO,GAAG,OAAO,iBAAE,OAAO,EAAE,CAAC;AAAA,EACnF,iBAAE,OAAO,EAAE,MAAM,iBAAE,QAAQ,cAAc,GAAG,IAAI,iBAAE,OAAO,EAAE,CAAC;AAAA,EAC5D,iBAAE,OAAO,EAAE,MAAM,iBAAE,QAAQ,QAAQ,GAAG,IAAI,iBAAE,OAAO,GAAG,QAAQ,iBAAE,MAAM,iBAAiB,EAAE,CAAC;AAC5F,CAAC;AAEM,IAAM,mBAAmB,oBAAoB,WAAW;AAE/D,IAAM,sBAAsB,iBAAE,MAAM,CAAC,eAAe,WAAW,CAAC;AAChE,IAAM,qBAAqB,oBAAoB,mBAAmB;AAE3D,SAAS,WAAW,KAA0C;AACnE,SAAO,mBAAmB,OAAO,GAAG;AACtC;AASO,SAAS,WAAW,MAAkD;AAC3E,QAAM,UAAU,mBAAmB,OAAO,IAAI;AAC9C,SAAO,QAAQ,KAAK,QAAQ,UAAU;AACxC;","names":[]}
@@ -1,7 +1,7 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
3
  getLogFilePath
4
- } from "./chunk-2UN5AR7V.js";
4
+ } from "./chunk-ZAOPND5G.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-IISLTKYY.js.map
43
+ //# sourceMappingURL=chunk-TU63KZFW.js.map
@@ -0,0 +1,186 @@
1
+ #!/usr/bin/env node
2
+ import {
3
+ external_exports
4
+ } from "./chunk-CNR7O5YH.js";
5
+
6
+ // src/services/session/cursor-runner-protocol.ts
7
+ var McpServersSchema = external_exports.record(external_exports.string(), external_exports.unknown());
8
+ var DaemonToRunnerSchema = external_exports.discriminatedUnion("kind", [
9
+ external_exports.object({
10
+ kind: external_exports.literal("init"),
11
+ taskId: external_exports.string(),
12
+ cwd: external_exports.string(),
13
+ apiKey: external_exports.string(),
14
+ modelId: external_exports.string(),
15
+ mcpServers: McpServersSchema,
16
+ harnessUrl: external_exports.string(),
17
+ harnessToken: external_exports.string(),
18
+ /**
19
+ * Absolute filesystem path where skill files for this task are
20
+ * written (see plan v2 E#V7). The runner does not enumerate the
21
+ * directory — it just forwards the path to the SDK so any future
22
+ * skills-loading code has the location. May be empty string if
23
+ * skills are disabled for this run.
24
+ */
25
+ skillsDir: external_exports.string(),
26
+ sessionId: external_exports.string(),
27
+ generation: external_exports.number().int().nonnegative(),
28
+ /**
29
+ * Initial Fast Mode flag from `composerSettings.fastMode`. When true
30
+ * AND `fastModeParam` is non-null, the runner attaches the param to
31
+ * every `agent.send()` via `SendOptions.model.params`. When false
32
+ * (or `fastModeParam` is null), the runner sends the plain
33
+ * `agent.send(message)` shape. Optional/additive — pre-Fast-Mode
34
+ * daemons omit the field and the runner treats it as false.
35
+ */
36
+ fastMode: external_exports.boolean().optional(),
37
+ /**
38
+ * Resolved Fast Mode binding for `modelId`, sourced from the
39
+ * daemon's `cursor-models-cache`. `null` means "the cache has no
40
+ * Fast-Mode binding for this model right now" (unknown id, refresh
41
+ * failed, or the model declares no Fast-Mode-shaped parameter).
42
+ * The runner treats both `null` and `undefined` as "no binding,
43
+ * never attach params" — gating Fast Mode the same way regardless
44
+ * of why it's missing.
45
+ */
46
+ fastModeParam: external_exports.object({ id: external_exports.string(), value: external_exports.string() }).nullable().optional()
47
+ }),
48
+ external_exports.object({
49
+ kind: external_exports.literal("push_message"),
50
+ generation: external_exports.number().int().nonnegative(),
51
+ /**
52
+ * Content blocks the daemon wants to send as a user turn. Shape is
53
+ * Shipyard's internal `ContentBlock[]` — the runner translates to
54
+ * Cursor's `SDKUserMessage` at the boundary (see cursor-content-builder.ts).
55
+ *
56
+ * Image attachments ride as `resource` blocks with `BlobResourceContents`
57
+ * (mime + base64 blob). The runner extracts these into `SDKImage[]` and
58
+ * attaches them to the `SDKUserMessage` it sends to `Agent.send()`.
59
+ */
60
+ content: external_exports.array(external_exports.unknown())
61
+ }),
62
+ external_exports.object({
63
+ kind: external_exports.literal("interrupt"),
64
+ generation: external_exports.number().int().nonnegative()
65
+ }),
66
+ external_exports.object({
67
+ kind: external_exports.literal("set_model"),
68
+ generation: external_exports.number().int().nonnegative(),
69
+ modelId: external_exports.string()
70
+ }),
71
+ external_exports.object({
72
+ kind: external_exports.literal("set_mcp_servers"),
73
+ generation: external_exports.number().int().nonnegative(),
74
+ servers: McpServersSchema
75
+ }),
76
+ external_exports.object({
77
+ kind: external_exports.literal("set_harness_task_id"),
78
+ generation: external_exports.number().int().nonnegative(),
79
+ taskId: external_exports.string(),
80
+ token: external_exports.string()
81
+ }),
82
+ /**
83
+ * Daemon→runner notification that the permission mode changed. The runner
84
+ * logs this for diagnostic observability; it takes no action (mode-file
85
+ * is the authoritative source the hook script reads on each tool call).
86
+ */
87
+ external_exports.object({
88
+ kind: external_exports.literal("set_permission_mode"),
89
+ generation: external_exports.number().int().nonnegative(),
90
+ mode: external_exports.enum(["default", "accept-edits", "plan", "bypass", "auto"])
91
+ }),
92
+ /**
93
+ * Daemon→runner notification that Fast Mode was toggled mid-session.
94
+ * Carries the current generation (not bumped — this is per-state, not
95
+ * per-turn) so the runner can correlate the update. Effective on the
96
+ * NEXT `push_message`: the runner records the flag and rebuilds
97
+ * `SendOptions` on each `agent.send()`.
98
+ */
99
+ external_exports.object({
100
+ kind: external_exports.literal("set_fast_mode"),
101
+ generation: external_exports.number().int().nonnegative(),
102
+ fastMode: external_exports.boolean()
103
+ }),
104
+ external_exports.object({ kind: external_exports.literal("close") })
105
+ ]);
106
+ var RunnerToDaemonSchema = external_exports.discriminatedUnion("kind", [
107
+ external_exports.object({
108
+ kind: external_exports.literal("init_ok"),
109
+ sessionId: external_exports.string(),
110
+ tools: external_exports.array(external_exports.string()),
111
+ model: external_exports.string(),
112
+ mcpStatus: external_exports.array(external_exports.object({ name: external_exports.string(), status: external_exports.string() }))
113
+ }),
114
+ external_exports.object({
115
+ kind: external_exports.literal("init_error"),
116
+ error: external_exports.string(),
117
+ /**
118
+ * `true` for transient failures (network, rate limit) where the
119
+ * daemon should consider a retry; `false` for configuration errors
120
+ * (bad API key, unknown model) where retry will not help.
121
+ */
122
+ retryable: external_exports.boolean()
123
+ }),
124
+ external_exports.object({
125
+ kind: external_exports.literal("sdk_message"),
126
+ generation: external_exports.number().int().nonnegative(),
127
+ payload: external_exports.unknown()
128
+ }),
129
+ external_exports.object({
130
+ kind: external_exports.literal("run_complete"),
131
+ generation: external_exports.number().int().nonnegative(),
132
+ /**
133
+ * Cursor's `RunResult` — kept as `unknown` on the wire (see
134
+ * `sdk_message.payload` rationale). The daemon-side adapter
135
+ * narrows this against `RunResult` from `@cursor/sdk` at the
136
+ * call site.
137
+ */
138
+ result: external_exports.unknown()
139
+ }),
140
+ external_exports.object({
141
+ kind: external_exports.literal("run_error"),
142
+ generation: external_exports.number().int().nonnegative(),
143
+ error: external_exports.string(),
144
+ errorCode: external_exports.string().optional(),
145
+ statusCode: external_exports.number().int().optional(),
146
+ isRetryable: external_exports.boolean()
147
+ }),
148
+ external_exports.object({
149
+ kind: external_exports.literal("log"),
150
+ level: external_exports.enum(["info", "warn", "error"]),
151
+ message: external_exports.string(),
152
+ data: external_exports.unknown().optional()
153
+ }),
154
+ /**
155
+ * Runner→daemon notification that the agent proposed a plan via the
156
+ * `createPlan` hook interception path. The daemon routes this to
157
+ * `plan-handler.ts` which writes `~/.shipyard/plans/{taskId}.md` and
158
+ * broadcasts `plan_detected`.
159
+ *
160
+ * Note: In v1 this message is reserved for future hook-socket capture.
161
+ * The primary plan capture path is the `createPlan` tool_use interception
162
+ * in `cursor-event-translator.ts` (turn-complete fallback). The protocol
163
+ * variant is defined now so the wire schema is stable when hook-socket
164
+ * plan capture ships.
165
+ */
166
+ external_exports.object({
167
+ kind: external_exports.literal("plan_proposed"),
168
+ generation: external_exports.number().int().nonnegative(),
169
+ /** Markdown plan content emitted by the agent via createPlan. */
170
+ plan: external_exports.string()
171
+ })
172
+ ]);
173
+ function parseDaemonToRunner(raw) {
174
+ const result = DaemonToRunnerSchema.safeParse(raw);
175
+ return result.success ? result.data : null;
176
+ }
177
+ function parseRunnerToDaemon(raw) {
178
+ const result = RunnerToDaemonSchema.safeParse(raw);
179
+ return result.success ? result.data : null;
180
+ }
181
+
182
+ export {
183
+ parseDaemonToRunner,
184
+ parseRunnerToDaemon
185
+ };
186
+ //# sourceMappingURL=chunk-TX6DK4PK.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/services/session/cursor-runner-protocol.ts"],"sourcesContent":["import { z } from 'zod';\n\n/**\n * IPC protocol between the daemon (parent) and the `cursor-runner` forked\n * child subprocess.\n *\n * Wire format: structured-clone over the Node IPC channel\n * (`child.send(msg)` / `process.on('message', msg => ...)`). The schemas\n * below are Zod-parsed on receive so a corrupt or version-skewed peer\n * cannot drive the recipient into an undefined state.\n *\n * Stdout/stderr of the runner are inherited from the daemon (see S2b's\n * `child_process.fork({ stdio: 'inherit', silent: false })`) — those are\n * NOT carrying IPC traffic, they are for human-readable logs and native\n * panic stack traces.\n *\n * `generation` is a per-turn monotonic counter minted by the daemon-side\n * adapter. Every per-turn message in either direction is stamped with the\n * value live at the time of mint; the daemon drops inbound events whose\n * generation does not match the current run (closes Invariant #16 — the\n * transport discriminator must be present at every layer). The runner\n * does NOT drop on its own — it echoes whatever generation the daemon\n * stamped, so the daemon can correlate.\n *\n * Lifecycle events that are not per-turn (`init`, `init_ok`,\n * `init_error`, `log`, `close`) carry no generation.\n *\n * MCP server config shape: `Record<string, unknown>` here is intentional.\n * We keep the wire schema decoupled from `@cursor/sdk`'s `McpServerConfig`\n * to avoid coupling the daemon and runner to the same exact SDK version\n * at the type level. The runner re-validates against the SDK shape at\n * the call site.\n */\n\nconst McpServersSchema = z.record(z.string(), z.unknown());\n\n/**\n * DaemonToRunner — outbound from the daemon-side `CursorAgentSubprocess`\n * adapter to the forked runner child.\n */\nexport const DaemonToRunnerSchema = z.discriminatedUnion('kind', [\n z.object({\n kind: z.literal('init'),\n taskId: z.string(),\n cwd: z.string(),\n apiKey: z.string(),\n modelId: z.string(),\n mcpServers: McpServersSchema,\n harnessUrl: z.string(),\n harnessToken: z.string(),\n /**\n * Absolute filesystem path where skill files for this task are\n * written (see plan v2 E#V7). The runner does not enumerate the\n * directory — it just forwards the path to the SDK so any future\n * skills-loading code has the location. May be empty string if\n * skills are disabled for this run.\n */\n skillsDir: z.string(),\n sessionId: z.string(),\n generation: z.number().int().nonnegative(),\n /**\n * Initial Fast Mode flag from `composerSettings.fastMode`. When true\n * AND `fastModeParam` is non-null, the runner attaches the param to\n * every `agent.send()` via `SendOptions.model.params`. When false\n * (or `fastModeParam` is null), the runner sends the plain\n * `agent.send(message)` shape. Optional/additive — pre-Fast-Mode\n * daemons omit the field and the runner treats it as false.\n */\n fastMode: z.boolean().optional(),\n /**\n * Resolved Fast Mode binding for `modelId`, sourced from the\n * daemon's `cursor-models-cache`. `null` means \"the cache has no\n * Fast-Mode binding for this model right now\" (unknown id, refresh\n * failed, or the model declares no Fast-Mode-shaped parameter).\n * The runner treats both `null` and `undefined` as \"no binding,\n * never attach params\" — gating Fast Mode the same way regardless\n * of why it's missing.\n */\n fastModeParam: z.object({ id: z.string(), value: z.string() }).nullable().optional(),\n }),\n z.object({\n kind: z.literal('push_message'),\n generation: z.number().int().nonnegative(),\n /**\n * Content blocks the daemon wants to send as a user turn. Shape is\n * Shipyard's internal `ContentBlock[]` — the runner translates to\n * Cursor's `SDKUserMessage` at the boundary (see cursor-content-builder.ts).\n *\n * Image attachments ride as `resource` blocks with `BlobResourceContents`\n * (mime + base64 blob). The runner extracts these into `SDKImage[]` and\n * attaches them to the `SDKUserMessage` it sends to `Agent.send()`.\n */\n content: z.array(z.unknown()),\n }),\n z.object({\n kind: z.literal('interrupt'),\n generation: z.number().int().nonnegative(),\n }),\n z.object({\n kind: z.literal('set_model'),\n generation: z.number().int().nonnegative(),\n modelId: z.string(),\n }),\n z.object({\n kind: z.literal('set_mcp_servers'),\n generation: z.number().int().nonnegative(),\n servers: McpServersSchema,\n }),\n z.object({\n kind: z.literal('set_harness_task_id'),\n generation: z.number().int().nonnegative(),\n taskId: z.string(),\n token: z.string(),\n }),\n /**\n * Daemon→runner notification that the permission mode changed. The runner\n * logs this for diagnostic observability; it takes no action (mode-file\n * is the authoritative source the hook script reads on each tool call).\n */\n z.object({\n kind: z.literal('set_permission_mode'),\n generation: z.number().int().nonnegative(),\n mode: z.enum(['default', 'accept-edits', 'plan', 'bypass', 'auto']),\n }),\n /**\n * Daemon→runner notification that Fast Mode was toggled mid-session.\n * Carries the current generation (not bumped — this is per-state, not\n * per-turn) so the runner can correlate the update. Effective on the\n * NEXT `push_message`: the runner records the flag and rebuilds\n * `SendOptions` on each `agent.send()`.\n */\n z.object({\n kind: z.literal('set_fast_mode'),\n generation: z.number().int().nonnegative(),\n fastMode: z.boolean(),\n }),\n z.object({ kind: z.literal('close') }),\n]);\nexport type DaemonToRunner = z.infer<typeof DaemonToRunnerSchema>;\n\n/**\n * RunnerToDaemon — inbound to the daemon-side adapter from the runner.\n *\n * `sdk_message.payload` is typed as `unknown` on the wire and narrowed\n * to `SDKMessage` (from `@cursor/sdk`) by the daemon-side translator\n * (`cursor-event-translator.ts`, S2b). Keeping it `unknown` at the\n * protocol boundary insulates the daemon from new SDK message variants\n * — the translator decides what to drop versus translate.\n */\nexport const RunnerToDaemonSchema = z.discriminatedUnion('kind', [\n z.object({\n kind: z.literal('init_ok'),\n sessionId: z.string(),\n tools: z.array(z.string()),\n model: z.string(),\n mcpStatus: z.array(z.object({ name: z.string(), status: z.string() })),\n }),\n z.object({\n kind: z.literal('init_error'),\n error: z.string(),\n /**\n * `true` for transient failures (network, rate limit) where the\n * daemon should consider a retry; `false` for configuration errors\n * (bad API key, unknown model) where retry will not help.\n */\n retryable: z.boolean(),\n }),\n z.object({\n kind: z.literal('sdk_message'),\n generation: z.number().int().nonnegative(),\n payload: z.unknown(),\n }),\n z.object({\n kind: z.literal('run_complete'),\n generation: z.number().int().nonnegative(),\n /**\n * Cursor's `RunResult` — kept as `unknown` on the wire (see\n * `sdk_message.payload` rationale). The daemon-side adapter\n * narrows this against `RunResult` from `@cursor/sdk` at the\n * call site.\n */\n result: z.unknown(),\n }),\n z.object({\n kind: z.literal('run_error'),\n generation: z.number().int().nonnegative(),\n error: z.string(),\n errorCode: z.string().optional(),\n statusCode: z.number().int().optional(),\n isRetryable: z.boolean(),\n }),\n z.object({\n kind: z.literal('log'),\n level: z.enum(['info', 'warn', 'error']),\n message: z.string(),\n data: z.unknown().optional(),\n }),\n /**\n * Runner→daemon notification that the agent proposed a plan via the\n * `createPlan` hook interception path. The daemon routes this to\n * `plan-handler.ts` which writes `~/.shipyard/plans/{taskId}.md` and\n * broadcasts `plan_detected`.\n *\n * Note: In v1 this message is reserved for future hook-socket capture.\n * The primary plan capture path is the `createPlan` tool_use interception\n * in `cursor-event-translator.ts` (turn-complete fallback). The protocol\n * variant is defined now so the wire schema is stable when hook-socket\n * plan capture ships.\n */\n z.object({\n kind: z.literal('plan_proposed'),\n generation: z.number().int().nonnegative(),\n /** Markdown plan content emitted by the agent via createPlan. */\n plan: z.string(),\n }),\n]);\nexport type RunnerToDaemon = z.infer<typeof RunnerToDaemonSchema>;\n\n/**\n * Parse an inbound IPC message. Returns the parsed message or `null`\n * if the payload does not match the expected schema (matching the\n * watcher-worker pattern: log and continue, never throw out of the\n * IPC handler).\n */\nexport function parseDaemonToRunner(raw: unknown): DaemonToRunner | null {\n const result = DaemonToRunnerSchema.safeParse(raw);\n return result.success ? result.data : null;\n}\n\nexport function parseRunnerToDaemon(raw: unknown): RunnerToDaemon | null {\n const result = RunnerToDaemonSchema.safeParse(raw);\n return result.success ? result.data : null;\n}\n"],"mappings":"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","names":[]}
@@ -0,0 +1,48 @@
1
+ #!/usr/bin/env node
2
+ import {
3
+ external_exports
4
+ } from "./chunk-CNR7O5YH.js";
5
+
6
+ // src/shared/daemon-version.ts
7
+ import { readFileSync } from "fs";
8
+ import { dirname, join } from "path";
9
+ import { fileURLToPath } from "url";
10
+ var PackageJsonSchema = external_exports.object({ version: external_exports.string().min(1) }).loose();
11
+ var cached = null;
12
+ function getDaemonVersion() {
13
+ if (cached !== null) return cached;
14
+ cached = readDaemonVersion();
15
+ return cached;
16
+ }
17
+ function readDaemonVersion() {
18
+ if ("3.8.0".length > 0) {
19
+ return "3.8.0";
20
+ }
21
+ try {
22
+ const here = dirname(fileURLToPath(import.meta.url));
23
+ for (let depth = 0; depth <= 5; depth++) {
24
+ const candidate = depth === 0 ? join(here, "package.json") : join(here, "../".repeat(depth), "package.json");
25
+ try {
26
+ const raw = readFileSync(candidate, "utf-8");
27
+ const parsed = JSON.parse(raw);
28
+ const result = PackageJsonSchema.safeParse(parsed);
29
+ if (result.success) {
30
+ return result.data.version;
31
+ }
32
+ } catch {
33
+ }
34
+ }
35
+ } catch {
36
+ }
37
+ if (process.env.SHIPYARD_ARTIFACT === "electron") {
38
+ throw new Error(
39
+ "getDaemonVersion: could not resolve daemon version in packaged Electron build. Ensure electron-builder.yml ships package-npm.json as daemon/package.json, and that __DAEMON_NPM_VERSION__ is defined in the tsup build."
40
+ );
41
+ }
42
+ return "0.0.0-dev";
43
+ }
44
+
45
+ export {
46
+ getDaemonVersion
47
+ };
48
+ //# sourceMappingURL=chunk-UQVXWOPT.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/shared/daemon-version.ts"],"sourcesContent":["/**\n * Daemon version resolver.\n *\n * Resolution order:\n * 1. Build-time constant `__DAEMON_NPM_VERSION__` (injected by tsup — always\n * present in compiled artefacts, both npm dist and Electron packaged build).\n * 2. Disk walk up from `import.meta.url` (depth 0 first = sibling of the\n * running entrypoint, which is the correct layout inside packaged Electron's\n * Resources/daemon/). Walks up to 5 levels as a fallback for the source\n * tree which is typically 3 levels deep.\n *\n * If neither resolves and the binary is running in a packaged Electron context\n * (`SHIPYARD_ARTIFACT === 'electron'`), we throw rather than silently returning\n * '0.0.0-dev'. A daemon that doesn't know its own version inside Electron is a\n * misconfigured build — failing loudly is better than advertising a phantom version\n * that breaks the update-rotation guard and version-mismatch banner.\n *\n * In source-tree dev runs (no `SHIPYARD_ARTIFACT`), '0.0.0-dev' is acceptable.\n *\n * Caches after the first read.\n */\n\nimport { readFileSync } from 'node:fs';\nimport { dirname, join } from 'node:path';\nimport { fileURLToPath } from 'node:url';\nimport { z } from 'zod';\n\n/**\n * Injected by tsup `define` at build time from `package-npm.json`. Absent in\n * source-tree runs where the file hasn't been compiled.\n */\ndeclare const __DAEMON_NPM_VERSION__: string | undefined;\n\nconst PackageJsonSchema = z.object({ version: z.string().min(1) }).loose();\n\nlet cached: string | null = null;\n\nexport function getDaemonVersion(): string {\n if (cached !== null) return cached;\n cached = readDaemonVersion();\n return cached;\n}\n\nfunction readDaemonVersion(): string {\n /*\n * 1. Prefer the build-time constant injected by tsup — always correct in\n * compiled artefacts (both npm dist and packaged Electron).\n */\n if (typeof __DAEMON_NPM_VERSION__ !== 'undefined' && __DAEMON_NPM_VERSION__.length > 0) {\n return __DAEMON_NPM_VERSION__;\n }\n\n /*\n * 2. Fall back to disk walk. Start at depth=0 so we check the sibling\n * package.json first — which is the layout inside packaged Electron's\n * Resources/daemon/ directory (electron-utility.js and package.json are\n * siblings). Previously this loop started at depth=1, which skipped the\n * sibling and caused every packaged build to return '0.0.0-dev'.\n */\n try {\n const here = dirname(fileURLToPath(import.meta.url));\n for (let depth = 0; depth <= 5; depth++) {\n const candidate =\n depth === 0 ? join(here, 'package.json') : join(here, '../'.repeat(depth), 'package.json');\n try {\n const raw = readFileSync(candidate, 'utf-8');\n const parsed: unknown = JSON.parse(raw);\n const result = PackageJsonSchema.safeParse(parsed);\n if (result.success) {\n return result.data.version;\n }\n } catch {\n /** Try next level. */\n }\n }\n } catch {\n /** Fall through to fallback logic. */\n }\n\n /*\n * 3. Neither the build constant nor the disk walk resolved a version.\n * In a packaged Electron context this is a configuration error — throw so\n * the build pipeline catches it rather than silently advertising '0.0.0-dev'.\n */\n if (process.env.SHIPYARD_ARTIFACT === 'electron') {\n throw new Error(\n 'getDaemonVersion: could not resolve daemon version in packaged Electron build. ' +\n 'Ensure electron-builder.yml ships package-npm.json as daemon/package.json, and that ' +\n '__DAEMON_NPM_VERSION__ is defined in the tsup build.'\n );\n }\n\n /**\n * Reached only in source-tree dev runs where no package.json is discoverable.\n * Return a non-\"unknown\" sentinel so the version-rotate guard still has a\n * stable string to compare; rotation will then fire exactly once per boot\n * with a different daemon, which is the safe direction.\n */\n return '0.0.0-dev';\n}\n\n/** Test-only helper to reset the cache. */\nexport function __resetDaemonVersionForTests(): void {\n cached = null;\n}\n\n/**\n * Test-only helper to inject a specific version, bypassing all resolution\n * logic. Used to simulate the build-time constant being defined (T5) or a\n * specific filesystem layout (T6).\n */\nexport function __setDaemonVersionForTests(version: string): void {\n cached = version;\n}\n"],"mappings":";;;;;;AAsBA,SAAS,oBAAoB;AAC7B,SAAS,SAAS,YAAY;AAC9B,SAAS,qBAAqB;AAS9B,IAAM,oBAAoB,iBAAE,OAAO,EAAE,SAAS,iBAAE,OAAO,EAAE,IAAI,CAAC,EAAE,CAAC,EAAE,MAAM;AAEzE,IAAI,SAAwB;AAErB,SAAS,mBAA2B;AACzC,MAAI,WAAW,KAAM,QAAO;AAC5B,WAAS,kBAAkB;AAC3B,SAAO;AACT;AAEA,SAAS,oBAA4B;AAKnC,MAAqD,QAAuB,SAAS,GAAG;AACtF,WAAO;AAAA,EACT;AASA,MAAI;AACF,UAAM,OAAO,QAAQ,cAAc,YAAY,GAAG,CAAC;AACnD,aAAS,QAAQ,GAAG,SAAS,GAAG,SAAS;AACvC,YAAM,YACJ,UAAU,IAAI,KAAK,MAAM,cAAc,IAAI,KAAK,MAAM,MAAM,OAAO,KAAK,GAAG,cAAc;AAC3F,UAAI;AACF,cAAM,MAAM,aAAa,WAAW,OAAO;AAC3C,cAAM,SAAkB,KAAK,MAAM,GAAG;AACtC,cAAM,SAAS,kBAAkB,UAAU,MAAM;AACjD,YAAI,OAAO,SAAS;AAClB,iBAAO,OAAO,KAAK;AAAA,QACrB;AAAA,MACF,QAAQ;AAAA,MAER;AAAA,IACF;AAAA,EACF,QAAQ;AAAA,EAER;AAOA,MAAI,QAAQ,IAAI,sBAAsB,YAAY;AAChD,UAAM,IAAI;AAAA,MACR;AAAA,IAGF;AAAA,EACF;AAQA,SAAO;AACT;","names":[]}