@ricsam/r5d-worker 0.0.109 → 0.0.111
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 +12 -0
- package/dist/cjs/job-resources.cjs +335 -0
- package/dist/cjs/main.cjs +158 -37
- package/dist/cjs/package.json +1 -1
- package/dist/mjs/job-resources.mjs +293 -0
- package/dist/mjs/main.mjs +162 -37
- package/dist/mjs/package.json +1 -1
- package/dist/types/job-resources.d.ts +61 -0
- package/package.json +1 -1
package/dist/cjs/main.cjs
CHANGED
|
@@ -62,6 +62,7 @@ var import_plan_lint = require("./plan-lint.cjs");
|
|
|
62
62
|
var import_node_fs = __toESM(require("node:fs"), 1);
|
|
63
63
|
var import_recovery_store = require("./recovery-store.cjs");
|
|
64
64
|
var import_recovery_output_window = require("./recovery-output-window.cjs");
|
|
65
|
+
var import_job_resources = require("./job-resources.cjs");
|
|
65
66
|
var import_recovery_protocol = require("./recovery-protocol.cjs");
|
|
66
67
|
var import_node_path = __toESM(require("node:path"), 1);
|
|
67
68
|
var import_node_crypto = require("node:crypto");
|
|
@@ -154,6 +155,7 @@ const PTY_INPUT_BUSY_GRACE_MS = 3e3;
|
|
|
154
155
|
const PTY_FOREGROUND_POLL_MS = 1e3;
|
|
155
156
|
const PTY_FOREGROUND_IDLE_ENABLED = process.env.R5D_PTY_FOREGROUND_IDLE !== "0";
|
|
156
157
|
const PTY_TMP_PATH_PREFIX = "r5d-worker-tmp://";
|
|
158
|
+
let workerJobResources;
|
|
157
159
|
const activeProcesses = /* @__PURE__ */ new Map();
|
|
158
160
|
const credentialBearingProcessGroups = /* @__PURE__ */ new Map();
|
|
159
161
|
const credentialBearingProcessGroupTargets = /* @__PURE__ */ new Map();
|
|
@@ -202,8 +204,23 @@ function renewWorkerExecutionLease(leaseMs) {
|
|
|
202
204
|
() => {
|
|
203
205
|
if (Date.now() < workerExecutionLeaseDeadline) return;
|
|
204
206
|
workerLeaseExpired = true;
|
|
207
|
+
try {
|
|
208
|
+
workerJobResources?.cancelMatching((id) => id.startsWith("pty:"));
|
|
209
|
+
} catch (error) {
|
|
210
|
+
process.stderr.write(`[r5d-worker] expired PTY resource termination failed: ${error}
|
|
211
|
+
`);
|
|
212
|
+
}
|
|
213
|
+
closeAllPtys();
|
|
205
214
|
for (const operation of recoveryStore?.operations() ?? []) {
|
|
206
|
-
if (operation.sessionId && operation.state !== "completed")
|
|
215
|
+
if (operation.sessionId && operation.state !== "completed") {
|
|
216
|
+
recoveryStore?.cancelSession(operation.sessionId);
|
|
217
|
+
try {
|
|
218
|
+
workerJobResources?.cancelSession(operation.sessionId);
|
|
219
|
+
} catch (error) {
|
|
220
|
+
process.stderr.write(`[r5d-worker] expired command resource termination failed: ${error}
|
|
221
|
+
`);
|
|
222
|
+
}
|
|
223
|
+
}
|
|
207
224
|
}
|
|
208
225
|
for (const [runId, active] of activeProcesses) {
|
|
209
226
|
if (!active.sessionId) continue;
|
|
@@ -1597,7 +1614,7 @@ function credentialReapContractStatus(probe) {
|
|
|
1597
1614
|
if (process.platform !== "linux") return "direct";
|
|
1598
1615
|
try {
|
|
1599
1616
|
const cgroupText = import_node_fs.default.readFileSync("/proc/self/cgroup", "utf8");
|
|
1600
|
-
const unit =
|
|
1617
|
+
const unit = /(?:^|\/)r5d-worker\.service(?:\/|$|\n)/m.test(cgroupText) ? "r5d-worker.service" : void 0;
|
|
1601
1618
|
if (unit !== "r5d-worker.service") {
|
|
1602
1619
|
return SYSTEMD_INVOCATION_IDENTIFIER_PATTERN.test(process.env.INVOCATION_ID ?? "") ? "systemd_unverified" : "direct";
|
|
1603
1620
|
}
|
|
@@ -1631,7 +1648,7 @@ function credentialReapContractStatus(probe) {
|
|
|
1631
1648
|
}
|
|
1632
1649
|
}
|
|
1633
1650
|
if (resolved.platform !== "linux") return "direct";
|
|
1634
|
-
if (!/(?:^|\/)r5d-worker\.service(
|
|
1651
|
+
if (!/(?:^|\/)r5d-worker\.service(?:\/|$|\n)/m.test(resolved.cgroupText)) {
|
|
1635
1652
|
return SYSTEMD_INVOCATION_IDENTIFIER_PATTERN.test(resolved.invocationId ?? "") ? "systemd_unverified" : "direct";
|
|
1636
1653
|
}
|
|
1637
1654
|
if (!SYSTEMD_INVOCATION_IDENTIFIER_PATTERN.test(resolved.invocationId ?? "")) return "systemd_unverified";
|
|
@@ -2013,12 +2030,6 @@ function resolveCommandCwd(branchPath, cwd) {
|
|
|
2013
2030
|
}
|
|
2014
2031
|
return resolved;
|
|
2015
2032
|
}
|
|
2016
|
-
async function collectStream(stream) {
|
|
2017
|
-
if (!stream) {
|
|
2018
|
-
return "";
|
|
2019
|
-
}
|
|
2020
|
-
return await new Response(stream).text();
|
|
2021
|
-
}
|
|
2022
2033
|
async function streamCommandOutput(stream, onData) {
|
|
2023
2034
|
if (!stream) {
|
|
2024
2035
|
return;
|
|
@@ -2739,8 +2750,37 @@ async function prepareShellEnvForTarget(input) {
|
|
|
2739
2750
|
...targetProcessEnv(input.rootDir, input.target)
|
|
2740
2751
|
};
|
|
2741
2752
|
}
|
|
2753
|
+
async function releaseWorkerJobResources(resources) {
|
|
2754
|
+
if (!resources) return;
|
|
2755
|
+
try {
|
|
2756
|
+
await resources.release();
|
|
2757
|
+
} catch (error) {
|
|
2758
|
+
process.stderr.write(`[r5d-worker] job resource cleanup failed: ${error instanceof Error ? error.message : String(error)}
|
|
2759
|
+
`);
|
|
2760
|
+
}
|
|
2761
|
+
}
|
|
2762
|
+
async function acquireWorkerCommandResources(ws, message, assertAdmission) {
|
|
2763
|
+
const id = message.type === "pty_open" ? `pty:${message.ptyId}` : message.runId;
|
|
2764
|
+
const operationId = message.type === "pty_open" ? id : `exec:${id}`;
|
|
2765
|
+
sendWorkerMessage(ws, { type: "heartbeat_lease", operationId, active: true });
|
|
2766
|
+
try {
|
|
2767
|
+
return await workerJobResources.acquire({
|
|
2768
|
+
id,
|
|
2769
|
+
lane: message.type === "exec" && message.workspaceEffect === "none" ? "utility" : "general",
|
|
2770
|
+
..."sessionId" in message && message.sessionId ? { sessionId: message.sessionId } : {},
|
|
2771
|
+
assertAdmission: () => {
|
|
2772
|
+
assertAdmission();
|
|
2773
|
+
if (cancelledProcessRuns.has(id)) throw new import_job_resources.WorkerJobCancelledError();
|
|
2774
|
+
}
|
|
2775
|
+
});
|
|
2776
|
+
} finally {
|
|
2777
|
+
sendWorkerMessage(ws, { type: "heartbeat_lease", operationId, active: false });
|
|
2778
|
+
}
|
|
2779
|
+
}
|
|
2742
2780
|
async function executeCommand(input) {
|
|
2743
2781
|
const startedAt = Date.now();
|
|
2782
|
+
const outputAbort = new AbortController();
|
|
2783
|
+
let outputReaders = [];
|
|
2744
2784
|
let timeout;
|
|
2745
2785
|
let spawnedProcess;
|
|
2746
2786
|
let preparationSettled = false;
|
|
@@ -2761,7 +2801,7 @@ async function executeCommand(input) {
|
|
|
2761
2801
|
});
|
|
2762
2802
|
const cwd = resolveCommandCwd(input.resolvedTarget.rootPath, input.message.cwd);
|
|
2763
2803
|
input.assertAdmission();
|
|
2764
|
-
const subprocess = Bun.spawn(input.message.argv, {
|
|
2804
|
+
const subprocess = Bun.spawn(input.resources.wrap(input.message.argv), {
|
|
2765
2805
|
cwd,
|
|
2766
2806
|
stdout: "pipe",
|
|
2767
2807
|
stderr: "pipe",
|
|
@@ -2789,6 +2829,7 @@ async function executeCommand(input) {
|
|
|
2789
2829
|
settlePreparation();
|
|
2790
2830
|
if (input.message.timeoutMs) {
|
|
2791
2831
|
timeout = setTimeout(() => {
|
|
2832
|
+
input.resources.kill();
|
|
2792
2833
|
void (0, import_process_tree.terminateProcessTree)(subprocess).catch((error) => {
|
|
2793
2834
|
process.stderr.write(
|
|
2794
2835
|
`[r5d-worker] failed to terminate timed-out command ${input.message.runId}: ${error instanceof Error ? error.message : String(error)}
|
|
@@ -2797,21 +2838,26 @@ async function executeCommand(input) {
|
|
|
2797
2838
|
});
|
|
2798
2839
|
}, input.message.timeoutMs);
|
|
2799
2840
|
}
|
|
2800
|
-
|
|
2801
|
-
|
|
2802
|
-
|
|
2803
|
-
|
|
2804
|
-
]);
|
|
2841
|
+
outputReaders = [
|
|
2842
|
+
(0, import_job_resources.collectWorkerInlineOutput)(subprocess.stdout, void 0, outputAbort.signal),
|
|
2843
|
+
(0, import_job_resources.collectWorkerInlineOutput)(subprocess.stderr, void 0, outputAbort.signal)
|
|
2844
|
+
];
|
|
2845
|
+
const [stdout, stderr, exitCode] = await Promise.all([outputReaders[0], outputReaders[1], subprocess.exited]);
|
|
2805
2846
|
return {
|
|
2806
2847
|
type: "exec_result",
|
|
2807
2848
|
requestId: input.message.requestId,
|
|
2808
2849
|
stdout,
|
|
2809
2850
|
stderr,
|
|
2810
2851
|
exitCode,
|
|
2811
|
-
durationMs: Date.now() - startedAt
|
|
2852
|
+
durationMs: Date.now() - startedAt,
|
|
2853
|
+
...input.resources.memoryLimitExceeded() ? {
|
|
2854
|
+
error: `Command exceeded its worker memory limit (${input.resources.memoryBytes} bytes). Reduce parallelism or memory use and retry.`
|
|
2855
|
+
} : {}
|
|
2812
2856
|
};
|
|
2813
2857
|
} catch (error) {
|
|
2858
|
+
outputAbort.abort(error);
|
|
2814
2859
|
if (error instanceof StaleWorkerAdmissionError) throw error;
|
|
2860
|
+
input.resources.kill();
|
|
2815
2861
|
return {
|
|
2816
2862
|
type: "exec_result",
|
|
2817
2863
|
requestId: input.message.requestId,
|
|
@@ -2829,6 +2875,8 @@ async function executeCommand(input) {
|
|
|
2829
2875
|
if (spawnedProcess) {
|
|
2830
2876
|
await reapCompletedCredentialBearingProcessGroup(input.message.runId, spawnedProcess);
|
|
2831
2877
|
}
|
|
2878
|
+
outputAbort.abort();
|
|
2879
|
+
await Promise.allSettled(outputReaders);
|
|
2832
2880
|
activeProcesses.delete(input.message.runId);
|
|
2833
2881
|
}
|
|
2834
2882
|
}
|
|
@@ -2866,7 +2914,7 @@ async function executeStreamingCommand(input) {
|
|
|
2866
2914
|
const cwd = resolveCommandCwd(input.resolvedTarget.rootPath, input.message.cwd);
|
|
2867
2915
|
const interactive = input.message.interactive === true;
|
|
2868
2916
|
input.assertAdmission();
|
|
2869
|
-
const subprocess = Bun.spawn(input.message.argv, {
|
|
2917
|
+
const subprocess = Bun.spawn(input.resources.wrap(input.message.argv), {
|
|
2870
2918
|
cwd,
|
|
2871
2919
|
// Without an explicit stdin the process reads /dev/null and interactive
|
|
2872
2920
|
// prompts see immediate EOF; "pipe" keeps stdin open for exec_stdin.
|
|
@@ -2909,6 +2957,7 @@ async function executeStreamingCommand(input) {
|
|
|
2909
2957
|
if (input.message.timeoutMs) {
|
|
2910
2958
|
timeout = setTimeout(() => {
|
|
2911
2959
|
timedOut = true;
|
|
2960
|
+
input.resources.kill();
|
|
2912
2961
|
closeProcessStdin(activeProcesses.get(input.message.runId));
|
|
2913
2962
|
void (0, import_process_tree.terminateProcessTree)(subprocess).catch((error) => {
|
|
2914
2963
|
process.stderr.write(
|
|
@@ -2937,6 +2986,16 @@ async function executeStreamingCommand(input) {
|
|
|
2937
2986
|
});
|
|
2938
2987
|
})
|
|
2939
2988
|
]);
|
|
2989
|
+
if (input.resources.memoryLimitExceeded()) {
|
|
2990
|
+
sendWorkerMessage(input.ws, {
|
|
2991
|
+
type: "exec_output",
|
|
2992
|
+
runId: input.message.runId,
|
|
2993
|
+
stream: "stderr",
|
|
2994
|
+
data: `
|
|
2995
|
+
[r5d-worker] Command exceeded its memory limit (${input.resources.memoryBytes} bytes). Reduce parallelism or memory use and retry.
|
|
2996
|
+
`
|
|
2997
|
+
});
|
|
2998
|
+
}
|
|
2940
2999
|
const canonicalWorkspaceHead = canonicalSyncTerminalHead(input.resolvedTarget);
|
|
2941
3000
|
const terminal = {
|
|
2942
3001
|
type: "exec_exit",
|
|
@@ -3366,6 +3425,7 @@ async function openPty(input) {
|
|
|
3366
3425
|
}) : {};
|
|
3367
3426
|
const targetEnv = targetProcessEnv(input.rootDir, input.resolvedTarget.target);
|
|
3368
3427
|
const shell = resolveHostShell(input.message.command);
|
|
3428
|
+
const shellArgv = input.resources.wrap([shell.file, ...shell.args]);
|
|
3369
3429
|
const outputCoalescer = (0, import_pty_output_coalescer.createPtyOutputCoalescer)({
|
|
3370
3430
|
send: (data) => {
|
|
3371
3431
|
sendWorkerMessage(input.ws, {
|
|
@@ -3385,8 +3445,8 @@ async function openPty(input) {
|
|
|
3385
3445
|
};
|
|
3386
3446
|
try {
|
|
3387
3447
|
const ptyProcess = createNodePtyBridge({
|
|
3388
|
-
file:
|
|
3389
|
-
args:
|
|
3448
|
+
file: shellArgv[0],
|
|
3449
|
+
args: shellArgv.slice(1),
|
|
3390
3450
|
ptyOptions: {
|
|
3391
3451
|
name: "xterm-256color",
|
|
3392
3452
|
cols: Math.max(1, Math.min(Math.floor(input.message.cols || 80), 500)),
|
|
@@ -3415,11 +3475,18 @@ async function openPty(input) {
|
|
|
3415
3475
|
outputCoalescer.push(data);
|
|
3416
3476
|
},
|
|
3417
3477
|
onExit: (event) => {
|
|
3478
|
+
if (input.resources.memoryLimitExceeded())
|
|
3479
|
+
outputCoalescer.push(
|
|
3480
|
+
`\r
|
|
3481
|
+
[r5d-worker] Terminal exceeded its memory limit (${input.resources.memoryBytes} bytes). Reduce parallelism or memory use and retry.\r
|
|
3482
|
+
`
|
|
3483
|
+
);
|
|
3418
3484
|
outputCoalescer.flush();
|
|
3419
3485
|
input.releaseWorkspaceMutation?.();
|
|
3420
3486
|
activePtys.delete(input.message.ptyId);
|
|
3421
3487
|
cleanupEnvFiles();
|
|
3422
3488
|
input.onTerminal?.();
|
|
3489
|
+
void releaseWorkerJobResources(input.resources);
|
|
3423
3490
|
sendWorkerMessage(input.ws, {
|
|
3424
3491
|
type: "pty_exit",
|
|
3425
3492
|
ptyId: input.message.ptyId,
|
|
@@ -3433,6 +3500,7 @@ async function openPty(input) {
|
|
|
3433
3500
|
activePtys.delete(input.message.ptyId);
|
|
3434
3501
|
cleanupEnvFiles();
|
|
3435
3502
|
input.onTerminal?.();
|
|
3503
|
+
void releaseWorkerJobResources(input.resources);
|
|
3436
3504
|
sendWorkerMessage(input.ws, {
|
|
3437
3505
|
type: "pty_error",
|
|
3438
3506
|
requestId: input.message.requestId,
|
|
@@ -3482,6 +3550,7 @@ function resizePty(message) {
|
|
|
3482
3550
|
ptyProcess.resize(Math.max(1, Math.min(Math.floor(message.cols || 80), 500)), Math.max(1, Math.min(Math.floor(message.rows || 24), 500)));
|
|
3483
3551
|
}
|
|
3484
3552
|
function closePty(message) {
|
|
3553
|
+
workerJobResources?.cancel(`pty:${message.ptyId}`);
|
|
3485
3554
|
const ptyProcess = activePtys.get(message.ptyId);
|
|
3486
3555
|
if (!ptyProcess) {
|
|
3487
3556
|
return;
|
|
@@ -3500,7 +3569,7 @@ function closePtysBestEffort(ptys, reportFailure) {
|
|
|
3500
3569
|
function closeAllPtys() {
|
|
3501
3570
|
closePtysBestEffort(activePtys, (ptyId, error) => {
|
|
3502
3571
|
process.stderr.write(
|
|
3503
|
-
`[r5d-worker] failed to close PTY ${ptyId} after
|
|
3572
|
+
`[r5d-worker] failed to close PTY ${ptyId} after execution lease expiry: ${error instanceof Error ? error.message : String(error)}
|
|
3504
3573
|
`
|
|
3505
3574
|
);
|
|
3506
3575
|
});
|
|
@@ -3573,6 +3642,11 @@ async function startWorker(options, projectRuntime = {
|
|
|
3573
3642
|
initializeCredentialStoreDirectoryOnce();
|
|
3574
3643
|
const label = requireValue(options.label, "Missing required --label <label>");
|
|
3575
3644
|
validateLabel(label);
|
|
3645
|
+
if (!workerJobResources) {
|
|
3646
|
+
workerJobResources = (0, import_job_resources.createWorkerJobResources)();
|
|
3647
|
+
process.stdout.write(`[r5d-worker] job resources: ${workerJobResources.isolation}; ${JSON.stringify(workerJobResources.budget)}
|
|
3648
|
+
`);
|
|
3649
|
+
}
|
|
3576
3650
|
const rootDir = import_node_path.default.resolve(options.rootDir ?? process.env.R5D_ROOT ?? defaultRootDir());
|
|
3577
3651
|
recoveryStore ??= new import_recovery_store.WorkerRecoveryStore(
|
|
3578
3652
|
import_node_path.default.join(rootDir, ".worker-recovery", (0, import_node_crypto.createHash)("sha256").update(`${baseUrl}\0${label}`).digest("hex"), "protocol.sqlite")
|
|
@@ -5777,6 +5851,7 @@ async function startWorker(options, projectRuntime = {
|
|
|
5777
5851
|
}
|
|
5778
5852
|
if (message.type === "cancel_session") {
|
|
5779
5853
|
recoveryStore.cancelSession(message.sessionId);
|
|
5854
|
+
workerJobResources?.cancelSession(message.sessionId);
|
|
5780
5855
|
await Promise.all(
|
|
5781
5856
|
[...activeProcesses].filter(([, active]) => active.sessionId === message.sessionId).map(async ([runId, active]) => {
|
|
5782
5857
|
cancelledProcessRuns.add(runId);
|
|
@@ -5840,18 +5915,13 @@ async function startWorker(options, projectRuntime = {
|
|
|
5840
5915
|
const refreshesDeferredConfiguration = deferredWorkspaceConfiguration !== null && activeWorkspaceIncidentId === null && message.deferWorkspaceSyncForIncidentId === void 0;
|
|
5841
5916
|
const digest = (0, import_recovery_store.workerOperationDigest)(message);
|
|
5842
5917
|
const cached = projectRuntime.configuration;
|
|
5843
|
-
|
|
5844
|
-
|
|
5845
|
-
|
|
5846
|
-
|
|
5847
|
-
workspaceConfigured = true;
|
|
5848
|
-
activeWorkspaceIncidentId = cached.incidentId;
|
|
5849
|
-
deferredWorkspaceConfiguration = cached.deferred;
|
|
5918
|
+
const canReuseConfiguration = cached?.digest === digest && cached.workspaceSync?.remoteUrl === message.workspaceRemoteUrl && Boolean(cached.workspaceSync.credentialHelper) && cached.workspaceSync.credentialUsername === workerCredentialUsername && cached.workspaceSync.gitIdentity?.name === message.gitIdentity.name && cached.workspaceSync.gitIdentity?.email === message.gitIdentity.email && !unsafeWorkspaceSyncBouncePending && credentialGenerationFingerprint({ ...message, workerBaseUrl: baseUrl, workerAuthHeader: bearerAuthHeader }) === configuredCredentialGenerationFingerprint;
|
|
5919
|
+
if (!canReuseConfiguration) {
|
|
5920
|
+
advanceWorkerAdmissionGeneration();
|
|
5921
|
+
projectRuntime.configuration = void 0;
|
|
5850
5922
|
}
|
|
5851
|
-
|
|
5852
|
-
|
|
5853
|
-
if (configured.result.outcome !== "failed") unsafeWorkspaceSyncRecoveryAttempts = 0;
|
|
5854
|
-
sendWorkerMessageFromCurrentSource(ws, {
|
|
5923
|
+
const configured = canReuseConfiguration ? cached.configured : await configureWorkerWorkspace(message, receiptGeneration);
|
|
5924
|
+
const sendConfigured = () => sendWorkerMessageFromCurrentSource(ws, {
|
|
5855
5925
|
type: "workspace_configured",
|
|
5856
5926
|
requestId: message.requestId,
|
|
5857
5927
|
result: configured.result,
|
|
@@ -5859,6 +5929,34 @@ async function startWorker(options, projectRuntime = {
|
|
|
5859
5929
|
aheadOfOriginBranches: configured.aheadOfOriginBranches,
|
|
5860
5930
|
...configured.deferredWorkspaceSyncForIncidentId ? { deferredWorkspaceSyncForIncidentId: configured.deferredWorkspaceSyncForIncidentId } : {}
|
|
5861
5931
|
});
|
|
5932
|
+
if (receiptGeneration !== workspaceConfigurationReceiptGeneration || currentWorkerSocket !== ws || ws.readyState !== WebSocket.OPEN) {
|
|
5933
|
+
sendConfigured();
|
|
5934
|
+
return;
|
|
5935
|
+
}
|
|
5936
|
+
if (canReuseConfiguration && configured.result.outcome !== "failed") {
|
|
5937
|
+
workspaceRemoteUrl = cached.workspaceSync.remoteUrl;
|
|
5938
|
+
workspaceCredentialHelper = cached.workspaceSync.credentialHelper;
|
|
5939
|
+
workspaceCredentialUsername = cached.workspaceSync.credentialUsername;
|
|
5940
|
+
workspaceGitIdentity = cached.workspaceSync.gitIdentity;
|
|
5941
|
+
workspaceConfigured = true;
|
|
5942
|
+
activeWorkspaceIncidentId = cached.incidentId;
|
|
5943
|
+
deferredWorkspaceConfiguration = cached.deferred;
|
|
5944
|
+
}
|
|
5945
|
+
projectRuntime.configuration = configured.result.outcome !== "failed" && workspaceRemoteUrl && workspaceCredentialHelper && workspaceCredentialUsername && workspaceGitIdentity ? {
|
|
5946
|
+
digest,
|
|
5947
|
+
workspaceSync: {
|
|
5948
|
+
remoteUrl: workspaceRemoteUrl,
|
|
5949
|
+
credentialHelper: workspaceCredentialHelper,
|
|
5950
|
+
credentialUsername: workspaceCredentialUsername,
|
|
5951
|
+
gitIdentity: workspaceGitIdentity
|
|
5952
|
+
},
|
|
5953
|
+
configured,
|
|
5954
|
+
incidentId: activeWorkspaceIncidentId,
|
|
5955
|
+
deferred: deferredWorkspaceConfiguration
|
|
5956
|
+
} : void 0;
|
|
5957
|
+
unsafeWorkspaceSyncBouncePending = false;
|
|
5958
|
+
if (configured.result.outcome !== "failed") unsafeWorkspaceSyncRecoveryAttempts = 0;
|
|
5959
|
+
sendConfigured();
|
|
5862
5960
|
sendLifecycleProgress(
|
|
5863
5961
|
configured.result.outcome === "failed" ? {
|
|
5864
5962
|
phase: "failed",
|
|
@@ -6240,8 +6338,14 @@ async function startWorker(options, projectRuntime = {
|
|
|
6240
6338
|
return;
|
|
6241
6339
|
}
|
|
6242
6340
|
if (message.type === "cancel") {
|
|
6341
|
+
let resourceCancellationError;
|
|
6342
|
+
try {
|
|
6343
|
+
workerJobResources?.cancel(message.runId);
|
|
6344
|
+
} catch (error) {
|
|
6345
|
+
resourceCancellationError = error instanceof Error ? error.message : String(error);
|
|
6346
|
+
}
|
|
6243
6347
|
const active = activeProcesses.get(message.runId);
|
|
6244
|
-
let cancelled =
|
|
6348
|
+
let cancelled = resourceCancellationError === void 0;
|
|
6245
6349
|
let cancelMessage;
|
|
6246
6350
|
if (active) {
|
|
6247
6351
|
try {
|
|
@@ -6256,6 +6360,7 @@ async function startWorker(options, projectRuntime = {
|
|
|
6256
6360
|
cancelledProcessRuns.add(message.runId);
|
|
6257
6361
|
cancelMessage = "Cancellation queued before command start";
|
|
6258
6362
|
}
|
|
6363
|
+
if (resourceCancellationError) cancelMessage += `; job resource termination failed: ${resourceCancellationError}`;
|
|
6259
6364
|
sendSerializedWorkerMessage(
|
|
6260
6365
|
ws,
|
|
6261
6366
|
JSON.stringify({
|
|
@@ -6342,13 +6447,16 @@ async function startWorker(options, projectRuntime = {
|
|
|
6342
6447
|
});
|
|
6343
6448
|
return;
|
|
6344
6449
|
}
|
|
6345
|
-
|
|
6346
|
-
|
|
6347
|
-
if (targetMayMutateVisibleWorkspace(message.target)) recordVisibleWorkspaceMutation(message.target);
|
|
6348
|
-
});
|
|
6450
|
+
let resources;
|
|
6451
|
+
let resourcesTransferred = false;
|
|
6349
6452
|
let releaseWorkspaceMutation;
|
|
6350
6453
|
let mutationLeaseTransferred = false;
|
|
6351
6454
|
try {
|
|
6455
|
+
resources = await acquireWorkerCommandResources(ws, message, assertMessageAdmission);
|
|
6456
|
+
await (0, import_workspace_command_sync_policy.reserveWorkspaceCommandAfterCurrentSync)(message.target, workspaceSyncSingleFlight, () => {
|
|
6457
|
+
workspaceSyncPriorityPtyTargets.set(message.ptyId, message.target);
|
|
6458
|
+
if (targetMayMutateVisibleWorkspace(message.target)) recordVisibleWorkspaceMutation(message.target);
|
|
6459
|
+
});
|
|
6352
6460
|
releaseWorkspaceMutation = await (0, import_workspace_command_sync_policy.acquireWorkspaceCommandMutation)(message.target, workspaceSyncSingleFlight);
|
|
6353
6461
|
if (workspaceOperationsAreFenced(deferredWorkspaceConfiguration, activeWorkspaceIncidentId)) {
|
|
6354
6462
|
throw new Error(
|
|
@@ -6359,6 +6467,7 @@ async function startWorker(options, projectRuntime = {
|
|
|
6359
6467
|
process.stdout.write(`[r5d-worker] pty ${message.ptyId}: ${describeWorkerSessionTarget(message.target)}
|
|
6360
6468
|
`);
|
|
6361
6469
|
await openPty({
|
|
6470
|
+
resources,
|
|
6362
6471
|
ws,
|
|
6363
6472
|
message,
|
|
6364
6473
|
resolvedTarget,
|
|
@@ -6371,6 +6480,7 @@ async function startWorker(options, projectRuntime = {
|
|
|
6371
6480
|
}
|
|
6372
6481
|
} : {}
|
|
6373
6482
|
});
|
|
6483
|
+
resourcesTransferred = true;
|
|
6374
6484
|
mutationLeaseTransferred = releaseWorkspaceMutation !== void 0;
|
|
6375
6485
|
} catch (error) {
|
|
6376
6486
|
sendWorkerMessageFromCurrentSource(ws, {
|
|
@@ -6382,6 +6492,7 @@ async function startWorker(options, projectRuntime = {
|
|
|
6382
6492
|
} finally {
|
|
6383
6493
|
workspaceSyncPriorityPtyTargets.delete(message.ptyId);
|
|
6384
6494
|
if (!mutationLeaseTransferred) releaseWorkspaceMutation?.();
|
|
6495
|
+
if (!resourcesTransferred) await releaseWorkerJobResources(resources);
|
|
6385
6496
|
}
|
|
6386
6497
|
return;
|
|
6387
6498
|
}
|
|
@@ -6440,9 +6551,11 @@ async function startWorker(options, projectRuntime = {
|
|
|
6440
6551
|
return;
|
|
6441
6552
|
}
|
|
6442
6553
|
let result;
|
|
6554
|
+
let resources;
|
|
6443
6555
|
let targetReserved = false;
|
|
6444
6556
|
const hasWorkspaceEffect = workerCommandHasWorkspaceEffect(message);
|
|
6445
6557
|
try {
|
|
6558
|
+
resources = await acquireWorkerCommandResources(ws, message, assertMessageAdmission);
|
|
6446
6559
|
if (hasWorkspaceEffect) {
|
|
6447
6560
|
await (0, import_workspace_command_sync_policy.reserveWorkspaceCommandAfterCurrentSync)(message.target, workspaceSyncSingleFlight, () => {
|
|
6448
6561
|
workspaceSyncPriorityProcessTargets.set(message.runId, message.target);
|
|
@@ -6460,6 +6573,7 @@ async function startWorker(options, projectRuntime = {
|
|
|
6460
6573
|
process.stdout.write(`[r5d-worker] exec ${message.runId}: ${message.argv.join(" ")}
|
|
6461
6574
|
`);
|
|
6462
6575
|
return await executeCommand({
|
|
6576
|
+
resources,
|
|
6463
6577
|
message,
|
|
6464
6578
|
resolvedTarget,
|
|
6465
6579
|
baseUrl,
|
|
@@ -6495,6 +6609,8 @@ async function startWorker(options, projectRuntime = {
|
|
|
6495
6609
|
};
|
|
6496
6610
|
} finally {
|
|
6497
6611
|
if (targetReserved) workspaceSyncPriorityProcessTargets.delete(message.runId);
|
|
6612
|
+
await releaseWorkerJobResources(resources);
|
|
6613
|
+
cancelledProcessRuns.delete(message.runId);
|
|
6498
6614
|
}
|
|
6499
6615
|
if (infrastructureStoppedRunIds.has(message.runId)) {
|
|
6500
6616
|
recoveryStore.unknown(message.requestId);
|
|
@@ -6526,12 +6642,14 @@ async function startWorker(options, projectRuntime = {
|
|
|
6526
6642
|
runId: message.runId
|
|
6527
6643
|
});
|
|
6528
6644
|
const hasWorkspaceEffect = workerCommandHasWorkspaceEffect(message);
|
|
6645
|
+
let resources;
|
|
6529
6646
|
const runCommand = async () => {
|
|
6530
6647
|
try {
|
|
6531
6648
|
const resolvedTarget = resolveMessageTarget(message.target);
|
|
6532
6649
|
process.stdout.write(`[r5d-worker] exec_start ${message.runId}: ${message.argv.join(" ")}
|
|
6533
6650
|
`);
|
|
6534
6651
|
await executeStreamingCommand({
|
|
6652
|
+
resources,
|
|
6535
6653
|
ws,
|
|
6536
6654
|
message,
|
|
6537
6655
|
resolvedTarget,
|
|
@@ -6565,6 +6683,7 @@ async function startWorker(options, projectRuntime = {
|
|
|
6565
6683
|
const execution = (async () => {
|
|
6566
6684
|
let targetReserved = false;
|
|
6567
6685
|
try {
|
|
6686
|
+
resources = await acquireWorkerCommandResources(ws, message, assertMessageAdmission);
|
|
6568
6687
|
if (hasWorkspaceEffect) {
|
|
6569
6688
|
await (0, import_workspace_command_sync_policy.reserveWorkspaceCommandAfterCurrentSync)(message.target, workspaceSyncSingleFlight, () => {
|
|
6570
6689
|
workspaceSyncPriorityProcessTargets.set(message.runId, message.target);
|
|
@@ -6580,6 +6699,8 @@ async function startWorker(options, projectRuntime = {
|
|
|
6580
6699
|
});
|
|
6581
6700
|
} finally {
|
|
6582
6701
|
if (targetReserved) workspaceSyncPriorityProcessTargets.delete(message.runId);
|
|
6702
|
+
await releaseWorkerJobResources(resources);
|
|
6703
|
+
cancelledProcessRuns.delete(message.runId);
|
|
6583
6704
|
}
|
|
6584
6705
|
})();
|
|
6585
6706
|
void execution.catch((error) => {
|
|
@@ -6701,7 +6822,7 @@ async function startWorker(options, projectRuntime = {
|
|
|
6701
6822
|
clearInterval(terminalReplayTimer);
|
|
6702
6823
|
terminalReplayTimer = void 0;
|
|
6703
6824
|
}
|
|
6704
|
-
if (projectRuntime.configuration) {
|
|
6825
|
+
if (currentWorkerSocket === ws && projectRuntime.configuration) {
|
|
6705
6826
|
projectRuntime.configuration.incidentId = activeWorkspaceIncidentId;
|
|
6706
6827
|
projectRuntime.configuration.deferred = deferredWorkspaceConfiguration;
|
|
6707
6828
|
}
|
package/dist/cjs/package.json
CHANGED