@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.
- package/dist/{auth-SS7LV5XK.js → auth-EXHO3AG5.js} +4 -4
- package/dist/capability-detector-worker.js +142 -0
- package/dist/capability-detector-worker.js.map +1 -0
- package/dist/{chunk-DKMDBOFU.js → chunk-2CNIEBKO.js} +21 -11
- package/dist/chunk-2CNIEBKO.js.map +1 -0
- package/dist/chunk-4T2OQAVL.js +51 -0
- package/dist/chunk-4T2OQAVL.js.map +1 -0
- package/dist/chunk-5ER6ZHA2.js +46 -0
- package/dist/chunk-5ER6ZHA2.js.map +1 -0
- package/dist/chunk-7LSEE26O.js +24227 -0
- package/dist/chunk-7LSEE26O.js.map +1 -0
- package/dist/{chunk-7AHRFPAL.js → chunk-7YOU7MBN.js} +183 -17
- package/dist/chunk-7YOU7MBN.js.map +1 -0
- package/dist/chunk-CMGJGK6R.js +382 -0
- package/dist/chunk-CMGJGK6R.js.map +1 -0
- package/dist/{chunk-VPMN47TL.js → chunk-CNR7O5YH.js} +1 -2
- package/dist/{chunk-2J3WSIAF.js → chunk-EF2DAODF.js} +18 -3
- package/dist/chunk-EF2DAODF.js.map +1 -0
- package/dist/chunk-HQ43PHOH.js +1203 -0
- package/dist/chunk-HQ43PHOH.js.map +1 -0
- package/dist/chunk-KITSAHTX.js +134 -0
- package/dist/chunk-KITSAHTX.js.map +1 -0
- package/dist/chunk-LESHN5J5.js +6898 -0
- package/dist/chunk-LESHN5J5.js.map +1 -0
- package/dist/{chunk-LW2MS4T5.js → chunk-LMJFHKRD.js} +15 -12
- package/dist/chunk-LMJFHKRD.js.map +1 -0
- package/dist/{chunk-SNYEQHUK.js → chunk-NACJENDW.js} +14 -21
- package/dist/chunk-NACJENDW.js.map +1 -0
- package/dist/{chunk-IISLTKYY.js → chunk-TU63KZFW.js} +2 -2
- package/dist/chunk-TX6DK4PK.js +186 -0
- package/dist/chunk-TX6DK4PK.js.map +1 -0
- package/dist/chunk-UQVXWOPT.js +48 -0
- package/dist/chunk-UQVXWOPT.js.map +1 -0
- package/dist/{chunk-3MNPDCO5.js → chunk-WBB4XHLH.js} +139 -140
- package/dist/chunk-WBB4XHLH.js.map +1 -0
- package/dist/chunk-X3MULCV5.js +11 -0
- package/dist/chunk-X3MULCV5.js.map +1 -0
- package/dist/chunk-YZ3Z3ZYI.js +787 -0
- package/dist/chunk-YZ3Z3ZYI.js.map +1 -0
- package/dist/{chunk-2UN5AR7V.js → chunk-ZAOPND5G.js} +2 -2
- package/dist/chunk-ZFKJAYAN.js +542 -0
- package/dist/chunk-ZFKJAYAN.js.map +1 -0
- package/dist/cursor-hook-shim.js +316 -0
- package/dist/cursor-hook-shim.js.map +1 -0
- package/dist/cursor-runner.js +358 -0
- package/dist/cursor-runner.js.map +1 -0
- package/dist/electron-utility.js +111 -0
- package/dist/electron-utility.js.map +1 -0
- package/dist/git-pool-V73Q53NX.js +18 -0
- package/dist/{git-repo-VRT57DGC.js → git-repo-TN3VZXQV.js} +9 -6
- package/dist/index.js +12 -12
- package/dist/index.js.map +1 -1
- package/dist/{logger-GQCSLSZH.js → logger-QHPTO22N.js} +4 -4
- package/dist/login-Q7SZI7JJ.js +20 -0
- package/dist/{logout-VUNCW5B2.js → logout-O4AVMO5S.js} +6 -6
- package/dist/mcp-servers-F64M5T4I.js +24 -0
- package/dist/{roi-Y3MX5UW4.js → roi-EYDLPOCS.js} +5 -5
- package/dist/rss-worker.js +159 -0
- package/dist/rss-worker.js.map +1 -0
- package/dist/{serve-O53FNK64.js → serve-6A7RJWEF.js} +89862 -102999
- package/dist/{serve-O53FNK64.js.map → serve-6A7RJWEF.js.map} +1 -1
- package/dist/skills-ZHEPSBHW.js +11 -0
- package/dist/{start-IDFDHRD6.js → start-YGYYIK53.js} +229 -27
- package/dist/start-YGYYIK53.js.map +1 -0
- package/dist/vault-crypto-BKDOA65F.js +13 -0
- package/dist/vault-crypto-BKDOA65F.js.map +1 -0
- package/dist/worker.js +6 -3
- package/dist/worker.js.map +1 -1
- package/package.json +17 -10
- package/dist/chunk-2J3WSIAF.js.map +0 -1
- package/dist/chunk-3MNPDCO5.js.map +0 -1
- package/dist/chunk-66OBOZ3X.js +0 -79
- package/dist/chunk-66OBOZ3X.js.map +0 -1
- package/dist/chunk-7AHRFPAL.js.map +0 -1
- package/dist/chunk-DKMDBOFU.js.map +0 -1
- package/dist/chunk-L2WQMPWS.js +0 -666
- package/dist/chunk-L2WQMPWS.js.map +0 -1
- package/dist/chunk-LW2MS4T5.js.map +0 -1
- package/dist/chunk-PI77CUEP.js +0 -49
- package/dist/chunk-PI77CUEP.js.map +0 -1
- package/dist/chunk-RXI4637N.js +0 -395
- package/dist/chunk-RXI4637N.js.map +0 -1
- package/dist/chunk-SNYEQHUK.js.map +0 -1
- package/dist/chunk-VBPHGPBR.js +0 -126
- package/dist/chunk-VBPHGPBR.js.map +0 -1
- package/dist/index.d.ts +0 -2
- package/dist/login-L4BBPUYO.js +0 -20
- package/dist/mcp-servers-MXS5VAWI.js +0 -18
- package/dist/shell-V36EX2IJ.js +0 -27
- package/dist/skills-GPGRNV4R.js +0 -9
- package/dist/start-IDFDHRD6.js.map +0 -1
- package/dist/worker.d.ts +0 -49
- /package/dist/{auth-SS7LV5XK.js.map → auth-EXHO3AG5.js.map} +0 -0
- /package/dist/{chunk-VPMN47TL.js.map → chunk-CNR7O5YH.js.map} +0 -0
- /package/dist/{chunk-IISLTKYY.js.map → chunk-TU63KZFW.js.map} +0 -0
- /package/dist/{chunk-2UN5AR7V.js.map → chunk-ZAOPND5G.js.map} +0 -0
- /package/dist/{git-repo-VRT57DGC.js.map → git-pool-V73Q53NX.js.map} +0 -0
- /package/dist/{logger-GQCSLSZH.js.map → git-repo-TN3VZXQV.js.map} +0 -0
- /package/dist/{login-L4BBPUYO.js.map → logger-QHPTO22N.js.map} +0 -0
- /package/dist/{mcp-servers-MXS5VAWI.js.map → login-Q7SZI7JJ.js.map} +0 -0
- /package/dist/{logout-VUNCW5B2.js.map → logout-O4AVMO5S.js.map} +0 -0
- /package/dist/{shell-V36EX2IJ.js.map → mcp-servers-F64M5T4I.js.map} +0 -0
- /package/dist/{roi-Y3MX5UW4.js.map → roi-EYDLPOCS.js.map} +0 -0
- /package/dist/{skills-GPGRNV4R.js.map → skills-ZHEPSBHW.js.map} +0 -0
|
@@ -1,9 +1,14 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import {
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
3
|
+
WorkerCommandCodec,
|
|
4
|
+
WorkerReplyCodec
|
|
5
|
+
} from "./chunk-NACJENDW.js";
|
|
6
|
+
import {
|
|
7
|
+
createServiceSupervisor
|
|
8
|
+
} from "./chunk-ZFKJAYAN.js";
|
|
9
|
+
import {
|
|
10
|
+
assertNever
|
|
11
|
+
} from "./chunk-X3MULCV5.js";
|
|
7
12
|
|
|
8
13
|
// src/services/metrics/metrics-collector.ts
|
|
9
14
|
var NOOP_METRICS = {
|
|
@@ -73,6 +78,8 @@ function createMetricsCollector(workerUrl, authToken, telemetryEnabled) {
|
|
|
73
78
|
|
|
74
79
|
// src/shared/file-watcher-guard.ts
|
|
75
80
|
import { fork as childProcessFork } from "child_process";
|
|
81
|
+
import { existsSync } from "fs";
|
|
82
|
+
import { dirname } from "path";
|
|
76
83
|
|
|
77
84
|
// src/services/watcher-worker/worker-supervisor.ts
|
|
78
85
|
import { randomUUID } from "crypto";
|
|
@@ -81,34 +88,72 @@ var SHUTDOWN_GRACEFUL_MS = 2e3;
|
|
|
81
88
|
var SHUTDOWN_SIGTERM_MS = 500;
|
|
82
89
|
function createWatcherWorkerSupervisor(deps) {
|
|
83
90
|
const subscriptions = /* @__PURE__ */ new Map();
|
|
84
|
-
let child = null;
|
|
85
91
|
let restartState = makeInitialAttemptState();
|
|
86
|
-
let
|
|
92
|
+
let liveGeneration = 0;
|
|
93
|
+
let lastSpawnedAt = 0;
|
|
94
|
+
let lastPid = null;
|
|
87
95
|
let shutdownInitiated = false;
|
|
88
96
|
let restartTimer = null;
|
|
89
97
|
let pendingRespawnGeneration = null;
|
|
98
|
+
let nextReplayStartedAt = null;
|
|
90
99
|
function log(entry) {
|
|
91
100
|
deps.log(entry);
|
|
92
101
|
}
|
|
93
102
|
function captureMetric(eventType, properties) {
|
|
94
103
|
deps.metrics?.capture(eventType, properties);
|
|
95
104
|
}
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
105
|
+
const adaptedFork = (modulePath, args, options) => {
|
|
106
|
+
return deps.fork(modulePath, Array.from(args), options);
|
|
107
|
+
};
|
|
108
|
+
const supervisor = createServiceSupervisor({
|
|
109
|
+
fork: adaptedFork,
|
|
110
|
+
workerPath: deps.workerPath,
|
|
111
|
+
commandCodec: WorkerCommandCodec,
|
|
112
|
+
replyCodec: WorkerReplyCodec,
|
|
113
|
+
onReply: (reply, fromGeneration) => dispatchReply(reply, fromGeneration),
|
|
114
|
+
onChildSpawned: ({ generation, pid }) => {
|
|
115
|
+
liveGeneration = generation;
|
|
116
|
+
lastPid = pid;
|
|
117
|
+
lastSpawnedAt = deps.now();
|
|
118
|
+
log({ event: "watcher_worker_started", pid });
|
|
119
|
+
captureMetric("watcher_worker_started", { pid });
|
|
120
|
+
},
|
|
121
|
+
onChildExit: ({ code, signal, generation, uptimeMs }) => {
|
|
122
|
+
handleChildExit(generation, code, signal, uptimeMs);
|
|
123
|
+
},
|
|
124
|
+
onDecodeFailed: ({ line }) => log({ event: "watcher_worker_decode_failed", line }),
|
|
125
|
+
onStderrLine: (line) => {
|
|
126
|
+
if (!line.trim()) return;
|
|
127
|
+
log({ event: "watcher_worker_stderr", line });
|
|
128
|
+
},
|
|
129
|
+
onMissingStdout: () => log({ event: "watcher_worker_no_stdout" }),
|
|
130
|
+
onOverflow: ({ stream, bytes, maxBytes }) => log({ event: "watcher_worker_stdio_overflow", stream, bytes, maxBytes }),
|
|
131
|
+
onPeerError: (err) => log({ event: "watcher_worker_error", err: err.message }),
|
|
132
|
+
log,
|
|
133
|
+
now: deps.now
|
|
134
|
+
/** Watcher uses its own file-watcher-guard circuit; opt out of the primitive's. */
|
|
135
|
+
});
|
|
136
|
+
function sendCommand(cmd) {
|
|
137
|
+
const sent = supervisor.send(cmd);
|
|
138
|
+
if (!sent && supervisor.getCurrentProcess() !== null) {
|
|
104
139
|
log({
|
|
105
140
|
event: "watcher_worker_stdin_write_failed",
|
|
106
|
-
err:
|
|
141
|
+
err: "stdio peer send returned false"
|
|
107
142
|
});
|
|
108
|
-
return false;
|
|
109
143
|
}
|
|
144
|
+
return sent;
|
|
110
145
|
}
|
|
111
146
|
function dispatchReply(reply, fromGeneration) {
|
|
147
|
+
if (fromGeneration !== liveGeneration) {
|
|
148
|
+
log({
|
|
149
|
+
event: "watcher_worker_stale_reply_dropped",
|
|
150
|
+
replyType: reply.type,
|
|
151
|
+
id: reply.id,
|
|
152
|
+
generation: fromGeneration,
|
|
153
|
+
liveGeneration
|
|
154
|
+
});
|
|
155
|
+
return;
|
|
156
|
+
}
|
|
112
157
|
switch (reply.type) {
|
|
113
158
|
case "subscribed":
|
|
114
159
|
handleSubscribedReply(reply.id, fromGeneration);
|
|
@@ -165,68 +210,11 @@ function createWatcherWorkerSupervisor(deps) {
|
|
|
165
210
|
if (!entry || entry.status === "unsubscribing") return;
|
|
166
211
|
entry.callback(null, toCallbackEvents(events));
|
|
167
212
|
}
|
|
168
|
-
function
|
|
169
|
-
|
|
170
|
-
const { stdout, stderr } = proc;
|
|
171
|
-
if (!stdout) {
|
|
172
|
-
log({ event: "watcher_worker_no_stdout" });
|
|
173
|
-
return;
|
|
174
|
-
}
|
|
175
|
-
let stdoutBuffer = "";
|
|
176
|
-
const fromGeneration = holder.generation;
|
|
177
|
-
stdout.setEncoding("utf8");
|
|
178
|
-
stdout.on("data", (chunk) => {
|
|
179
|
-
stdoutBuffer += chunk;
|
|
180
|
-
let nl = stdoutBuffer.indexOf("\n");
|
|
181
|
-
while (nl !== -1) {
|
|
182
|
-
const line = stdoutBuffer.slice(0, nl);
|
|
183
|
-
stdoutBuffer = stdoutBuffer.slice(nl + 1);
|
|
184
|
-
nl = stdoutBuffer.indexOf("\n");
|
|
185
|
-
if (line.length === 0) continue;
|
|
186
|
-
const decoded = decodeLine(line);
|
|
187
|
-
if (decoded === null) {
|
|
188
|
-
log({ event: "watcher_worker_decode_failed", line });
|
|
189
|
-
continue;
|
|
190
|
-
}
|
|
191
|
-
if (!("type" in decoded)) {
|
|
192
|
-
log({ event: "watcher_worker_unexpected_command", cmd: decoded.cmd });
|
|
193
|
-
continue;
|
|
194
|
-
}
|
|
195
|
-
dispatchReply(decoded, fromGeneration);
|
|
196
|
-
}
|
|
197
|
-
});
|
|
198
|
-
if (stderr) {
|
|
199
|
-
stderr.setEncoding("utf8");
|
|
200
|
-
let stderrBuffer = "";
|
|
201
|
-
stderr.on("data", (chunk) => {
|
|
202
|
-
stderrBuffer += chunk;
|
|
203
|
-
let nl = stderrBuffer.indexOf("\n");
|
|
204
|
-
while (nl !== -1) {
|
|
205
|
-
const line = stderrBuffer.slice(0, nl);
|
|
206
|
-
stderrBuffer = stderrBuffer.slice(nl + 1);
|
|
207
|
-
nl = stderrBuffer.indexOf("\n");
|
|
208
|
-
if (line.length > 0) {
|
|
209
|
-
log({ event: "watcher_worker_stderr", line });
|
|
210
|
-
}
|
|
211
|
-
}
|
|
212
|
-
});
|
|
213
|
-
}
|
|
214
|
-
proc.on("exit", (code, signal) => {
|
|
215
|
-
handleChildExit(holder, code, signal);
|
|
216
|
-
});
|
|
217
|
-
proc.on("error", (err) => {
|
|
218
|
-
log({ event: "watcher_worker_error", err: err.message });
|
|
219
|
-
});
|
|
220
|
-
}
|
|
221
|
-
function handleChildExit(holder, code, signal) {
|
|
222
|
-
holder.exited = true;
|
|
223
|
-
if (child !== holder) return;
|
|
224
|
-
child = null;
|
|
225
|
-
if (pendingRespawnGeneration === holder.generation) {
|
|
213
|
+
function handleChildExit(generation, code, signal, uptimeMs) {
|
|
214
|
+
if (pendingRespawnGeneration === generation) {
|
|
226
215
|
pendingRespawnGeneration = null;
|
|
227
216
|
}
|
|
228
217
|
const replayCount = subscriptions.size;
|
|
229
|
-
const uptimeMs = deps.now() - holder.spawnedAt;
|
|
230
218
|
if (shutdownInitiated) {
|
|
231
219
|
log({ event: "watcher_worker_exited_during_shutdown", code, signal, uptimeMs });
|
|
232
220
|
return;
|
|
@@ -243,11 +231,11 @@ function createWatcherWorkerSupervisor(deps) {
|
|
|
243
231
|
replayCount,
|
|
244
232
|
uptimeMs
|
|
245
233
|
});
|
|
246
|
-
recordRestartFailure(
|
|
234
|
+
recordRestartFailure(uptimeMs);
|
|
247
235
|
fireSyntheticEvictionToAll();
|
|
248
236
|
scheduleRespawn();
|
|
249
237
|
}
|
|
250
|
-
function recordRestartFailure(
|
|
238
|
+
function recordRestartFailure(uptimeMs) {
|
|
251
239
|
const failureDecision = decideAction(restartState, { kind: "subscribe_failure" }, deps.now());
|
|
252
240
|
restartState = failureDecision.state;
|
|
253
241
|
for (const sig of failureDecision.signals) {
|
|
@@ -255,7 +243,7 @@ function createWatcherWorkerSupervisor(deps) {
|
|
|
255
243
|
log({ event: "watcher_worker_circuit_open" });
|
|
256
244
|
captureMetric("watcher_worker_circuit_open", {
|
|
257
245
|
deathCount: restartState.failureCount,
|
|
258
|
-
windowMs:
|
|
246
|
+
windowMs: uptimeMs
|
|
259
247
|
});
|
|
260
248
|
} else if (sig.kind === "circuit_closed") {
|
|
261
249
|
log({ event: "watcher_worker_circuit_closed" });
|
|
@@ -342,45 +330,37 @@ function createWatcherWorkerSupervisor(deps) {
|
|
|
342
330
|
}
|
|
343
331
|
}
|
|
344
332
|
function forkAndReplay() {
|
|
345
|
-
const isRespawn =
|
|
346
|
-
|
|
347
|
-
const
|
|
348
|
-
if (
|
|
349
|
-
|
|
333
|
+
const isRespawn = liveGeneration >= 1;
|
|
334
|
+
nextReplayStartedAt = deps.now();
|
|
335
|
+
const shouldMarkPendingRespawn = isRespawn && subscriptions.size > 0;
|
|
336
|
+
if (liveGeneration === 0) {
|
|
337
|
+
supervisor.start();
|
|
338
|
+
} else {
|
|
339
|
+
supervisor.restart();
|
|
350
340
|
}
|
|
351
|
-
|
|
341
|
+
if (shouldMarkPendingRespawn) {
|
|
342
|
+
pendingRespawnGeneration = liveGeneration;
|
|
343
|
+
}
|
|
344
|
+
const replayResult = replaySubscriptions();
|
|
352
345
|
if (replayResult.kind === "pipe_failed") {
|
|
353
|
-
handleReplayPipeFailure(
|
|
346
|
+
handleReplayPipeFailure(replayResult.replayedCount);
|
|
354
347
|
return;
|
|
355
348
|
}
|
|
356
349
|
if (replayResult.replayedCount > 0) {
|
|
357
350
|
log({ event: "watcher_worker_replayed", replayCount: replayResult.replayedCount });
|
|
358
351
|
}
|
|
359
352
|
if (isRespawn) {
|
|
360
|
-
recordRespawnTelemetry(
|
|
353
|
+
recordRespawnTelemetry(
|
|
354
|
+
replayResult.replayedCount,
|
|
355
|
+
deps.now() - (nextReplayStartedAt ?? deps.now())
|
|
356
|
+
);
|
|
361
357
|
}
|
|
362
358
|
}
|
|
363
|
-
function
|
|
364
|
-
const proc = deps.fork(deps.workerPath, [], {
|
|
365
|
-
stdio: ["pipe", "pipe", "pipe", "ipc"]
|
|
366
|
-
});
|
|
367
|
-
const holder = {
|
|
368
|
-
process: proc,
|
|
369
|
-
spawnedAt: deps.now(),
|
|
370
|
-
generation: nextGeneration++,
|
|
371
|
-
exited: false
|
|
372
|
-
};
|
|
373
|
-
child = holder;
|
|
374
|
-
log({ event: "watcher_worker_started", pid: proc.pid ?? null });
|
|
375
|
-
captureMetric("watcher_worker_started", { pid: proc.pid ?? null });
|
|
376
|
-
attachChildIo(holder);
|
|
377
|
-
return holder;
|
|
378
|
-
}
|
|
379
|
-
function replaySubscriptions(holder) {
|
|
359
|
+
function replaySubscriptions() {
|
|
380
360
|
let replayedCount = 0;
|
|
381
361
|
for (const entry of subscriptions.values()) {
|
|
382
362
|
entry.status = "pending";
|
|
383
|
-
const ok = sendCommand(
|
|
363
|
+
const ok = sendCommand({
|
|
384
364
|
cmd: "subscribe",
|
|
385
365
|
id: entry.id,
|
|
386
366
|
path: entry.path,
|
|
@@ -393,17 +373,18 @@ function createWatcherWorkerSupervisor(deps) {
|
|
|
393
373
|
}
|
|
394
374
|
return { kind: "completed", replayedCount };
|
|
395
375
|
}
|
|
396
|
-
function handleReplayPipeFailure(
|
|
376
|
+
function handleReplayPipeFailure(replayedCount) {
|
|
397
377
|
log({
|
|
398
378
|
event: "watcher_worker_replay_pipe_failed",
|
|
399
|
-
pid:
|
|
379
|
+
pid: lastPid,
|
|
400
380
|
replayedCount,
|
|
401
381
|
pendingCount: subscriptions.size - replayedCount
|
|
402
382
|
});
|
|
403
|
-
recordRestartFailure(
|
|
404
|
-
|
|
383
|
+
recordRestartFailure(deps.now() - lastSpawnedAt);
|
|
384
|
+
const proc = supervisor.getCurrentProcess();
|
|
385
|
+
if (proc !== null) {
|
|
405
386
|
try {
|
|
406
|
-
|
|
387
|
+
proc.kill("SIGKILL");
|
|
407
388
|
} catch (err) {
|
|
408
389
|
log({
|
|
409
390
|
event: "watcher_worker_kill_failed",
|
|
@@ -414,15 +395,15 @@ function createWatcherWorkerSupervisor(deps) {
|
|
|
414
395
|
}
|
|
415
396
|
scheduleRespawn();
|
|
416
397
|
}
|
|
417
|
-
function recordRespawnTelemetry(
|
|
398
|
+
function recordRespawnTelemetry(replayCount, replayDurationMs) {
|
|
418
399
|
log({
|
|
419
400
|
event: "watcher_worker_respawned",
|
|
420
|
-
pid:
|
|
401
|
+
pid: lastPid,
|
|
421
402
|
replayCount,
|
|
422
403
|
replayDurationMs
|
|
423
404
|
});
|
|
424
405
|
captureMetric("watcher_worker_respawned", {
|
|
425
|
-
newPid:
|
|
406
|
+
newPid: lastPid,
|
|
426
407
|
replayCount,
|
|
427
408
|
replayDurationMs
|
|
428
409
|
});
|
|
@@ -435,13 +416,13 @@ function createWatcherWorkerSupervisor(deps) {
|
|
|
435
416
|
function unsubscribeEntry(entry) {
|
|
436
417
|
if (!subscriptions.has(entry.id)) return Promise.resolve();
|
|
437
418
|
entry.status = "unsubscribing";
|
|
438
|
-
if (
|
|
419
|
+
if (supervisor.getCurrentProcess() === null) {
|
|
439
420
|
subscriptions.delete(entry.id);
|
|
440
421
|
return Promise.resolve();
|
|
441
422
|
}
|
|
442
423
|
return new Promise((resolve) => {
|
|
443
424
|
entry.resolveUnsubscribed = resolve;
|
|
444
|
-
const sent =
|
|
425
|
+
const sent = sendCommand({ cmd: "unsubscribe", id: entry.id });
|
|
445
426
|
if (!sent) {
|
|
446
427
|
subscriptions.delete(entry.id);
|
|
447
428
|
resolve();
|
|
@@ -453,7 +434,7 @@ function createWatcherWorkerSupervisor(deps) {
|
|
|
453
434
|
} };
|
|
454
435
|
}
|
|
455
436
|
async function start() {
|
|
456
|
-
if (
|
|
437
|
+
if (supervisor.getCurrentProcess() !== null) return;
|
|
457
438
|
if (shutdownInitiated) {
|
|
458
439
|
throw new Error("WatcherWorkerSupervisor: cannot start after shutdown");
|
|
459
440
|
}
|
|
@@ -483,10 +464,10 @@ function createWatcherWorkerSupervisor(deps) {
|
|
|
483
464
|
entry.rejectSubscribed = reject;
|
|
484
465
|
});
|
|
485
466
|
subscriptions.set(id, entry);
|
|
486
|
-
if (
|
|
467
|
+
if (supervisor.getCurrentProcess() === null) {
|
|
487
468
|
await start();
|
|
488
469
|
} else {
|
|
489
|
-
sendCommand(
|
|
470
|
+
sendCommand({ cmd: "subscribe", id, path, opts });
|
|
490
471
|
}
|
|
491
472
|
return promise;
|
|
492
473
|
}
|
|
@@ -505,17 +486,18 @@ function createWatcherWorkerSupervisor(deps) {
|
|
|
505
486
|
entry.resolveUnsubscribed = void 0;
|
|
506
487
|
}
|
|
507
488
|
subscriptions.clear();
|
|
508
|
-
const
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
489
|
+
const proc = supervisor.getCurrentProcess();
|
|
490
|
+
supervisor.dispose();
|
|
491
|
+
if (proc === null) return;
|
|
492
|
+
sendCommand({ cmd: "shutdown" });
|
|
493
|
+
await escalateShutdown(proc);
|
|
512
494
|
}
|
|
513
|
-
function killWithSignal(
|
|
495
|
+
function killWithSignal(proc, signal) {
|
|
514
496
|
log({
|
|
515
497
|
event: signal === "SIGTERM" ? "watcher_worker_shutdown_sigterm" : "watcher_worker_shutdown_sigkill"
|
|
516
498
|
});
|
|
517
499
|
try {
|
|
518
|
-
|
|
500
|
+
proc.kill(signal);
|
|
519
501
|
} catch (err) {
|
|
520
502
|
log({
|
|
521
503
|
event: "watcher_worker_kill_failed",
|
|
@@ -524,30 +506,29 @@ function createWatcherWorkerSupervisor(deps) {
|
|
|
524
506
|
});
|
|
525
507
|
}
|
|
526
508
|
}
|
|
527
|
-
function escalateShutdown(
|
|
509
|
+
function escalateShutdown(proc) {
|
|
528
510
|
return new Promise((resolve) => {
|
|
529
|
-
const ctx = { done: false };
|
|
511
|
+
const ctx = { done: false, exited: false };
|
|
530
512
|
const finish = () => {
|
|
531
513
|
if (ctx.done) return;
|
|
532
514
|
ctx.done = true;
|
|
533
515
|
resolve();
|
|
534
516
|
};
|
|
535
|
-
|
|
536
|
-
|
|
517
|
+
proc.once("exit", () => {
|
|
518
|
+
ctx.exited = true;
|
|
537
519
|
finish();
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
deps.setTimeout(() => onSigtermDeadline(holder, ctx, finish), SHUTDOWN_GRACEFUL_MS);
|
|
520
|
+
});
|
|
521
|
+
deps.setTimeout(() => onSigtermDeadline(proc, ctx, finish), SHUTDOWN_GRACEFUL_MS);
|
|
541
522
|
});
|
|
542
523
|
}
|
|
543
|
-
function onSigtermDeadline(
|
|
544
|
-
if (ctx.done ||
|
|
545
|
-
killWithSignal(
|
|
546
|
-
deps.setTimeout(() => onSigkillDeadline(
|
|
524
|
+
function onSigtermDeadline(proc, ctx, finish) {
|
|
525
|
+
if (ctx.done || ctx.exited) return;
|
|
526
|
+
killWithSignal(proc, "SIGTERM");
|
|
527
|
+
deps.setTimeout(() => onSigkillDeadline(proc, ctx, finish), SHUTDOWN_SIGTERM_MS);
|
|
547
528
|
}
|
|
548
|
-
function onSigkillDeadline(
|
|
549
|
-
if (ctx.done ||
|
|
550
|
-
killWithSignal(
|
|
529
|
+
function onSigkillDeadline(proc, ctx, finish) {
|
|
530
|
+
if (ctx.done || ctx.exited) return;
|
|
531
|
+
killWithSignal(proc, "SIGKILL");
|
|
551
532
|
finish();
|
|
552
533
|
}
|
|
553
534
|
return { start, subscribe, shutdown };
|
|
@@ -777,7 +758,9 @@ function toGuardedEvent(e) {
|
|
|
777
758
|
}
|
|
778
759
|
function ensureSupervisor() {
|
|
779
760
|
if (moduleState.supervisor) return moduleState.supervisor;
|
|
780
|
-
const
|
|
761
|
+
const selfUrl = import.meta.url;
|
|
762
|
+
const isDevMode = selfUrl.endsWith(".ts");
|
|
763
|
+
const workerPath = isDevMode ? new URL("../services/watcher-worker/worker.ts", selfUrl).pathname : new URL("./worker.js", selfUrl).pathname;
|
|
781
764
|
moduleState.supervisor = createWatcherWorkerSupervisor({
|
|
782
765
|
fork: nodeFork,
|
|
783
766
|
workerPath,
|
|
@@ -894,10 +877,19 @@ async function performSubscribe(path, consumerCallback, opts, tier) {
|
|
|
894
877
|
const priorState = moduleState.perPath.get(path) ?? makeInitialAttemptState();
|
|
895
878
|
const failureDecision = decideAction(priorState, { kind: "subscribe_failure" }, now);
|
|
896
879
|
moduleState.perPath.set(path, failureDecision.state);
|
|
880
|
+
const parentExists = pathExistsForLog(dirname(path));
|
|
897
881
|
deps.log({
|
|
898
882
|
event: "file_watcher_subscribe_failed",
|
|
899
883
|
path,
|
|
900
|
-
|
|
884
|
+
pathExists: pathExistsForLog(path),
|
|
885
|
+
parentExists,
|
|
886
|
+
err: err instanceof Error ? err.message : String(err),
|
|
887
|
+
/**
|
|
888
|
+
* The expected-ENOENT fallback path (file doesn't exist yet but
|
|
889
|
+
* parent does) is a normal poll cycle, not a failure. Downgrade
|
|
890
|
+
* the log level so log triage doesn't flag 40+ of these per task.
|
|
891
|
+
*/
|
|
892
|
+
...parentExists ? { level: "debug" } : {}
|
|
901
893
|
});
|
|
902
894
|
emitSignals(path, failureDecision.signals);
|
|
903
895
|
if (failureDecision.state.circuitOpenedAt !== null) {
|
|
@@ -906,6 +898,13 @@ async function performSubscribe(path, consumerCallback, opts, tier) {
|
|
|
906
898
|
throw err;
|
|
907
899
|
}
|
|
908
900
|
}
|
|
901
|
+
function pathExistsForLog(path) {
|
|
902
|
+
try {
|
|
903
|
+
return existsSync(path);
|
|
904
|
+
} catch {
|
|
905
|
+
return false;
|
|
906
|
+
}
|
|
907
|
+
}
|
|
909
908
|
function pickOldest(entries, tier) {
|
|
910
909
|
let target = null;
|
|
911
910
|
for (const entry of entries) {
|
|
@@ -1008,4 +1007,4 @@ export {
|
|
|
1008
1007
|
shutdownFileWatcherGuard,
|
|
1009
1008
|
guardedSubscribe
|
|
1010
1009
|
};
|
|
1011
|
-
//# sourceMappingURL=chunk-
|
|
1010
|
+
//# sourceMappingURL=chunk-WBB4XHLH.js.map
|