@ricsam/r5d-worker 0.0.123 → 0.0.125
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/README.md +1 -1
- package/dist/cjs/command-launcher.cjs +6 -2
- package/dist/cjs/main.cjs +496 -238
- package/dist/cjs/package.json +1 -1
- package/dist/cjs/project-checkout-garbage.cjs +32 -3
- package/dist/cjs/project-workspace-state.cjs +51 -35
- package/dist/cjs/project-worktrees.cjs +79 -34
- package/dist/cjs/recovery-journal-protocol.cjs +56 -0
- package/dist/cjs/recovery-journal-runtime.cjs +133 -0
- package/dist/cjs/recovery-journal-thread.cjs +9 -0
- package/dist/cjs/recovery-journal.cjs +735 -0
- package/dist/cjs/recovery-store.cjs +8 -0
- package/dist/cjs/session-file-mutations.cjs +61 -0
- package/dist/cjs/working-tree-mirror.cjs +1 -0
- package/dist/cjs/workspace-command-sync-policy.cjs +37 -8
- package/dist/cjs/workspace-filesystem-executor-thread.cjs +36 -0
- package/dist/cjs/workspace-filesystem-executor.cjs +327 -0
- package/dist/cjs/workspace-filesystem-job-types.cjs +134 -0
- package/dist/cjs/workspace-filesystem-jobs.cjs +57 -0
- package/dist/cjs/workspace-git-sync.cjs +275 -201
- package/dist/cjs/workspace-mount-hold-fence.cjs +120 -0
- package/dist/mjs/command-launcher.mjs +6 -2
- package/dist/mjs/main.mjs +499 -244
- package/dist/mjs/package.json +1 -1
- package/dist/mjs/project-checkout-garbage.mjs +30 -2
- package/dist/mjs/project-workspace-state.mjs +51 -35
- package/dist/mjs/project-worktrees.mjs +74 -34
- package/dist/mjs/recovery-journal-protocol.mjs +30 -0
- package/dist/mjs/recovery-journal-runtime.mjs +112 -0
- package/dist/mjs/recovery-journal-thread.mjs +8 -0
- package/dist/mjs/recovery-journal.mjs +687 -0
- package/dist/mjs/recovery-store.mjs +8 -0
- package/dist/mjs/session-file-mutations.mjs +37 -0
- package/dist/mjs/working-tree-mirror.mjs +1 -0
- package/dist/mjs/workspace-command-sync-policy.mjs +37 -8
- package/dist/mjs/workspace-filesystem-executor-thread.mjs +38 -0
- package/dist/mjs/workspace-filesystem-executor.mjs +287 -0
- package/dist/mjs/workspace-filesystem-job-types.mjs +106 -0
- package/dist/mjs/workspace-filesystem-jobs.mjs +51 -0
- package/dist/mjs/workspace-git-sync.mjs +264 -202
- package/dist/mjs/workspace-mount-hold-fence.mjs +95 -0
- package/dist/types/command-launcher.d.ts +2 -1
- package/dist/types/main.d.ts +30 -19
- package/dist/types/project-checkout-garbage.d.ts +19 -2
- package/dist/types/project-workspace-state.d.ts +9 -9
- package/dist/types/project-worktrees.d.ts +49 -5
- package/dist/types/recovery-journal-protocol.d.ts +35 -0
- package/dist/types/recovery-journal-runtime.d.ts +12 -0
- package/dist/types/recovery-journal-stall-fixture.d.ts +1 -0
- package/dist/types/recovery-journal-thread.d.ts +1 -0
- package/dist/types/recovery-journal.d.ts +246 -0
- package/dist/types/recovery-store.d.ts +6 -0
- package/dist/types/session-file-mutations.d.ts +22 -0
- package/dist/types/workspace-command-sync-policy.d.ts +22 -7
- package/dist/types/workspace-filesystem-executor-thread.d.ts +1 -0
- package/dist/types/workspace-filesystem-executor.d.ts +123 -0
- package/dist/types/workspace-filesystem-job-types.d.ts +246 -0
- package/dist/types/workspace-filesystem-jobs.d.ts +7 -0
- package/dist/types/workspace-git-sync.d.ts +113 -7
- package/dist/types/workspace-mount-hold-fence.d.ts +42 -0
- package/package.json +1 -1
- package/dist/cjs/project-snapshot-recovery-runner.cjs +0 -171
- package/dist/mjs/project-snapshot-recovery-runner.mjs +0 -135
- package/dist/types/project-snapshot-recovery-runner.d.ts +0 -10
package/dist/cjs/main.cjs
CHANGED
|
@@ -61,6 +61,8 @@ var import_plan_paths = require("./plan-paths.cjs");
|
|
|
61
61
|
var import_plan_parser = require("./plan-parser.cjs");
|
|
62
62
|
var import_plan_lint = require("./plan-lint.cjs");
|
|
63
63
|
var import_node_fs = __toESM(require("node:fs"), 1);
|
|
64
|
+
var import_recovery_journal = require("./recovery-journal.cjs");
|
|
65
|
+
var import_session_file_mutations = require("./session-file-mutations.cjs");
|
|
64
66
|
var import_recovery_store = require("./recovery-store.cjs");
|
|
65
67
|
var import_recovery_output_window = require("./recovery-output-window.cjs");
|
|
66
68
|
var import_command_launcher = require("./command-launcher.cjs");
|
|
@@ -83,6 +85,8 @@ var import_workspace_incident_state = require("./workspace-incident-state.cjs");
|
|
|
83
85
|
var import_workspace_automatic_sync_policy = require("./workspace-automatic-sync-policy.cjs");
|
|
84
86
|
var import_workspace_command_sync_policy = require("./workspace-command-sync-policy.cjs");
|
|
85
87
|
var import_workspace_mutation_gate = require("./workspace-mutation-gate.cjs");
|
|
88
|
+
var import_workspace_mount_hold_fence = require("./workspace-mount-hold-fence.cjs");
|
|
89
|
+
var import_workspace_filesystem_executor = require("./workspace-filesystem-executor.cjs");
|
|
86
90
|
var import_workspace_projection_ledger = require("./workspace-projection-ledger.cjs");
|
|
87
91
|
var import_workspace_sync_coalescer = require("./workspace-sync-coalescer.cjs");
|
|
88
92
|
var import_workspace_path_move = require("./workspace-path-move.cjs");
|
|
@@ -95,7 +99,6 @@ var import_supervisor = require("./supervisor.cjs");
|
|
|
95
99
|
var import_managed_paths = require("./managed-paths.cjs");
|
|
96
100
|
var import_project_worktrees = require("./project-worktrees.cjs");
|
|
97
101
|
var import_project_mirror_fetch_policy = require("./project-mirror-fetch-policy.cjs");
|
|
98
|
-
var import_project_snapshot_recovery_runner = require("./project-snapshot-recovery-runner.cjs");
|
|
99
102
|
var import_project_workspace_state = require("./project-workspace-state.cjs");
|
|
100
103
|
var import_workspace_git_sync = require("./workspace-git-sync.cjs");
|
|
101
104
|
const import_meta = {};
|
|
@@ -111,7 +114,7 @@ function workerServerHeartbeatAction(input) {
|
|
|
111
114
|
return input.probeElapsedMs >= input.timeoutMs ? "terminate" : "wait";
|
|
112
115
|
}
|
|
113
116
|
const WORKSPACE_HYDRATION_LEASE_OPERATION_PREFIX = "hydration:";
|
|
114
|
-
function createWorkspaceHydrationLeaseHooks(send, options
|
|
117
|
+
function createWorkspaceHydrationLeaseHooks(send, options) {
|
|
115
118
|
const yieldToEventLoop = options.yieldToEventLoop ?? (() => new Promise((resolve) => setImmediate(resolve)));
|
|
116
119
|
const log = options.log ?? ((line) => process.stdout.write(`${line}
|
|
117
120
|
`));
|
|
@@ -132,13 +135,17 @@ function createWorkspaceHydrationLeaseHooks(send, options = {}) {
|
|
|
132
135
|
log(
|
|
133
136
|
`[r5d-worker] released hydration lease ${context.transactionId} after ${startedAt === void 0 ? "?" : Math.round(now() - startedAt)}ms`
|
|
134
137
|
);
|
|
138
|
+
},
|
|
139
|
+
holdMounts(mountIds, description) {
|
|
140
|
+
return options.fence.hold(mountIds, description);
|
|
135
141
|
}
|
|
136
142
|
};
|
|
137
143
|
}
|
|
138
144
|
const workerLifecycleTestHarness = {
|
|
139
145
|
boundedWorkerLifecycleProgress,
|
|
140
146
|
workerServerHeartbeatAction,
|
|
141
|
-
createWorkspaceHydrationLeaseHooks
|
|
147
|
+
createWorkspaceHydrationLeaseHooks,
|
|
148
|
+
workerCommandHasWorkspaceEffect
|
|
142
149
|
};
|
|
143
150
|
class ProjectWorkspaceConfigurationDeferredError extends Error {
|
|
144
151
|
}
|
|
@@ -191,7 +198,7 @@ const workspaceSyncPriorityPtyTargets = /* @__PURE__ */ new Map();
|
|
|
191
198
|
let currentWorkerSocket = null;
|
|
192
199
|
const workerRuntimeId = crypto.randomUUID();
|
|
193
200
|
const workerOutputWindow = new import_recovery_output_window.WorkerOutputWindow();
|
|
194
|
-
let
|
|
201
|
+
let recoveryJournal;
|
|
195
202
|
let workerReconnectAttempt = 0;
|
|
196
203
|
let workerRecoveryId;
|
|
197
204
|
let workerExecutionLeaseDeadline = 0;
|
|
@@ -203,13 +210,13 @@ let runtimeExitPromise;
|
|
|
203
210
|
function fenceWorkerRuntimeOperations() {
|
|
204
211
|
launcherFailureInProgress = true;
|
|
205
212
|
workerAdmissionGeneration += 1;
|
|
206
|
-
for (const operation of
|
|
207
|
-
if (operation.state !== "completed")
|
|
213
|
+
for (const operation of recoveryJournal?.activeAndPendingOperations() ?? []) {
|
|
214
|
+
if (operation.state !== "completed") recoveryJournal?.unknown(operation.requestId);
|
|
208
215
|
}
|
|
209
216
|
for (const runId of workerCommandLauncher?.activeIds ?? []) {
|
|
210
217
|
infrastructureStoppedRunIds.add(runId);
|
|
211
218
|
cancelledProcessRuns.add(runId);
|
|
212
|
-
|
|
219
|
+
recoveryJournal?.unknownRun(runId);
|
|
213
220
|
}
|
|
214
221
|
for (const runId of activeProcesses.keys()) infrastructureStoppedRunIds.add(runId);
|
|
215
222
|
}
|
|
@@ -218,13 +225,21 @@ function exitWorkerRuntime(code) {
|
|
|
218
225
|
fenceWorkerRuntimeOperations();
|
|
219
226
|
runtimeExitPromise = (async () => {
|
|
220
227
|
await Promise.race([
|
|
221
|
-
Promise.allSettled([terminateActiveCredentialBearingChildren(), workerCommandLauncher?.close()]),
|
|
228
|
+
Promise.allSettled([terminateActiveCredentialBearingChildren(), workerCommandLauncher?.close(), recoveryJournal?.flush()]),
|
|
222
229
|
Bun.sleep(4e3)
|
|
223
230
|
]);
|
|
224
231
|
process.exit(code);
|
|
225
232
|
})();
|
|
226
233
|
return runtimeExitPromise;
|
|
227
234
|
}
|
|
235
|
+
function handleRecoveryJournalFailure(error) {
|
|
236
|
+
if (launcherFailureInProgress) return;
|
|
237
|
+
fenceWorkerRuntimeOperations();
|
|
238
|
+
process.stderr.write(`[r5d-worker] ${error.message}; stopping runtime for recovery journal restart
|
|
239
|
+
`);
|
|
240
|
+
currentWorkerSocket?.close(1012, "Recovery journal failure");
|
|
241
|
+
void exitWorkerRuntime(import_supervisor.WORKER_RECONNECT_EXIT_CODE);
|
|
242
|
+
}
|
|
228
243
|
function handleWorkerCommandLauncherFailure(error) {
|
|
229
244
|
if (!workerCommandLauncher || launcherFailureInProgress) return;
|
|
230
245
|
fenceWorkerRuntimeOperations();
|
|
@@ -245,7 +260,7 @@ async function retryWorkerServerRead(read) {
|
|
|
245
260
|
try {
|
|
246
261
|
return await read();
|
|
247
262
|
} catch (error) {
|
|
248
|
-
if (!
|
|
263
|
+
if (!recoveryJournal || !isWorkerCommunicationFailure(error instanceof Error ? error.message : String(error))) throw error;
|
|
249
264
|
deadline ??= Date.now() + import_recovery_protocol.WORKER_RECOVERY_TIMEOUT_MS;
|
|
250
265
|
if (workerLeaseExpired || Date.now() >= deadline) throw new WorkerServerUnavailableError("Worker communication recovery expired");
|
|
251
266
|
currentWorkerSocket?.close(1012, "Communication recovery");
|
|
@@ -267,9 +282,10 @@ function renewWorkerExecutionLease(leaseMs) {
|
|
|
267
282
|
`);
|
|
268
283
|
});
|
|
269
284
|
closeAllPtys();
|
|
270
|
-
for (const operation of
|
|
285
|
+
for (const operation of recoveryJournal?.activeAndPendingOperations() ?? []) {
|
|
271
286
|
if (operation.sessionId && operation.state !== "completed") {
|
|
272
|
-
|
|
287
|
+
void recoveryJournal?.cancelSession(operation.sessionId);
|
|
288
|
+
abortPendingReservations("Execution lease expired before command start", operation.sessionId);
|
|
273
289
|
void workerCommandLauncher?.cancelSession(operation.sessionId).catch((error) => {
|
|
274
290
|
process.stderr.write(`[r5d-worker] expired command launcher cancellation failed: ${error}
|
|
275
291
|
`);
|
|
@@ -279,7 +295,7 @@ function renewWorkerExecutionLease(leaseMs) {
|
|
|
279
295
|
for (const [runId, active] of activeProcesses) {
|
|
280
296
|
if (!active.sessionId) continue;
|
|
281
297
|
infrastructureStoppedRunIds.add(runId);
|
|
282
|
-
|
|
298
|
+
recoveryJournal?.unknownRun(runId);
|
|
283
299
|
cancelledProcessRuns.add(runId);
|
|
284
300
|
closeProcessStdin(active);
|
|
285
301
|
void (0, import_process_tree.terminateProcessTree)(active.process).catch(
|
|
@@ -294,6 +310,32 @@ function renewWorkerExecutionLease(leaseMs) {
|
|
|
294
310
|
}
|
|
295
311
|
let workerAdmissionGeneration = 0;
|
|
296
312
|
const workspaceMutationGate = new import_workspace_mutation_gate.WorkspaceMutationGate();
|
|
313
|
+
const workspaceMountHoldFence = new import_workspace_mount_hold_fence.WorkspaceMountHoldFence();
|
|
314
|
+
const projectMountId = (projectId, branchName) => `project:${projectId}:${encodeURIComponent(branchName)}`;
|
|
315
|
+
const pendingReservationAborts = /* @__PURE__ */ new Map();
|
|
316
|
+
const sessionFileMutations = new import_session_file_mutations.SessionFileMutationTracker();
|
|
317
|
+
function registerPendingReservation(key, sessionId) {
|
|
318
|
+
abortPendingReservation(key, "Reservation superseded by a newer request with the same id");
|
|
319
|
+
const controller = new AbortController();
|
|
320
|
+
pendingReservationAborts.set(key, { controller, sessionId });
|
|
321
|
+
return controller;
|
|
322
|
+
}
|
|
323
|
+
function unregisterPendingReservation(key, controller) {
|
|
324
|
+
if (pendingReservationAborts.get(key)?.controller === controller) pendingReservationAborts.delete(key);
|
|
325
|
+
}
|
|
326
|
+
function abortPendingReservation(key, reason) {
|
|
327
|
+
const pending = pendingReservationAborts.get(key);
|
|
328
|
+
if (!pending) return false;
|
|
329
|
+
pendingReservationAborts.delete(key);
|
|
330
|
+
pending.controller.abort(new import_workspace_mount_hold_fence.WorkspaceMountHoldAbortedError(reason));
|
|
331
|
+
return true;
|
|
332
|
+
}
|
|
333
|
+
function abortPendingReservations(reason, sessionId) {
|
|
334
|
+
for (const [key, pending] of [...pendingReservationAborts]) {
|
|
335
|
+
if (sessionId !== void 0 && pending.sessionId !== sessionId) continue;
|
|
336
|
+
abortPendingReservation(key, reason);
|
|
337
|
+
}
|
|
338
|
+
}
|
|
297
339
|
let workspaceSyncQueue = Promise.resolve();
|
|
298
340
|
let startupProjectSnapshotRecoveryCompleted = false;
|
|
299
341
|
const checkoutGarbageCollectors = /* @__PURE__ */ new Map();
|
|
@@ -328,6 +370,17 @@ const workspaceSyncSingleFlight = {
|
|
|
328
370
|
},
|
|
329
371
|
afterCurrent() {
|
|
330
372
|
return workspaceSyncQueue;
|
|
373
|
+
},
|
|
374
|
+
mountHold(target, signal) {
|
|
375
|
+
if (target.type !== "project") return null;
|
|
376
|
+
const mountIds = [projectMountId(target.projectId, target.branchName)];
|
|
377
|
+
if (!workspaceMountHoldFence.isHeld(mountIds)) return null;
|
|
378
|
+
const holder = workspaceMountHoldFence.describeHold(mountIds);
|
|
379
|
+
process.stdout.write(
|
|
380
|
+
`[r5d-worker] command for ${target.projectId}/${target.branchName} waits for ${holder ?? "a workspace filesystem job"}
|
|
381
|
+
`
|
|
382
|
+
);
|
|
383
|
+
return workspaceMountHoldFence.waitForRelease(mountIds, { signal });
|
|
331
384
|
}
|
|
332
385
|
};
|
|
333
386
|
let githubCredential = null;
|
|
@@ -984,14 +1037,15 @@ async function runGitAsync(args, options = {}) {
|
|
|
984
1037
|
env: (0, import_git_process_environment.workerGitProcessEnvironment)(),
|
|
985
1038
|
...bounded ? { detached: true } : {}
|
|
986
1039
|
});
|
|
987
|
-
const completion = Promise.all([
|
|
988
|
-
|
|
989
|
-
|
|
990
|
-
subprocess.exited
|
|
991
|
-
]).then(([stdout, stderr, exitCode]) => ({ stdout, stderr, exitCode }));
|
|
1040
|
+
const completion = Promise.all([new Response(subprocess.stdout).text(), new Response(subprocess.stderr).text(), subprocess.exited]).then(
|
|
1041
|
+
([stdout, stderr, exitCode]) => ({ stdout, stderr, exitCode })
|
|
1042
|
+
);
|
|
992
1043
|
let timer;
|
|
993
1044
|
const timedOut = bounded ? new Promise((_resolve, reject) => {
|
|
994
|
-
timer = setTimeout(
|
|
1045
|
+
timer = setTimeout(
|
|
1046
|
+
() => reject(new GitTransportTimeoutError(`git ${args.join(" ")} timed out after ${options.timeoutMs}ms`)),
|
|
1047
|
+
options.timeoutMs
|
|
1048
|
+
);
|
|
995
1049
|
}) : null;
|
|
996
1050
|
try {
|
|
997
1051
|
const { stdout, stderr, exitCode } = timedOut ? await Promise.race([completion, timedOut]) : await completion;
|
|
@@ -2080,12 +2134,12 @@ async function streamCommandOutput(stream, onData) {
|
|
|
2080
2134
|
}
|
|
2081
2135
|
const text = decoder.decode(chunk.value, { stream: true });
|
|
2082
2136
|
if (text.length > 0) {
|
|
2083
|
-
onData(text);
|
|
2137
|
+
await onData(text);
|
|
2084
2138
|
}
|
|
2085
2139
|
}
|
|
2086
2140
|
const trailing = decoder.decode();
|
|
2087
2141
|
if (trailing.length > 0) {
|
|
2088
|
-
onData(trailing);
|
|
2142
|
+
await onData(trailing);
|
|
2089
2143
|
}
|
|
2090
2144
|
} finally {
|
|
2091
2145
|
reader.releaseLock();
|
|
@@ -2224,12 +2278,21 @@ function mutationQueueKey(target, resolved) {
|
|
|
2224
2278
|
}
|
|
2225
2279
|
return `${describeWorkerSessionTarget(target)}:${import_node_path.default.posix.normalize(resolved.repoRelativePath)}`;
|
|
2226
2280
|
}
|
|
2227
|
-
function
|
|
2281
|
+
async function statIfExists(targetPath) {
|
|
2282
|
+
try {
|
|
2283
|
+
return await import_node_fs.default.promises.stat(targetPath);
|
|
2284
|
+
} catch (error) {
|
|
2285
|
+
const code = error.code;
|
|
2286
|
+
if (code === "ENOENT" || code === "ENOTDIR") return null;
|
|
2287
|
+
throw error;
|
|
2288
|
+
}
|
|
2289
|
+
}
|
|
2290
|
+
async function readWorkerTextFile(branchPath, filePath, offset, limit, builtInPaths) {
|
|
2228
2291
|
const resolved = resolveWorkerFilePath(branchPath, filePath, builtInPaths);
|
|
2229
|
-
if (!
|
|
2292
|
+
if (!(await statIfExists(resolved.absolutePath))?.isFile()) {
|
|
2230
2293
|
throw new Error(`File not found: ${resolved.displayPath}`);
|
|
2231
2294
|
}
|
|
2232
|
-
const buffer = import_node_fs.default.
|
|
2295
|
+
const buffer = await import_node_fs.default.promises.readFile(resolved.absolutePath);
|
|
2233
2296
|
const formatted = formatLineNumberedContent(buffer.toString("utf8"), offset, limit);
|
|
2234
2297
|
return {
|
|
2235
2298
|
type: "read",
|
|
@@ -2239,25 +2302,25 @@ function readWorkerTextFile(branchPath, filePath, offset, limit, builtInPaths) {
|
|
|
2239
2302
|
...formatted
|
|
2240
2303
|
};
|
|
2241
2304
|
}
|
|
2242
|
-
function writeWorkerTextFile(branchPath, filePath, content, builtInPaths) {
|
|
2305
|
+
async function writeWorkerTextFile(branchPath, filePath, content, builtInPaths) {
|
|
2243
2306
|
const resolved = resolveWorkerFilePath(branchPath, filePath, builtInPaths);
|
|
2244
|
-
import_node_fs.default.
|
|
2245
|
-
import_node_fs.default.
|
|
2307
|
+
await import_node_fs.default.promises.mkdir(import_node_path.default.dirname(resolved.absolutePath), { recursive: true });
|
|
2308
|
+
await import_node_fs.default.promises.writeFile(resolved.absolutePath, content, "utf8");
|
|
2246
2309
|
return {
|
|
2247
2310
|
type: "write",
|
|
2248
2311
|
file: resolved.displayPath,
|
|
2249
2312
|
gitBlobHash: getGitBlobHashForContent(content)
|
|
2250
2313
|
};
|
|
2251
2314
|
}
|
|
2252
|
-
function editWorkerTextFile(branchPath, filePath, edits, builtInPaths) {
|
|
2315
|
+
async function editWorkerTextFile(branchPath, filePath, edits, builtInPaths) {
|
|
2253
2316
|
const resolved = resolveWorkerFilePath(branchPath, filePath, builtInPaths);
|
|
2254
2317
|
if (!Array.isArray(edits) || edits.length === 0) {
|
|
2255
2318
|
throw new Error("edit requires at least one replacement");
|
|
2256
2319
|
}
|
|
2257
|
-
if (!
|
|
2320
|
+
if (!(await statIfExists(resolved.absolutePath))?.isFile()) {
|
|
2258
2321
|
throw new Error(`File not found: ${resolved.displayPath}`);
|
|
2259
2322
|
}
|
|
2260
|
-
const originalContent = import_node_fs.default.
|
|
2323
|
+
const originalContent = await import_node_fs.default.promises.readFile(resolved.absolutePath, "utf8");
|
|
2261
2324
|
const ranges = edits.map((edit, index) => {
|
|
2262
2325
|
if (edit.oldText === edit.newText) {
|
|
2263
2326
|
throw new Error(`edits[${index}].oldText and edits[${index}].newText must be different`);
|
|
@@ -2289,7 +2352,7 @@ function editWorkerTextFile(branchPath, filePath, edits, builtInPaths) {
|
|
|
2289
2352
|
for (const range of [...sortedRanges].reverse()) {
|
|
2290
2353
|
nextContent = nextContent.slice(0, range.start) + range.edit.newText + nextContent.slice(range.end);
|
|
2291
2354
|
}
|
|
2292
|
-
import_node_fs.default.
|
|
2355
|
+
await import_node_fs.default.promises.writeFile(resolved.absolutePath, nextContent, "utf8");
|
|
2293
2356
|
return {
|
|
2294
2357
|
type: "edit",
|
|
2295
2358
|
file: resolved.displayPath,
|
|
@@ -2322,7 +2385,7 @@ async function executePlanOperation(input) {
|
|
|
2322
2385
|
let created = false;
|
|
2323
2386
|
if (message.createIfMissing) {
|
|
2324
2387
|
try {
|
|
2325
|
-
import_node_fs.default.
|
|
2388
|
+
await import_node_fs.default.promises.writeFile(resolved.absolutePath, (0, import_plan_parser.createPlanMarkdown)(message.planId), { encoding: "utf8", flag: "wx" });
|
|
2326
2389
|
created = true;
|
|
2327
2390
|
} catch (error) {
|
|
2328
2391
|
if (error.code !== "EEXIST") throw error;
|
|
@@ -2331,11 +2394,16 @@ async function executePlanOperation(input) {
|
|
|
2331
2394
|
return {
|
|
2332
2395
|
type: "read_plan",
|
|
2333
2396
|
created,
|
|
2334
|
-
read: readWorkerTextFile(resolvedTarget.rootPath, filePath, void 0, void 0, builtInPaths)
|
|
2397
|
+
read: await readWorkerTextFile(resolvedTarget.rootPath, filePath, void 0, void 0, builtInPaths)
|
|
2335
2398
|
};
|
|
2336
2399
|
}
|
|
2337
|
-
const content = import_node_fs.default.
|
|
2338
|
-
writeWorkerTextFile(
|
|
2400
|
+
const content = await import_node_fs.default.promises.readFile(resolved.absolutePath, "utf8");
|
|
2401
|
+
await writeWorkerTextFile(
|
|
2402
|
+
resolvedTarget.rootPath,
|
|
2403
|
+
filePath,
|
|
2404
|
+
(0, import_plan_parser.updateTaskCheckbox)(content, message.taskId, message.checked),
|
|
2405
|
+
builtInPaths
|
|
2406
|
+
);
|
|
2339
2407
|
return {
|
|
2340
2408
|
type: "update_task_status",
|
|
2341
2409
|
planId: message.planId,
|
|
@@ -2356,7 +2424,11 @@ async function executeReadFileOperation(input) {
|
|
|
2356
2424
|
rootDir: input.rootDir,
|
|
2357
2425
|
access: "read"
|
|
2358
2426
|
});
|
|
2359
|
-
|
|
2427
|
+
const resolved = resolveWorkerFilePath(input.resolvedTarget.rootPath, input.message.filePath, builtInPaths);
|
|
2428
|
+
return withFileMutationQueue(
|
|
2429
|
+
mutationQueueKey(input.resolvedTarget.target, resolved),
|
|
2430
|
+
() => readWorkerTextFile(input.resolvedTarget.rootPath, input.message.filePath, input.message.offset, input.message.limit, builtInPaths)
|
|
2431
|
+
);
|
|
2360
2432
|
}
|
|
2361
2433
|
async function executeWriteFileOperation(input) {
|
|
2362
2434
|
const builtInPaths = await prepareBuiltInToolPathsForTarget({
|
|
@@ -2409,16 +2481,16 @@ function wildcardToRegex(pattern) {
|
|
|
2409
2481
|
function normalizeFindPattern(pattern) {
|
|
2410
2482
|
return /[*?]/.test(pattern) ? pattern : `*${pattern}*`;
|
|
2411
2483
|
}
|
|
2412
|
-
function walkWorkerEntries(branchPath, start) {
|
|
2484
|
+
async function walkWorkerEntries(branchPath, start) {
|
|
2413
2485
|
const entries = [];
|
|
2414
2486
|
const visitedDirectories = /* @__PURE__ */ new Set();
|
|
2415
|
-
const realVirtualRoot = start.scope === "virtual" ? import_node_fs.default.
|
|
2416
|
-
const visit = (absolutePath, isRoot) => {
|
|
2487
|
+
const realVirtualRoot = start.scope === "virtual" ? await import_node_fs.default.promises.realpath(start.virtualRootPath) : null;
|
|
2488
|
+
const visit = async (absolutePath, isRoot) => {
|
|
2417
2489
|
let stat;
|
|
2418
2490
|
try {
|
|
2419
|
-
stat = import_node_fs.default.
|
|
2491
|
+
stat = await import_node_fs.default.promises.stat(absolutePath);
|
|
2420
2492
|
if (realVirtualRoot) {
|
|
2421
|
-
assertInsideRoot(realVirtualRoot, import_node_fs.default.
|
|
2493
|
+
assertInsideRoot(realVirtualRoot, await import_node_fs.default.promises.realpath(absolutePath), `${start.displayPath} path`);
|
|
2422
2494
|
}
|
|
2423
2495
|
} catch (error) {
|
|
2424
2496
|
if (isRoot) throw error;
|
|
@@ -2431,7 +2503,7 @@ function walkWorkerEntries(branchPath, start) {
|
|
|
2431
2503
|
if (!stat.isDirectory()) return;
|
|
2432
2504
|
let realDirectoryPath;
|
|
2433
2505
|
try {
|
|
2434
|
-
realDirectoryPath = import_node_fs.default.
|
|
2506
|
+
realDirectoryPath = await import_node_fs.default.promises.realpath(absolutePath);
|
|
2435
2507
|
} catch (error) {
|
|
2436
2508
|
if (isRoot) throw error;
|
|
2437
2509
|
return;
|
|
@@ -2440,25 +2512,25 @@ function walkWorkerEntries(branchPath, start) {
|
|
|
2440
2512
|
visitedDirectories.add(realDirectoryPath);
|
|
2441
2513
|
let children;
|
|
2442
2514
|
try {
|
|
2443
|
-
children = import_node_fs.default.
|
|
2515
|
+
children = (await import_node_fs.default.promises.readdir(absolutePath, { withFileTypes: true })).sort((a, b) => a.name.localeCompare(b.name));
|
|
2444
2516
|
} catch (error) {
|
|
2445
2517
|
if (isRoot) throw error;
|
|
2446
2518
|
return;
|
|
2447
2519
|
}
|
|
2448
2520
|
for (const child of children) {
|
|
2449
2521
|
if (IGNORED_ENTRY_NAMES.has(child.name)) continue;
|
|
2450
|
-
visit(import_node_path.default.join(absolutePath, child.name), false);
|
|
2522
|
+
await visit(import_node_path.default.join(absolutePath, child.name), false);
|
|
2451
2523
|
}
|
|
2452
2524
|
};
|
|
2453
|
-
visit(start.absolutePath, true);
|
|
2525
|
+
await visit(start.absolutePath, true);
|
|
2454
2526
|
return entries;
|
|
2455
2527
|
}
|
|
2456
2528
|
function isProbablyText(bytes) {
|
|
2457
2529
|
return !bytes.subarray(0, Math.min(bytes.length, 4096)).includes(0);
|
|
2458
2530
|
}
|
|
2459
|
-
function grepWorkerFiles(branchPath, input, builtInPaths) {
|
|
2531
|
+
async function grepWorkerFiles(branchPath, input, builtInPaths) {
|
|
2460
2532
|
const start = resolveWorkerFilePath(branchPath, input.path ?? ".", builtInPaths);
|
|
2461
|
-
if (!
|
|
2533
|
+
if (!await statIfExists(start.absolutePath)) {
|
|
2462
2534
|
throw new Error(`Path not found: ${start.displayPath}`);
|
|
2463
2535
|
}
|
|
2464
2536
|
const limit = normalizeToolLimit(input.limit, 100, 1e3);
|
|
@@ -2468,12 +2540,12 @@ function grepWorkerFiles(branchPath, input, builtInPaths) {
|
|
|
2468
2540
|
const lines = [];
|
|
2469
2541
|
let matchCount = 0;
|
|
2470
2542
|
let truncated = false;
|
|
2471
|
-
for (const entry of walkWorkerEntries(branchPath, start)) {
|
|
2543
|
+
for (const entry of await walkWorkerEntries(branchPath, start)) {
|
|
2472
2544
|
if (!entry.stat.isFile()) continue;
|
|
2473
2545
|
if (globRegex && !globRegex.test(entry.matchPath)) continue;
|
|
2474
2546
|
let bytes;
|
|
2475
2547
|
try {
|
|
2476
|
-
bytes = import_node_fs.default.
|
|
2548
|
+
bytes = await import_node_fs.default.promises.readFile(entry.absolutePath);
|
|
2477
2549
|
} catch {
|
|
2478
2550
|
continue;
|
|
2479
2551
|
}
|
|
@@ -2526,9 +2598,9 @@ async function executeGrepOperation(input) {
|
|
|
2526
2598
|
builtInPaths
|
|
2527
2599
|
);
|
|
2528
2600
|
}
|
|
2529
|
-
function findWorkerFiles(branchPath, input, builtInPaths) {
|
|
2601
|
+
async function findWorkerFiles(branchPath, input, builtInPaths) {
|
|
2530
2602
|
const start = resolveWorkerFilePath(branchPath, input.path ?? ".", builtInPaths);
|
|
2531
|
-
if (!
|
|
2603
|
+
if (!(await statIfExists(start.absolutePath))?.isDirectory()) {
|
|
2532
2604
|
throw new Error(`Directory not found: ${start.displayPath}`);
|
|
2533
2605
|
}
|
|
2534
2606
|
const pattern = normalizeFindPattern(input.pattern ?? "*");
|
|
@@ -2538,7 +2610,7 @@ function findWorkerFiles(branchPath, input, builtInPaths) {
|
|
|
2538
2610
|
const matches = [];
|
|
2539
2611
|
let count = 0;
|
|
2540
2612
|
let truncated = false;
|
|
2541
|
-
for (const entry of walkWorkerEntries(branchPath, start)) {
|
|
2613
|
+
for (const entry of await walkWorkerEntries(branchPath, start)) {
|
|
2542
2614
|
if (entry.absolutePath === start.absolutePath) continue;
|
|
2543
2615
|
const isDirectory = entry.stat.isDirectory();
|
|
2544
2616
|
if (entryType === "file" && !entry.stat.isFile()) continue;
|
|
@@ -2583,13 +2655,13 @@ async function executeFindOperation(input) {
|
|
|
2583
2655
|
builtInPaths
|
|
2584
2656
|
);
|
|
2585
2657
|
}
|
|
2586
|
-
function listWorkerDirectory(branchPath, inputPath = ".", inputLimit, builtInPaths) {
|
|
2658
|
+
async function listWorkerDirectory(branchPath, inputPath = ".", inputLimit, builtInPaths) {
|
|
2587
2659
|
const resolved = resolveWorkerFilePath(branchPath, inputPath, builtInPaths);
|
|
2588
|
-
if (!
|
|
2660
|
+
if (!(await statIfExists(resolved.absolutePath))?.isDirectory()) {
|
|
2589
2661
|
throw new Error(`Directory not found: ${resolved.displayPath}`);
|
|
2590
2662
|
}
|
|
2591
2663
|
const limit = normalizeToolLimit(inputLimit, 200, 1e3);
|
|
2592
|
-
const entries = import_node_fs.default.
|
|
2664
|
+
const entries = (await import_node_fs.default.promises.readdir(resolved.absolutePath, { withFileTypes: true })).filter((entry) => !IGNORED_ENTRY_NAMES.has(entry.name)).sort((a, b) => Number(b.isDirectory()) - Number(a.isDirectory()) || a.name.localeCompare(b.name));
|
|
2593
2665
|
const displayed = entries.slice(0, limit).map((entry) => `${entry.name}${entry.isDirectory() ? "/" : ""}`);
|
|
2594
2666
|
const truncated = entries.length > displayed.length;
|
|
2595
2667
|
const hint = truncated ? "\n... truncated. Increase limit to continue." : "";
|
|
@@ -2640,36 +2712,40 @@ function resolveWorkerCodePath(branchPath, inputPath) {
|
|
|
2640
2712
|
}
|
|
2641
2713
|
return { absolutePath, displayPath };
|
|
2642
2714
|
}
|
|
2643
|
-
function listWorkerCodeDirectory(branchPath, inputPath) {
|
|
2715
|
+
async function listWorkerCodeDirectory(branchPath, inputPath) {
|
|
2644
2716
|
const resolved = resolveWorkerCodePath(branchPath, inputPath);
|
|
2645
|
-
if (!import_node_fs.default.
|
|
2717
|
+
if (!(await import_node_fs.default.promises.stat(resolved.absolutePath)).isDirectory()) {
|
|
2646
2718
|
throw new Error(`Code directory not found: ${resolved.displayPath}`);
|
|
2647
2719
|
}
|
|
2648
|
-
const
|
|
2720
|
+
const listed = (await import_node_fs.default.promises.readdir(resolved.absolutePath, { withFileTypes: true })).filter(
|
|
2721
|
+
(entry) => entry.name.toLowerCase() !== ".git"
|
|
2722
|
+
);
|
|
2723
|
+
const entries = [];
|
|
2724
|
+
for (const entry of listed) {
|
|
2649
2725
|
const entryPath = import_node_path.default.posix.join(resolved.displayPath, entry.name);
|
|
2650
2726
|
try {
|
|
2651
2727
|
const resolvedEntry = resolveWorkerCodePath(branchPath, entryPath);
|
|
2652
|
-
const stats = import_node_fs.default.
|
|
2653
|
-
if (!stats.isFile() && !stats.isDirectory())
|
|
2654
|
-
|
|
2655
|
-
|
|
2656
|
-
|
|
2657
|
-
|
|
2658
|
-
|
|
2659
|
-
|
|
2660
|
-
}
|
|
2661
|
-
];
|
|
2728
|
+
const stats = await import_node_fs.default.promises.stat(resolvedEntry.absolutePath);
|
|
2729
|
+
if (!stats.isFile() && !stats.isDirectory()) continue;
|
|
2730
|
+
entries.push({
|
|
2731
|
+
name: entry.name,
|
|
2732
|
+
type: stats.isDirectory() ? "directory" : "file",
|
|
2733
|
+
size: stats.isDirectory() ? null : stats.size,
|
|
2734
|
+
mtime: stats.mtime.toISOString()
|
|
2735
|
+
});
|
|
2662
2736
|
} catch {
|
|
2663
|
-
return [];
|
|
2664
2737
|
}
|
|
2665
|
-
}
|
|
2738
|
+
}
|
|
2739
|
+
entries.sort(
|
|
2740
|
+
(left, right) => Number(right.type === "directory") - Number(left.type === "directory") || left.name.localeCompare(right.name)
|
|
2741
|
+
);
|
|
2666
2742
|
return { type: "code_list", path: resolved.displayPath, entries };
|
|
2667
2743
|
}
|
|
2668
|
-
function readWorkerCodeFile(branchPath, inputPath) {
|
|
2744
|
+
async function readWorkerCodeFile(branchPath, inputPath) {
|
|
2669
2745
|
const resolved = resolveWorkerCodePath(branchPath, inputPath);
|
|
2670
|
-
const stats = import_node_fs.default.
|
|
2746
|
+
const stats = await import_node_fs.default.promises.stat(resolved.absolutePath);
|
|
2671
2747
|
if (!stats.isFile()) throw new Error(`Code file not found: ${resolved.displayPath}`);
|
|
2672
|
-
const bytes = import_node_fs.default.
|
|
2748
|
+
const bytes = await import_node_fs.default.promises.readFile(resolved.absolutePath);
|
|
2673
2749
|
return {
|
|
2674
2750
|
type: "code_read",
|
|
2675
2751
|
path: resolved.displayPath,
|
|
@@ -2678,17 +2754,17 @@ function readWorkerCodeFile(branchPath, inputPath) {
|
|
|
2678
2754
|
base64: bytes.toString("base64")
|
|
2679
2755
|
};
|
|
2680
2756
|
}
|
|
2681
|
-
function executeCodeListOperation(input) {
|
|
2757
|
+
async function executeCodeListOperation(input) {
|
|
2682
2758
|
if (input.resolvedTarget.target.type !== "project") throw new Error("Code listing requires a project branch target");
|
|
2683
2759
|
return listWorkerCodeDirectory(input.resolvedTarget.rootPath, input.message.path);
|
|
2684
2760
|
}
|
|
2685
|
-
function executeCodeReadOperation(input) {
|
|
2761
|
+
async function executeCodeReadOperation(input) {
|
|
2686
2762
|
if (input.resolvedTarget.target.type !== "project") throw new Error("Code reading requires a project branch target");
|
|
2687
2763
|
return readWorkerCodeFile(input.resolvedTarget.rootPath, input.message.path);
|
|
2688
2764
|
}
|
|
2689
|
-
function readWorkerImageFile(branchPath, filePath, builtInPaths) {
|
|
2765
|
+
async function readWorkerImageFile(branchPath, filePath, builtInPaths) {
|
|
2690
2766
|
const resolved = resolveWorkerFilePath(branchPath, filePath, builtInPaths);
|
|
2691
|
-
if (!
|
|
2767
|
+
if (!(await statIfExists(resolved.absolutePath))?.isFile()) {
|
|
2692
2768
|
throw new Error(`File not found: ${resolved.displayPath}`);
|
|
2693
2769
|
}
|
|
2694
2770
|
const extension = import_node_path.default.extname(resolved.absolutePath).toLowerCase();
|
|
@@ -2696,7 +2772,7 @@ function readWorkerImageFile(branchPath, filePath, builtInPaths) {
|
|
|
2696
2772
|
if (!mediaType) {
|
|
2697
2773
|
throw new Error("read supports PNG and JPEG image inputs only");
|
|
2698
2774
|
}
|
|
2699
|
-
const bytes = import_node_fs.default.
|
|
2775
|
+
const bytes = await import_node_fs.default.promises.readFile(resolved.absolutePath);
|
|
2700
2776
|
const dimensions = readImageDimensions(bytes, mediaType);
|
|
2701
2777
|
return {
|
|
2702
2778
|
type: "view_file_bytes",
|
|
@@ -3023,26 +3099,28 @@ async function executeStreamingCommand(input) {
|
|
|
3023
3099
|
}
|
|
3024
3100
|
const [exitCode] = await Promise.all([
|
|
3025
3101
|
subprocess.exited,
|
|
3026
|
-
streamCommandOutput(
|
|
3027
|
-
|
|
3102
|
+
streamCommandOutput(
|
|
3103
|
+
subprocess.stdout,
|
|
3104
|
+
(data) => sendWorkerOutput(input.ws, {
|
|
3028
3105
|
type: "exec_output",
|
|
3029
3106
|
runId: input.message.runId,
|
|
3030
3107
|
stream: "stdout",
|
|
3031
3108
|
data
|
|
3032
|
-
})
|
|
3033
|
-
|
|
3034
|
-
streamCommandOutput(
|
|
3035
|
-
|
|
3109
|
+
})
|
|
3110
|
+
),
|
|
3111
|
+
streamCommandOutput(
|
|
3112
|
+
subprocess.stderr,
|
|
3113
|
+
(data) => sendWorkerOutput(input.ws, {
|
|
3036
3114
|
type: "exec_output",
|
|
3037
3115
|
runId: input.message.runId,
|
|
3038
3116
|
stream: "stderr",
|
|
3039
3117
|
data
|
|
3040
|
-
})
|
|
3041
|
-
|
|
3118
|
+
})
|
|
3119
|
+
)
|
|
3042
3120
|
]);
|
|
3043
3121
|
const diagnostic = await input.resources.diagnose();
|
|
3044
3122
|
if (diagnostic) {
|
|
3045
|
-
|
|
3123
|
+
await sendWorkerOutput(input.ws, {
|
|
3046
3124
|
type: "exec_output",
|
|
3047
3125
|
runId: input.message.runId,
|
|
3048
3126
|
stream: "stderr",
|
|
@@ -3114,6 +3192,39 @@ async function reapCompletedCredentialBearingProcessGroup(runId, subprocess) {
|
|
|
3114
3192
|
);
|
|
3115
3193
|
}
|
|
3116
3194
|
}
|
|
3195
|
+
async function cancelProcessRun(runId, scope) {
|
|
3196
|
+
const spawned = activeProcesses.get(runId);
|
|
3197
|
+
if (scope === "unstarted" && spawned) return { cancelled: false, message: `${spawned.command} already started`, outcome: "started" };
|
|
3198
|
+
if (!spawned) cancelledProcessRuns.add(runId);
|
|
3199
|
+
const revocation = workerCommandLauncher?.cancel(runId);
|
|
3200
|
+
let resourceCancellationError;
|
|
3201
|
+
let launchRevoked = false;
|
|
3202
|
+
try {
|
|
3203
|
+
launchRevoked = await revocation ?? false;
|
|
3204
|
+
} catch (error) {
|
|
3205
|
+
resourceCancellationError = error instanceof Error ? error.message : String(error);
|
|
3206
|
+
}
|
|
3207
|
+
let cancelled = resourceCancellationError === void 0;
|
|
3208
|
+
let cancelMessage;
|
|
3209
|
+
let outcome;
|
|
3210
|
+
if (spawned) {
|
|
3211
|
+
outcome = "stopped";
|
|
3212
|
+
try {
|
|
3213
|
+
closeProcessStdin(spawned);
|
|
3214
|
+
await (0, import_process_tree.terminateProcessTree)(spawned.process);
|
|
3215
|
+
cancelMessage = `Stopped ${spawned.command}`;
|
|
3216
|
+
} catch (error) {
|
|
3217
|
+
cancelled = false;
|
|
3218
|
+
cancelMessage = `Failed to stop ${spawned.command}: ${error instanceof Error ? error.message : String(error)}`;
|
|
3219
|
+
}
|
|
3220
|
+
} else {
|
|
3221
|
+
const reservationAborted = abortPendingReservation(`run:${runId}`, "Cancellation queued before command start");
|
|
3222
|
+
outcome = launchRevoked || reservationAborted ? "unstarted" : "unknown";
|
|
3223
|
+
cancelMessage = "Cancellation queued before command start";
|
|
3224
|
+
}
|
|
3225
|
+
if (resourceCancellationError) cancelMessage += `; job resource termination failed: ${resourceCancellationError}`;
|
|
3226
|
+
return { cancelled, message: cancelMessage, outcome };
|
|
3227
|
+
}
|
|
3117
3228
|
function closeProcessStdin(active) {
|
|
3118
3229
|
if (!active?.stdin) {
|
|
3119
3230
|
return;
|
|
@@ -3139,21 +3250,35 @@ function sendReplayWorkerMessage(ws, message) {
|
|
|
3139
3250
|
}
|
|
3140
3251
|
function sendWorkerMessage(ws, message) {
|
|
3141
3252
|
if (launcherFailureInProgress) return;
|
|
3142
|
-
if (recoveryStore && "requestId" in message && typeof message.requestId === "string" && recoveryStore.isUnknown(message.requestId))
|
|
3143
|
-
return;
|
|
3144
3253
|
const transportError = "error" in message && typeof message.error === "string" ? message.error : message.type === "workspace_sync_result" && message.result.outcome === "failed" ? message.result.error : void 0;
|
|
3145
|
-
if (
|
|
3146
|
-
|
|
3254
|
+
if (recoveryJournal && transportError && isWorkerCommunicationFailure(transportError) && "requestId" in message && typeof message.requestId === "string") {
|
|
3255
|
+
recoveryJournal.unknown(message.requestId);
|
|
3147
3256
|
currentWorkerSocket?.close(1012, "Communication recovery");
|
|
3148
3257
|
return;
|
|
3149
3258
|
}
|
|
3150
|
-
|
|
3151
|
-
|
|
3152
|
-
recoveryStore?.unknownRun(message.runId);
|
|
3259
|
+
if (!recoveryJournal) {
|
|
3260
|
+
sendReplayWorkerMessage(ws, message);
|
|
3153
3261
|
return;
|
|
3154
3262
|
}
|
|
3155
|
-
|
|
3156
|
-
|
|
3263
|
+
recoveryJournal.send(message);
|
|
3264
|
+
}
|
|
3265
|
+
function sendWorkerOutput(ws, frame) {
|
|
3266
|
+
if (launcherFailureInProgress) return Promise.resolve();
|
|
3267
|
+
if (!recoveryJournal) {
|
|
3268
|
+
sendReplayWorkerMessage(ws, frame);
|
|
3269
|
+
return Promise.resolve();
|
|
3270
|
+
}
|
|
3271
|
+
return recoveryJournal.sendOutput(frame);
|
|
3272
|
+
}
|
|
3273
|
+
function deliverJournaledWorkerMessage(message) {
|
|
3274
|
+
const target = currentWorkerSocket;
|
|
3275
|
+
if ((message.type === "exec_exit" || message.type === "exec_error") && typeof message.runId === "string" && infrastructureStoppedRunIds.has(message.runId)) {
|
|
3276
|
+
recoveryJournal?.unknownRun(message.runId);
|
|
3277
|
+
return true;
|
|
3278
|
+
}
|
|
3279
|
+
if (!target || target.readyState !== WebSocket.OPEN) return false;
|
|
3280
|
+
if (message.type === "exec_output" && workerRecoveryId !== void 0) return false;
|
|
3281
|
+
return sendReplayWorkerMessage(target, message);
|
|
3157
3282
|
}
|
|
3158
3283
|
function sendSerializedWorkerMessage(ws, serialized) {
|
|
3159
3284
|
sendWorkerMessage(ws, JSON.parse(serialized));
|
|
@@ -3716,9 +3841,19 @@ async function startWorker(options, projectRuntime = {
|
|
|
3716
3841
|
`);
|
|
3717
3842
|
}
|
|
3718
3843
|
const rootDir = import_node_path.default.resolve(options.rootDir ?? process.env.R5D_ROOT ?? defaultRootDir());
|
|
3719
|
-
|
|
3720
|
-
|
|
3721
|
-
|
|
3844
|
+
if (!recoveryJournal) {
|
|
3845
|
+
const journal = await import_recovery_journal.WorkerRecoveryJournal.open({
|
|
3846
|
+
filename: import_node_path.default.join(
|
|
3847
|
+
rootDir,
|
|
3848
|
+
".worker-recovery",
|
|
3849
|
+
(0, import_node_crypto.createHash)("sha256").update(`${baseUrl}\0${label}`).digest("hex"),
|
|
3850
|
+
"protocol.sqlite"
|
|
3851
|
+
)
|
|
3852
|
+
});
|
|
3853
|
+
journal.deliver = deliverJournaledWorkerMessage;
|
|
3854
|
+
journal.onFailure = handleRecoveryJournalFailure;
|
|
3855
|
+
recoveryJournal = journal;
|
|
3856
|
+
}
|
|
3722
3857
|
const projectsRoot = import_node_path.default.join(rootDir, "projects");
|
|
3723
3858
|
const syncRoot = import_node_path.default.join(rootDir, "sync");
|
|
3724
3859
|
const workspaceShadowRoot = import_node_path.default.join(syncRoot, "workspace");
|
|
@@ -3821,7 +3956,6 @@ async function startWorker(options, projectRuntime = {
|
|
|
3821
3956
|
const bearerAuthHeader = `Authorization: Bearer ${token}`;
|
|
3822
3957
|
const workerCredentialUsername = credentialUsernameForAuthHeader(bearerAuthHeader);
|
|
3823
3958
|
const projectBranchKey = (projectId, branchName) => `${projectId}\0${branchName}`;
|
|
3824
|
-
const projectMountId = (projectId, branchName) => `project:${projectId}:${encodeURIComponent(branchName)}`;
|
|
3825
3959
|
const projectPlanMountId = (projectId, branchName) => `plan:${projectId}:${encodeURIComponent(branchName)}`;
|
|
3826
3960
|
const workspaceProjectRelativePath = (projectId, branchName) => import_node_path.default.posix.join("projects", projectId, "branches", encodeURIComponent(branchName));
|
|
3827
3961
|
const workspacePlanRelativePath = (projectId, branchName) => import_node_path.default.posix.join("plans", projectId, encodeURIComponent(branchName));
|
|
@@ -3904,7 +4038,8 @@ async function startWorker(options, projectRuntime = {
|
|
|
3904
4038
|
sourcePath: configuredProjectBranchPath(projectsRoot, project, branchName),
|
|
3905
4039
|
planSourcePath: import_node_path.default.join(planRoot, project.projectId, ...branchName.split("/"))
|
|
3906
4040
|
}));
|
|
3907
|
-
(0, import_project_worktrees.removeProjectWorktrees)({ projectRoot, branchNames: branches.map(({ branchName }) => branchName) });
|
|
4041
|
+
const removed = (0, import_project_worktrees.removeProjectWorktrees)({ projectsRoot, projectRoot, branchNames: branches.map(({ branchName }) => branchName) });
|
|
4042
|
+
if (removed.stagedForCollectionPath) checkoutGarbageCollector.enqueue(removed.stagedForCollectionPath);
|
|
3908
4043
|
for (const branch of branches) {
|
|
3909
4044
|
import_node_fs.default.rmSync(branch.planSourcePath, { recursive: true, force: true });
|
|
3910
4045
|
const key = projectBranchKey(project.projectId, branch.branchName);
|
|
@@ -3992,14 +4127,14 @@ async function startWorker(options, projectRuntime = {
|
|
|
3992
4127
|
)
|
|
3993
4128
|
};
|
|
3994
4129
|
};
|
|
3995
|
-
const stageDurableWorkspaceDeletions = () => {
|
|
4130
|
+
const stageDurableWorkspaceDeletions = async () => {
|
|
3996
4131
|
for (const deletion of projectWorkspaceState.pendingTreeDeletions) {
|
|
3997
4132
|
const cleanupConfig = cleanupConfigForDurableDeletion(deletion);
|
|
3998
4133
|
if (deletion.kind === "project") {
|
|
3999
4134
|
if (!deletion.projectDeleted) {
|
|
4000
4135
|
const nextProject = projectConfigById.get(deletion.projectId);
|
|
4001
4136
|
if (!nextProject) throw new Error(`Checkout-path move lost desired project ${deletion.projectId}`);
|
|
4002
|
-
(0,
|
|
4137
|
+
await (0, import_workspace_filesystem_executor.workspaceFilesystemExecutor)().run("project_checkout_path_move", {
|
|
4003
4138
|
oldProjectRoot: deletion.managedPath,
|
|
4004
4139
|
newProjectRoot: configuredProjectRoot(projectsRoot, nextProject),
|
|
4005
4140
|
projectsDurabilityRoot: projectsRoot,
|
|
@@ -4366,7 +4501,7 @@ async function startWorker(options, projectRuntime = {
|
|
|
4366
4501
|
}
|
|
4367
4502
|
}
|
|
4368
4503
|
if (transitionReseedRequired) {
|
|
4369
|
-
projectWorkspaceState = projectWorkspaceStateStore.recordEnabledRepositoryTransition({
|
|
4504
|
+
projectWorkspaceState = await projectWorkspaceStateStore.recordEnabledRepositoryTransition({
|
|
4370
4505
|
projectId: project.projectId,
|
|
4371
4506
|
transitionId: project.repositoryTransitionId
|
|
4372
4507
|
});
|
|
@@ -4535,7 +4670,7 @@ async function startWorker(options, projectRuntime = {
|
|
|
4535
4670
|
)
|
|
4536
4671
|
];
|
|
4537
4672
|
if (publishedTombstoneIds.length > 0) {
|
|
4538
|
-
projectWorkspaceState = projectWorkspaceStateStore.recordTreePublication({
|
|
4673
|
+
projectWorkspaceState = await projectWorkspaceStateStore.recordTreePublication({
|
|
4539
4674
|
tombstoneIds: publishedTombstoneIds,
|
|
4540
4675
|
publishedHead
|
|
4541
4676
|
});
|
|
@@ -4554,7 +4689,7 @@ async function startWorker(options, projectRuntime = {
|
|
|
4554
4689
|
forgetLocalProjectMirrorRefsTokens(deletion.projectId, deletion.branchName);
|
|
4555
4690
|
}
|
|
4556
4691
|
if (deletion.tombstoneId && !deletion.projectDeleted) {
|
|
4557
|
-
projectWorkspaceState = projectWorkspaceStateStore.recordMirrorRefDeletion({
|
|
4692
|
+
projectWorkspaceState = await projectWorkspaceStateStore.recordMirrorRefDeletion({
|
|
4558
4693
|
tombstoneId: deletion.tombstoneId,
|
|
4559
4694
|
branchName: deletion.branchName
|
|
4560
4695
|
});
|
|
@@ -4751,7 +4886,10 @@ async function startWorker(options, projectRuntime = {
|
|
|
4751
4886
|
};
|
|
4752
4887
|
};
|
|
4753
4888
|
const workspaceHydrationTransactionHooks = createWorkspaceHydrationLeaseHooks(
|
|
4754
|
-
(message) => sendWorkerMessageFromCurrentSource(ws, message)
|
|
4889
|
+
(message) => sendWorkerMessageFromCurrentSource(ws, message),
|
|
4890
|
+
{
|
|
4891
|
+
fence: workspaceMountHoldFence
|
|
4892
|
+
}
|
|
4755
4893
|
);
|
|
4756
4894
|
const performWorkspaceSync = async (input) => {
|
|
4757
4895
|
if (!workspaceRemoteUrl || !workspaceCredentialHelper || !workspaceCredentialUsername || !workspaceGitIdentity) {
|
|
@@ -5409,7 +5547,7 @@ async function startWorker(options, projectRuntime = {
|
|
|
5409
5547
|
(project) => project.preserveOnlyBranches.map(({ branchId, branchName }) => ({ branchId, projectId: project.projectId, branchName }))
|
|
5410
5548
|
);
|
|
5411
5549
|
if (!incidentDeferral.incidentId) {
|
|
5412
|
-
projectWorkspaceState = projectWorkspaceStateStore.reconcile({
|
|
5550
|
+
projectWorkspaceState = await projectWorkspaceStateStore.reconcile({
|
|
5413
5551
|
desiredProjects: message.projects,
|
|
5414
5552
|
preserveOnlyBranches
|
|
5415
5553
|
});
|
|
@@ -5420,7 +5558,7 @@ async function startWorker(options, projectRuntime = {
|
|
|
5420
5558
|
(candidate) => candidate.projectId === project.projectId && candidate.branchName === branch.branchName
|
|
5421
5559
|
);
|
|
5422
5560
|
if (pending2 && pending2.branchId === branch.branchId) {
|
|
5423
|
-
projectWorkspaceState = projectWorkspaceStateStore.clearPendingCreatedBranch(pending2);
|
|
5561
|
+
projectWorkspaceState = await projectWorkspaceStateStore.clearPendingCreatedBranch(pending2);
|
|
5424
5562
|
}
|
|
5425
5563
|
}
|
|
5426
5564
|
}
|
|
@@ -5588,7 +5726,7 @@ async function startWorker(options, projectRuntime = {
|
|
|
5588
5726
|
for (const projectId of [...readyProjectIds]) {
|
|
5589
5727
|
if (!projectConfigById.has(projectId)) readyProjectIds.delete(projectId);
|
|
5590
5728
|
}
|
|
5591
|
-
stageDurableWorkspaceDeletions();
|
|
5729
|
+
await stageDurableWorkspaceDeletions();
|
|
5592
5730
|
const mountsBeforeGit = buildWorkspaceMounts();
|
|
5593
5731
|
await (0, import_workspace_git_sync.hydrateWorkspaceGitMounts)(
|
|
5594
5732
|
workspaceShadowRoot,
|
|
@@ -5634,7 +5772,12 @@ async function startWorker(options, projectRuntime = {
|
|
|
5634
5772
|
const connectSyncMs = Date.now() - connectSyncStartedAtMs;
|
|
5635
5773
|
const result = syncResult.telemetry ? syncResult : {
|
|
5636
5774
|
...syncResult,
|
|
5637
|
-
telemetry: {
|
|
5775
|
+
telemetry: {
|
|
5776
|
+
totalMs: connectSyncMs,
|
|
5777
|
+
queueMs: 0,
|
|
5778
|
+
prepareMs: lastWorkspaceSyncMirrorObservationMs,
|
|
5779
|
+
synchronizeMs: connectSyncMs
|
|
5780
|
+
}
|
|
5638
5781
|
};
|
|
5639
5782
|
assertConfigurationSyncStillAdmitted();
|
|
5640
5783
|
if (receiptGeneration !== workspaceConfigurationReceiptGeneration) {
|
|
@@ -5653,7 +5796,7 @@ async function startWorker(options, projectRuntime = {
|
|
|
5653
5796
|
) ? [deletion.tombstoneId] : []
|
|
5654
5797
|
);
|
|
5655
5798
|
if (layoutCleanupIds.length > 0) {
|
|
5656
|
-
projectWorkspaceState = projectWorkspaceStateStore.recordTreePublication({
|
|
5799
|
+
projectWorkspaceState = await projectWorkspaceStateStore.recordTreePublication({
|
|
5657
5800
|
tombstoneIds: layoutCleanupIds,
|
|
5658
5801
|
publishedHead
|
|
5659
5802
|
});
|
|
@@ -5760,26 +5903,33 @@ async function startWorker(options, projectRuntime = {
|
|
|
5760
5903
|
if (startupProjectSnapshotRecoveryCompleted) return;
|
|
5761
5904
|
sendLifecycleProgress({ phase: "recovering", operationId, detail: "Checking interrupted workspace transactions" });
|
|
5762
5905
|
const snapshotRecovery = await workspaceSyncSingleFlight.runExclusive(
|
|
5763
|
-
async () => await (0,
|
|
5764
|
-
|
|
5765
|
-
|
|
5766
|
-
|
|
5767
|
-
|
|
5768
|
-
|
|
5769
|
-
|
|
5770
|
-
|
|
5771
|
-
|
|
5772
|
-
|
|
5773
|
-
|
|
5774
|
-
|
|
5775
|
-
|
|
5776
|
-
|
|
5777
|
-
|
|
5778
|
-
|
|
5906
|
+
async () => await (0, import_workspace_filesystem_executor.workspaceFilesystemExecutor)().run(
|
|
5907
|
+
"project_snapshots_recover",
|
|
5908
|
+
{
|
|
5909
|
+
projectsRoot,
|
|
5910
|
+
temporaryRoot: projectWorktreeSnapshotsRoot,
|
|
5911
|
+
currentProcessId: process.pid,
|
|
5912
|
+
currentOwnerSessionId: import_project_worktrees.PROJECT_WORKTREE_SNAPSHOT_OWNER_SESSION_ID
|
|
5913
|
+
},
|
|
5914
|
+
{
|
|
5915
|
+
onProgress: (progress) => {
|
|
5916
|
+
const lifecycleProgress = {
|
|
5917
|
+
phase: "recovering",
|
|
5918
|
+
operationId,
|
|
5919
|
+
detail: describeSnapshotProgress(progress),
|
|
5920
|
+
completedBytes: progress.completedBytes,
|
|
5921
|
+
completedItems: progress.completedBranches,
|
|
5922
|
+
totalItems: progress.totalBranches,
|
|
5923
|
+
...progress.totalBytes === void 0 ? {} : { totalBytes: progress.totalBytes }
|
|
5924
|
+
};
|
|
5925
|
+
sendLifecycleProgress(lifecycleProgress);
|
|
5926
|
+
process.stdout.write(
|
|
5927
|
+
`[r5d-worker] ${lifecycleProgress.detail}: ${progress.completedBranches}/${progress.totalBranches} branch(es), ${progress.completedBytes}/${progress.totalBytes ?? "?"} bytes
|
|
5779
5928
|
`
|
|
5780
|
-
|
|
5929
|
+
);
|
|
5930
|
+
}
|
|
5781
5931
|
}
|
|
5782
|
-
|
|
5932
|
+
)
|
|
5783
5933
|
);
|
|
5784
5934
|
if (snapshotRecovery.restored.length > 0) {
|
|
5785
5935
|
process.stdout.write(`[r5d-worker] restored ${snapshotRecovery.restored.length} interrupted project snapshot(s)
|
|
@@ -5874,7 +6024,7 @@ async function startWorker(options, projectRuntime = {
|
|
|
5874
6024
|
type: "hello",
|
|
5875
6025
|
resumableProtocol: import_recovery_protocol.WORKER_RESUMABLE_PROTOCOL,
|
|
5876
6026
|
runtimeId: workerRuntimeId,
|
|
5877
|
-
ledgerId:
|
|
6027
|
+
ledgerId: recoveryJournal.ledgerId,
|
|
5878
6028
|
startupReady: startupRecoveryComplete,
|
|
5879
6029
|
hostInfo: {
|
|
5880
6030
|
hostname: (0, import_node_os.hostname)(),
|
|
@@ -5902,16 +6052,39 @@ async function startWorker(options, projectRuntime = {
|
|
|
5902
6052
|
}
|
|
5903
6053
|
};
|
|
5904
6054
|
sendSerializedWorkerMessage(ws, JSON.stringify(hello));
|
|
6055
|
+
let outputPumpInFlight = false;
|
|
5905
6056
|
outputReplayTimer = setInterval(() => {
|
|
5906
|
-
if (workerRecoveryId !== void 0 || currentWorkerSocket !== ws) return;
|
|
5907
|
-
|
|
5908
|
-
|
|
5909
|
-
|
|
6057
|
+
if (workerRecoveryId !== void 0 || currentWorkerSocket !== ws || outputPumpInFlight || !recoveryJournal) return;
|
|
6058
|
+
outputPumpInFlight = true;
|
|
6059
|
+
void (async () => {
|
|
6060
|
+
try {
|
|
6061
|
+
for await (const page of recoveryJournal.outputReplay(void 0)) {
|
|
6062
|
+
for (const output of page) {
|
|
6063
|
+
if (workerRecoveryId !== void 0 || currentWorkerSocket !== ws || !sendReplayWorkerMessage(ws, output)) return;
|
|
6064
|
+
}
|
|
6065
|
+
}
|
|
6066
|
+
} catch (error) {
|
|
6067
|
+
process.stderr.write(`[r5d-worker] output replay pump failed: ${error instanceof Error ? error.message : String(error)}
|
|
6068
|
+
`);
|
|
6069
|
+
} finally {
|
|
6070
|
+
outputPumpInFlight = false;
|
|
6071
|
+
}
|
|
6072
|
+
})();
|
|
5910
6073
|
}, 250);
|
|
5911
6074
|
outputReplayTimer.unref();
|
|
6075
|
+
let terminalPumpInFlight = false;
|
|
5912
6076
|
terminalReplayTimer = setInterval(() => {
|
|
5913
|
-
if (workerRecoveryId !== void 0) return;
|
|
5914
|
-
|
|
6077
|
+
if (workerRecoveryId !== void 0 || terminalPumpInFlight || !recoveryJournal) return;
|
|
6078
|
+
terminalPumpInFlight = true;
|
|
6079
|
+
void recoveryJournal.terminals().then((terminals) => {
|
|
6080
|
+
if (workerRecoveryId !== void 0 || currentWorkerSocket !== ws) return;
|
|
6081
|
+
for (const terminal of terminals) sendReplayWorkerMessage(ws, terminal);
|
|
6082
|
+
}).catch((error) => {
|
|
6083
|
+
process.stderr.write(`[r5d-worker] terminal replay pump failed: ${error instanceof Error ? error.message : String(error)}
|
|
6084
|
+
`);
|
|
6085
|
+
}).finally(() => {
|
|
6086
|
+
terminalPumpInFlight = false;
|
|
6087
|
+
});
|
|
5915
6088
|
}, 5e3);
|
|
5916
6089
|
terminalReplayTimer.unref();
|
|
5917
6090
|
process.stdout.write("[r5d-worker] connected\n");
|
|
@@ -5924,28 +6097,37 @@ async function startWorker(options, projectRuntime = {
|
|
|
5924
6097
|
if (message.type === "recovery_request") {
|
|
5925
6098
|
workerRecoveryId = message.recoveryId;
|
|
5926
6099
|
sendActiveProcessReport(ws);
|
|
5927
|
-
|
|
5928
|
-
|
|
5929
|
-
|
|
5930
|
-
|
|
5931
|
-
|
|
5932
|
-
|
|
5933
|
-
|
|
5934
|
-
|
|
5935
|
-
|
|
5936
|
-
|
|
5937
|
-
|
|
5938
|
-
|
|
5939
|
-
|
|
5940
|
-
|
|
5941
|
-
|
|
5942
|
-
|
|
5943
|
-
|
|
6100
|
+
recoveryJournal.pauseDelivery();
|
|
6101
|
+
try {
|
|
6102
|
+
const state = await recoveryJournal.recoveryState(message.requests);
|
|
6103
|
+
if (currentWorkerSocket !== ws || workerRecoveryId !== message.recoveryId) return;
|
|
6104
|
+
sendWorkerMessage(ws, {
|
|
6105
|
+
type: "recovery_state",
|
|
6106
|
+
recoveryId: message.recoveryId,
|
|
6107
|
+
runtimeId: workerRuntimeId,
|
|
6108
|
+
ledgerId: recoveryJournal.ledgerId,
|
|
6109
|
+
operations: state.operations
|
|
6110
|
+
});
|
|
6111
|
+
const cursors = new Map(state.cursors.map((cursor) => [cursor.runId, cursor]));
|
|
6112
|
+
for (const cursor of message.outputOffsets) cursors.set(cursor.runId, cursor);
|
|
6113
|
+
workerOutputWindow.reset([...cursors.values()]);
|
|
6114
|
+
for (const response of state.responses) sendReplayWorkerMessage(ws, response);
|
|
6115
|
+
for await (const page of recoveryJournal.outputReplay(message.outputOffsets)) {
|
|
6116
|
+
for (const output of page) {
|
|
6117
|
+
while (true) {
|
|
6118
|
+
if (currentWorkerSocket !== ws || workerRecoveryId !== message.recoveryId || ws.readyState !== WebSocket.OPEN || workerLeaseExpired)
|
|
6119
|
+
return;
|
|
6120
|
+
if (sendReplayWorkerMessage(ws, output)) break;
|
|
6121
|
+
await Bun.sleep(10);
|
|
6122
|
+
}
|
|
6123
|
+
}
|
|
5944
6124
|
}
|
|
6125
|
+
if (currentWorkerSocket !== ws || workerRecoveryId !== message.recoveryId) return;
|
|
6126
|
+
for (const terminal of state.terminals) sendReplayWorkerMessage(ws, terminal);
|
|
6127
|
+
sendWorkerMessage(ws, { type: "recovery_complete", recoveryId: message.recoveryId });
|
|
6128
|
+
} finally {
|
|
6129
|
+
recoveryJournal.resumeDelivery();
|
|
5945
6130
|
}
|
|
5946
|
-
if (currentWorkerSocket !== ws || workerRecoveryId !== message.recoveryId) return;
|
|
5947
|
-
for (const terminal of recoveryStore.terminals()) sendReplayWorkerMessage(ws, terminal);
|
|
5948
|
-
sendWorkerMessage(ws, { type: "recovery_complete", recoveryId: message.recoveryId });
|
|
5949
6131
|
return;
|
|
5950
6132
|
}
|
|
5951
6133
|
if (message.type === "recovery_ready") {
|
|
@@ -5960,17 +6142,23 @@ async function startWorker(options, projectRuntime = {
|
|
|
5960
6142
|
return;
|
|
5961
6143
|
}
|
|
5962
6144
|
if (message.type === "operation_result_ack") {
|
|
5963
|
-
|
|
6145
|
+
recoveryJournal.acknowledgeResult(message.requestId);
|
|
5964
6146
|
return;
|
|
5965
6147
|
}
|
|
5966
6148
|
if (message.type === "exec_output_ack") {
|
|
5967
|
-
|
|
6149
|
+
recoveryJournal.acknowledgeOutput(message.runId, message.stream, message.nextOffset);
|
|
5968
6150
|
workerOutputWindow.acknowledge(message.runId, message.stream, message.nextOffset);
|
|
5969
6151
|
return;
|
|
5970
6152
|
}
|
|
5971
6153
|
if (message.type === "cancel_session") {
|
|
5972
|
-
|
|
6154
|
+
const durableCancellation = recoveryJournal.cancelSession(message.sessionId);
|
|
6155
|
+
abortPendingReservations("Session cancelled before command start", message.sessionId);
|
|
5973
6156
|
await Promise.all([
|
|
6157
|
+
durableCancellation,
|
|
6158
|
+
// File writes already in flight for the session settle before the
|
|
6159
|
+
// acknowledgement; queued ones re-check the cancellation before
|
|
6160
|
+
// their first write and never start.
|
|
6161
|
+
sessionFileMutations.settled(message.sessionId),
|
|
5974
6162
|
workerCommandLauncher?.cancelSession(message.sessionId),
|
|
5975
6163
|
...[...activeProcesses].filter(([, active]) => active.sessionId === message.sessionId).map(async ([runId, active]) => {
|
|
5976
6164
|
cancelledProcessRuns.add(runId);
|
|
@@ -5985,27 +6173,75 @@ async function startWorker(options, projectRuntime = {
|
|
|
5985
6173
|
if (!startupRecoveryComplete && message.type !== "ping") return;
|
|
5986
6174
|
if (currentWorkerSocket !== ws) return;
|
|
5987
6175
|
const operationRequestId = "requestId" in message && typeof message.requestId === "string" && message.type !== "workspace_config" ? message.requestId : void 0;
|
|
6176
|
+
if (message.type === "cancel") {
|
|
6177
|
+
const admission = recoveryJournal.admit({ ...message, requestId: message.requestId });
|
|
6178
|
+
const cancelled = await cancelProcessRun(message.runId, message.scope);
|
|
6179
|
+
let admitted;
|
|
6180
|
+
try {
|
|
6181
|
+
admitted = await admission;
|
|
6182
|
+
} catch (error) {
|
|
6183
|
+
if (!(error instanceof import_recovery_journal.WorkerRecoveryJournalBusyError)) throw error;
|
|
6184
|
+
process.stderr.write(`[r5d-worker] cancel ${message.runId} stopped the run but was not durably admitted: ${error.message}
|
|
6185
|
+
`);
|
|
6186
|
+
return;
|
|
6187
|
+
}
|
|
6188
|
+
if (admitted.admission === "conflict") {
|
|
6189
|
+
ws.close(1008, "Operation identity conflict");
|
|
6190
|
+
return;
|
|
6191
|
+
}
|
|
6192
|
+
sendWorkerMessage(ws, { type: "operation_received", requestId: message.requestId });
|
|
6193
|
+
if (admitted.admission !== "new") {
|
|
6194
|
+
if (admitted.response && admitted.admission !== "unknown") sendReplayWorkerMessage(ws, admitted.response);
|
|
6195
|
+
return;
|
|
6196
|
+
}
|
|
6197
|
+
sendSerializedWorkerMessage(
|
|
6198
|
+
ws,
|
|
6199
|
+
JSON.stringify({
|
|
6200
|
+
type: "cancel_result",
|
|
6201
|
+
requestId: message.requestId,
|
|
6202
|
+
runId: message.runId,
|
|
6203
|
+
cancelled: cancelled.cancelled,
|
|
6204
|
+
message: cancelled.message,
|
|
6205
|
+
outcome: cancelled.outcome
|
|
6206
|
+
})
|
|
6207
|
+
);
|
|
6208
|
+
return;
|
|
6209
|
+
}
|
|
5988
6210
|
if (operationRequestId) {
|
|
5989
|
-
|
|
5990
|
-
|
|
6211
|
+
let admitted;
|
|
6212
|
+
try {
|
|
6213
|
+
admitted = await recoveryJournal.admit({ ...message, requestId: operationRequestId });
|
|
6214
|
+
} catch (error) {
|
|
6215
|
+
if (!(error instanceof import_recovery_journal.WorkerRecoveryJournalBusyError)) throw error;
|
|
6216
|
+
process.stderr.write(`[r5d-worker] ${message.type} ${operationRequestId} was not admitted: ${error.message}
|
|
6217
|
+
`);
|
|
6218
|
+
return;
|
|
6219
|
+
}
|
|
6220
|
+
if (admitted.admission === "conflict") {
|
|
5991
6221
|
ws.close(1008, "Operation identity conflict");
|
|
5992
6222
|
return;
|
|
5993
6223
|
}
|
|
5994
6224
|
sendWorkerMessage(ws, { type: "operation_received", requestId: operationRequestId });
|
|
5995
|
-
if (admission !== "new") {
|
|
5996
|
-
|
|
5997
|
-
if (
|
|
5998
|
-
|
|
6225
|
+
if (admitted.admission !== "new") {
|
|
6226
|
+
if (admitted.response && admitted.admission !== "unknown") sendReplayWorkerMessage(ws, admitted.response);
|
|
6227
|
+
if (!(admitted.admission === "unknown" && message.type === "delete_project_branch" && await recoveryJournal.readmitUnknownBranchDeletion(
|
|
6228
|
+
operationRequestId,
|
|
6229
|
+
"sessionId" in message && typeof message.sessionId === "string" ? message.sessionId : void 0
|
|
6230
|
+
))) {
|
|
5999
6231
|
return;
|
|
6000
6232
|
}
|
|
6001
6233
|
}
|
|
6234
|
+
if (recoveryJournal.isRequestCancelled(operationRequestId)) {
|
|
6235
|
+
recoveryJournal.unknown(operationRequestId);
|
|
6236
|
+
return;
|
|
6237
|
+
}
|
|
6002
6238
|
}
|
|
6003
6239
|
const messageAdmissionGeneration = workerAdmissionGeneration;
|
|
6004
6240
|
const admissionCredentials = configuredCredentialGenerationFingerprint;
|
|
6005
6241
|
const assertMessageAdmission = () => {
|
|
6006
6242
|
if (operationRequestId) {
|
|
6007
|
-
if (workerLeaseExpired ||
|
|
6008
|
-
|
|
6243
|
+
if (workerLeaseExpired || recoveryJournal.isRequestCancelled(operationRequestId) || admissionCredentials !== configuredCredentialGenerationFingerprint || shutdownAfterClose || !workspaceConfigured) {
|
|
6244
|
+
recoveryJournal.unknown(operationRequestId);
|
|
6009
6245
|
throw new StaleWorkerAdmissionError();
|
|
6010
6246
|
}
|
|
6011
6247
|
return;
|
|
@@ -6142,7 +6378,7 @@ async function startWorker(options, projectRuntime = {
|
|
|
6142
6378
|
}
|
|
6143
6379
|
recoveryDeadline ??= Date.now() + import_recovery_protocol.WORKER_RECOVERY_TIMEOUT_MS;
|
|
6144
6380
|
if (workerLeaseExpired || Date.now() >= recoveryDeadline) {
|
|
6145
|
-
|
|
6381
|
+
recoveryJournal.unknown(message.requestId);
|
|
6146
6382
|
return;
|
|
6147
6383
|
}
|
|
6148
6384
|
currentWorkerSocket?.close(1012, "Communication recovery");
|
|
@@ -6172,7 +6408,7 @@ async function startWorker(options, projectRuntime = {
|
|
|
6172
6408
|
projectId: message.projectId,
|
|
6173
6409
|
branchName: message.targetBranch
|
|
6174
6410
|
};
|
|
6175
|
-
const created = await workspaceSyncSingleFlight.runBranchMutation(() => {
|
|
6411
|
+
const created = await workspaceSyncSingleFlight.runBranchMutation(async () => {
|
|
6176
6412
|
if (workspaceOperationsAreFenced(deferredWorkspaceConfiguration, activeWorkspaceIncidentId)) {
|
|
6177
6413
|
throw new Error(
|
|
6178
6414
|
`Project branch creation was fenced while queued by workspace incident ${deferredWorkspaceConfiguration?.incidentId ?? activeWorkspaceIncidentId}`
|
|
@@ -6187,7 +6423,7 @@ async function startWorker(options, projectRuntime = {
|
|
|
6187
6423
|
const pendingWasRecorded = projectWorkspaceState.locallyPendingCreatedBranches.some(
|
|
6188
6424
|
({ projectId, branchName }) => projectId === message.projectId && branchName === message.targetBranch
|
|
6189
6425
|
);
|
|
6190
|
-
projectWorkspaceState = projectWorkspaceStateStore.recordPendingCreatedBranch(pendingBranch);
|
|
6426
|
+
projectWorkspaceState = await projectWorkspaceStateStore.recordPendingCreatedBranch(pendingBranch);
|
|
6191
6427
|
let gitBranchCreated = false;
|
|
6192
6428
|
try {
|
|
6193
6429
|
if (!readyProjectIds.has(project.projectId)) {
|
|
@@ -6208,7 +6444,7 @@ async function startWorker(options, projectRuntime = {
|
|
|
6208
6444
|
}
|
|
6209
6445
|
return { branchPath: existingPath, baseCommitHash: existingBranch.baseCommitHash };
|
|
6210
6446
|
}
|
|
6211
|
-
const createdBranch = (0, import_project_worktrees.createOrRetryLinkedProjectBranch)({
|
|
6447
|
+
const createdBranch = await (0, import_project_worktrees.createOrRetryLinkedProjectBranch)({
|
|
6212
6448
|
projectRoot: configuredProjectRoot(projectsRoot, project),
|
|
6213
6449
|
primaryBranchName: primaryProjectBranch(project),
|
|
6214
6450
|
sourceBranchName: message.sourceBranch,
|
|
@@ -6233,7 +6469,7 @@ async function startWorker(options, projectRuntime = {
|
|
|
6233
6469
|
return createdBranch;
|
|
6234
6470
|
} catch (error) {
|
|
6235
6471
|
if (!pendingWasRecorded && !gitBranchCreated && !(0, import_project_worktrees.projectBranchMayExistAfterCreateFailure)(error)) {
|
|
6236
|
-
projectWorkspaceState = projectWorkspaceStateStore.rollbackPendingCreatedBranch(pendingBranch);
|
|
6472
|
+
projectWorkspaceState = await projectWorkspaceStateStore.rollbackPendingCreatedBranch(pendingBranch);
|
|
6237
6473
|
}
|
|
6238
6474
|
throw error;
|
|
6239
6475
|
}
|
|
@@ -6277,7 +6513,7 @@ async function startWorker(options, projectRuntime = {
|
|
|
6277
6513
|
});
|
|
6278
6514
|
try {
|
|
6279
6515
|
const deletionKey = projectBranchKey(message.projectId, message.branchName);
|
|
6280
|
-
const deletionNeedsSync = await workspaceSyncSingleFlight.runBranchMutation(() => {
|
|
6516
|
+
const deletionNeedsSync = await workspaceSyncSingleFlight.runBranchMutation(async () => {
|
|
6281
6517
|
if (workspaceOperationsAreFenced(deferredWorkspaceConfiguration, activeWorkspaceIncidentId)) {
|
|
6282
6518
|
throw new Error(
|
|
6283
6519
|
`Project branch deletion was fenced while queued by workspace incident ${deferredWorkspaceConfiguration?.incidentId ?? activeWorkspaceIncidentId}`
|
|
@@ -6304,12 +6540,12 @@ async function startWorker(options, projectRuntime = {
|
|
|
6304
6540
|
if (!configuredBranch && !pendingLocalBranch && !hasDurableTombstone && !pendingMirrorDeletes.has(deletionKey)) {
|
|
6305
6541
|
return false;
|
|
6306
6542
|
}
|
|
6307
|
-
projectWorkspaceState = projectWorkspaceStateStore.beginBranchDeletion({
|
|
6543
|
+
projectWorkspaceState = await projectWorkspaceStateStore.beginBranchDeletion({
|
|
6308
6544
|
projectId: message.projectId,
|
|
6309
6545
|
branchName: message.branchName
|
|
6310
6546
|
});
|
|
6311
6547
|
if (pendingLocalBranch) {
|
|
6312
|
-
projectWorkspaceState = projectWorkspaceStateStore.clearPendingCreatedBranch(pendingLocalBranch);
|
|
6548
|
+
projectWorkspaceState = await projectWorkspaceStateStore.clearPendingCreatedBranch(pendingLocalBranch);
|
|
6313
6549
|
}
|
|
6314
6550
|
pendingCreatedBranchPublicationNotBefore.delete((0, import_workspace_automatic_sync_policy.pendingCreatedBranchKey)(message.projectId, message.branchName));
|
|
6315
6551
|
if (pendingMirrorDeletes.has(deletionKey)) return true;
|
|
@@ -6413,7 +6649,7 @@ async function startWorker(options, projectRuntime = {
|
|
|
6413
6649
|
}
|
|
6414
6650
|
if (message.type === "exec_terminal_ack") {
|
|
6415
6651
|
pendingProcessTerminals.delete(message.runId);
|
|
6416
|
-
|
|
6652
|
+
recoveryJournal.acknowledgeTerminal(message.runId);
|
|
6417
6653
|
return;
|
|
6418
6654
|
}
|
|
6419
6655
|
if (message.type === "port_forward_connect") {
|
|
@@ -6468,42 +6704,6 @@ async function startWorker(options, projectRuntime = {
|
|
|
6468
6704
|
sendSerializedWorkerMessage(ws, JSON.stringify({ type: "pong" }));
|
|
6469
6705
|
return;
|
|
6470
6706
|
}
|
|
6471
|
-
if (message.type === "cancel") {
|
|
6472
|
-
let resourceCancellationError;
|
|
6473
|
-
try {
|
|
6474
|
-
await workerCommandLauncher?.cancel(message.runId);
|
|
6475
|
-
} catch (error) {
|
|
6476
|
-
resourceCancellationError = error instanceof Error ? error.message : String(error);
|
|
6477
|
-
}
|
|
6478
|
-
const active = activeProcesses.get(message.runId);
|
|
6479
|
-
let cancelled = resourceCancellationError === void 0;
|
|
6480
|
-
let cancelMessage;
|
|
6481
|
-
if (active) {
|
|
6482
|
-
try {
|
|
6483
|
-
closeProcessStdin(active);
|
|
6484
|
-
await (0, import_process_tree.terminateProcessTree)(active.process);
|
|
6485
|
-
cancelMessage = `Stopped ${active.command}`;
|
|
6486
|
-
} catch (error) {
|
|
6487
|
-
cancelled = false;
|
|
6488
|
-
cancelMessage = `Failed to stop ${active.command}: ${error instanceof Error ? error.message : String(error)}`;
|
|
6489
|
-
}
|
|
6490
|
-
} else {
|
|
6491
|
-
cancelledProcessRuns.add(message.runId);
|
|
6492
|
-
cancelMessage = "Cancellation queued before command start";
|
|
6493
|
-
}
|
|
6494
|
-
if (resourceCancellationError) cancelMessage += `; job resource termination failed: ${resourceCancellationError}`;
|
|
6495
|
-
sendSerializedWorkerMessage(
|
|
6496
|
-
ws,
|
|
6497
|
-
JSON.stringify({
|
|
6498
|
-
type: "cancel_result",
|
|
6499
|
-
requestId: message.requestId,
|
|
6500
|
-
runId: message.runId,
|
|
6501
|
-
cancelled,
|
|
6502
|
-
message: cancelMessage
|
|
6503
|
-
})
|
|
6504
|
-
);
|
|
6505
|
-
return;
|
|
6506
|
-
}
|
|
6507
6707
|
if (message.type === "exec_stdin") {
|
|
6508
6708
|
const sendAck = (payload) => sendSerializedWorkerMessage(
|
|
6509
6709
|
ws,
|
|
@@ -6584,10 +6784,20 @@ async function startWorker(options, projectRuntime = {
|
|
|
6584
6784
|
let mutationLeaseTransferred = false;
|
|
6585
6785
|
try {
|
|
6586
6786
|
resources = await acquireWorkerCommandLaunch(ws, message, assertMessageAdmission);
|
|
6587
|
-
|
|
6588
|
-
|
|
6589
|
-
|
|
6590
|
-
|
|
6787
|
+
const reservationAbort = registerPendingReservation(`pty:${message.ptyId}`, void 0);
|
|
6788
|
+
try {
|
|
6789
|
+
await (0, import_workspace_command_sync_policy.reserveWorkspaceCommandAfterCurrentSync)(
|
|
6790
|
+
message.target,
|
|
6791
|
+
workspaceSyncSingleFlight,
|
|
6792
|
+
() => {
|
|
6793
|
+
workspaceSyncPriorityPtyTargets.set(message.ptyId, message.target);
|
|
6794
|
+
if (targetMayMutateVisibleWorkspace(message.target)) recordVisibleWorkspaceMutation(message.target);
|
|
6795
|
+
},
|
|
6796
|
+
{ signal: reservationAbort.signal }
|
|
6797
|
+
);
|
|
6798
|
+
} finally {
|
|
6799
|
+
unregisterPendingReservation(`pty:${message.ptyId}`, reservationAbort);
|
|
6800
|
+
}
|
|
6591
6801
|
releaseWorkspaceMutation = await (0, import_workspace_command_sync_policy.acquireWorkspaceCommandMutation)(message.target, workspaceSyncSingleFlight);
|
|
6592
6802
|
if (workspaceOperationsAreFenced(deferredWorkspaceConfiguration, activeWorkspaceIncidentId)) {
|
|
6593
6803
|
throw new Error(
|
|
@@ -6652,6 +6862,7 @@ async function startWorker(options, projectRuntime = {
|
|
|
6652
6862
|
}
|
|
6653
6863
|
if (message.type === "pty_close") {
|
|
6654
6864
|
const activePty = activePtys.get(message.ptyId);
|
|
6865
|
+
abortPendingReservation(`pty:${message.ptyId}`, "Shell closed before it started");
|
|
6655
6866
|
await closePty(message);
|
|
6656
6867
|
if (activePty && targetMayMutateVisibleWorkspace(activePty.target)) {
|
|
6657
6868
|
markWorkspaceDirty({ type: "process_terminal", detail: `pty ${message.ptyId} closed` }, true, activePty.target);
|
|
@@ -6688,11 +6899,21 @@ async function startWorker(options, projectRuntime = {
|
|
|
6688
6899
|
try {
|
|
6689
6900
|
resources = await acquireWorkerCommandLaunch(ws, message, assertMessageAdmission);
|
|
6690
6901
|
if (hasWorkspaceEffect) {
|
|
6691
|
-
|
|
6692
|
-
|
|
6693
|
-
|
|
6694
|
-
|
|
6695
|
-
|
|
6902
|
+
const reservationAbort = registerPendingReservation(`run:${message.runId}`, message.sessionId);
|
|
6903
|
+
try {
|
|
6904
|
+
await (0, import_workspace_command_sync_policy.reserveWorkspaceCommandAfterCurrentSync)(
|
|
6905
|
+
message.target,
|
|
6906
|
+
workspaceSyncSingleFlight,
|
|
6907
|
+
() => {
|
|
6908
|
+
workspaceSyncPriorityProcessTargets.set(message.runId, message.target);
|
|
6909
|
+
targetReserved = true;
|
|
6910
|
+
if (targetMayMutateVisibleWorkspace(message.target)) recordVisibleWorkspaceMutation(message.target);
|
|
6911
|
+
},
|
|
6912
|
+
{ signal: reservationAbort.signal }
|
|
6913
|
+
);
|
|
6914
|
+
} finally {
|
|
6915
|
+
unregisterPendingReservation(`run:${message.runId}`, reservationAbort);
|
|
6916
|
+
}
|
|
6696
6917
|
}
|
|
6697
6918
|
const runCommand = async () => {
|
|
6698
6919
|
if (!workspaceCommandTransportIsAllowed(deferredWorkspaceConfiguration, activeWorkspaceIncidentId, "exec")) {
|
|
@@ -6744,7 +6965,7 @@ async function startWorker(options, projectRuntime = {
|
|
|
6744
6965
|
cancelledProcessRuns.delete(message.runId);
|
|
6745
6966
|
}
|
|
6746
6967
|
if (infrastructureStoppedRunIds.has(message.runId)) {
|
|
6747
|
-
|
|
6968
|
+
recoveryJournal.unknown(message.requestId);
|
|
6748
6969
|
return;
|
|
6749
6970
|
}
|
|
6750
6971
|
sendSerializedWorkerMessage(ws, JSON.stringify(result));
|
|
@@ -6818,11 +7039,21 @@ async function startWorker(options, projectRuntime = {
|
|
|
6818
7039
|
try {
|
|
6819
7040
|
resources = await acquireWorkerCommandLaunch(ws, message, assertMessageAdmission);
|
|
6820
7041
|
if (hasWorkspaceEffect) {
|
|
6821
|
-
|
|
6822
|
-
|
|
6823
|
-
|
|
6824
|
-
|
|
6825
|
-
|
|
7042
|
+
const reservationAbort = registerPendingReservation(`run:${message.runId}`, message.sessionId);
|
|
7043
|
+
try {
|
|
7044
|
+
await (0, import_workspace_command_sync_policy.reserveWorkspaceCommandAfterCurrentSync)(
|
|
7045
|
+
message.target,
|
|
7046
|
+
workspaceSyncSingleFlight,
|
|
7047
|
+
() => {
|
|
7048
|
+
workspaceSyncPriorityProcessTargets.set(message.runId, message.target);
|
|
7049
|
+
targetReserved = true;
|
|
7050
|
+
if (targetMayMutateVisibleWorkspace(message.target)) recordVisibleWorkspaceMutation(message.target);
|
|
7051
|
+
},
|
|
7052
|
+
{ signal: reservationAbort.signal }
|
|
7053
|
+
);
|
|
7054
|
+
} finally {
|
|
7055
|
+
unregisterPendingReservation(`run:${message.runId}`, reservationAbort);
|
|
7056
|
+
}
|
|
6826
7057
|
}
|
|
6827
7058
|
await (0, import_workspace_command_sync_policy.runReservedWorkspaceCommand)(message.target, workspaceSyncSingleFlight, runCommand, () => {
|
|
6828
7059
|
if (targetReserved) {
|
|
@@ -6857,16 +7088,40 @@ async function startWorker(options, projectRuntime = {
|
|
|
6857
7088
|
return;
|
|
6858
7089
|
}
|
|
6859
7090
|
const reservesVisibleWorkspace = targetMayMutateVisibleWorkspace(message.target);
|
|
6860
|
-
const
|
|
7091
|
+
const mutatesFiles = message.type === "write" || message.type === "edit" || message.type === "update_task_status" || message.type === "read_plan" && message.createIfMissing;
|
|
7092
|
+
const mutatesVisibleWorkspace = mutatesFiles && targetMayMutateVisibleWorkspace(message.target);
|
|
6861
7093
|
if (reservesVisibleWorkspace) {
|
|
6862
|
-
|
|
6863
|
-
|
|
6864
|
-
|
|
6865
|
-
|
|
7094
|
+
const reservationAbort = registerPendingReservation(
|
|
7095
|
+
`op:${message.requestId}`,
|
|
7096
|
+
"sessionId" in message && typeof message.sessionId === "string" ? message.sessionId : void 0
|
|
7097
|
+
);
|
|
7098
|
+
try {
|
|
7099
|
+
await (0, import_workspace_command_sync_policy.reserveWorkspaceCommandAfterCurrentSync)(
|
|
7100
|
+
message.target,
|
|
7101
|
+
workspaceSyncSingleFlight,
|
|
7102
|
+
() => {
|
|
7103
|
+
workspaceSyncPriorityOperationTargets.set(message.requestId, message.target);
|
|
7104
|
+
if (mutatesVisibleWorkspace) recordVisibleWorkspaceMutation(message.target);
|
|
7105
|
+
},
|
|
7106
|
+
{ signal: reservationAbort.signal }
|
|
7107
|
+
);
|
|
7108
|
+
} catch (error) {
|
|
7109
|
+
sendSerializedWorkerMessage(
|
|
7110
|
+
ws,
|
|
7111
|
+
JSON.stringify({
|
|
7112
|
+
type: "operation_result",
|
|
7113
|
+
requestId: message.requestId,
|
|
7114
|
+
error: error instanceof Error ? error.message : String(error)
|
|
7115
|
+
})
|
|
7116
|
+
);
|
|
7117
|
+
return;
|
|
7118
|
+
} finally {
|
|
7119
|
+
unregisterPendingReservation(`op:${message.requestId}`, reservationAbort);
|
|
7120
|
+
}
|
|
6866
7121
|
}
|
|
6867
7122
|
let dirtyTrigger;
|
|
6868
7123
|
try {
|
|
6869
|
-
const
|
|
7124
|
+
const operation = (0, import_workspace_command_sync_policy.runWorkspaceCommand)(message.target, workspaceSyncSingleFlight, async () => {
|
|
6870
7125
|
const resolvedTarget = resolveMessageTarget(message.target);
|
|
6871
7126
|
return await executeOperation({
|
|
6872
7127
|
message,
|
|
@@ -6877,6 +7132,8 @@ async function startWorker(options, projectRuntime = {
|
|
|
6877
7132
|
assertAdmission: assertMessageAdmission
|
|
6878
7133
|
});
|
|
6879
7134
|
});
|
|
7135
|
+
if (mutatesFiles && typeof message.sessionId === "string") sessionFileMutations.track(message.sessionId, operation);
|
|
7136
|
+
const result = await operation;
|
|
6880
7137
|
sendSerializedWorkerMessage(
|
|
6881
7138
|
ws,
|
|
6882
7139
|
JSON.stringify({
|
|
@@ -6960,6 +7217,7 @@ async function startWorker(options, projectRuntime = {
|
|
|
6960
7217
|
projectRuntime.configuration.deferred = deferredWorkspaceConfiguration;
|
|
6961
7218
|
}
|
|
6962
7219
|
if (currentWorkerSocket === ws) currentWorkerSocket = null;
|
|
7220
|
+
abortPendingReservations("Worker connection closed before command start");
|
|
6963
7221
|
workerRecoveryId = "reconnecting";
|
|
6964
7222
|
const reason = event.reason ? `: ${event.reason}` : "";
|
|
6965
7223
|
process.stderr.write(`[r5d-worker] disconnected (${event.code}${reason})
|
|
@@ -6993,6 +7251,11 @@ async function startWorker(options, projectRuntime = {
|
|
|
6993
7251
|
});
|
|
6994
7252
|
}
|
|
6995
7253
|
function installWorkerRuntimeFailureLogging() {
|
|
7254
|
+
(0, import_workspace_filesystem_executor.installWorkspaceFilesystemExecutorFailureHandler)((error) => {
|
|
7255
|
+
process.stderr.write(`[r5d-worker] ${error.message}; restarting the worker runtime
|
|
7256
|
+
`);
|
|
7257
|
+
void exitWorkerRuntime(import_supervisor.WORKER_RECONNECT_EXIT_CODE);
|
|
7258
|
+
});
|
|
6996
7259
|
process.on("uncaughtException", (error) => {
|
|
6997
7260
|
process.stderr.write(`[r5d-worker] uncaught exception: ${error instanceof Error ? error.stack ?? error.message : String(error)}
|
|
6998
7261
|
`);
|
|
@@ -7005,11 +7268,6 @@ function installWorkerRuntimeFailureLogging() {
|
|
|
7005
7268
|
});
|
|
7006
7269
|
}
|
|
7007
7270
|
async function main() {
|
|
7008
|
-
if (process.argv[2] === import_project_snapshot_recovery_runner.PROJECT_SNAPSHOT_RECOVERY_HELPER_COMMAND) {
|
|
7009
|
-
const exitCode2 = (0, import_project_snapshot_recovery_runner.runProjectSnapshotRecoveryHelper)(process.argv.slice(3));
|
|
7010
|
-
if (exitCode2 !== 0) process.exit(exitCode2);
|
|
7011
|
-
return;
|
|
7012
|
-
}
|
|
7013
7271
|
const parsed = parseArgs(process.argv.slice(2));
|
|
7014
7272
|
if (parsed.command === "help") {
|
|
7015
7273
|
printHelp();
|