@ricsam/r5d-worker 0.0.80 → 0.0.82
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 +7 -1
- package/dist/cjs/main.cjs +331 -98
- package/dist/cjs/package.json +1 -1
- package/dist/cjs/workspace-command-sync-policy.cjs +3 -3
- package/dist/cjs/workspace-git-sync.cjs +377 -122
- package/dist/cjs/workspace-merge-projection.cjs +392 -0
- package/dist/mjs/main.mjs +329 -98
- package/dist/mjs/package.json +1 -1
- package/dist/mjs/workspace-command-sync-policy.mjs +3 -3
- package/dist/mjs/workspace-git-sync.mjs +381 -122
- package/dist/mjs/workspace-merge-projection.mjs +355 -0
- package/dist/types/main.d.ts +63 -0
- package/dist/types/working-tree-mirror.d.ts +1 -2
- package/dist/types/workspace-command-sync-policy.d.ts +4 -3
- package/dist/types/workspace-git-sync.d.ts +6 -0
- package/dist/types/workspace-merge-projection.d.ts +42 -0
- package/package.json +1 -1
package/dist/cjs/main.cjs
CHANGED
|
@@ -50,6 +50,8 @@ __export(main_exports, {
|
|
|
50
50
|
syncSessionArtifacts: () => syncSessionArtifacts,
|
|
51
51
|
workerChildProcessEnvironment: () => workerChildProcessEnvironment,
|
|
52
52
|
workerGitSecurityTestHarness: () => workerGitSecurityTestHarness,
|
|
53
|
+
workerPtyBridgeTestHarness: () => workerPtyBridgeTestHarness,
|
|
54
|
+
workerPtyTestHarness: () => workerPtyTestHarness,
|
|
53
55
|
writeWorkerTextFile: () => writeWorkerTextFile
|
|
54
56
|
});
|
|
55
57
|
module.exports = __toCommonJS(main_exports);
|
|
@@ -95,6 +97,10 @@ const DEFAULT_READ_MAX_BYTES = 5e4;
|
|
|
95
97
|
const MAX_LINE_LENGTH = 2e3;
|
|
96
98
|
const WORKSPACE_GIT_QUIET_MS = 5e3;
|
|
97
99
|
const WORKSPACE_GIT_PERIODIC_MS = 6e4;
|
|
100
|
+
const PTY_INPUT_BUSY_GRACE_MS = 3e3;
|
|
101
|
+
const PTY_FOREGROUND_POLL_MS = 1e3;
|
|
102
|
+
const PTY_FOREGROUND_IDLE_ENABLED = process.env.R5D_PTY_FOREGROUND_IDLE !== "0";
|
|
103
|
+
const PTY_TMP_PATH_PREFIX = "r5d-worker-tmp://";
|
|
98
104
|
const activeProcesses = /* @__PURE__ */ new Map();
|
|
99
105
|
const credentialBearingProcessGroups = /* @__PURE__ */ new Map();
|
|
100
106
|
const credentialBearingProcessGroupTargets = /* @__PURE__ */ new Map();
|
|
@@ -152,6 +158,106 @@ function assertWorkerChildAdmission(input) {
|
|
|
152
158
|
throw new StaleWorkerAdmissionError();
|
|
153
159
|
}
|
|
154
160
|
}
|
|
161
|
+
function pathIsInsideRoot(rootPath, candidatePath) {
|
|
162
|
+
const relative = import_node_path.default.relative(rootPath, candidatePath);
|
|
163
|
+
return relative === "" || !relative.startsWith(`..${import_node_path.default.sep}`) && relative !== ".." && !import_node_path.default.isAbsolute(relative);
|
|
164
|
+
}
|
|
165
|
+
function resolvePtyEnvFilePath(requestedPath, temporaryRoot = import_node_os.default.tmpdir()) {
|
|
166
|
+
const canonicalTemporaryRoot = import_node_fs.default.realpathSync.native(temporaryRoot);
|
|
167
|
+
if (requestedPath.startsWith(PTY_TMP_PATH_PREFIX)) {
|
|
168
|
+
const filename = requestedPath.slice(PTY_TMP_PATH_PREFIX.length);
|
|
169
|
+
if (!/^[A-Za-z0-9][A-Za-z0-9._-]{0,199}$/.test(filename)) {
|
|
170
|
+
throw new Error("PTY environment file temporary token must contain one safe filename");
|
|
171
|
+
}
|
|
172
|
+
return import_node_path.default.join(canonicalTemporaryRoot, filename);
|
|
173
|
+
}
|
|
174
|
+
if (!import_node_path.default.isAbsolute(requestedPath)) {
|
|
175
|
+
throw new Error(`PTY environment file path must be absolute or use ${PTY_TMP_PATH_PREFIX}`);
|
|
176
|
+
}
|
|
177
|
+
const resolvedPath = import_node_path.default.resolve(requestedPath);
|
|
178
|
+
const canonicalParent = import_node_fs.default.realpathSync.native(import_node_path.default.dirname(resolvedPath));
|
|
179
|
+
const canonicalPath = import_node_path.default.join(canonicalParent, import_node_path.default.basename(resolvedPath));
|
|
180
|
+
if (!pathIsInsideRoot(canonicalTemporaryRoot, canonicalPath) || canonicalPath === canonicalTemporaryRoot) {
|
|
181
|
+
throw new Error(`PTY environment file path must be inside ${canonicalTemporaryRoot}`);
|
|
182
|
+
}
|
|
183
|
+
return canonicalPath;
|
|
184
|
+
}
|
|
185
|
+
function removePtyEnvFiles(paths) {
|
|
186
|
+
for (const filePath of paths) {
|
|
187
|
+
try {
|
|
188
|
+
import_node_fs.default.rmSync(filePath, { force: true });
|
|
189
|
+
} catch (error) {
|
|
190
|
+
process.stderr.write(
|
|
191
|
+
`[r5d-worker] failed to remove PTY environment file ${filePath}: ${error instanceof Error ? error.message : String(error)}
|
|
192
|
+
`
|
|
193
|
+
);
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
function stagePtyEnvFiles(envFiles, temporaryRoot = import_node_os.default.tmpdir()) {
|
|
198
|
+
const staged = { paths: [], resolvedByRequestedPath: /* @__PURE__ */ new Map() };
|
|
199
|
+
try {
|
|
200
|
+
for (const envFile of envFiles ?? []) {
|
|
201
|
+
if (!envFile || typeof envFile.path !== "string" || typeof envFile.content !== "string") {
|
|
202
|
+
throw new Error("PTY environment files require string path and content values");
|
|
203
|
+
}
|
|
204
|
+
if (envFile.mode !== void 0 && envFile.mode !== 384) {
|
|
205
|
+
throw new Error("PTY environment files must use mode 0600");
|
|
206
|
+
}
|
|
207
|
+
const resolvedPath = resolvePtyEnvFilePath(envFile.path, temporaryRoot);
|
|
208
|
+
if (staged.resolvedByRequestedPath.has(envFile.path) || staged.paths.includes(resolvedPath)) {
|
|
209
|
+
throw new Error(`Duplicate PTY environment file path: ${envFile.path}`);
|
|
210
|
+
}
|
|
211
|
+
const descriptor = import_node_fs.default.openSync(
|
|
212
|
+
resolvedPath,
|
|
213
|
+
import_node_fs.default.constants.O_WRONLY | import_node_fs.default.constants.O_CREAT | import_node_fs.default.constants.O_EXCL | (typeof import_node_fs.default.constants.O_NOFOLLOW === "number" ? import_node_fs.default.constants.O_NOFOLLOW : 0),
|
|
214
|
+
384
|
|
215
|
+
);
|
|
216
|
+
staged.paths.push(resolvedPath);
|
|
217
|
+
staged.resolvedByRequestedPath.set(envFile.path, resolvedPath);
|
|
218
|
+
try {
|
|
219
|
+
import_node_fs.default.writeFileSync(descriptor, envFile.content, "utf8");
|
|
220
|
+
import_node_fs.default.fchmodSync(descriptor, 384);
|
|
221
|
+
import_node_fs.default.fsyncSync(descriptor);
|
|
222
|
+
} finally {
|
|
223
|
+
import_node_fs.default.closeSync(descriptor);
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
return staged;
|
|
227
|
+
} catch (error) {
|
|
228
|
+
removePtyEnvFiles(staged.paths);
|
|
229
|
+
throw error;
|
|
230
|
+
}
|
|
231
|
+
}
|
|
232
|
+
function resolvePtyEnvFileReferences(env, staged) {
|
|
233
|
+
return Object.fromEntries(Object.entries(env).map(([name, value]) => [name, staged.resolvedByRequestedPath.get(value) ?? value]));
|
|
234
|
+
}
|
|
235
|
+
function workerCommandHasWorkspaceEffect(message) {
|
|
236
|
+
return message.workspaceEffect !== "none";
|
|
237
|
+
}
|
|
238
|
+
function workerPtyIsWorkspaceBusy(pty, now = Date.now(), foregroundIdleEnabled = PTY_FOREGROUND_IDLE_ENABLED) {
|
|
239
|
+
return !foregroundIdleEnabled || pty.foregroundBusy || now - pty.lastInputAt < PTY_INPUT_BUSY_GRACE_MS;
|
|
240
|
+
}
|
|
241
|
+
function parseLinuxPtyForegroundBusy(stat) {
|
|
242
|
+
const commandEnd = stat.lastIndexOf(")");
|
|
243
|
+
if (commandEnd < 0) return null;
|
|
244
|
+
const fields = stat.slice(commandEnd + 1).trim().split(/\s+/);
|
|
245
|
+
const processGroup = Number(fields[2]);
|
|
246
|
+
const foregroundProcessGroup = Number(fields[5]);
|
|
247
|
+
if (!Number.isSafeInteger(processGroup) || processGroup <= 0 || !Number.isSafeInteger(foregroundProcessGroup)) return null;
|
|
248
|
+
return foregroundProcessGroup !== processGroup;
|
|
249
|
+
}
|
|
250
|
+
const workerPtyTestHarness = {
|
|
251
|
+
temporaryPathPrefix: PTY_TMP_PATH_PREFIX,
|
|
252
|
+
inputBusyGraceMs: PTY_INPUT_BUSY_GRACE_MS,
|
|
253
|
+
resolveEnvFilePath: resolvePtyEnvFilePath,
|
|
254
|
+
stageEnvFiles: stagePtyEnvFiles,
|
|
255
|
+
removeEnvFiles: removePtyEnvFiles,
|
|
256
|
+
resolveEnvFileReferences: resolvePtyEnvFileReferences,
|
|
257
|
+
commandHasWorkspaceEffect: workerCommandHasWorkspaceEffect,
|
|
258
|
+
ptyIsWorkspaceBusy: workerPtyIsWorkspaceBusy,
|
|
259
|
+
parseLinuxForegroundBusy: parseLinuxPtyForegroundBusy
|
|
260
|
+
};
|
|
155
261
|
function defaultConfigPath() {
|
|
156
262
|
return import_node_path.default.join(import_node_os.default.homedir(), ".config", "r5d", "r5dctl", "config.json");
|
|
157
263
|
}
|
|
@@ -629,7 +735,7 @@ function preparePlanEnvForShell(input) {
|
|
|
629
735
|
import_node_fs.default.mkdirSync(plansDir, { recursive: true });
|
|
630
736
|
return planEnv(input.planRoot, input.target, input.activePlanId);
|
|
631
737
|
}
|
|
632
|
-
async function verifyR5dctlAuth(baseUrl, token) {
|
|
738
|
+
async function verifyR5dctlAuth(baseUrl, token, workerLabel) {
|
|
633
739
|
const statusUrl = new URL("/api/r5dctl/auth/status", baseUrl);
|
|
634
740
|
let response;
|
|
635
741
|
try {
|
|
@@ -658,13 +764,16 @@ async function verifyR5dctlAuth(baseUrl, token) {
|
|
|
658
764
|
throw new WorkerServerUnavailableError(`Server unavailable at ${baseUrl} (${response.status})${detail}`);
|
|
659
765
|
}
|
|
660
766
|
throw new Error(
|
|
661
|
-
`Authentication failed against ${baseUrl} (${response.status})${detail}. Run \`r5dctl auth login --base-url ${baseUrl}\` or pass
|
|
767
|
+
`Authentication failed against ${baseUrl} (${response.status})${detail}. Run \`r5dctl auth login --base-url ${baseUrl} --worker-label ${workerLabel}\` or pass that exact label-bound worker token.`
|
|
662
768
|
);
|
|
663
769
|
}
|
|
664
770
|
function resolveCredentials(options, config) {
|
|
665
771
|
const token = options.token ?? process.env.R5D_WORKER_TOKEN ?? process.env.R5D_TOKEN ?? process.env.R5DCTL_TOKEN ?? config.token ?? options.apiKey ?? process.env.R5D_API_KEY ?? process.env.R5DCTL_API_KEY ?? config.apiKey;
|
|
666
772
|
if (!token) {
|
|
667
|
-
|
|
773
|
+
const workerLabel = options.label ?? "<label>";
|
|
774
|
+
throw new Error(
|
|
775
|
+
`Authentication required. Run \`r5dctl auth login --worker-label ${workerLabel}\` or set R5D_WORKER_TOKEN to that label-bound token.`
|
|
776
|
+
);
|
|
668
777
|
}
|
|
669
778
|
return {
|
|
670
779
|
baseUrl: normalizeBaseUrl(
|
|
@@ -2330,7 +2439,9 @@ async function executeCommand(input) {
|
|
|
2330
2439
|
});
|
|
2331
2440
|
spawnedProcess = subprocess;
|
|
2332
2441
|
credentialBearingProcessGroups.set(subprocess.pid, subprocess);
|
|
2333
|
-
|
|
2442
|
+
if (workerCommandHasWorkspaceEffect(input.message)) {
|
|
2443
|
+
credentialBearingProcessGroupTargets.set(subprocess.pid, input.resolvedTarget.target);
|
|
2444
|
+
}
|
|
2334
2445
|
activeProcesses.set(input.message.runId, {
|
|
2335
2446
|
process: subprocess,
|
|
2336
2447
|
target: input.resolvedTarget.target,
|
|
@@ -2341,7 +2452,8 @@ async function executeCommand(input) {
|
|
|
2341
2452
|
argv: input.message.argv,
|
|
2342
2453
|
command: input.message.argv.join(" "),
|
|
2343
2454
|
cwd,
|
|
2344
|
-
startedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
2455
|
+
startedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
2456
|
+
...input.message.workspaceEffect === "none" ? { workspaceEffect: "none" } : {}
|
|
2345
2457
|
});
|
|
2346
2458
|
if (input.message.timeoutMs) {
|
|
2347
2459
|
timeout = setTimeout(() => {
|
|
@@ -2427,7 +2539,9 @@ async function executeStreamingCommand(input) {
|
|
|
2427
2539
|
});
|
|
2428
2540
|
spawnedProcess = subprocess;
|
|
2429
2541
|
credentialBearingProcessGroups.set(subprocess.pid, subprocess);
|
|
2430
|
-
|
|
2542
|
+
if (workerCommandHasWorkspaceEffect(input.message)) {
|
|
2543
|
+
credentialBearingProcessGroupTargets.set(subprocess.pid, input.resolvedTarget.target);
|
|
2544
|
+
}
|
|
2431
2545
|
activeProcesses.set(input.message.runId, {
|
|
2432
2546
|
process: subprocess,
|
|
2433
2547
|
target: input.resolvedTarget.target,
|
|
@@ -2440,7 +2554,8 @@ async function executeStreamingCommand(input) {
|
|
|
2440
2554
|
command: input.message.command,
|
|
2441
2555
|
cwd,
|
|
2442
2556
|
startedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
2443
|
-
...interactive ? { interactive: true, stdin: subprocess.stdin } : {}
|
|
2557
|
+
...interactive ? { interactive: true, stdin: subprocess.stdin } : {},
|
|
2558
|
+
...input.message.workspaceEffect === "none" ? { workspaceEffect: "none" } : {}
|
|
2444
2559
|
});
|
|
2445
2560
|
started = true;
|
|
2446
2561
|
sendWorkerMessage(input.ws, {
|
|
@@ -2487,7 +2602,8 @@ async function executeStreamingCommand(input) {
|
|
|
2487
2602
|
runId: input.message.runId,
|
|
2488
2603
|
exitCode,
|
|
2489
2604
|
durationMs: Date.now() - startedAt,
|
|
2490
|
-
...timedOut ? { timedOut: true } : {}
|
|
2605
|
+
...timedOut ? { timedOut: true } : {},
|
|
2606
|
+
...input.message.workspaceEffect === "none" ? { workspaceEffect: "none" } : {}
|
|
2491
2607
|
};
|
|
2492
2608
|
pendingProcessTerminals.set(input.message.runId, terminal);
|
|
2493
2609
|
sendWorkerMessage(input.ws, terminal);
|
|
@@ -2499,7 +2615,8 @@ async function executeStreamingCommand(input) {
|
|
|
2499
2615
|
type: "exec_error",
|
|
2500
2616
|
runId: input.message.runId,
|
|
2501
2617
|
error: message,
|
|
2502
|
-
durationMs: Date.now() - startedAt
|
|
2618
|
+
durationMs: Date.now() - startedAt,
|
|
2619
|
+
...input.message.workspaceEffect === "none" ? { workspaceEffect: "none" } : {}
|
|
2503
2620
|
};
|
|
2504
2621
|
pendingProcessTerminals.set(input.message.runId, terminal);
|
|
2505
2622
|
sendWorkerMessage(input.ws, terminal);
|
|
@@ -2580,7 +2697,8 @@ function buildActiveProcessReports() {
|
|
|
2580
2697
|
command: active.command,
|
|
2581
2698
|
...active.cwd ? { cwd: active.cwd } : {},
|
|
2582
2699
|
startedAt: active.startedAt,
|
|
2583
|
-
...active.interactive ? { interactive: true } : {}
|
|
2700
|
+
...active.interactive ? { interactive: true } : {},
|
|
2701
|
+
...active.workspaceEffect === "none" ? { workspaceEffect: "none" } : {}
|
|
2584
2702
|
}));
|
|
2585
2703
|
}
|
|
2586
2704
|
function sendActiveProcessReport(ws) {
|
|
@@ -2592,8 +2710,13 @@ function sendActiveProcessReport(ws) {
|
|
|
2592
2710
|
const PTY_BRIDGE_SCRIPT = String.raw`
|
|
2593
2711
|
const readline = require("node:readline");
|
|
2594
2712
|
const nodePty = require("node-pty");
|
|
2713
|
+
const fs = require("node:fs");
|
|
2714
|
+
const { execFile } = require("node:child_process");
|
|
2595
2715
|
|
|
2596
2716
|
let ptyProcess = null;
|
|
2717
|
+
let foregroundTimer = null;
|
|
2718
|
+
let foregroundPollInFlight = false;
|
|
2719
|
+
let lastForegroundBusy = null;
|
|
2597
2720
|
|
|
2598
2721
|
function send(message, callback) {
|
|
2599
2722
|
process.stdout.write(JSON.stringify(message) + "\n", callback);
|
|
@@ -2603,6 +2726,46 @@ function decode(data) {
|
|
|
2603
2726
|
return Buffer.from(data, "base64").toString("utf8");
|
|
2604
2727
|
}
|
|
2605
2728
|
|
|
2729
|
+
function emitForeground(busy) {
|
|
2730
|
+
if (busy === lastForegroundBusy) return;
|
|
2731
|
+
lastForegroundBusy = busy;
|
|
2732
|
+
send({ type: "foreground", busy });
|
|
2733
|
+
}
|
|
2734
|
+
|
|
2735
|
+
function linuxForegroundBusy(pid) {
|
|
2736
|
+
const stat = fs.readFileSync("/proc/" + pid + "/stat", "utf8");
|
|
2737
|
+
const commandEnd = stat.lastIndexOf(")");
|
|
2738
|
+
if (commandEnd < 0) throw new Error("invalid /proc stat");
|
|
2739
|
+
const fields = stat.slice(commandEnd + 1).trim().split(/\s+/);
|
|
2740
|
+
const processGroup = Number(fields[2]);
|
|
2741
|
+
const foregroundProcessGroup = Number(fields[5]);
|
|
2742
|
+
if (!Number.isSafeInteger(processGroup) || processGroup <= 0 || !Number.isSafeInteger(foregroundProcessGroup)) {
|
|
2743
|
+
throw new Error("invalid process group fields");
|
|
2744
|
+
}
|
|
2745
|
+
return foregroundProcessGroup !== processGroup;
|
|
2746
|
+
}
|
|
2747
|
+
|
|
2748
|
+
function pollForeground() {
|
|
2749
|
+
if (!ptyProcess || !Number.isSafeInteger(ptyProcess.pid) || ptyProcess.pid <= 0) return;
|
|
2750
|
+
if (process.platform === "linux") {
|
|
2751
|
+
try {
|
|
2752
|
+
emitForeground(linuxForegroundBusy(ptyProcess.pid));
|
|
2753
|
+
} catch {
|
|
2754
|
+
emitForeground(true);
|
|
2755
|
+
}
|
|
2756
|
+
return;
|
|
2757
|
+
}
|
|
2758
|
+
if (process.platform !== "darwin" || foregroundPollInFlight) return;
|
|
2759
|
+
foregroundPollInFlight = true;
|
|
2760
|
+
const pid = ptyProcess.pid;
|
|
2761
|
+
execFile("/bin/ps", ["-o", "tpgid=", "-p", String(pid)], (error, stdout) => {
|
|
2762
|
+
foregroundPollInFlight = false;
|
|
2763
|
+
if (!ptyProcess || ptyProcess.pid !== pid) return;
|
|
2764
|
+
const foregroundProcessGroup = Number(String(stdout).trim());
|
|
2765
|
+
emitForeground(Boolean(error) || !Number.isSafeInteger(foregroundProcessGroup) || foregroundProcessGroup !== pid);
|
|
2766
|
+
});
|
|
2767
|
+
}
|
|
2768
|
+
|
|
2606
2769
|
const rl = readline.createInterface({ input: process.stdin });
|
|
2607
2770
|
|
|
2608
2771
|
rl.on("line", (line) => {
|
|
@@ -2621,9 +2784,14 @@ rl.on("line", (line) => {
|
|
|
2621
2784
|
send({ type: "output", data: Buffer.from(data, "utf8").toString("base64") });
|
|
2622
2785
|
});
|
|
2623
2786
|
ptyProcess.onExit((event) => {
|
|
2787
|
+
if (foregroundTimer) clearInterval(foregroundTimer);
|
|
2788
|
+
foregroundTimer = null;
|
|
2624
2789
|
send({ type: "exit", exitCode: event.exitCode, signal: event.signal }, () => process.exit(0));
|
|
2625
2790
|
});
|
|
2626
|
-
send({ type: "opened" });
|
|
2791
|
+
send({ type: "opened", pid: ptyProcess.pid });
|
|
2792
|
+
pollForeground();
|
|
2793
|
+
foregroundTimer = setInterval(pollForeground, ${PTY_FOREGROUND_POLL_MS});
|
|
2794
|
+
foregroundTimer.unref();
|
|
2627
2795
|
return;
|
|
2628
2796
|
}
|
|
2629
2797
|
|
|
@@ -2716,7 +2884,11 @@ function createNodePtyBridge(options) {
|
|
|
2716
2884
|
};
|
|
2717
2885
|
const handleEvent = (event) => {
|
|
2718
2886
|
if (event.type === "opened") {
|
|
2719
|
-
options.onOpened();
|
|
2887
|
+
options.onOpened(event.pid);
|
|
2888
|
+
return;
|
|
2889
|
+
}
|
|
2890
|
+
if (event.type === "foreground") {
|
|
2891
|
+
options.onForeground(event.busy);
|
|
2720
2892
|
return;
|
|
2721
2893
|
}
|
|
2722
2894
|
if (event.type === "output") {
|
|
@@ -2797,6 +2969,9 @@ function createNodePtyBridge(options) {
|
|
|
2797
2969
|
}
|
|
2798
2970
|
};
|
|
2799
2971
|
}
|
|
2972
|
+
const workerPtyBridgeTestHarness = {
|
|
2973
|
+
create: createNodePtyBridge
|
|
2974
|
+
};
|
|
2800
2975
|
function resolveHostShell(command, platform = process.platform) {
|
|
2801
2976
|
if (platform === "win32") {
|
|
2802
2977
|
const file2 = process.env.COMSPEC || "powershell.exe";
|
|
@@ -2832,64 +3007,90 @@ async function openPty(input) {
|
|
|
2832
3007
|
}
|
|
2833
3008
|
});
|
|
2834
3009
|
input.assertAdmission();
|
|
2835
|
-
const
|
|
2836
|
-
|
|
2837
|
-
|
|
2838
|
-
|
|
2839
|
-
|
|
2840
|
-
|
|
2841
|
-
|
|
2842
|
-
|
|
2843
|
-
|
|
2844
|
-
|
|
2845
|
-
|
|
2846
|
-
|
|
2847
|
-
|
|
2848
|
-
|
|
2849
|
-
|
|
2850
|
-
|
|
2851
|
-
|
|
2852
|
-
|
|
2853
|
-
|
|
2854
|
-
|
|
2855
|
-
|
|
2856
|
-
|
|
2857
|
-
|
|
2858
|
-
|
|
2859
|
-
|
|
2860
|
-
|
|
2861
|
-
|
|
2862
|
-
|
|
2863
|
-
|
|
2864
|
-
|
|
2865
|
-
}
|
|
2866
|
-
|
|
2867
|
-
|
|
2868
|
-
|
|
2869
|
-
|
|
2870
|
-
|
|
2871
|
-
|
|
2872
|
-
|
|
2873
|
-
|
|
2874
|
-
|
|
2875
|
-
|
|
2876
|
-
|
|
2877
|
-
|
|
2878
|
-
|
|
2879
|
-
|
|
2880
|
-
|
|
2881
|
-
|
|
2882
|
-
|
|
2883
|
-
|
|
2884
|
-
|
|
2885
|
-
}
|
|
2886
|
-
|
|
2887
|
-
|
|
2888
|
-
|
|
2889
|
-
|
|
2890
|
-
|
|
2891
|
-
|
|
2892
|
-
|
|
3010
|
+
const stagedEnvFiles = stagePtyEnvFiles(input.message.envFiles);
|
|
3011
|
+
let envFilesRemoved = false;
|
|
3012
|
+
const cleanupEnvFiles = () => {
|
|
3013
|
+
if (envFilesRemoved) return;
|
|
3014
|
+
envFilesRemoved = true;
|
|
3015
|
+
removePtyEnvFiles(stagedEnvFiles.paths);
|
|
3016
|
+
};
|
|
3017
|
+
try {
|
|
3018
|
+
const ptyProcess = createNodePtyBridge({
|
|
3019
|
+
file: shell.file,
|
|
3020
|
+
args: shell.args,
|
|
3021
|
+
ptyOptions: {
|
|
3022
|
+
name: "xterm-256color",
|
|
3023
|
+
cols: Math.max(1, Math.min(Math.floor(input.message.cols || 80), 500)),
|
|
3024
|
+
rows: Math.max(1, Math.min(Math.floor(input.message.rows || 24), 500)),
|
|
3025
|
+
cwd: input.resolvedTarget.rootPath,
|
|
3026
|
+
env: workerChildProcessEnvironment([
|
|
3027
|
+
githubProcessEnv(),
|
|
3028
|
+
resolvePtyEnvFileReferences(input.message.env ?? {}, stagedEnvFiles),
|
|
3029
|
+
planProcessEnv,
|
|
3030
|
+
targetProcessEnv,
|
|
3031
|
+
shell.env ?? {}
|
|
3032
|
+
])
|
|
3033
|
+
},
|
|
3034
|
+
onOpened: () => {
|
|
3035
|
+
sendWorkerMessage(input.ws, {
|
|
3036
|
+
type: "pty_opened",
|
|
3037
|
+
requestId: input.message.requestId,
|
|
3038
|
+
ptyId: input.message.ptyId
|
|
3039
|
+
});
|
|
3040
|
+
},
|
|
3041
|
+
onForeground: (busy) => {
|
|
3042
|
+
const activePty = activePtys.get(input.message.ptyId);
|
|
3043
|
+
if (activePty && (busy || input.message.command === void 0)) activePty.foregroundBusy = busy;
|
|
3044
|
+
},
|
|
3045
|
+
onOutput: (data) => {
|
|
3046
|
+
outputCoalescer.push(data);
|
|
3047
|
+
},
|
|
3048
|
+
onExit: (event) => {
|
|
3049
|
+
outputCoalescer.flush();
|
|
3050
|
+
input.releaseWorkspaceMutation?.();
|
|
3051
|
+
activePtys.delete(input.message.ptyId);
|
|
3052
|
+
cleanupEnvFiles();
|
|
3053
|
+
input.onTerminal?.();
|
|
3054
|
+
sendWorkerMessage(input.ws, {
|
|
3055
|
+
type: "pty_exit",
|
|
3056
|
+
ptyId: input.message.ptyId,
|
|
3057
|
+
exitCode: event.exitCode,
|
|
3058
|
+
signal: event.signal
|
|
3059
|
+
});
|
|
3060
|
+
},
|
|
3061
|
+
onError: (error) => {
|
|
3062
|
+
outputCoalescer.flush();
|
|
3063
|
+
input.releaseWorkspaceMutation?.();
|
|
3064
|
+
activePtys.delete(input.message.ptyId);
|
|
3065
|
+
cleanupEnvFiles();
|
|
3066
|
+
input.onTerminal?.();
|
|
3067
|
+
sendWorkerMessage(input.ws, {
|
|
3068
|
+
type: "pty_error",
|
|
3069
|
+
requestId: input.message.requestId,
|
|
3070
|
+
ptyId: input.message.ptyId,
|
|
3071
|
+
error: error.message
|
|
3072
|
+
});
|
|
3073
|
+
},
|
|
3074
|
+
onTerminationError: (error) => {
|
|
3075
|
+
sendWorkerMessage(input.ws, {
|
|
3076
|
+
type: "pty_error",
|
|
3077
|
+
requestId: input.message.requestId,
|
|
3078
|
+
ptyId: input.message.ptyId,
|
|
3079
|
+
error: error.message
|
|
3080
|
+
});
|
|
3081
|
+
}
|
|
3082
|
+
});
|
|
3083
|
+
activePtys.set(input.message.ptyId, {
|
|
3084
|
+
...ptyProcess,
|
|
3085
|
+
target: input.resolvedTarget.target,
|
|
3086
|
+
foregroundBusy: true,
|
|
3087
|
+
lastInputAt: 0,
|
|
3088
|
+
...input.releaseWorkspaceMutation ? { releaseWorkspaceMutation: input.releaseWorkspaceMutation } : {}
|
|
3089
|
+
});
|
|
3090
|
+
} catch (error) {
|
|
3091
|
+
cleanupEnvFiles();
|
|
3092
|
+
throw error;
|
|
3093
|
+
}
|
|
2893
3094
|
}
|
|
2894
3095
|
function writePty(ws, message) {
|
|
2895
3096
|
const ptyProcess = activePtys.get(message.ptyId);
|
|
@@ -2901,6 +3102,7 @@ function writePty(ws, message) {
|
|
|
2901
3102
|
});
|
|
2902
3103
|
return;
|
|
2903
3104
|
}
|
|
3105
|
+
ptyProcess.lastInputAt = Date.now();
|
|
2904
3106
|
ptyProcess.write(message.data);
|
|
2905
3107
|
}
|
|
2906
3108
|
function resizePty(message) {
|
|
@@ -3021,7 +3223,7 @@ async function startWorker(options) {
|
|
|
3021
3223
|
process.stdout.write(`[r5d-worker] server: ${baseUrl}
|
|
3022
3224
|
`);
|
|
3023
3225
|
runGit(["--version"]);
|
|
3024
|
-
await verifyR5dctlAuth(baseUrl, token);
|
|
3226
|
+
await verifyR5dctlAuth(baseUrl, token, label);
|
|
3025
3227
|
const initializedWorkspaceState = await workspaceSyncSingleFlight.runExclusive(() => {
|
|
3026
3228
|
if (!startupProjectSnapshotRecoveryCompleted) {
|
|
3027
3229
|
const snapshotRecovery = (0, import_project_worktrees.recoverStaleProjectWorktreeSnapshots)({
|
|
@@ -3338,13 +3540,24 @@ async function startWorker(options) {
|
|
|
3338
3540
|
});
|
|
3339
3541
|
};
|
|
3340
3542
|
const activeWorkspaceMutationTargets = () => [
|
|
3341
|
-
...Array.from(activeProcesses.values()
|
|
3543
|
+
...Array.from(activeProcesses.values()).filter(workerCommandHasWorkspaceEffect).map(({ target }) => target),
|
|
3342
3544
|
...credentialBearingProcessGroupTargets.values(),
|
|
3343
3545
|
...workspaceSyncPriorityProcessTargets.values(),
|
|
3344
3546
|
...workspaceSyncPriorityOperationTargets.values(),
|
|
3345
|
-
...Array.from(activePtys.values()
|
|
3547
|
+
...Array.from(activePtys.values()).filter((pty) => workerPtyIsWorkspaceBusy(pty)).map(({ target }) => target),
|
|
3346
3548
|
...workspaceSyncPriorityPtyTargets.values()
|
|
3347
3549
|
];
|
|
3550
|
+
let visibleWorkspaceMutationEpoch = 0;
|
|
3551
|
+
const projectWorkspaceMutationEpochs = /* @__PURE__ */ new Map();
|
|
3552
|
+
const recordVisibleWorkspaceMutation = (target) => {
|
|
3553
|
+
if (target.type === "workspace") {
|
|
3554
|
+
if (target.rootProfile === "visible_projects") visibleWorkspaceMutationEpoch += 1;
|
|
3555
|
+
return;
|
|
3556
|
+
}
|
|
3557
|
+
const key = projectBranchKey(target.projectId, target.branchName);
|
|
3558
|
+
projectWorkspaceMutationEpochs.set(key, (projectWorkspaceMutationEpochs.get(key) ?? 0) + 1);
|
|
3559
|
+
};
|
|
3560
|
+
const projectWorkspaceMutationToken = (projectId, branchName) => `${visibleWorkspaceMutationEpoch}:${projectWorkspaceMutationEpochs.get(projectBranchKey(projectId, branchName)) ?? 0}`;
|
|
3348
3561
|
const canonicalWorkspaceMutationIsActive = (targets) => targets.some((target) => target.type === "workspace" && target.rootProfile === "canonical_sync");
|
|
3349
3562
|
const projectBranchMountActivityBusy = (projectId, branchName, branchPath) => {
|
|
3350
3563
|
const activeTargets = activeWorkspaceMutationTargets();
|
|
@@ -3381,6 +3594,7 @@ async function startWorker(options) {
|
|
|
3381
3594
|
preserveLocalOnInitialOuterAbsence: creatorLocalIncarnation,
|
|
3382
3595
|
preserveLocalOnHydrationBasisChange: preservesCheckoutPathMove,
|
|
3383
3596
|
busy: () => projectBranchMountBusy(project.projectId, branch.branchName, branchPath),
|
|
3597
|
+
mutationToken: () => projectWorkspaceMutationToken(project.projectId, branch.branchName),
|
|
3384
3598
|
busyForRecovery: () => projectBranchMountActivityBusy(project.projectId, branch.branchName, branchPath)
|
|
3385
3599
|
});
|
|
3386
3600
|
mounts.push({
|
|
@@ -3394,6 +3608,7 @@ async function startWorker(options) {
|
|
|
3394
3608
|
preserveLocalOnInitialOuterAbsence: creatorLocalIncarnation,
|
|
3395
3609
|
preserveLocalOnHydrationBasisChange: preservesCheckoutPathMove,
|
|
3396
3610
|
busy: () => projectBranchMountBusy(project.projectId, branch.branchName, branchPath),
|
|
3611
|
+
mutationToken: () => projectWorkspaceMutationToken(project.projectId, branch.branchName),
|
|
3397
3612
|
busyForRecovery: () => projectBranchMountActivityBusy(project.projectId, branch.branchName, branchPath)
|
|
3398
3613
|
});
|
|
3399
3614
|
}
|
|
@@ -3414,6 +3629,7 @@ async function startWorker(options) {
|
|
|
3414
3629
|
const activeTargets = activeWorkspaceMutationTargets();
|
|
3415
3630
|
return canonicalWorkspaceMutationIsActive(activeTargets) || (0, import_workspace_automatic_sync_policy.hasActiveVisibleProjectsWorkspaceTarget)(activeTargets);
|
|
3416
3631
|
},
|
|
3632
|
+
mutationToken: () => String(visibleWorkspaceMutationEpoch),
|
|
3417
3633
|
busyForRecovery: () => {
|
|
3418
3634
|
const activeTargets = activeWorkspaceMutationTargets();
|
|
3419
3635
|
return canonicalWorkspaceMutationIsActive(activeTargets) || (0, import_workspace_automatic_sync_policy.hasActiveVisibleProjectsWorkspaceTarget)(activeTargets);
|
|
@@ -3771,6 +3987,7 @@ async function startWorker(options) {
|
|
|
3771
3987
|
localChangesDiscarded: false,
|
|
3772
3988
|
...result.conflictPaths ? { conflictPaths: result.conflictPaths } : {},
|
|
3773
3989
|
...result.conflictSnapshotRefs ? { conflictSnapshotRefs: result.conflictSnapshotRefs } : {},
|
|
3990
|
+
...result.conflictKind ? { conflictKind: result.conflictKind } : {},
|
|
3774
3991
|
...result.error ? { error: result.error } : {}
|
|
3775
3992
|
};
|
|
3776
3993
|
};
|
|
@@ -3837,10 +4054,7 @@ async function startWorker(options) {
|
|
|
3837
4054
|
};
|
|
3838
4055
|
}
|
|
3839
4056
|
const outerRemediation = input.trigger.type === "remediation" || input.trigger.type === "remediation_confirm";
|
|
3840
|
-
const
|
|
3841
|
-
(mount) => !(mount.deleteWhenSourceMissing && !import_node_fs.default.existsSync(mount.sourcePath)) && mount.busy?.()
|
|
3842
|
-
);
|
|
3843
|
-
const observedProjectHeads = outerRemediation || ordinaryCycleHasBusyLiveMount ? [] : observeProjectHeadsBeforeOuterWorkspace({ allowNonFastForward: false, failClosed: false });
|
|
4057
|
+
const observedProjectHeads = outerRemediation ? [] : observeProjectHeadsBeforeOuterWorkspace({ allowNonFastForward: false, failClosed: false });
|
|
3844
4058
|
const inboundMoveMountIds = new Set(
|
|
3845
4059
|
observedProjectHeads.flatMap(
|
|
3846
4060
|
({ projectId, observations }) => observations.filter(({ shouldMove }) => shouldMove).map(({ branchName }) => projectMountId(projectId, branchName))
|
|
@@ -3996,7 +4210,8 @@ async function startWorker(options) {
|
|
|
3996
4210
|
);
|
|
3997
4211
|
workspaceAutomaticTimer.unref();
|
|
3998
4212
|
};
|
|
3999
|
-
const markWorkspaceDirty = (trigger, force = false) => {
|
|
4213
|
+
const markWorkspaceDirty = (trigger, force = false, target) => {
|
|
4214
|
+
if (target) recordVisibleWorkspaceMutation(target);
|
|
4000
4215
|
scheduleAutomaticWorkspaceSync(trigger, force ? 0 : WORKSPACE_GIT_QUIET_MS);
|
|
4001
4216
|
};
|
|
4002
4217
|
const targetMayMutateVisibleWorkspace = (target) => target.type === "project" || target.rootProfile === "visible_projects";
|
|
@@ -4257,7 +4472,7 @@ async function startWorker(options) {
|
|
|
4257
4472
|
sourcePath: recoverySourceOverrides.get(mount.id) ?? mount.sourcePath,
|
|
4258
4473
|
busy: mount.busyForRecovery ?? mount.busy
|
|
4259
4474
|
}));
|
|
4260
|
-
(0, import_workspace_git_sync.recoverWorkspaceGitHydration)(workspaceShadowRoot, recoveryMounts);
|
|
4475
|
+
(0, import_workspace_git_sync.recoverWorkspaceGitHydration)(workspaceShadowRoot, recoveryMounts, { preserveStaleBases: true });
|
|
4261
4476
|
for (const project of message.projects) {
|
|
4262
4477
|
for (const { branchName } of project.preserveOnlyBranches) {
|
|
4263
4478
|
if (stillPendingCreatedBranches.has((0, import_workspace_automatic_sync_policy.pendingCreatedBranchKey)(project.projectId, branchName))) {
|
|
@@ -4433,7 +4648,8 @@ async function startWorker(options) {
|
|
|
4433
4648
|
capabilities: {
|
|
4434
4649
|
updateClis: true,
|
|
4435
4650
|
browserPortForwarding: true,
|
|
4436
|
-
execStdinV1: true
|
|
4651
|
+
execStdinV1: true,
|
|
4652
|
+
ptyEnvFilesV1: true
|
|
4437
4653
|
},
|
|
4438
4654
|
projectRoot: projectsRoot,
|
|
4439
4655
|
artifactRoot,
|
|
@@ -4808,6 +5024,9 @@ async function startWorker(options) {
|
|
|
4808
5024
|
});
|
|
4809
5025
|
return;
|
|
4810
5026
|
}
|
|
5027
|
+
if (active.workspaceEffect !== "none" && targetMayMutateVisibleWorkspace(active.target)) {
|
|
5028
|
+
recordVisibleWorkspaceMutation(active.target);
|
|
5029
|
+
}
|
|
4811
5030
|
try {
|
|
4812
5031
|
let bytesWritten = 0;
|
|
4813
5032
|
if (message.data !== void 0 && message.data.length > 0) {
|
|
@@ -4818,8 +5037,8 @@ async function startWorker(options) {
|
|
|
4818
5037
|
await active.stdin.end();
|
|
4819
5038
|
active.stdin = void 0;
|
|
4820
5039
|
}
|
|
4821
|
-
if (targetMayMutateVisibleWorkspace(active.target)) {
|
|
4822
|
-
markWorkspaceDirty({ type: "shell_inline", detail: `exec stdin ${message.runId}` });
|
|
5040
|
+
if (active.workspaceEffect !== "none" && targetMayMutateVisibleWorkspace(active.target)) {
|
|
5041
|
+
markWorkspaceDirty({ type: "shell_inline", detail: `exec stdin ${message.runId}` }, false, active.target);
|
|
4823
5042
|
}
|
|
4824
5043
|
sendAck({
|
|
4825
5044
|
result: {
|
|
@@ -4846,6 +5065,7 @@ async function startWorker(options) {
|
|
|
4846
5065
|
}
|
|
4847
5066
|
await (0, import_workspace_command_sync_policy.reserveWorkspaceCommandAfterCurrentSync)(message.target, workspaceSyncSingleFlight, () => {
|
|
4848
5067
|
workspaceSyncPriorityPtyTargets.set(message.ptyId, message.target);
|
|
5068
|
+
if (targetMayMutateVisibleWorkspace(message.target)) recordVisibleWorkspaceMutation(message.target);
|
|
4849
5069
|
});
|
|
4850
5070
|
let releaseWorkspaceMutation;
|
|
4851
5071
|
let mutationLeaseTransferred = false;
|
|
@@ -4863,7 +5083,7 @@ async function startWorker(options) {
|
|
|
4863
5083
|
...releaseWorkspaceMutation ? { releaseWorkspaceMutation } : {},
|
|
4864
5084
|
...targetMayMutateVisibleWorkspace(message.target) ? {
|
|
4865
5085
|
onTerminal: () => {
|
|
4866
|
-
markWorkspaceDirty({ type: "process_terminal", detail: `pty ${message.ptyId} completed` }, true);
|
|
5086
|
+
markWorkspaceDirty({ type: "process_terminal", detail: `pty ${message.ptyId} completed` }, true, message.target);
|
|
4867
5087
|
}
|
|
4868
5088
|
} : {}
|
|
4869
5089
|
});
|
|
@@ -4884,7 +5104,7 @@ async function startWorker(options) {
|
|
|
4884
5104
|
if (message.type === "pty_input") {
|
|
4885
5105
|
const activePty = activePtys.get(message.ptyId);
|
|
4886
5106
|
if (activePty && targetMayMutateVisibleWorkspace(activePty.target)) {
|
|
4887
|
-
markWorkspaceDirty({ type: "shell_inline", detail: `pty ${message.ptyId}` });
|
|
5107
|
+
markWorkspaceDirty({ type: "shell_inline", detail: `pty ${message.ptyId}` }, false, activePty.target);
|
|
4888
5108
|
}
|
|
4889
5109
|
writePty(ws, message);
|
|
4890
5110
|
return;
|
|
@@ -4897,7 +5117,7 @@ async function startWorker(options) {
|
|
|
4897
5117
|
const activePty = activePtys.get(message.ptyId);
|
|
4898
5118
|
closePty(message);
|
|
4899
5119
|
if (activePty && targetMayMutateVisibleWorkspace(activePty.target)) {
|
|
4900
|
-
markWorkspaceDirty({ type: "process_terminal", detail: `pty ${message.ptyId} closed` }, true);
|
|
5120
|
+
markWorkspaceDirty({ type: "process_terminal", detail: `pty ${message.ptyId} closed` }, true, activePty.target);
|
|
4901
5121
|
}
|
|
4902
5122
|
return;
|
|
4903
5123
|
}
|
|
@@ -4914,11 +5134,15 @@ async function startWorker(options) {
|
|
|
4914
5134
|
}
|
|
4915
5135
|
let result;
|
|
4916
5136
|
let targetReserved = false;
|
|
5137
|
+
const hasWorkspaceEffect = workerCommandHasWorkspaceEffect(message);
|
|
4917
5138
|
try {
|
|
4918
|
-
|
|
4919
|
-
|
|
4920
|
-
|
|
4921
|
-
|
|
5139
|
+
if (hasWorkspaceEffect) {
|
|
5140
|
+
await (0, import_workspace_command_sync_policy.reserveWorkspaceCommandAfterCurrentSync)(message.target, workspaceSyncSingleFlight, () => {
|
|
5141
|
+
workspaceSyncPriorityProcessTargets.set(message.runId, message.target);
|
|
5142
|
+
targetReserved = true;
|
|
5143
|
+
if (targetMayMutateVisibleWorkspace(message.target)) recordVisibleWorkspaceMutation(message.target);
|
|
5144
|
+
});
|
|
5145
|
+
}
|
|
4922
5146
|
const runCommand = async () => {
|
|
4923
5147
|
const resolvedTarget = resolveMessageTarget(message.target);
|
|
4924
5148
|
process.stdout.write(`[r5d-worker] exec ${message.runId}: ${message.argv.join(" ")}
|
|
@@ -4948,10 +5172,11 @@ async function startWorker(options) {
|
|
|
4948
5172
|
if (targetReserved) workspaceSyncPriorityProcessTargets.delete(message.runId);
|
|
4949
5173
|
}
|
|
4950
5174
|
ws.send(JSON.stringify(result));
|
|
4951
|
-
if (targetMayMutateVisibleWorkspace(message.target)) {
|
|
5175
|
+
if (hasWorkspaceEffect && targetMayMutateVisibleWorkspace(message.target)) {
|
|
4952
5176
|
markWorkspaceDirty(
|
|
4953
5177
|
{ type: "shell_inline", sessionId: message.sessionId, processRunId: message.runId, detail: "foreground command completed" },
|
|
4954
|
-
true
|
|
5178
|
+
true,
|
|
5179
|
+
message.target
|
|
4955
5180
|
);
|
|
4956
5181
|
}
|
|
4957
5182
|
return;
|
|
@@ -4971,6 +5196,7 @@ async function startWorker(options) {
|
|
|
4971
5196
|
requestId: message.requestId,
|
|
4972
5197
|
runId: message.runId
|
|
4973
5198
|
});
|
|
5199
|
+
const hasWorkspaceEffect = workerCommandHasWorkspaceEffect(message);
|
|
4974
5200
|
const runCommand = async () => {
|
|
4975
5201
|
try {
|
|
4976
5202
|
const resolvedTarget = resolveMessageTarget(message.target);
|
|
@@ -4987,7 +5213,7 @@ async function startWorker(options) {
|
|
|
4987
5213
|
assertAdmission: assertMessageAdmission
|
|
4988
5214
|
});
|
|
4989
5215
|
} finally {
|
|
4990
|
-
if (targetMayMutateVisibleWorkspace(message.target)) {
|
|
5216
|
+
if (hasWorkspaceEffect && targetMayMutateVisibleWorkspace(message.target)) {
|
|
4991
5217
|
markWorkspaceDirty(
|
|
4992
5218
|
{
|
|
4993
5219
|
type: "process_terminal",
|
|
@@ -4995,7 +5221,8 @@ async function startWorker(options) {
|
|
|
4995
5221
|
processRunId: message.runId,
|
|
4996
5222
|
detail: "process completed"
|
|
4997
5223
|
},
|
|
4998
|
-
true
|
|
5224
|
+
true,
|
|
5225
|
+
message.target
|
|
4999
5226
|
);
|
|
5000
5227
|
}
|
|
5001
5228
|
}
|
|
@@ -5003,10 +5230,13 @@ async function startWorker(options) {
|
|
|
5003
5230
|
const execution = (async () => {
|
|
5004
5231
|
let targetReserved = false;
|
|
5005
5232
|
try {
|
|
5006
|
-
|
|
5007
|
-
|
|
5008
|
-
|
|
5009
|
-
|
|
5233
|
+
if (hasWorkspaceEffect) {
|
|
5234
|
+
await (0, import_workspace_command_sync_policy.reserveWorkspaceCommandAfterCurrentSync)(message.target, workspaceSyncSingleFlight, () => {
|
|
5235
|
+
workspaceSyncPriorityProcessTargets.set(message.runId, message.target);
|
|
5236
|
+
targetReserved = true;
|
|
5237
|
+
if (targetMayMutateVisibleWorkspace(message.target)) recordVisibleWorkspaceMutation(message.target);
|
|
5238
|
+
});
|
|
5239
|
+
}
|
|
5010
5240
|
await (0, import_workspace_command_sync_policy.runWorkspaceCommand)(message.target, workspaceSyncSingleFlight, runCommand);
|
|
5011
5241
|
} finally {
|
|
5012
5242
|
if (targetReserved) workspaceSyncPriorityProcessTargets.delete(message.runId);
|
|
@@ -5029,6 +5259,7 @@ async function startWorker(options) {
|
|
|
5029
5259
|
if (reservesVisibleWorkspace) {
|
|
5030
5260
|
await (0, import_workspace_command_sync_policy.reserveWorkspaceCommandAfterCurrentSync)(message.target, workspaceSyncSingleFlight, () => {
|
|
5031
5261
|
workspaceSyncPriorityOperationTargets.set(message.requestId, message.target);
|
|
5262
|
+
if (mutatesVisibleWorkspace) recordVisibleWorkspaceMutation(message.target);
|
|
5032
5263
|
});
|
|
5033
5264
|
}
|
|
5034
5265
|
let dirtyTrigger;
|
|
@@ -5058,7 +5289,7 @@ async function startWorker(options) {
|
|
|
5058
5289
|
toolCallId: message.requestId,
|
|
5059
5290
|
...message.target.type === "project" ? { projectId: message.target.projectId, branchName: message.target.branchName } : {}
|
|
5060
5291
|
};
|
|
5061
|
-
markWorkspaceDirty(dirtyTrigger);
|
|
5292
|
+
markWorkspaceDirty(dirtyTrigger, false, message.target);
|
|
5062
5293
|
}
|
|
5063
5294
|
} catch (error) {
|
|
5064
5295
|
ws.send(
|
|
@@ -5219,5 +5450,7 @@ if (isCliEntrypoint()) {
|
|
|
5219
5450
|
syncSessionArtifacts,
|
|
5220
5451
|
workerChildProcessEnvironment,
|
|
5221
5452
|
workerGitSecurityTestHarness,
|
|
5453
|
+
workerPtyBridgeTestHarness,
|
|
5454
|
+
workerPtyTestHarness,
|
|
5222
5455
|
writeWorkerTextFile
|
|
5223
5456
|
});
|