@ricsam/r5d-worker 0.0.53 → 0.0.55
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/cjs/heartbeat.cjs +15 -2
- package/dist/cjs/main.cjs +186 -145
- package/dist/cjs/package.json +1 -1
- package/dist/cjs/workspace-incident-state.cjs +39 -0
- package/dist/cjs/workspace-mutation-gate.cjs +92 -0
- package/dist/cjs/workspace-sync.cjs +73 -41
- package/dist/mjs/heartbeat.mjs +12 -1
- package/dist/mjs/main.mjs +191 -146
- package/dist/mjs/package.json +1 -1
- package/dist/mjs/workspace-incident-state.mjs +14 -0
- package/dist/mjs/workspace-mutation-gate.mjs +68 -0
- package/dist/mjs/workspace-sync.mjs +71 -41
- package/dist/types/heartbeat.d.ts +7 -0
- package/dist/types/workspace-incident-state.d.ts +8 -0
- package/dist/types/workspace-mutation-gate.d.ts +19 -0
- package/dist/types/workspace-sync.d.ts +14 -1
- package/package.json +1 -1
package/dist/cjs/heartbeat.cjs
CHANGED
|
@@ -20,7 +20,9 @@ var heartbeat_exports = {};
|
|
|
20
20
|
__export(heartbeat_exports, {
|
|
21
21
|
WORKER_HEARTBEAT_INTERVAL_MS: () => WORKER_HEARTBEAT_INTERVAL_MS,
|
|
22
22
|
WORKER_HEARTBEAT_TIMEOUT_MS: () => WORKER_HEARTBEAT_TIMEOUT_MS,
|
|
23
|
-
|
|
23
|
+
grantWorkerHeartbeatBusyGrace: () => grantWorkerHeartbeatBusyGrace,
|
|
24
|
+
hasWorkerHeartbeatTimedOut: () => hasWorkerHeartbeatTimedOut,
|
|
25
|
+
hasWorkerHeartbeatTimedOutWithBusyGrace: () => hasWorkerHeartbeatTimedOutWithBusyGrace
|
|
24
26
|
});
|
|
25
27
|
module.exports = __toCommonJS(heartbeat_exports);
|
|
26
28
|
const WORKER_HEARTBEAT_INTERVAL_MS = 15e3;
|
|
@@ -28,9 +30,20 @@ const WORKER_HEARTBEAT_TIMEOUT_MS = 45e3;
|
|
|
28
30
|
function hasWorkerHeartbeatTimedOut(lastHeartbeatAt, now = Date.now(), timeoutMs = WORKER_HEARTBEAT_TIMEOUT_MS) {
|
|
29
31
|
return lastHeartbeatAt !== null && now - lastHeartbeatAt >= timeoutMs;
|
|
30
32
|
}
|
|
33
|
+
function grantWorkerHeartbeatBusyGrace(lastHeartbeatAt, currentGrace, now = Date.now(), timeoutMs = WORKER_HEARTBEAT_TIMEOUT_MS, graceMs = WORKER_HEARTBEAT_INTERVAL_MS) {
|
|
34
|
+
if (!hasWorkerHeartbeatTimedOut(lastHeartbeatAt, now, timeoutMs)) return currentGrace;
|
|
35
|
+
if (currentGrace?.heartbeatAt === lastHeartbeatAt) return currentGrace;
|
|
36
|
+
return { heartbeatAt: lastHeartbeatAt, expiresAt: now + graceMs };
|
|
37
|
+
}
|
|
38
|
+
function hasWorkerHeartbeatTimedOutWithBusyGrace(lastHeartbeatAt, busyGrace, now = Date.now(), timeoutMs = WORKER_HEARTBEAT_TIMEOUT_MS) {
|
|
39
|
+
if (lastHeartbeatAt !== null && busyGrace?.heartbeatAt === lastHeartbeatAt && now < busyGrace.expiresAt) return false;
|
|
40
|
+
return hasWorkerHeartbeatTimedOut(lastHeartbeatAt, now, timeoutMs);
|
|
41
|
+
}
|
|
31
42
|
// Annotate the CommonJS export names for ESM import in node:
|
|
32
43
|
0 && (module.exports = {
|
|
33
44
|
WORKER_HEARTBEAT_INTERVAL_MS,
|
|
34
45
|
WORKER_HEARTBEAT_TIMEOUT_MS,
|
|
35
|
-
|
|
46
|
+
grantWorkerHeartbeatBusyGrace,
|
|
47
|
+
hasWorkerHeartbeatTimedOut,
|
|
48
|
+
hasWorkerHeartbeatTimedOutWithBusyGrace
|
|
36
49
|
});
|
package/dist/cjs/main.cjs
CHANGED
|
@@ -63,6 +63,7 @@ var import_git_identity = require("./git-identity.cjs");
|
|
|
63
63
|
var import_heartbeat = require("./heartbeat.cjs");
|
|
64
64
|
var import_process_tree = require("./process-tree.cjs");
|
|
65
65
|
var import_port_forward_client = require("./port-forward-client.cjs");
|
|
66
|
+
var import_workspace_incident_state = require("./workspace-incident-state.cjs");
|
|
66
67
|
var import_supervisor = require("./supervisor.cjs");
|
|
67
68
|
var import_managed_paths = require("./managed-paths.cjs");
|
|
68
69
|
var import_workspace_sync = require("./workspace-sync.cjs");
|
|
@@ -81,6 +82,7 @@ const pendingProcessTerminals = /* @__PURE__ */ new Map();
|
|
|
81
82
|
const cancelledProcessRuns = /* @__PURE__ */ new Set();
|
|
82
83
|
const activePtys = /* @__PURE__ */ new Map();
|
|
83
84
|
let currentWorkerSocket = null;
|
|
85
|
+
const workspaceSyncSingleFlight = new import_workspace_sync.WorkspaceSyncSingleFlight();
|
|
84
86
|
let githubCredential = null;
|
|
85
87
|
let visibleGitIdentity = null;
|
|
86
88
|
function defaultConfigPath() {
|
|
@@ -2252,75 +2254,72 @@ function resolveHostShell(command, platform = process.platform) {
|
|
|
2252
2254
|
return { file, args: command === void 0 ? ["-l"] : ["-lc", command] };
|
|
2253
2255
|
}
|
|
2254
2256
|
async function openPty(input) {
|
|
2255
|
-
|
|
2256
|
-
|
|
2257
|
-
target: input.resolvedTarget.target,
|
|
2258
|
-
planRoot: input.planRoot,
|
|
2259
|
-
activePlanId: input.message.env?.R5D_ACTIVE_PLAN_ID
|
|
2260
|
-
}) : {};
|
|
2261
|
-
const targetProcessEnv = targetIdentityEnv(input.resolvedTarget.target);
|
|
2262
|
-
const shell = resolveHostShell(input.message.command);
|
|
2263
|
-
const ptyProcess = createNodePtyBridge({
|
|
2264
|
-
file: shell.file,
|
|
2265
|
-
args: shell.args,
|
|
2266
|
-
ptyOptions: {
|
|
2267
|
-
name: "xterm-256color",
|
|
2268
|
-
cols: Math.max(1, Math.min(Math.floor(input.message.cols || 80), 500)),
|
|
2269
|
-
rows: Math.max(1, Math.min(Math.floor(input.message.rows || 24), 500)),
|
|
2270
|
-
cwd: input.resolvedTarget.rootPath,
|
|
2271
|
-
env: {
|
|
2272
|
-
...process.env,
|
|
2273
|
-
...githubProcessEnv(),
|
|
2274
|
-
...input.message.env ?? {},
|
|
2275
|
-
...planProcessEnv,
|
|
2276
|
-
...targetProcessEnv
|
|
2277
|
-
}
|
|
2278
|
-
},
|
|
2279
|
-
onOpened: () => {
|
|
2280
|
-
sendWorkerMessage(input.ws, {
|
|
2281
|
-
type: "pty_opened",
|
|
2282
|
-
requestId: input.message.requestId,
|
|
2283
|
-
ptyId: input.message.ptyId
|
|
2284
|
-
});
|
|
2285
|
-
},
|
|
2286
|
-
onOutput: (data) => {
|
|
2287
|
-
sendWorkerMessage(input.ws, {
|
|
2288
|
-
type: "pty_output",
|
|
2289
|
-
ptyId: input.message.ptyId,
|
|
2290
|
-
data
|
|
2291
|
-
});
|
|
2292
|
-
},
|
|
2293
|
-
onExit: (event) => {
|
|
2294
|
-
activePtys.delete(input.message.ptyId);
|
|
2295
|
-
sendWorkerMessage(input.ws, {
|
|
2296
|
-
type: "pty_exit",
|
|
2297
|
-
ptyId: input.message.ptyId,
|
|
2298
|
-
exitCode: event.exitCode,
|
|
2299
|
-
signal: event.signal
|
|
2300
|
-
});
|
|
2301
|
-
},
|
|
2302
|
-
onError: (error) => {
|
|
2303
|
-
activePtys.delete(input.message.ptyId);
|
|
2304
|
-
sendWorkerMessage(input.ws, {
|
|
2305
|
-
type: "pty_error",
|
|
2306
|
-
requestId: input.message.requestId,
|
|
2307
|
-
ptyId: input.message.ptyId,
|
|
2308
|
-
error: error.message
|
|
2309
|
-
});
|
|
2310
|
-
}
|
|
2311
|
-
});
|
|
2312
|
-
activePtys.set(input.message.ptyId, {
|
|
2313
|
-
...ptyProcess,
|
|
2314
|
-
target: input.resolvedTarget.target
|
|
2315
|
-
});
|
|
2316
|
-
} catch (error) {
|
|
2317
|
-
sendWorkerMessage(input.ws, {
|
|
2318
|
-
type: "pty_error",
|
|
2319
|
-
requestId: input.message.requestId,
|
|
2320
|
-
ptyId: input.message.ptyId,
|
|
2321
|
-
error: error instanceof Error ? error.message : String(error)
|
|
2322
|
-
});
|
|
2257
|
+
if (activePtys.has(input.message.ptyId)) {
|
|
2258
|
+
throw new Error(`Shell ${input.message.ptyId} is already open`);
|
|
2323
2259
|
}
|
|
2260
|
+
const planProcessEnv = !(input.resolvedTarget.target.type === "workspace" && input.resolvedTarget.target.rootProfile === "canonical_sync") ? preparePlanEnvForShell({
|
|
2261
|
+
target: input.resolvedTarget.target,
|
|
2262
|
+
planRoot: input.planRoot,
|
|
2263
|
+
activePlanId: input.message.env?.R5D_ACTIVE_PLAN_ID
|
|
2264
|
+
}) : {};
|
|
2265
|
+
const targetProcessEnv = targetIdentityEnv(input.resolvedTarget.target);
|
|
2266
|
+
const shell = resolveHostShell(input.message.command);
|
|
2267
|
+
const ptyProcess = createNodePtyBridge({
|
|
2268
|
+
file: shell.file,
|
|
2269
|
+
args: shell.args,
|
|
2270
|
+
ptyOptions: {
|
|
2271
|
+
name: "xterm-256color",
|
|
2272
|
+
cols: Math.max(1, Math.min(Math.floor(input.message.cols || 80), 500)),
|
|
2273
|
+
rows: Math.max(1, Math.min(Math.floor(input.message.rows || 24), 500)),
|
|
2274
|
+
cwd: input.resolvedTarget.rootPath,
|
|
2275
|
+
env: {
|
|
2276
|
+
...process.env,
|
|
2277
|
+
...githubProcessEnv(),
|
|
2278
|
+
...input.message.env ?? {},
|
|
2279
|
+
...planProcessEnv,
|
|
2280
|
+
...targetProcessEnv
|
|
2281
|
+
}
|
|
2282
|
+
},
|
|
2283
|
+
onOpened: () => {
|
|
2284
|
+
sendWorkerMessage(input.ws, {
|
|
2285
|
+
type: "pty_opened",
|
|
2286
|
+
requestId: input.message.requestId,
|
|
2287
|
+
ptyId: input.message.ptyId
|
|
2288
|
+
});
|
|
2289
|
+
},
|
|
2290
|
+
onOutput: (data) => {
|
|
2291
|
+
sendWorkerMessage(input.ws, {
|
|
2292
|
+
type: "pty_output",
|
|
2293
|
+
ptyId: input.message.ptyId,
|
|
2294
|
+
data
|
|
2295
|
+
});
|
|
2296
|
+
},
|
|
2297
|
+
onExit: (event) => {
|
|
2298
|
+
input.releaseWorkspaceMutation();
|
|
2299
|
+
activePtys.delete(input.message.ptyId);
|
|
2300
|
+
sendWorkerMessage(input.ws, {
|
|
2301
|
+
type: "pty_exit",
|
|
2302
|
+
ptyId: input.message.ptyId,
|
|
2303
|
+
exitCode: event.exitCode,
|
|
2304
|
+
signal: event.signal
|
|
2305
|
+
});
|
|
2306
|
+
},
|
|
2307
|
+
onError: (error) => {
|
|
2308
|
+
input.releaseWorkspaceMutation();
|
|
2309
|
+
activePtys.delete(input.message.ptyId);
|
|
2310
|
+
sendWorkerMessage(input.ws, {
|
|
2311
|
+
type: "pty_error",
|
|
2312
|
+
requestId: input.message.requestId,
|
|
2313
|
+
ptyId: input.message.ptyId,
|
|
2314
|
+
error: error.message
|
|
2315
|
+
});
|
|
2316
|
+
}
|
|
2317
|
+
});
|
|
2318
|
+
activePtys.set(input.message.ptyId, {
|
|
2319
|
+
...ptyProcess,
|
|
2320
|
+
target: input.resolvedTarget.target,
|
|
2321
|
+
releaseWorkspaceMutation: input.releaseWorkspaceMutation
|
|
2322
|
+
});
|
|
2324
2323
|
}
|
|
2325
2324
|
function writePty(ws, message) {
|
|
2326
2325
|
const ptyProcess = activePtys.get(message.ptyId);
|
|
@@ -2397,7 +2396,6 @@ async function startWorker(options) {
|
|
|
2397
2396
|
import_node_fs.default.mkdirSync(planRoot, { recursive: true });
|
|
2398
2397
|
const manifestByProjectId = /* @__PURE__ */ new Map();
|
|
2399
2398
|
const pendingManifestCheckouts = /* @__PURE__ */ new Map();
|
|
2400
|
-
const workspaceSyncSingleFlight = new import_workspace_sync.WorkspaceSyncSingleFlight();
|
|
2401
2399
|
let workspaceRemoteUrl = null;
|
|
2402
2400
|
let activeWorkspaceIncidentId = null;
|
|
2403
2401
|
let previousPeriodicFingerprint = null;
|
|
@@ -2423,8 +2421,32 @@ async function startWorker(options) {
|
|
|
2423
2421
|
...overrides
|
|
2424
2422
|
};
|
|
2425
2423
|
};
|
|
2426
|
-
const sendWorkspaceSyncResult = (
|
|
2427
|
-
|
|
2424
|
+
const sendWorkspaceSyncResult = (authority, result) => {
|
|
2425
|
+
try {
|
|
2426
|
+
ws.send(JSON.stringify({ type: "workspace_sync_result", ...authority, result }));
|
|
2427
|
+
} catch (error) {
|
|
2428
|
+
process.stderr.write(
|
|
2429
|
+
`[r5d-worker] failed to send workspace_sync_result on its issuing connection: ${error instanceof Error ? error.message : String(error)}
|
|
2430
|
+
`
|
|
2431
|
+
);
|
|
2432
|
+
}
|
|
2433
|
+
};
|
|
2434
|
+
const requestWorkspaceSync = (trigger, fingerprint) => {
|
|
2435
|
+
try {
|
|
2436
|
+
ws.send(
|
|
2437
|
+
JSON.stringify({
|
|
2438
|
+
type: "workspace_sync_requested",
|
|
2439
|
+
observationId: crypto.randomUUID(),
|
|
2440
|
+
trigger,
|
|
2441
|
+
...fingerprint ? { fingerprint } : {}
|
|
2442
|
+
})
|
|
2443
|
+
);
|
|
2444
|
+
} catch (error) {
|
|
2445
|
+
process.stderr.write(
|
|
2446
|
+
`[r5d-worker] failed to send workspace sync observation on its current connection: ${error instanceof Error ? error.message : String(error)}
|
|
2447
|
+
`
|
|
2448
|
+
);
|
|
2449
|
+
}
|
|
2428
2450
|
};
|
|
2429
2451
|
const manifestCheckoutKey = (projectId, branchName) => `${projectId}\0${branchName}`;
|
|
2430
2452
|
const allManifestCheckouts = () => [...manifestByProjectId.values()].flatMap(
|
|
@@ -2475,7 +2497,7 @@ async function startWorker(options) {
|
|
|
2475
2497
|
}
|
|
2476
2498
|
return created;
|
|
2477
2499
|
};
|
|
2478
|
-
const runRequestedWorkspaceSync = async (
|
|
2500
|
+
const runRequestedWorkspaceSync = async (authority, trigger, overrides = {}) => {
|
|
2479
2501
|
workspaceSyncRequestsInFlight += 1;
|
|
2480
2502
|
try {
|
|
2481
2503
|
let pendingTargets = [];
|
|
@@ -2519,7 +2541,7 @@ async function startWorker(options) {
|
|
|
2519
2541
|
}
|
|
2520
2542
|
}
|
|
2521
2543
|
previousPeriodicFingerprint = null;
|
|
2522
|
-
sendWorkspaceSyncResult(
|
|
2544
|
+
sendWorkspaceSyncResult(authority, result);
|
|
2523
2545
|
return result;
|
|
2524
2546
|
} catch (error) {
|
|
2525
2547
|
const result = {
|
|
@@ -2539,7 +2561,7 @@ async function startWorker(options) {
|
|
|
2539
2561
|
error: error instanceof Error ? error.message : String(error)
|
|
2540
2562
|
};
|
|
2541
2563
|
previousPeriodicFingerprint = null;
|
|
2542
|
-
sendWorkspaceSyncResult(
|
|
2564
|
+
sendWorkspaceSyncResult(authority, result);
|
|
2543
2565
|
return result;
|
|
2544
2566
|
} finally {
|
|
2545
2567
|
workspaceSyncRequestsInFlight -= 1;
|
|
@@ -2551,6 +2573,7 @@ async function startWorker(options) {
|
|
|
2551
2573
|
}
|
|
2552
2574
|
});
|
|
2553
2575
|
let lastServerHeartbeatAt = null;
|
|
2576
|
+
let heartbeatBusyGrace = null;
|
|
2554
2577
|
let heartbeatTimedOut = false;
|
|
2555
2578
|
let heartbeatWatchdog;
|
|
2556
2579
|
const stopHeartbeatWatchdog = () => {
|
|
@@ -2562,7 +2585,7 @@ async function startWorker(options) {
|
|
|
2562
2585
|
ws.addEventListener("open", () => {
|
|
2563
2586
|
currentWorkerSocket = ws;
|
|
2564
2587
|
heartbeatWatchdog = setInterval(() => {
|
|
2565
|
-
if (heartbeatTimedOut || !(0, import_heartbeat.
|
|
2588
|
+
if (heartbeatTimedOut || !(0, import_heartbeat.hasWorkerHeartbeatTimedOutWithBusyGrace)(lastServerHeartbeatAt, heartbeatBusyGrace)) {
|
|
2566
2589
|
return;
|
|
2567
2590
|
}
|
|
2568
2591
|
heartbeatTimedOut = true;
|
|
@@ -2582,7 +2605,8 @@ async function startWorker(options) {
|
|
|
2582
2605
|
capabilities: {
|
|
2583
2606
|
updateClis: true,
|
|
2584
2607
|
canonicalResolverCheckout: true,
|
|
2585
|
-
browserPortForwarding: true
|
|
2608
|
+
browserPortForwarding: true,
|
|
2609
|
+
globalWorkspaceSyncLeaseV1: true
|
|
2586
2610
|
},
|
|
2587
2611
|
projectRoot: projectsRoot,
|
|
2588
2612
|
artifactRoot,
|
|
@@ -2609,33 +2633,35 @@ async function startWorker(options) {
|
|
|
2609
2633
|
return;
|
|
2610
2634
|
}
|
|
2611
2635
|
if (message.type === "workspace_manifest") {
|
|
2612
|
-
|
|
2613
|
-
|
|
2614
|
-
|
|
2615
|
-
|
|
2616
|
-
|
|
2617
|
-
|
|
2618
|
-
|
|
2619
|
-
|
|
2620
|
-
|
|
2621
|
-
|
|
2622
|
-
|
|
2623
|
-
|
|
2624
|
-
|
|
2625
|
-
|
|
2626
|
-
|
|
2627
|
-
|
|
2628
|
-
|
|
2629
|
-
const
|
|
2630
|
-
|
|
2631
|
-
|
|
2632
|
-
|
|
2633
|
-
|
|
2634
|
-
|
|
2635
|
-
|
|
2636
|
-
|
|
2636
|
+
await workspaceSyncSingleFlight.runMutation(() => {
|
|
2637
|
+
configureGitHubAuth(message.githubCredential);
|
|
2638
|
+
visibleGitIdentity = message.gitIdentity;
|
|
2639
|
+
workspaceRemoteUrl = message.workspaceRemoteUrl;
|
|
2640
|
+
const previousCheckoutKeys = new Set(
|
|
2641
|
+
allManifestCheckouts().map(({ projectId, branchName }) => manifestCheckoutKey(projectId, branchName))
|
|
2642
|
+
);
|
|
2643
|
+
const migratedProjectIds = (0, import_managed_paths.migrateLegacyProjectRoots)(projectsRoot, message.projects);
|
|
2644
|
+
manifestByProjectId.clear();
|
|
2645
|
+
for (const project of message.projects) manifestByProjectId.set(project.projectId, project);
|
|
2646
|
+
const currentCheckouts = allManifestCheckouts();
|
|
2647
|
+
const currentCheckoutKeys = new Set(
|
|
2648
|
+
currentCheckouts.map(({ projectId, branchName }) => manifestCheckoutKey(projectId, branchName))
|
|
2649
|
+
);
|
|
2650
|
+
for (const key of pendingManifestCheckouts.keys()) {
|
|
2651
|
+
if (!currentCheckoutKeys.has(key)) pendingManifestCheckouts.delete(key);
|
|
2652
|
+
}
|
|
2653
|
+
for (const checkout of currentCheckouts) {
|
|
2654
|
+
const key = manifestCheckoutKey(checkout.projectId, checkout.branchName);
|
|
2655
|
+
if (previousCheckoutKeys.has(key)) continue;
|
|
2656
|
+
const branchPath = import_node_path.default.join(projectRootFor(projectsRoot, checkout.projectId, manifestByProjectId), checkout.branchName);
|
|
2657
|
+
if (!hasNormalVisibleGitDir(branchPath)) pendingManifestCheckouts.set(key, checkout);
|
|
2658
|
+
}
|
|
2659
|
+
previousPeriodicFingerprint = null;
|
|
2660
|
+
process.stdout.write(
|
|
2661
|
+
`[r5d-worker] workspace manifest: ${message.projects.length} projects${migratedProjectIds.length > 0 ? `; migrated ${migratedProjectIds.length} project checkout root(s)` : ""}
|
|
2637
2662
|
`
|
|
2638
|
-
|
|
2663
|
+
);
|
|
2664
|
+
});
|
|
2639
2665
|
if (!periodicWorkspaceScan) {
|
|
2640
2666
|
const emptyFingerprint = (0, import_node_crypto.createHash)("sha256").update("").digest("hex");
|
|
2641
2667
|
periodicWorkspaceScan = setInterval(() => {
|
|
@@ -2652,7 +2678,7 @@ async function startWorker(options) {
|
|
|
2652
2678
|
);
|
|
2653
2679
|
if (hasPendingGenericCheckout) {
|
|
2654
2680
|
previousPeriodicFingerprint = null;
|
|
2655
|
-
|
|
2681
|
+
requestWorkspaceSync({
|
|
2656
2682
|
type: "periodic",
|
|
2657
2683
|
detail: "workspace manifest additions"
|
|
2658
2684
|
});
|
|
@@ -2668,7 +2694,7 @@ async function startWorker(options) {
|
|
|
2668
2694
|
return;
|
|
2669
2695
|
}
|
|
2670
2696
|
previousPeriodicFingerprint = null;
|
|
2671
|
-
|
|
2697
|
+
requestWorkspaceSync({ type: "periodic" }, fingerprint);
|
|
2672
2698
|
})().catch((error) => {
|
|
2673
2699
|
process.stderr.write(
|
|
2674
2700
|
`[r5d-worker] periodic workspace synchronization failed: ${error instanceof Error ? error.message : String(error)}
|
|
@@ -2676,6 +2702,7 @@ async function startWorker(options) {
|
|
|
2676
2702
|
);
|
|
2677
2703
|
}).finally(() => {
|
|
2678
2704
|
periodicWorkspaceScanInFlight = false;
|
|
2705
|
+
heartbeatBusyGrace = (0, import_heartbeat.grantWorkerHeartbeatBusyGrace)(lastServerHeartbeatAt, heartbeatBusyGrace);
|
|
2679
2706
|
});
|
|
2680
2707
|
}, import_workspace_sync.WORKSPACE_PERIODIC_SCAN_INTERVAL_MS);
|
|
2681
2708
|
periodicWorkspaceScan.unref();
|
|
@@ -2683,23 +2710,23 @@ async function startWorker(options) {
|
|
|
2683
2710
|
return;
|
|
2684
2711
|
}
|
|
2685
2712
|
if (message.type === "sync_workspace") {
|
|
2686
|
-
await runRequestedWorkspaceSync(
|
|
2687
|
-
|
|
2688
|
-
|
|
2689
|
-
|
|
2690
|
-
|
|
2691
|
-
|
|
2692
|
-
|
|
2693
|
-
|
|
2694
|
-
|
|
2695
|
-
|
|
2696
|
-
|
|
2697
|
-
|
|
2698
|
-
|
|
2699
|
-
|
|
2700
|
-
|
|
2701
|
-
|
|
2702
|
-
|
|
2713
|
+
await runRequestedWorkspaceSync(
|
|
2714
|
+
{
|
|
2715
|
+
requestId: message.requestId,
|
|
2716
|
+
intentId: message.intentId,
|
|
2717
|
+
leaseEpoch: message.leaseEpoch,
|
|
2718
|
+
leaseToken: message.leaseToken
|
|
2719
|
+
},
|
|
2720
|
+
message.trigger,
|
|
2721
|
+
{
|
|
2722
|
+
attemptId: message.attemptId,
|
|
2723
|
+
quarantineRef: message.quarantineRef,
|
|
2724
|
+
confirmedLargeDiff: message.confirmedLargeDiff,
|
|
2725
|
+
confirmationReason: message.confirmationReason,
|
|
2726
|
+
resetToCanonical: message.resetToCanonical,
|
|
2727
|
+
skipVisibleMirror: message.skipVisibleMirror
|
|
2728
|
+
}
|
|
2729
|
+
);
|
|
2703
2730
|
return;
|
|
2704
2731
|
}
|
|
2705
2732
|
if (message.type === "sync_session_artifacts") {
|
|
@@ -2729,8 +2756,17 @@ async function startWorker(options) {
|
|
|
2729
2756
|
return;
|
|
2730
2757
|
}
|
|
2731
2758
|
if (message.type === "workspace_incident_updated") {
|
|
2732
|
-
|
|
2759
|
+
const previousIncidentId = activeWorkspaceIncidentId;
|
|
2760
|
+
activeWorkspaceIncidentId = (0, import_workspace_incident_state.applyWorkspaceIncidentUpdate)(activeWorkspaceIncidentId, message);
|
|
2733
2761
|
previousPeriodicFingerprint = null;
|
|
2762
|
+
const releasedHead = (0, import_workspace_incident_state.releasePendingWorkspaceHead)(
|
|
2763
|
+
previousIncidentId,
|
|
2764
|
+
activeWorkspaceIncidentId,
|
|
2765
|
+
message.status === "resolved" || message.status === "confirmed" || message.status === "reset" ? message.incidentId : null
|
|
2766
|
+
);
|
|
2767
|
+
if (releasedHead.syncHead) {
|
|
2768
|
+
requestWorkspaceSync({ type: "inbound_head", detail: releasedHead.syncHead });
|
|
2769
|
+
}
|
|
2734
2770
|
return;
|
|
2735
2771
|
}
|
|
2736
2772
|
if (message.type === "exec_terminal_ack") {
|
|
@@ -2787,6 +2823,7 @@ async function startWorker(options) {
|
|
|
2787
2823
|
}
|
|
2788
2824
|
if (message.type === "ping") {
|
|
2789
2825
|
lastServerHeartbeatAt = Date.now();
|
|
2826
|
+
heartbeatBusyGrace = null;
|
|
2790
2827
|
ws.send(JSON.stringify({ type: "pong" }));
|
|
2791
2828
|
return;
|
|
2792
2829
|
}
|
|
@@ -2827,11 +2864,14 @@ async function startWorker(options) {
|
|
|
2827
2864
|
});
|
|
2828
2865
|
return;
|
|
2829
2866
|
}
|
|
2867
|
+
const releaseWorkspaceMutation = await workspaceSyncSingleFlight.acquireMutation();
|
|
2868
|
+
let leaseTransferred = false;
|
|
2830
2869
|
try {
|
|
2831
2870
|
const resolvedTarget = resolveMessageTarget(message.target);
|
|
2832
2871
|
process.stdout.write(`[r5d-worker] pty ${message.ptyId}: ${describeWorkerSessionTarget(message.target)}
|
|
2833
2872
|
`);
|
|
2834
|
-
await openPty({ ws, message, resolvedTarget, planRoot });
|
|
2873
|
+
await openPty({ ws, message, resolvedTarget, planRoot, releaseWorkspaceMutation });
|
|
2874
|
+
leaseTransferred = true;
|
|
2835
2875
|
} catch (error) {
|
|
2836
2876
|
sendWorkerMessage(ws, {
|
|
2837
2877
|
type: "pty_error",
|
|
@@ -2839,6 +2879,8 @@ async function startWorker(options) {
|
|
|
2839
2879
|
ptyId: message.ptyId,
|
|
2840
2880
|
error: error instanceof Error ? error.message : String(error)
|
|
2841
2881
|
});
|
|
2882
|
+
} finally {
|
|
2883
|
+
if (!leaseTransferred) releaseWorkspaceMutation();
|
|
2842
2884
|
}
|
|
2843
2885
|
return;
|
|
2844
2886
|
}
|
|
@@ -2867,10 +2909,12 @@ async function startWorker(options) {
|
|
|
2867
2909
|
}
|
|
2868
2910
|
let result;
|
|
2869
2911
|
try {
|
|
2870
|
-
|
|
2871
|
-
|
|
2912
|
+
result = await workspaceSyncSingleFlight.runMutation(async () => {
|
|
2913
|
+
const resolvedTarget = resolveMessageTarget(message.target);
|
|
2914
|
+
process.stdout.write(`[r5d-worker] exec ${message.runId}: ${message.argv.join(" ")}
|
|
2872
2915
|
`);
|
|
2873
|
-
|
|
2916
|
+
return await executeCommand({ message, resolvedTarget, baseUrl, token, artifactRoot, planRoot });
|
|
2917
|
+
});
|
|
2874
2918
|
} catch (error) {
|
|
2875
2919
|
result = {
|
|
2876
2920
|
type: "exec_result",
|
|
@@ -2899,11 +2943,11 @@ async function startWorker(options) {
|
|
|
2899
2943
|
requestId: message.requestId,
|
|
2900
2944
|
runId: message.runId
|
|
2901
2945
|
});
|
|
2902
|
-
|
|
2946
|
+
void workspaceSyncSingleFlight.runMutation(async () => {
|
|
2903
2947
|
const resolvedTarget = resolveMessageTarget(message.target);
|
|
2904
2948
|
process.stdout.write(`[r5d-worker] exec_start ${message.runId}: ${message.argv.join(" ")}
|
|
2905
2949
|
`);
|
|
2906
|
-
|
|
2950
|
+
await executeStreamingCommand({
|
|
2907
2951
|
ws,
|
|
2908
2952
|
message,
|
|
2909
2953
|
resolvedTarget,
|
|
@@ -2911,34 +2955,29 @@ async function startWorker(options) {
|
|
|
2911
2955
|
token,
|
|
2912
2956
|
artifactRoot,
|
|
2913
2957
|
planRoot
|
|
2914
|
-
}).catch((error) => {
|
|
2915
|
-
sendWorkerMessage(ws, {
|
|
2916
|
-
type: "exec_start_error",
|
|
2917
|
-
requestId: message.requestId,
|
|
2918
|
-
runId: message.runId,
|
|
2919
|
-
error: error instanceof Error ? error.message : String(error)
|
|
2920
|
-
});
|
|
2921
2958
|
});
|
|
2922
|
-
}
|
|
2959
|
+
}).catch((error) => {
|
|
2923
2960
|
sendWorkerMessage(ws, {
|
|
2924
2961
|
type: "exec_start_error",
|
|
2925
2962
|
requestId: message.requestId,
|
|
2926
2963
|
runId: message.runId,
|
|
2927
2964
|
error: error instanceof Error ? error.message : String(error)
|
|
2928
2965
|
});
|
|
2929
|
-
}
|
|
2966
|
+
});
|
|
2930
2967
|
return;
|
|
2931
2968
|
}
|
|
2932
2969
|
if (message.type === "read" || message.type === "write" || message.type === "edit" || message.type === "grep" || message.type === "find" || message.type === "ls" || message.type === "view_file_bytes") {
|
|
2933
2970
|
try {
|
|
2934
|
-
const
|
|
2935
|
-
|
|
2936
|
-
|
|
2937
|
-
|
|
2938
|
-
|
|
2939
|
-
|
|
2940
|
-
|
|
2941
|
-
|
|
2971
|
+
const result = await workspaceSyncSingleFlight.runMutation(async () => {
|
|
2972
|
+
const resolvedTarget = resolveMessageTarget(message.target);
|
|
2973
|
+
return await executeOperation({
|
|
2974
|
+
message,
|
|
2975
|
+
resolvedTarget,
|
|
2976
|
+
baseUrl,
|
|
2977
|
+
token,
|
|
2978
|
+
artifactRoot,
|
|
2979
|
+
planRoot
|
|
2980
|
+
});
|
|
2942
2981
|
});
|
|
2943
2982
|
ws.send(
|
|
2944
2983
|
JSON.stringify({
|
|
@@ -2971,6 +3010,8 @@ async function startWorker(options) {
|
|
|
2971
3010
|
})().catch((error) => {
|
|
2972
3011
|
process.stderr.write(`[r5d-worker] message handling failed: ${error instanceof Error ? error.message : String(error)}
|
|
2973
3012
|
`);
|
|
3013
|
+
}).finally(() => {
|
|
3014
|
+
heartbeatBusyGrace = (0, import_heartbeat.grantWorkerHeartbeatBusyGrace)(lastServerHeartbeatAt, heartbeatBusyGrace);
|
|
2974
3015
|
});
|
|
2975
3016
|
});
|
|
2976
3017
|
ws.addEventListener("close", (event) => {
|
package/dist/cjs/package.json
CHANGED
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __defProp = Object.defineProperty;
|
|
3
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
5
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
6
|
+
var __export = (target, all) => {
|
|
7
|
+
for (var name in all)
|
|
8
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
9
|
+
};
|
|
10
|
+
var __copyProps = (to, from, except, desc) => {
|
|
11
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
12
|
+
for (let key of __getOwnPropNames(from))
|
|
13
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
14
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
15
|
+
}
|
|
16
|
+
return to;
|
|
17
|
+
};
|
|
18
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
19
|
+
var workspace_incident_state_exports = {};
|
|
20
|
+
__export(workspace_incident_state_exports, {
|
|
21
|
+
applyWorkspaceIncidentUpdate: () => applyWorkspaceIncidentUpdate,
|
|
22
|
+
releasePendingWorkspaceHead: () => releasePendingWorkspaceHead
|
|
23
|
+
});
|
|
24
|
+
module.exports = __toCommonJS(workspace_incident_state_exports);
|
|
25
|
+
function applyWorkspaceIncidentUpdate(currentIncidentId, update) {
|
|
26
|
+
if (update.status === "remediating" || update.status === "waiting_for_worker") return update.incidentId;
|
|
27
|
+
if (currentIncidentId && update.incidentId && update.incidentId !== currentIncidentId) return currentIncidentId;
|
|
28
|
+
return null;
|
|
29
|
+
}
|
|
30
|
+
function releasePendingWorkspaceHead(previousIncidentId, currentIncidentId, terminalIncidentId) {
|
|
31
|
+
if (!previousIncidentId || currentIncidentId) return { syncHead: null };
|
|
32
|
+
if (terminalIncidentId === previousIncidentId) return { syncHead: "authoritative" };
|
|
33
|
+
return { syncHead: null };
|
|
34
|
+
}
|
|
35
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
36
|
+
0 && (module.exports = {
|
|
37
|
+
applyWorkspaceIncidentUpdate,
|
|
38
|
+
releasePendingWorkspaceHead
|
|
39
|
+
});
|