@schoolai/shipyard 3.7.0 → 3.8.0-rc.20260529.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (104) hide show
  1. package/dist/{auth-SS7LV5XK.js → auth-EXHO3AG5.js} +4 -4
  2. package/dist/capability-detector-worker.js +142 -0
  3. package/dist/capability-detector-worker.js.map +1 -0
  4. package/dist/{chunk-DKMDBOFU.js → chunk-2CNIEBKO.js} +21 -11
  5. package/dist/chunk-2CNIEBKO.js.map +1 -0
  6. package/dist/chunk-4T2OQAVL.js +51 -0
  7. package/dist/chunk-4T2OQAVL.js.map +1 -0
  8. package/dist/chunk-5ER6ZHA2.js +46 -0
  9. package/dist/chunk-5ER6ZHA2.js.map +1 -0
  10. package/dist/chunk-7LSEE26O.js +24227 -0
  11. package/dist/chunk-7LSEE26O.js.map +1 -0
  12. package/dist/{chunk-7AHRFPAL.js → chunk-7YOU7MBN.js} +183 -17
  13. package/dist/chunk-7YOU7MBN.js.map +1 -0
  14. package/dist/chunk-CMGJGK6R.js +382 -0
  15. package/dist/chunk-CMGJGK6R.js.map +1 -0
  16. package/dist/{chunk-VPMN47TL.js → chunk-CNR7O5YH.js} +1 -2
  17. package/dist/{chunk-2J3WSIAF.js → chunk-EF2DAODF.js} +18 -3
  18. package/dist/chunk-EF2DAODF.js.map +1 -0
  19. package/dist/chunk-HQ43PHOH.js +1203 -0
  20. package/dist/chunk-HQ43PHOH.js.map +1 -0
  21. package/dist/chunk-KITSAHTX.js +134 -0
  22. package/dist/chunk-KITSAHTX.js.map +1 -0
  23. package/dist/chunk-LESHN5J5.js +6898 -0
  24. package/dist/chunk-LESHN5J5.js.map +1 -0
  25. package/dist/{chunk-LW2MS4T5.js → chunk-LMJFHKRD.js} +15 -12
  26. package/dist/chunk-LMJFHKRD.js.map +1 -0
  27. package/dist/{chunk-SNYEQHUK.js → chunk-NACJENDW.js} +14 -21
  28. package/dist/chunk-NACJENDW.js.map +1 -0
  29. package/dist/{chunk-IISLTKYY.js → chunk-TU63KZFW.js} +2 -2
  30. package/dist/chunk-TX6DK4PK.js +186 -0
  31. package/dist/chunk-TX6DK4PK.js.map +1 -0
  32. package/dist/chunk-UQVXWOPT.js +48 -0
  33. package/dist/chunk-UQVXWOPT.js.map +1 -0
  34. package/dist/{chunk-3MNPDCO5.js → chunk-WBB4XHLH.js} +139 -140
  35. package/dist/chunk-WBB4XHLH.js.map +1 -0
  36. package/dist/chunk-X3MULCV5.js +11 -0
  37. package/dist/chunk-X3MULCV5.js.map +1 -0
  38. package/dist/chunk-YZ3Z3ZYI.js +787 -0
  39. package/dist/chunk-YZ3Z3ZYI.js.map +1 -0
  40. package/dist/{chunk-2UN5AR7V.js → chunk-ZAOPND5G.js} +2 -2
  41. package/dist/chunk-ZFKJAYAN.js +542 -0
  42. package/dist/chunk-ZFKJAYAN.js.map +1 -0
  43. package/dist/cursor-hook-shim.js +316 -0
  44. package/dist/cursor-hook-shim.js.map +1 -0
  45. package/dist/cursor-runner.js +358 -0
  46. package/dist/cursor-runner.js.map +1 -0
  47. package/dist/electron-utility.js +111 -0
  48. package/dist/electron-utility.js.map +1 -0
  49. package/dist/git-pool-V73Q53NX.js +18 -0
  50. package/dist/{git-repo-VRT57DGC.js → git-repo-TN3VZXQV.js} +9 -6
  51. package/dist/index.js +12 -12
  52. package/dist/index.js.map +1 -1
  53. package/dist/{logger-GQCSLSZH.js → logger-QHPTO22N.js} +4 -4
  54. package/dist/login-Q7SZI7JJ.js +20 -0
  55. package/dist/{logout-VUNCW5B2.js → logout-O4AVMO5S.js} +6 -6
  56. package/dist/mcp-servers-F64M5T4I.js +24 -0
  57. package/dist/{roi-Y3MX5UW4.js → roi-EYDLPOCS.js} +5 -5
  58. package/dist/rss-worker.js +159 -0
  59. package/dist/rss-worker.js.map +1 -0
  60. package/dist/{serve-O53FNK64.js → serve-6A7RJWEF.js} +89862 -102999
  61. package/dist/{serve-O53FNK64.js.map → serve-6A7RJWEF.js.map} +1 -1
  62. package/dist/skills-ZHEPSBHW.js +11 -0
  63. package/dist/{start-IDFDHRD6.js → start-YGYYIK53.js} +229 -27
  64. package/dist/start-YGYYIK53.js.map +1 -0
  65. package/dist/vault-crypto-BKDOA65F.js +13 -0
  66. package/dist/vault-crypto-BKDOA65F.js.map +1 -0
  67. package/dist/worker.js +6 -3
  68. package/dist/worker.js.map +1 -1
  69. package/package.json +17 -10
  70. package/dist/chunk-2J3WSIAF.js.map +0 -1
  71. package/dist/chunk-3MNPDCO5.js.map +0 -1
  72. package/dist/chunk-66OBOZ3X.js +0 -79
  73. package/dist/chunk-66OBOZ3X.js.map +0 -1
  74. package/dist/chunk-7AHRFPAL.js.map +0 -1
  75. package/dist/chunk-DKMDBOFU.js.map +0 -1
  76. package/dist/chunk-L2WQMPWS.js +0 -666
  77. package/dist/chunk-L2WQMPWS.js.map +0 -1
  78. package/dist/chunk-LW2MS4T5.js.map +0 -1
  79. package/dist/chunk-PI77CUEP.js +0 -49
  80. package/dist/chunk-PI77CUEP.js.map +0 -1
  81. package/dist/chunk-RXI4637N.js +0 -395
  82. package/dist/chunk-RXI4637N.js.map +0 -1
  83. package/dist/chunk-SNYEQHUK.js.map +0 -1
  84. package/dist/chunk-VBPHGPBR.js +0 -126
  85. package/dist/chunk-VBPHGPBR.js.map +0 -1
  86. package/dist/index.d.ts +0 -2
  87. package/dist/login-L4BBPUYO.js +0 -20
  88. package/dist/mcp-servers-MXS5VAWI.js +0 -18
  89. package/dist/shell-V36EX2IJ.js +0 -27
  90. package/dist/skills-GPGRNV4R.js +0 -9
  91. package/dist/start-IDFDHRD6.js.map +0 -1
  92. package/dist/worker.d.ts +0 -49
  93. /package/dist/{auth-SS7LV5XK.js.map → auth-EXHO3AG5.js.map} +0 -0
  94. /package/dist/{chunk-VPMN47TL.js.map → chunk-CNR7O5YH.js.map} +0 -0
  95. /package/dist/{chunk-IISLTKYY.js.map → chunk-TU63KZFW.js.map} +0 -0
  96. /package/dist/{chunk-2UN5AR7V.js.map → chunk-ZAOPND5G.js.map} +0 -0
  97. /package/dist/{git-repo-VRT57DGC.js.map → git-pool-V73Q53NX.js.map} +0 -0
  98. /package/dist/{logger-GQCSLSZH.js.map → git-repo-TN3VZXQV.js.map} +0 -0
  99. /package/dist/{login-L4BBPUYO.js.map → logger-QHPTO22N.js.map} +0 -0
  100. /package/dist/{mcp-servers-MXS5VAWI.js.map → login-Q7SZI7JJ.js.map} +0 -0
  101. /package/dist/{logout-VUNCW5B2.js.map → logout-O4AVMO5S.js.map} +0 -0
  102. /package/dist/{shell-V36EX2IJ.js.map → mcp-servers-F64M5T4I.js.map} +0 -0
  103. /package/dist/{roi-Y3MX5UW4.js.map → roi-EYDLPOCS.js.map} +0 -0
  104. /package/dist/{skills-GPGRNV4R.js.map → skills-ZHEPSBHW.js.map} +0 -0
@@ -0,0 +1,358 @@
1
+ #!/usr/bin/env node
2
+ import {
3
+ parseDaemonToRunner
4
+ } from "./chunk-TX6DK4PK.js";
5
+ import {
6
+ ContentBlockSchema,
7
+ buildCursorUserPrompt
8
+ } from "./chunk-7LSEE26O.js";
9
+ import "./chunk-7YOU7MBN.js";
10
+ import "./chunk-EHQITHQX.js";
11
+ import "./chunk-X3MULCV5.js";
12
+ import "./chunk-ZAOPND5G.js";
13
+ import "./chunk-KITSAHTX.js";
14
+ import "./chunk-CNR7O5YH.js";
15
+ import "./chunk-2H7UOFLK.js";
16
+
17
+ // src/services/session/cursor-runner.ts
18
+ import {
19
+ Agent,
20
+ Cursor
21
+ } from "@cursor/sdk";
22
+
23
+ // src/services/session/cursor-init-error-formatter.ts
24
+ function formatCursorInitError(input) {
25
+ const { requestedModelId, originalError, availableModels, listError } = input;
26
+ if (availableModels === null) {
27
+ const listPart = listError ? ` (Cursor.models.list() also failed: ${listError})` : " (Cursor.models.list() unavailable)";
28
+ return `Agent.create failed for model '${requestedModelId}'. ${originalError}${listPart}`;
29
+ }
30
+ if (availableModels.length === 0) {
31
+ return `Agent.create failed for model '${requestedModelId}'. Cursor.models.list() returned no models. ${originalError}`;
32
+ }
33
+ const requested = requestedModelId.toLowerCase();
34
+ const exactMatch = availableModels.find(
35
+ (m) => m.id.toLowerCase() === requested || (m.aliases ?? []).some((a) => a.toLowerCase() === requested)
36
+ );
37
+ const idsList = availableModels.map((m) => m.id).join(", ");
38
+ const aliasParts = availableModels.filter((m) => (m.aliases?.length ?? 0) > 0).map((m) => `${m.id}=[${(m.aliases ?? []).join("|")}]`);
39
+ const aliasesList = aliasParts.length > 0 ? aliasParts.join(", ") : "(none)";
40
+ const matchPart = exactMatch ? ` The id IS in the available set (${exactMatch.id}) \u2014 failure is unrelated to model resolution.` : ` The id is NOT in the available set \u2014 likely renamed or deprecated server-side.`;
41
+ return `Agent.create failed for model '${requestedModelId}'.${matchPart} Available ids: ${idsList}. Aliases: ${aliasesList}. Original: ${originalError}`;
42
+ }
43
+
44
+ // src/services/session/cursor-runner.ts
45
+ var state = {
46
+ agent: null,
47
+ currentRun: null,
48
+ currentGeneration: 0,
49
+ taskId: "",
50
+ harnessToken: "",
51
+ modelId: "",
52
+ fastMode: false,
53
+ fastModeParam: null
54
+ };
55
+ function send(msg) {
56
+ if (typeof process.send !== "function") {
57
+ process.stderr.write(
58
+ `${JSON.stringify({ level: "error", event: "runner_no_ipc_channel", dropped: msg.kind })}
59
+ `
60
+ );
61
+ return;
62
+ }
63
+ process.send(msg);
64
+ }
65
+ function log(level, message, data) {
66
+ send({ kind: "log", level, message, data });
67
+ }
68
+ async function handleInit(args) {
69
+ state.currentGeneration = args.generation;
70
+ state.taskId = args.taskId;
71
+ state.harnessToken = args.harnessToken;
72
+ state.modelId = args.modelId;
73
+ state.fastMode = args.fastMode;
74
+ state.fastModeParam = args.fastModeParam;
75
+ try {
76
+ const harnessServer = {
77
+ type: "http",
78
+ url: args.harnessUrl,
79
+ headers: {
80
+ "x-shipyard-task-id": args.taskId,
81
+ Authorization: `Bearer ${args.harnessToken}`
82
+ }
83
+ };
84
+ const mcpServers = {
85
+ ...args.mcpServers,
86
+ "shipyard-harness": harnessServer
87
+ };
88
+ const agent = await Agent.create({
89
+ apiKey: args.apiKey,
90
+ name: `shipyard-task-${args.taskId}`,
91
+ model: { id: args.modelId },
92
+ /**
93
+ * SDK accepts `cwd: string | string[]`. When the daemon supplies a
94
+ * skills directory, include it as a second search root so the SDK
95
+ * discovers Shipyard-written `.cursor/skills/*.md` (the task overlay
96
+ * guidance and shipyard-task-list.md) alongside the user's worktree.
97
+ */
98
+ local: args.skillsDir ? { cwd: [args.cwd, args.skillsDir] } : { cwd: args.cwd },
99
+ /**
100
+ * Cast at the boundary: the protocol keeps `mcpServers` as
101
+ * `Record<string, unknown>` to avoid version-coupling, and
102
+ * `Agent.create()` accepts `Record<string, McpServerConfig>`.
103
+ * The daemon side is responsible for shape correctness.
104
+ */
105
+ // eslint-disable-next-line no-restricted-syntax -- McpServerConfig union types require boundary cast
106
+ mcpServers
107
+ });
108
+ state.agent = agent;
109
+ send({
110
+ kind: "init_ok",
111
+ sessionId: agent.agentId,
112
+ tools: [],
113
+ model: args.modelId,
114
+ mcpStatus: []
115
+ });
116
+ } catch (err) {
117
+ const originalError = err instanceof Error ? err.message : String(err);
118
+ let availableModels = null;
119
+ let listError;
120
+ const LIST_TIMEOUT_MS = 5e3;
121
+ let timeoutHandle;
122
+ try {
123
+ const listPromise = Cursor.models.list({ apiKey: args.apiKey });
124
+ const timeoutPromise = new Promise((_, reject) => {
125
+ timeoutHandle = setTimeout(
126
+ () => reject(new Error(`timed out after ${LIST_TIMEOUT_MS}ms`)),
127
+ LIST_TIMEOUT_MS
128
+ );
129
+ });
130
+ const models = await Promise.race([listPromise, timeoutPromise]);
131
+ availableModels = models.map((m) => ({ id: m.id, aliases: m.aliases }));
132
+ } catch (listErr) {
133
+ listError = listErr instanceof Error ? listErr.message : String(listErr);
134
+ } finally {
135
+ if (timeoutHandle) clearTimeout(timeoutHandle);
136
+ }
137
+ send({
138
+ kind: "init_error",
139
+ error: formatCursorInitError({
140
+ requestedModelId: args.modelId,
141
+ originalError,
142
+ availableModels,
143
+ listError
144
+ }),
145
+ retryable: false
146
+ });
147
+ }
148
+ }
149
+ async function streamRun(run, generation) {
150
+ try {
151
+ for await (const event of run.stream()) {
152
+ if (state.currentGeneration !== generation) {
153
+ log("info", "runner_stream_aborted_stale_generation", {
154
+ generation,
155
+ currentGeneration: state.currentGeneration
156
+ });
157
+ return;
158
+ }
159
+ const message = event;
160
+ send({ kind: "sdk_message", generation, payload: message });
161
+ }
162
+ const result = await run.wait();
163
+ send({ kind: "run_complete", generation, result });
164
+ } catch (err) {
165
+ send({
166
+ kind: "run_error",
167
+ generation,
168
+ error: err instanceof Error ? err.message : String(err),
169
+ isRetryable: false
170
+ });
171
+ } finally {
172
+ if (state.currentRun === run) {
173
+ state.currentRun = null;
174
+ }
175
+ }
176
+ }
177
+ async function handlePushMessage(args) {
178
+ const agent = state.agent;
179
+ if (!agent) {
180
+ send({
181
+ kind: "run_error",
182
+ generation: args.generation,
183
+ error: "cursor agent not initialized",
184
+ isRetryable: false
185
+ });
186
+ return;
187
+ }
188
+ state.currentGeneration = args.generation;
189
+ const parsedContent = ContentBlockSchema.array().safeParse(args.content);
190
+ if (!parsedContent.success) {
191
+ log("warn", "runner_push_message_invalid_content", {
192
+ errorCount: parsedContent.error.issues.length,
193
+ firstIssue: parsedContent.error.issues[0]
194
+ });
195
+ return;
196
+ }
197
+ const userInput = buildCursorUserPrompt(
198
+ parsedContent.data,
199
+ (entry) => log(entry.event.includes("error") ? "warn" : "info", entry.event, entry)
200
+ );
201
+ if (userInput.text === "" && userInput.images.length === 0) {
202
+ log("warn", "runner_push_message_empty_after_build");
203
+ return;
204
+ }
205
+ if (userInput.images.length > 0) {
206
+ log("info", "runner_push_message_with_images", {
207
+ count: userInput.images.length,
208
+ totalBytes: userInput.images.reduce((acc, img) => {
209
+ const data = "data" in img ? img.data : "";
210
+ return acc + Math.floor(data.length * 3 / 4);
211
+ }, 0)
212
+ });
213
+ }
214
+ try {
215
+ const message = userInput.images.length > 0 ? { text: userInput.text, images: userInput.images } : userInput.text;
216
+ const sendOptions = state.fastMode && state.fastModeParam !== null ? { model: { id: state.modelId, params: [state.fastModeParam] } } : void 0;
217
+ const run = sendOptions ? await agent.send(message, sendOptions) : await agent.send(message);
218
+ state.currentRun = run;
219
+ await streamRun(run, args.generation);
220
+ } catch (err) {
221
+ send({
222
+ kind: "run_error",
223
+ generation: args.generation,
224
+ error: err instanceof Error ? err.message : String(err),
225
+ isRetryable: false
226
+ });
227
+ }
228
+ }
229
+ async function handleInterrupt(generation) {
230
+ if (!state.currentRun) {
231
+ log("info", "runner_interrupt_no_active_run", { generation });
232
+ return;
233
+ }
234
+ try {
235
+ await state.currentRun.cancel();
236
+ } catch (err) {
237
+ log("warn", "runner_interrupt_failed", {
238
+ error: err instanceof Error ? err.message : String(err)
239
+ });
240
+ }
241
+ }
242
+ async function handleClose() {
243
+ try {
244
+ if (state.currentRun) {
245
+ await state.currentRun.cancel();
246
+ }
247
+ } catch {
248
+ }
249
+ try {
250
+ if (state.agent) {
251
+ await state.agent[Symbol.asyncDispose]();
252
+ }
253
+ } catch {
254
+ }
255
+ process.exit(0);
256
+ }
257
+ async function dispatch(raw) {
258
+ const msg = parseDaemonToRunner(raw);
259
+ if (msg === null) {
260
+ log("warn", "runner_invalid_ipc_message", { raw });
261
+ return;
262
+ }
263
+ switch (msg.kind) {
264
+ case "init":
265
+ await handleInit({
266
+ taskId: msg.taskId,
267
+ cwd: msg.cwd,
268
+ apiKey: msg.apiKey,
269
+ modelId: msg.modelId,
270
+ mcpServers: msg.mcpServers,
271
+ harnessUrl: msg.harnessUrl,
272
+ harnessToken: msg.harnessToken,
273
+ skillsDir: msg.skillsDir,
274
+ generation: msg.generation,
275
+ fastMode: msg.fastMode ?? false,
276
+ fastModeParam: msg.fastModeParam ?? null
277
+ });
278
+ return;
279
+ case "push_message":
280
+ await handlePushMessage({
281
+ generation: msg.generation,
282
+ content: msg.content
283
+ });
284
+ return;
285
+ case "interrupt":
286
+ await handleInterrupt(msg.generation);
287
+ return;
288
+ case "set_model":
289
+ state.currentGeneration = msg.generation;
290
+ log("info", "runner_set_model_recorded", { modelId: msg.modelId });
291
+ return;
292
+ case "set_mcp_servers":
293
+ state.currentGeneration = msg.generation;
294
+ log("info", "runner_set_mcp_servers_recorded_no_live_reload", {
295
+ count: Object.keys(msg.servers).length
296
+ });
297
+ return;
298
+ case "set_harness_task_id":
299
+ state.taskId = msg.taskId;
300
+ state.harnessToken = msg.token;
301
+ state.currentGeneration = msg.generation;
302
+ log("info", "runner_harness_task_id_recorded", { taskId: msg.taskId });
303
+ return;
304
+ case "set_permission_mode":
305
+ state.currentGeneration = msg.generation;
306
+ log("info", "runner_set_permission_mode_recorded", { mode: msg.mode });
307
+ return;
308
+ case "set_fast_mode":
309
+ state.currentGeneration = msg.generation;
310
+ state.fastMode = msg.fastMode;
311
+ log("info", "runner_set_fast_mode_recorded", { fastMode: msg.fastMode });
312
+ return;
313
+ case "close":
314
+ await handleClose();
315
+ return;
316
+ default: {
317
+ const _exhaustive = msg;
318
+ log("error", "runner_unhandled_message_kind", {
319
+ msg: _exhaustive
320
+ });
321
+ return;
322
+ }
323
+ }
324
+ }
325
+ process.on("message", (raw) => {
326
+ void dispatch(raw).catch((err) => {
327
+ log("error", "runner_dispatch_unhandled_rejection", {
328
+ error: err instanceof Error ? err.message : String(err)
329
+ });
330
+ });
331
+ });
332
+ process.on("disconnect", () => {
333
+ process.exit(1);
334
+ });
335
+ process.on("uncaughtException", (err) => {
336
+ process.stderr.write(
337
+ `${JSON.stringify({
338
+ level: "error",
339
+ event: "runner_uncaught_exception",
340
+ error: err.message,
341
+ stack: err.stack
342
+ })}
343
+ `
344
+ );
345
+ process.exit(2);
346
+ });
347
+ process.on("unhandledRejection", (reason) => {
348
+ process.stderr.write(
349
+ `${JSON.stringify({
350
+ level: "error",
351
+ event: "runner_unhandled_rejection",
352
+ reason: reason instanceof Error ? reason.message : String(reason)
353
+ })}
354
+ `
355
+ );
356
+ process.exit(3);
357
+ });
358
+ //# sourceMappingURL=cursor-runner.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/services/session/cursor-runner.ts","../src/services/session/cursor-init-error-formatter.ts"],"sourcesContent":["/**\n * cursor-runner — forked Node child that owns the `@cursor/sdk` lifecycle\n * outside the daemon's V8 isolate (plan v2 §2).\n *\n * The daemon spawns this entry via `child_process.fork(require.resolve(\n * './cursor-runner.js'))` once per Cursor task. The child:\n * 1. waits for an `init` IPC message,\n * 2. calls `Agent.create()` from `@cursor/sdk`,\n * 3. on each `push_message`, calls `agent.send()` and iterates\n * `Run.stream()`, forwarding every `SDKMessage` back to the daemon,\n * 4. on `interrupt`, cancels the in-flight run,\n * 5. on `close`, disposes the agent and exits 0.\n *\n * Native panics (sqlite, FFI) crash THIS child only. The daemon-side\n * supervisor (S2b) observes the child exit, emits `subprocess_died`, and\n * the session FSM tears down cleanly.\n *\n * Per-tool gating uses the `.cursor/hooks.json` `preToolUse` hook script +\n * cursor-hook-socket (cursor-hook-socket.ts) — @cursor/sdk@1.0.13 has no\n * public respond-to-request API.\n */\n\nimport {\n Agent,\n Cursor,\n type Run,\n type SDKAgent,\n type SDKMessage,\n type SDKUserMessage,\n type SendOptions,\n} from '@cursor/sdk';\nimport { ContentBlockSchema } from '@shipyard/loro-schema';\n\nimport { buildCursorUserPrompt, type CursorUserInput } from './cursor-content-builder.js';\nimport { formatCursorInitError, type ModelInfo } from './cursor-init-error-formatter.js';\nimport { parseDaemonToRunner, type RunnerToDaemon } from './cursor-runner-protocol.js';\n\ninterface RunnerState {\n agent: SDKAgent | null;\n currentRun: Run | null;\n currentGeneration: number;\n taskId: string;\n harnessToken: string;\n /**\n * Cursor model id supplied at init. Threaded into every\n * `SendOptions.model.id` when Fast Mode is on so the runtime knows which\n * model the params belong to.\n */\n modelId: string;\n /**\n * Live Fast Mode flag. Initial value from `init`; updated by\n * `set_fast_mode` IPC. The runner attaches Fast Mode params only when\n * both this is true AND `fastModeParam` is non-null.\n */\n fastMode: boolean;\n /**\n * Resolved Fast Mode `{id, value}` binding for `modelId`, sourced from\n * the daemon's discovery cache. Null = no binding known; the runner\n * sends the plain `agent.send(message)` shape regardless of `fastMode`.\n */\n fastModeParam: { id: string; value: string } | null;\n}\n\nconst state: RunnerState = {\n agent: null,\n currentRun: null,\n currentGeneration: 0,\n taskId: '',\n harnessToken: '',\n modelId: '',\n fastMode: false,\n fastModeParam: null,\n};\n\nfunction send(msg: RunnerToDaemon): void {\n /**\n * `process.send` is only defined when this module is started via\n * `fork()` with an IPC channel. If it is undefined (e.g. the file is\n * accidentally executed directly), there is no way to reach the\n * parent — the runner is unusable; log to stderr and drop.\n */\n if (typeof process.send !== 'function') {\n process.stderr.write(\n `${JSON.stringify({ level: 'error', event: 'runner_no_ipc_channel', dropped: msg.kind })}\\n`\n );\n return;\n }\n process.send(msg);\n}\n\nfunction log(level: 'info' | 'warn' | 'error', message: string, data?: unknown): void {\n send({ kind: 'log', level, message, data });\n}\n\nasync function handleInit(args: {\n taskId: string;\n cwd: string;\n apiKey: string;\n modelId: string;\n mcpServers: Record<string, unknown>;\n harnessUrl: string;\n harnessToken: string;\n skillsDir: string;\n generation: number;\n fastMode: boolean;\n fastModeParam: { id: string; value: string } | null;\n}): Promise<void> {\n state.currentGeneration = args.generation;\n state.taskId = args.taskId;\n state.harnessToken = args.harnessToken;\n state.modelId = args.modelId;\n state.fastMode = args.fastMode;\n state.fastModeParam = args.fastModeParam;\n try {\n /**\n * The daemon validates `mcpServers` shape before sending. We pass\n * through as-is; a malformed entry will surface as an\n * `Agent.create()` rejection rather than a runner crash.\n */\n const harnessServer = {\n type: 'http' as const,\n url: args.harnessUrl,\n headers: {\n 'x-shipyard-task-id': args.taskId,\n Authorization: `Bearer ${args.harnessToken}`,\n },\n };\n const mcpServers: Record<string, unknown> = {\n ...args.mcpServers,\n 'shipyard-harness': harnessServer,\n };\n const agent = await Agent.create({\n apiKey: args.apiKey,\n name: `shipyard-task-${args.taskId}`,\n model: { id: args.modelId },\n /**\n * SDK accepts `cwd: string | string[]`. When the daemon supplies a\n * skills directory, include it as a second search root so the SDK\n * discovers Shipyard-written `.cursor/skills/*.md` (the task overlay\n * guidance and shipyard-task-list.md) alongside the user's worktree.\n */\n local: args.skillsDir ? { cwd: [args.cwd, args.skillsDir] } : { cwd: args.cwd },\n /**\n * Cast at the boundary: the protocol keeps `mcpServers` as\n * `Record<string, unknown>` to avoid version-coupling, and\n * `Agent.create()` accepts `Record<string, McpServerConfig>`.\n * The daemon side is responsible for shape correctness.\n */\n // eslint-disable-next-line no-restricted-syntax -- McpServerConfig union types require boundary cast\n mcpServers: mcpServers as never,\n });\n state.agent = agent;\n send({\n kind: 'init_ok',\n sessionId: agent.agentId,\n tools: [],\n model: args.modelId,\n mcpStatus: [],\n });\n } catch (err) {\n const originalError = err instanceof Error ? err.message : String(err);\n /**\n * On Agent.create failure, probe Cursor.models.list() once to capture\n * the authoritative {id, aliases} set. If the requested model is in\n * that set, the failure is unrelated to model resolution; if not, the\n * id is stale and the log line names the replacement immediately.\n *\n * Best-effort: the list call may itself fail (offline, auth, rate-\n * limited). The formatter handles a null `availableModels` by falling\n * back to the original error verbatim — no information loss vs the\n * pre-Item-1 behavior.\n */\n let availableModels: ModelInfo[] | null = null;\n let listError: string | undefined;\n /**\n * @cursor/sdk@1.0.13's CursorRequestOptions has no AbortSignal or timeout\n * field — a hung backend would block init_error indefinitely and wedge\n * the session FSM in \"initializing\". 5s cap restores the pre-Item-1\n * upper bound (the original code sent init_error immediately) at the\n * cost of degrading the diagnostic when Cursor's API is slow.\n */\n const LIST_TIMEOUT_MS = 5000;\n let timeoutHandle: ReturnType<typeof setTimeout> | undefined;\n try {\n const listPromise = Cursor.models.list({ apiKey: args.apiKey });\n const timeoutPromise = new Promise<never>((_, reject) => {\n timeoutHandle = setTimeout(\n () => reject(new Error(`timed out after ${LIST_TIMEOUT_MS}ms`)),\n LIST_TIMEOUT_MS\n );\n });\n const models = await Promise.race([listPromise, timeoutPromise]);\n availableModels = models.map((m) => ({ id: m.id, aliases: m.aliases }));\n } catch (listErr) {\n listError = listErr instanceof Error ? listErr.message : String(listErr);\n } finally {\n if (timeoutHandle) clearTimeout(timeoutHandle);\n }\n send({\n kind: 'init_error',\n error: formatCursorInitError({\n requestedModelId: args.modelId,\n originalError,\n availableModels,\n listError,\n }),\n retryable: false,\n });\n }\n}\n\nasync function streamRun(run: Run, generation: number): Promise<void> {\n try {\n for await (const event of run.stream()) {\n /**\n * If the generation rolled forward (interrupt + new push), stop\n * forwarding events from the stale run. The daemon will already\n * be ignoring them, but stopping here saves IPC traffic.\n */\n if (state.currentGeneration !== generation) {\n log('info', 'runner_stream_aborted_stale_generation', {\n generation,\n currentGeneration: state.currentGeneration,\n });\n return;\n }\n const message: SDKMessage = event;\n send({ kind: 'sdk_message', generation, payload: message });\n }\n const result = await run.wait();\n send({ kind: 'run_complete', generation, result });\n } catch (err) {\n send({\n kind: 'run_error',\n generation,\n error: err instanceof Error ? err.message : String(err),\n isRetryable: false,\n });\n } finally {\n if (state.currentRun === run) {\n state.currentRun = null;\n }\n }\n}\n\nasync function handlePushMessage(args: { generation: number; content: unknown[] }): Promise<void> {\n const agent = state.agent;\n if (!agent) {\n send({\n kind: 'run_error',\n generation: args.generation,\n error: 'cursor agent not initialized',\n isRetryable: false,\n });\n return;\n }\n state.currentGeneration = args.generation;\n /**\n * `args.content` arrives as `unknown[]` over IPC (the wire schema uses\n * `z.array(z.unknown())` for forward-compat — see comment in\n * cursor-runner-protocol.ts). Validate at the boundary via Zod rather\n * than cast: lint:extra forbids type assertions outside `as const`/`as never`,\n * and Zod gives us a runtime guarantee that matches the type.\n */\n const parsedContent = ContentBlockSchema.array().safeParse(args.content);\n if (!parsedContent.success) {\n log('warn', 'runner_push_message_invalid_content', {\n errorCount: parsedContent.error.issues.length,\n firstIssue: parsedContent.error.issues[0],\n });\n return;\n }\n const userInput: CursorUserInput = buildCursorUserPrompt(parsedContent.data, (entry) =>\n log(entry.event.includes('error') ? 'warn' : 'info', entry.event, entry)\n );\n if (userInput.text === '' && userInput.images.length === 0) {\n log('warn', 'runner_push_message_empty_after_build');\n return;\n }\n if (userInput.images.length > 0) {\n log('info', 'runner_push_message_with_images', {\n count: userInput.images.length,\n totalBytes: userInput.images.reduce((acc, img) => {\n const data = 'data' in img ? img.data : '';\n return acc + Math.floor((data.length * 3) / 4);\n }, 0),\n });\n }\n try {\n const message: string | SDKUserMessage =\n userInput.images.length > 0\n ? { text: userInput.text, images: userInput.images }\n : userInput.text;\n /**\n * Fast Mode attaches `SendOptions.model.params = [{id, value}]`. We\n * only build the options object when both knobs line up — the runner\n * must NOT send an empty `params: []` (Cursor's backend treats that\n * as \"the model has no params\", which collides with the standard\n * `agent.send(message)` shape we use otherwise).\n */\n const sendOptions: SendOptions | undefined =\n state.fastMode && state.fastModeParam !== null\n ? { model: { id: state.modelId, params: [state.fastModeParam] } }\n : undefined;\n const run = sendOptions ? await agent.send(message, sendOptions) : await agent.send(message);\n state.currentRun = run;\n await streamRun(run, args.generation);\n } catch (err) {\n send({\n kind: 'run_error',\n generation: args.generation,\n error: err instanceof Error ? err.message : String(err),\n isRetryable: false,\n });\n }\n}\n\nasync function handleInterrupt(generation: number): Promise<void> {\n if (!state.currentRun) {\n log('info', 'runner_interrupt_no_active_run', { generation });\n return;\n }\n try {\n await state.currentRun.cancel();\n } catch (err) {\n log('warn', 'runner_interrupt_failed', {\n error: err instanceof Error ? err.message : String(err),\n });\n }\n}\n\nasync function handleClose(): Promise<void> {\n try {\n if (state.currentRun) {\n await state.currentRun.cancel();\n }\n } catch {\n /**\n * Closing — already on the exit path, swallow.\n */\n }\n try {\n if (state.agent) {\n await state.agent[Symbol.asyncDispose]();\n }\n } catch {\n /** swallow — same reason */\n }\n process.exit(0);\n}\n\nasync function dispatch(raw: unknown): Promise<void> {\n const msg = parseDaemonToRunner(raw);\n if (msg === null) {\n log('warn', 'runner_invalid_ipc_message', { raw });\n return;\n }\n switch (msg.kind) {\n case 'init':\n await handleInit({\n taskId: msg.taskId,\n cwd: msg.cwd,\n apiKey: msg.apiKey,\n modelId: msg.modelId,\n mcpServers: msg.mcpServers,\n harnessUrl: msg.harnessUrl,\n harnessToken: msg.harnessToken,\n skillsDir: msg.skillsDir,\n generation: msg.generation,\n fastMode: msg.fastMode ?? false,\n fastModeParam: msg.fastModeParam ?? null,\n });\n return;\n case 'push_message':\n await handlePushMessage({\n generation: msg.generation,\n content: msg.content,\n });\n return;\n case 'interrupt':\n await handleInterrupt(msg.generation);\n return;\n case 'set_model':\n /**\n * `@cursor/sdk` accepts `model` via `SendOptions` on each\n * `send()` call; there is no `agent.setModel()`. The runner\n * stores the new id and applies it on the next push. (Wired\n * in S2b — for S2a we just acknowledge.)\n */\n state.currentGeneration = msg.generation;\n log('info', 'runner_set_model_recorded', { modelId: msg.modelId });\n return;\n case 'set_mcp_servers':\n /**\n * Mid-thread MCP reload requires re-creating the agent in\n * 1.0.13 (no `agent.setMcpServers`). Plan v2 calls this out as\n * a runner-mediated recreate (S2b). For S2a, log and skip.\n * The daemon already emitted `mcp_mid_thread_reload_needed` via\n * cursor-subprocess.ts to drive the \"Restart Cursor\" UI banner.\n */\n state.currentGeneration = msg.generation;\n log('info', 'runner_set_mcp_servers_recorded_no_live_reload', {\n count: Object.keys(msg.servers).length,\n });\n return;\n case 'set_harness_task_id':\n state.taskId = msg.taskId;\n state.harnessToken = msg.token;\n state.currentGeneration = msg.generation;\n log('info', 'runner_harness_task_id_recorded', { taskId: msg.taskId });\n return;\n case 'set_permission_mode':\n /**\n * R1 plan mode: the daemon rewrote the mode file and hooks.json before\n * sending this. The runner records the generation and logs for triage.\n * No action needed — the next hook invocation reads the new mode file.\n */\n state.currentGeneration = msg.generation;\n log('info', 'runner_set_permission_mode_recorded', { mode: msg.mode });\n return;\n case 'set_fast_mode':\n /**\n * Fast Mode is per-state, not per-turn — record the new flag without\n * bumping the generation. The next `push_message` will read\n * `state.fastMode` when building `SendOptions`.\n */\n state.currentGeneration = msg.generation;\n state.fastMode = msg.fastMode;\n log('info', 'runner_set_fast_mode_recorded', { fastMode: msg.fastMode });\n return;\n case 'close':\n await handleClose();\n return;\n default: {\n /**\n * Compile-time exhaustiveness — adding a new variant to the\n * protocol forces this switch to be updated.\n */\n const _exhaustive: never = msg;\n log('error', 'runner_unhandled_message_kind', {\n msg: _exhaustive,\n });\n return;\n }\n }\n}\n\nprocess.on('message', (raw) => {\n /**\n * `dispatch` is async but the IPC handler is sync. Swallow any\n * promise rejection that escapes dispatch's own try/catch — those\n * are programmer errors, and crashing the runner over them just\n * trades a logged warning for a process exit + supervisor respawn\n * cycle.\n */\n void dispatch(raw).catch((err: unknown) => {\n log('error', 'runner_dispatch_unhandled_rejection', {\n error: err instanceof Error ? err.message : String(err),\n });\n });\n});\n\nprocess.on('disconnect', () => {\n /**\n * Parent went away (daemon crash, fork channel torn down). Exit\n * with a non-zero code so the supervisor records the abnormal\n * termination.\n */\n process.exit(1);\n});\n\n/**\n * Crash-on-uncaught: any uncaught error or unhandled rejection in\n * native code (sqlite, FFI) or SDK internals SHOULD bring this child\n * down — the daemon-side supervisor records the exit and the session\n * FSM tears down. Silent recovery here would mask real bugs.\n */\nprocess.on('uncaughtException', (err) => {\n process.stderr.write(\n `${JSON.stringify({\n level: 'error',\n event: 'runner_uncaught_exception',\n error: err.message,\n stack: err.stack,\n })}\\n`\n );\n process.exit(2);\n});\n\nprocess.on('unhandledRejection', (reason) => {\n process.stderr.write(\n `${JSON.stringify({\n level: 'error',\n event: 'runner_unhandled_rejection',\n reason: reason instanceof Error ? reason.message : String(reason),\n })}\\n`\n );\n process.exit(3);\n});\n","/**\n * Pure helper that formats a structured `init_error` message for the daemon\n * when `Agent.create()` rejects.\n *\n * The Cursor SDK delegates model-id resolution to the backend, so an unknown\n * `composer-X.Y` id surfaces as an opaque `ModelNotFound` (or similar)\n * server error. Shipyard's catalog (`cursor-model-catalog.ts`) hardcodes\n * `composer-2.5` based on Cursor's docs; if Cursor renames the model\n * server-side, every spawn fails and our only signal is the opaque SDK\n * message. We've shipped 5 Cursor fixes without ever exercising the literal\n * model string — see PR history in #3680, #3744, #3752, #3758, #3787.\n *\n * On failure, the runner calls `Cursor.models.list()` once and feeds the\n * result through this formatter. The resulting `init_error.error` string\n * names the requested id, the original SDK error, and the available ids +\n * aliases — enough to identify a model-id mismatch from the daemon log\n * alone, without re-running with extra debug flags.\n *\n * Pure function: no I/O, no side effects, exhaustively unit-testable.\n * Lives alongside the runner per the FC/IS pattern in `engineering-standards.md`.\n */\n\nexport interface ModelInfo {\n id: string;\n aliases?: readonly string[];\n}\n\nexport interface CursorInitErrorInput {\n /** The model id Shipyard sent to `Agent.create()`. */\n requestedModelId: string;\n /** The SDK's original failure message (preserved verbatim at the end). */\n originalError: string;\n /**\n * Result of `Cursor.models.list()`. `null` means the list call itself\n * failed (offline, auth invalid, rate-limited) — formatter falls back\n * to the original error without speculating about available models.\n */\n availableModels: readonly ModelInfo[] | null;\n /** Optional: the error string from the failed list call, for triage. */\n listError?: string;\n}\n\nexport function formatCursorInitError(input: CursorInitErrorInput): string {\n const { requestedModelId, originalError, availableModels, listError } = input;\n if (availableModels === null) {\n const listPart = listError\n ? ` (Cursor.models.list() also failed: ${listError})`\n : ' (Cursor.models.list() unavailable)';\n return `Agent.create failed for model '${requestedModelId}'. ${originalError}${listPart}`;\n }\n if (availableModels.length === 0) {\n return `Agent.create failed for model '${requestedModelId}'. Cursor.models.list() returned no models. ${originalError}`;\n }\n const requested = requestedModelId.toLowerCase();\n const exactMatch = availableModels.find(\n (m) =>\n m.id.toLowerCase() === requested ||\n (m.aliases ?? []).some((a) => a.toLowerCase() === requested)\n );\n const idsList = availableModels.map((m) => m.id).join(', ');\n const aliasParts = availableModels\n .filter((m) => (m.aliases?.length ?? 0) > 0)\n .map((m) => `${m.id}=[${(m.aliases ?? []).join('|')}]`);\n const aliasesList = aliasParts.length > 0 ? aliasParts.join(', ') : '(none)';\n const matchPart = exactMatch\n ? ` The id IS in the available set (${exactMatch.id}) — failure is unrelated to model resolution.`\n : ` The id is NOT in the available set — likely renamed or deprecated server-side.`;\n return `Agent.create failed for model '${requestedModelId}'.${matchPart} Available ids: ${idsList}. Aliases: ${aliasesList}. Original: ${originalError}`;\n}\n"],"mappings":"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","names":[]}
@@ -0,0 +1,111 @@
1
+ #!/usr/bin/env node
2
+ import {
3
+ getDaemonVersion
4
+ } from "./chunk-UQVXWOPT.js";
5
+ import {
6
+ installWasmPanicBuffer
7
+ } from "./chunk-7H34LI75.js";
8
+ import {
9
+ logger
10
+ } from "./chunk-ZAOPND5G.js";
11
+ import {
12
+ getShipyardHome,
13
+ validateEnv
14
+ } from "./chunk-KITSAHTX.js";
15
+ import "./chunk-CNR7O5YH.js";
16
+ import "./chunk-2H7UOFLK.js";
17
+
18
+ // src/electron-utility.ts
19
+ import { appendFileSync, mkdirSync } from "fs";
20
+ import { dirname, join } from "path";
21
+ import WebSocket from "ws";
22
+ Error.stackTraceLimit = 50;
23
+ installWasmPanicBuffer();
24
+ var BREADCRUMB_PATH = join(getShipyardHome(), "logs", "electron-utility-startup.log");
25
+ function crumb(phase) {
26
+ try {
27
+ mkdirSync(dirname(BREADCRUMB_PATH), { recursive: true });
28
+ appendFileSync(BREADCRUMB_PATH, `${(/* @__PURE__ */ new Date()).toISOString()} pid=${process.pid} ${phase}
29
+ `);
30
+ } catch {
31
+ }
32
+ }
33
+ function isVersionQuery(data) {
34
+ return typeof data === "object" && data !== null && "type" in data && Reflect.get(data, "type") === "system:version";
35
+ }
36
+ async function loadAuthFromConfig(env) {
37
+ if (env.SHIPYARD_USER_TOKEN) return;
38
+ const { loadAuthToken } = await import("./auth-EXHO3AG5.js");
39
+ const auth = await loadAuthToken();
40
+ if (auth.status === "ok") {
41
+ env.SHIPYARD_USER_TOKEN = auth.token;
42
+ env.SHIPYARD_USER_ID = auth.userId;
43
+ env.SHIPYARD_USER_DISPLAY_NAME = auth.displayName;
44
+ if (auth.signalingUrl) env.SHIPYARD_SIGNALING_URL = auth.signalingUrl;
45
+ } else if (auth.status === "expired") {
46
+ logger.warn("Auth token expired. Sign in from the Electron renderer.");
47
+ } else {
48
+ logger.warn("No auth token found. Sign in from the Electron renderer.");
49
+ }
50
+ }
51
+ async function main() {
52
+ const parentPort = process.parentPort;
53
+ if (!parentPort) {
54
+ logger.error("electron-utility expects to run inside an Electron utilityProcess");
55
+ process.exit(1);
56
+ }
57
+ if (typeof Reflect.get(globalThis, "WebSocket") !== "function") {
58
+ Reflect.set(globalThis, "WebSocket", WebSocket);
59
+ }
60
+ process.env.SHIPYARD_TRANSPORT = "electron-port";
61
+ process.env.SHIPYARD_ARTIFACT = "electron";
62
+ crumb("pre-validate-env");
63
+ const env = validateEnv();
64
+ crumb("post-validate-env");
65
+ crumb("pre-auth-load");
66
+ await loadAuthFromConfig(env);
67
+ crumb("post-auth-load");
68
+ crumb("pre-serve-import");
69
+ const { serve } = await import("./serve-6A7RJWEF.js");
70
+ crumb("post-serve-import");
71
+ const portQueue = [];
72
+ let acceptor = null;
73
+ parentPort.on("message", (event) => {
74
+ if (isVersionQuery(event.data)) {
75
+ parentPort.postMessage({
76
+ type: "system:version:reply",
77
+ version: getDaemonVersion()
78
+ });
79
+ return;
80
+ }
81
+ for (const port of event.ports) {
82
+ if (acceptor) {
83
+ acceptor.acceptPort(port);
84
+ } else {
85
+ portQueue.push(port);
86
+ }
87
+ }
88
+ });
89
+ parentPort.on("close", () => {
90
+ logger.info("Electron host disposed utility \u2014 shutting down");
91
+ process.exit(0);
92
+ });
93
+ crumb("pre-serve-call");
94
+ await serve({
95
+ isDev: env.SHIPYARD_DEV,
96
+ onElectronTransport: (a) => {
97
+ acceptor = a;
98
+ for (const port of portQueue) a.acceptPort(port);
99
+ portQueue.length = 0;
100
+ }
101
+ });
102
+ crumb("serve-call-returned");
103
+ }
104
+ main().catch((error) => {
105
+ const errMsg = error instanceof Error ? error.message : String(error);
106
+ const errStack = error instanceof Error ? error.stack : void 0;
107
+ crumb(`fatal-${errMsg.slice(0, 80)}`);
108
+ logger.error({ err: errMsg, stack: errStack }, "Electron utility fatal error");
109
+ process.exit(1);
110
+ });
111
+ //# sourceMappingURL=electron-utility.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/electron-utility.ts"],"sourcesContent":["/**\n * Daemon entry point when running inside an Electron `utilityProcess`.\n *\n * Differences from the CLI entry (`src/index.ts`):\n * - `process.parentPort` (set by Electron) is the IPC channel — the host\n * transfers a `MessagePortMain` to us; we hand it to the local-direct\n * pipeline as a renderer connection.\n * - The loopback WebSocket transport is suppressed (env var); the\n * renderer reaches the daemon via the MessagePort instead.\n * - The host kills the utility from `app.before-quit`; we also watch for\n * the host's `close` event in case the host disposes us cooperatively.\n */\n\nimport { appendFileSync, mkdirSync } from 'node:fs';\nimport { dirname, join } from 'node:path';\nimport WebSocket from 'ws';\nimport { getDaemonVersion } from './shared/daemon-version.js';\nimport { type Env, getShipyardHome, validateEnv } from './shared/env.js';\nimport { logger } from './shared/logger.js';\nimport { installWasmPanicBuffer } from './shared/wasm-panic-buffer.js';\n\nError.stackTraceLimit = 50;\ninstallWasmPanicBuffer();\n\n/**\n * Synchronous breadcrumb for diagnosing electron-utility boot failures.\n * Pino's transport isn't wired this early (and may never be if validateEnv\n * fails), so phase-by-phase append-to-file is the only durable trace.\n * Best-effort: catches and swallows so it can never block startup.\n */\nconst BREADCRUMB_PATH = join(getShipyardHome(), 'logs', 'electron-utility-startup.log');\n\nfunction crumb(phase: string): void {\n try {\n mkdirSync(dirname(BREADCRUMB_PATH), { recursive: true });\n appendFileSync(BREADCRUMB_PATH, `${new Date().toISOString()} pid=${process.pid} ${phase}\\n`);\n } catch {\n /** Breadcrumb best-effort; never throw from here. */\n }\n}\n\ninterface ParentPortEvent {\n data: unknown;\n ports: ReadonlyArray<{\n postMessage(data: unknown, transfer?: unknown[]): void;\n start(): void;\n close(): void;\n on(event: 'message', listener: (event: { data: unknown }) => void): void;\n on(event: 'close', listener: () => void): void;\n }>;\n}\n\ninterface ParentPortLike {\n on(event: 'message', listener: (event: ParentPortEvent) => void): void;\n on(event: 'close', listener: () => void): void;\n postMessage(value: unknown): void;\n}\n\ndeclare const process: NodeJS.Process & { parentPort?: ParentPortLike };\n\nfunction isVersionQuery(data: unknown): data is { type: 'system:version' } {\n return (\n typeof data === 'object' &&\n data !== null &&\n 'type' in data &&\n Reflect.get(data, 'type') === 'system:version'\n );\n}\n\nasync function loadAuthFromConfig(env: Env): Promise<void> {\n if (env.SHIPYARD_USER_TOKEN) return;\n const { loadAuthToken } = await import('./services/bootstrap/auth.js');\n const auth = await loadAuthToken();\n if (auth.status === 'ok') {\n env.SHIPYARD_USER_TOKEN = auth.token;\n env.SHIPYARD_USER_ID = auth.userId;\n env.SHIPYARD_USER_DISPLAY_NAME = auth.displayName;\n if (auth.signalingUrl) env.SHIPYARD_SIGNALING_URL = auth.signalingUrl;\n } else if (auth.status === 'expired') {\n logger.warn('Auth token expired. Sign in from the Electron renderer.');\n } else {\n logger.warn('No auth token found. Sign in from the Electron renderer.');\n }\n}\n\nasync function main(): Promise<void> {\n const parentPort = process.parentPort;\n if (!parentPort) {\n logger.error('electron-utility expects to run inside an Electron utilityProcess');\n process.exit(1);\n }\n\n if (typeof Reflect.get(globalThis, 'WebSocket') !== 'function') {\n Reflect.set(globalThis, 'WebSocket', WebSocket);\n }\n\n process.env.SHIPYARD_TRANSPORT = 'electron-port';\n process.env.SHIPYARD_ARTIFACT = 'electron';\n\n crumb('pre-validate-env');\n const env = validateEnv();\n crumb('post-validate-env');\n\n crumb('pre-auth-load');\n await loadAuthFromConfig(env);\n crumb('post-auth-load');\n\n crumb('pre-serve-import');\n const { serve } = await import('./services/serve.js');\n crumb('post-serve-import');\n\n const portQueue: Array<ParentPortEvent['ports'][number]> = [];\n let acceptor: { acceptPort(port: ParentPortEvent['ports'][number]): void } | null = null;\n\n parentPort.on('message', (event) => {\n /*\n * Belt-and-suspenders: lets the Electron host verify this utility reports a real version,\n * not '0.0.0-dev', before delivering ports to the renderer.\n */\n if (isVersionQuery(event.data)) {\n parentPort.postMessage({\n type: 'system:version:reply',\n version: getDaemonVersion(),\n });\n return;\n }\n\n for (const port of event.ports) {\n if (acceptor) {\n acceptor.acceptPort(port);\n } else {\n portQueue.push(port);\n }\n }\n });\n\n parentPort.on('close', () => {\n logger.info('Electron host disposed utility — shutting down');\n process.exit(0);\n });\n\n crumb('pre-serve-call');\n await serve({\n isDev: env.SHIPYARD_DEV,\n onElectronTransport: (a) => {\n acceptor = a;\n for (const port of portQueue) a.acceptPort(port);\n portQueue.length = 0;\n },\n });\n crumb('serve-call-returned');\n}\n\nmain().catch((error: unknown) => {\n const errMsg = error instanceof Error ? error.message : String(error);\n const errStack = error instanceof Error ? error.stack : undefined;\n crumb(`fatal-${errMsg.slice(0, 80)}`);\n logger.error({ err: errMsg, stack: errStack }, 'Electron utility fatal error');\n process.exit(1);\n});\n"],"mappings":"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","names":[]}
@@ -0,0 +1,18 @@
1
+ #!/usr/bin/env node
2
+ import {
3
+ _testing,
4
+ getGitPool,
5
+ gitExec,
6
+ gitExecSafe,
7
+ invalidateGitPool
8
+ } from "./chunk-4T2OQAVL.js";
9
+ import "./chunk-ZFKJAYAN.js";
10
+ import "./chunk-2H7UOFLK.js";
11
+ export {
12
+ _testing,
13
+ getGitPool,
14
+ gitExec,
15
+ gitExecSafe,
16
+ invalidateGitPool
17
+ };
18
+ //# sourceMappingURL=git-pool-V73Q53NX.js.map
@@ -2,6 +2,7 @@
2
2
  import {
3
3
  _testing,
4
4
  detectEnvironments,
5
+ discoverWorktrees,
5
6
  findGitRepos,
6
7
  getGitTopLevel,
7
8
  getRepoDefaultBranch,
@@ -11,15 +12,17 @@ import {
11
12
  isGhAvailable,
12
13
  isGitRepo,
13
14
  parseOwnerRepo
14
- } from "./chunk-RXI4637N.js";
15
- import "./chunk-2UN5AR7V.js";
16
- import "./chunk-PI77CUEP.js";
17
- import "./chunk-VPMN47TL.js";
18
- import "./chunk-66OBOZ3X.js";
15
+ } from "./chunk-YZ3Z3ZYI.js";
16
+ import "./chunk-4T2OQAVL.js";
17
+ import "./chunk-ZFKJAYAN.js";
18
+ import "./chunk-ZAOPND5G.js";
19
+ import "./chunk-KITSAHTX.js";
20
+ import "./chunk-CNR7O5YH.js";
19
21
  import "./chunk-2H7UOFLK.js";
20
22
  export {
21
23
  _testing,
22
24
  detectEnvironments,
25
+ discoverWorktrees,
23
26
  findGitRepos,
24
27
  getGitTopLevel,
25
28
  getRepoDefaultBranch,
@@ -30,4 +33,4 @@ export {
30
33
  isGitRepo,
31
34
  parseOwnerRepo
32
35
  };
33
- //# sourceMappingURL=git-repo-VRT57DGC.js.map
36
+ //# sourceMappingURL=git-repo-TN3VZXQV.js.map