@ricsam/r5d-worker 0.0.44 → 0.0.46
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/main.cjs +361 -144
- package/dist/cjs/package.json +1 -1
- package/dist/cjs/workspace-sync.cjs +187 -49
- package/dist/mjs/main.mjs +361 -144
- package/dist/mjs/package.json +1 -1
- package/dist/mjs/workspace-sync.mjs +187 -49
- package/dist/types/main.d.ts +19 -8
- package/dist/types/workspace-sync.d.ts +3 -0
- package/package.json +1 -1
package/dist/cjs/main.cjs
CHANGED
|
@@ -282,7 +282,25 @@ async function readResponseText(response) {
|
|
|
282
282
|
}
|
|
283
283
|
}
|
|
284
284
|
const R5D_ARTIFACTS_DIR_REF = "$R5D_ARTIFACTS_DIR";
|
|
285
|
+
const R5D_PLANS_DIR_REF = "$R5D_PLANS_DIR";
|
|
286
|
+
const R5D_ACTIVE_PLAN_FILE_REF = "$R5D_ACTIVE_PLAN_FILE";
|
|
285
287
|
const R5D_PLANS_DIR_ENV = "R5D_PLANS_DIR";
|
|
288
|
+
const BUILT_IN_TOOL_PATH_REFS = [R5D_ARTIFACTS_DIR_REF, R5D_PLANS_DIR_REF, R5D_ACTIVE_PLAN_FILE_REF];
|
|
289
|
+
function parseBuiltInToolPath(inputPath) {
|
|
290
|
+
const normalized = inputPath.replace(/\\/g, "/");
|
|
291
|
+
const match = /^\$(?:\{([A-Za-z_][A-Za-z0-9_]*)\}|([A-Za-z_][A-Za-z0-9_]*))(?:\/(.*))?$/.exec(normalized);
|
|
292
|
+
if (!match) {
|
|
293
|
+
return null;
|
|
294
|
+
}
|
|
295
|
+
const ref = `$${match[1] ?? match[2]}`;
|
|
296
|
+
if (!BUILT_IN_TOOL_PATH_REFS.includes(ref)) {
|
|
297
|
+
throw new Error(`Unsupported tool path variable "${ref}". Supported variables: ${BUILT_IN_TOOL_PATH_REFS.join(", ")}.`);
|
|
298
|
+
}
|
|
299
|
+
return {
|
|
300
|
+
ref,
|
|
301
|
+
relativePath: match[3] ?? ""
|
|
302
|
+
};
|
|
303
|
+
}
|
|
286
304
|
function validateArtifactSessionId(sessionId) {
|
|
287
305
|
if (!/^(migration-)?[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i.test(sessionId)) {
|
|
288
306
|
throw new Error(`Invalid artifact session id: ${sessionId}`);
|
|
@@ -291,7 +309,7 @@ function validateArtifactSessionId(sessionId) {
|
|
|
291
309
|
function assertInsideRoot(rootPath, candidatePath, label) {
|
|
292
310
|
const relative = import_node_path.default.relative(rootPath, candidatePath);
|
|
293
311
|
if (relative === ".." || relative.startsWith(`..${import_node_path.default.sep}`) || import_node_path.default.isAbsolute(relative)) {
|
|
294
|
-
throw new Error(`${label} escapes
|
|
312
|
+
throw new Error(`${label} escapes its allowed root`);
|
|
295
313
|
}
|
|
296
314
|
}
|
|
297
315
|
function sessionArtifactDir(artifactRoot, sessionId) {
|
|
@@ -324,7 +342,11 @@ function planEnv(planRoot, projectId, branchName, activePlanId) {
|
|
|
324
342
|
return env;
|
|
325
343
|
}
|
|
326
344
|
function isArtifactEnvPath(filePath) {
|
|
327
|
-
|
|
345
|
+
try {
|
|
346
|
+
return parseBuiltInToolPath(filePath)?.ref === R5D_ARTIFACTS_DIR_REF;
|
|
347
|
+
} catch {
|
|
348
|
+
return false;
|
|
349
|
+
}
|
|
328
350
|
}
|
|
329
351
|
function artifactApiBasePath(projectId, branchName, sessionId) {
|
|
330
352
|
return `/preview-api/${encodeURIComponent(projectId)}/${encodeURIComponent(branchName)}/artifacts/${encodeURIComponent(sessionId)}`;
|
|
@@ -431,56 +453,72 @@ async function fetchSessionArtifactManifest(input) {
|
|
|
431
453
|
label: "artifact"
|
|
432
454
|
});
|
|
433
455
|
}
|
|
456
|
+
const sessionArtifactSyncQueues = /* @__PURE__ */ new Map();
|
|
434
457
|
async function syncSessionArtifacts(input) {
|
|
435
|
-
const
|
|
436
|
-
const
|
|
437
|
-
|
|
438
|
-
targetDir,
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
download: (artifact) => downloadRemoteSyncedFile({
|
|
442
|
-
url: artifactDownloadUrl(input.baseUrl, input.projectId, input.branchName, input.sessionId, artifact.filename),
|
|
443
|
-
token: input.token,
|
|
458
|
+
const queueKey = `${import_node_path.default.resolve(input.artifactRoot)}\0${input.sessionId}`;
|
|
459
|
+
const previous = sessionArtifactSyncQueues.get(queueKey);
|
|
460
|
+
const queued = (previous ? previous.catch(() => "") : Promise.resolve("")).then(async () => {
|
|
461
|
+
const targetDir = sessionArtifactDir(input.artifactRoot, input.sessionId);
|
|
462
|
+
const artifacts = await fetchSessionArtifactManifest(input);
|
|
463
|
+
return syncRemoteFileSet({
|
|
444
464
|
targetDir,
|
|
445
|
-
|
|
446
|
-
label: "
|
|
447
|
-
|
|
465
|
+
files: artifacts,
|
|
466
|
+
label: "Artifact path",
|
|
467
|
+
download: (artifact) => downloadRemoteSyncedFile({
|
|
468
|
+
url: artifactDownloadUrl(input.baseUrl, input.projectId, input.branchName, input.sessionId, artifact.filename),
|
|
469
|
+
token: input.token,
|
|
470
|
+
targetDir,
|
|
471
|
+
file: artifact,
|
|
472
|
+
label: "artifact"
|
|
473
|
+
})
|
|
474
|
+
});
|
|
448
475
|
});
|
|
476
|
+
sessionArtifactSyncQueues.set(queueKey, queued);
|
|
477
|
+
const release = () => {
|
|
478
|
+
if (sessionArtifactSyncQueues.get(queueKey) === queued) sessionArtifactSyncQueues.delete(queueKey);
|
|
479
|
+
};
|
|
480
|
+
void queued.then(release, release);
|
|
481
|
+
return queued;
|
|
449
482
|
}
|
|
450
|
-
async function
|
|
451
|
-
|
|
452
|
-
|
|
483
|
+
async function prepareBuiltInToolPaths(input) {
|
|
484
|
+
const parsed = parseBuiltInToolPath(input.filePath);
|
|
485
|
+
if (!parsed) {
|
|
486
|
+
return void 0;
|
|
453
487
|
}
|
|
454
|
-
if (
|
|
455
|
-
|
|
488
|
+
if (parsed.ref === R5D_ARTIFACTS_DIR_REF) {
|
|
489
|
+
if (input.access === "write") {
|
|
490
|
+
throw new Error(
|
|
491
|
+
`${R5D_ARTIFACTS_DIR_REF} attachments are read-only. Copy them into a project path before editing or creating files.`
|
|
492
|
+
);
|
|
493
|
+
}
|
|
494
|
+
if (!input.sessionId) {
|
|
495
|
+
throw new Error(`${R5D_ARTIFACTS_DIR_REF} paths require an active chat session`);
|
|
496
|
+
}
|
|
497
|
+
return {
|
|
498
|
+
artifactsDir: await syncSessionArtifacts({
|
|
499
|
+
baseUrl: input.baseUrl,
|
|
500
|
+
token: input.token,
|
|
501
|
+
projectId: input.projectId,
|
|
502
|
+
branchName: input.branchName,
|
|
503
|
+
sessionId: input.sessionId,
|
|
504
|
+
artifactRoot: input.artifactRoot
|
|
505
|
+
})
|
|
506
|
+
};
|
|
456
507
|
}
|
|
457
|
-
|
|
458
|
-
|
|
508
|
+
const plansDir = projectPlanDir(input.planRoot, input.projectId, input.branchName);
|
|
509
|
+
import_node_fs.default.mkdirSync(plansDir, { recursive: true });
|
|
510
|
+
if (parsed.ref === R5D_PLANS_DIR_REF) {
|
|
511
|
+
return { plansDir };
|
|
459
512
|
}
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
token: input.token,
|
|
463
|
-
projectId: input.projectId,
|
|
464
|
-
branchName: input.branchName,
|
|
465
|
-
sessionId: input.sessionId,
|
|
466
|
-
artifactRoot: input.artifactRoot
|
|
467
|
-
});
|
|
468
|
-
const relativePath = import_node_path.default.posix.normalize(input.filePath.slice(`${R5D_ARTIFACTS_DIR_REF}/`.length));
|
|
469
|
-
if (!relativePath || relativePath === "." || relativePath.startsWith("../") || relativePath.includes("\0")) {
|
|
470
|
-
throw new Error(`Invalid artifact path: ${input.filePath}`);
|
|
513
|
+
if (!input.activePlanId) {
|
|
514
|
+
throw new Error(`${R5D_ACTIVE_PLAN_FILE_REF} paths require an active plan`);
|
|
471
515
|
}
|
|
472
|
-
|
|
473
|
-
assertInsideRoot(targetDir, absolutePath, "Artifact path");
|
|
516
|
+
validatePlanId(input.activePlanId);
|
|
474
517
|
return {
|
|
475
|
-
|
|
476
|
-
|
|
518
|
+
plansDir,
|
|
519
|
+
activePlanFile: import_node_path.default.join(plansDir, `${input.activePlanId}.plan.md`)
|
|
477
520
|
};
|
|
478
521
|
}
|
|
479
|
-
function rejectArtifactWritePath(filePath) {
|
|
480
|
-
if (isArtifactEnvPath(filePath)) {
|
|
481
|
-
throw new Error(`${R5D_ARTIFACTS_DIR_REF} attachments are read-only. Copy them into a project path before editing or creating files.`);
|
|
482
|
-
}
|
|
483
|
-
}
|
|
484
522
|
async function prepareArtifactEnvForShell(input) {
|
|
485
523
|
if (!input.sessionId) {
|
|
486
524
|
return {};
|
|
@@ -631,10 +669,63 @@ function assertAllowedProjectPath(repoRelativePath, inputPath) {
|
|
|
631
669
|
throw new Error(`Invalid project file path: ${inputPath}`);
|
|
632
670
|
}
|
|
633
671
|
}
|
|
634
|
-
function
|
|
672
|
+
function assertPathInsideRealRoot(rootPath, candidatePath, label) {
|
|
673
|
+
const resolvedRoot = import_node_path.default.resolve(rootPath);
|
|
674
|
+
const resolvedCandidate = import_node_path.default.resolve(candidatePath);
|
|
675
|
+
assertInsideRoot(resolvedRoot, resolvedCandidate, label);
|
|
676
|
+
let existingPath = resolvedCandidate;
|
|
677
|
+
while (!import_node_fs.default.existsSync(existingPath)) {
|
|
678
|
+
const parentPath = import_node_path.default.dirname(existingPath);
|
|
679
|
+
if (parentPath === existingPath) {
|
|
680
|
+
throw new Error(`${label} root does not exist: ${rootPath}`);
|
|
681
|
+
}
|
|
682
|
+
existingPath = parentPath;
|
|
683
|
+
}
|
|
684
|
+
const realRoot = import_node_fs.default.realpathSync(resolvedRoot);
|
|
685
|
+
const realExistingPath = import_node_fs.default.realpathSync(existingPath);
|
|
686
|
+
assertInsideRoot(realRoot, realExistingPath, label);
|
|
687
|
+
}
|
|
688
|
+
function resolveVirtualWorkerFilePath(inputPath, parsed, builtInPaths) {
|
|
689
|
+
if (parsed.relativePath.split("/").includes("..")) {
|
|
690
|
+
throw new Error(`Invalid ${parsed.ref} path: ${inputPath}`);
|
|
691
|
+
}
|
|
692
|
+
let rootPath;
|
|
693
|
+
let absolutePath;
|
|
694
|
+
if (parsed.ref === R5D_ARTIFACTS_DIR_REF) {
|
|
695
|
+
rootPath = builtInPaths?.artifactsDir;
|
|
696
|
+
} else if (parsed.ref === R5D_PLANS_DIR_REF) {
|
|
697
|
+
rootPath = builtInPaths?.plansDir;
|
|
698
|
+
} else {
|
|
699
|
+
if (parsed.relativePath) {
|
|
700
|
+
throw new Error(`${R5D_ACTIVE_PLAN_FILE_REF} must reference the active plan file directly`);
|
|
701
|
+
}
|
|
702
|
+
rootPath = builtInPaths?.plansDir;
|
|
703
|
+
absolutePath = builtInPaths?.activePlanFile;
|
|
704
|
+
}
|
|
705
|
+
if (!rootPath || parsed.ref === R5D_ACTIVE_PLAN_FILE_REF && !absolutePath) {
|
|
706
|
+
const requirement = parsed.ref === R5D_ARTIFACTS_DIR_REF ? "an active chat session" : "an active plan";
|
|
707
|
+
throw new Error(`${parsed.ref} paths require ${requirement}`);
|
|
708
|
+
}
|
|
709
|
+
const normalizedPath = parsed.relativePath ? import_node_path.default.posix.normalize(parsed.relativePath).replace(/^\/+|\/+$/g, "") : "";
|
|
710
|
+
const normalizedRelativePath = normalizedPath === "." ? "" : normalizedPath;
|
|
711
|
+
absolutePath ??= normalizedRelativePath ? import_node_path.default.resolve(rootPath, ...normalizedRelativePath.split("/")) : import_node_path.default.resolve(rootPath);
|
|
712
|
+
assertPathInsideRealRoot(rootPath, absolutePath, `${parsed.ref} path`);
|
|
713
|
+
return {
|
|
714
|
+
absolutePath,
|
|
715
|
+
displayPath: normalizedRelativePath ? `${parsed.ref}/${normalizedRelativePath}` : parsed.ref,
|
|
716
|
+
repoRelativePath: null,
|
|
717
|
+
scope: "virtual",
|
|
718
|
+
virtualRootPath: import_node_path.default.resolve(rootPath)
|
|
719
|
+
};
|
|
720
|
+
}
|
|
721
|
+
function resolveWorkerFilePath(branchPath, inputPath, builtInPaths) {
|
|
635
722
|
if (typeof inputPath !== "string" || inputPath.length === 0 || inputPath.includes("\0")) {
|
|
636
723
|
throw new Error(`File path must be a non-empty string. Got: ${JSON.stringify(inputPath)}`);
|
|
637
724
|
}
|
|
725
|
+
const parsedBuiltInPath = parseBuiltInToolPath(inputPath);
|
|
726
|
+
if (parsedBuiltInPath) {
|
|
727
|
+
return resolveVirtualWorkerFilePath(inputPath, parsedBuiltInPath, builtInPaths);
|
|
728
|
+
}
|
|
638
729
|
const resolvedBranchPath = import_node_path.default.resolve(branchPath);
|
|
639
730
|
if (import_node_path.default.isAbsolute(inputPath)) {
|
|
640
731
|
const absolutePath2 = import_node_path.default.resolve(inputPath);
|
|
@@ -1091,13 +1182,13 @@ async function withFileMutationQueue(key, operation) {
|
|
|
1091
1182
|
}
|
|
1092
1183
|
}
|
|
1093
1184
|
function mutationQueueKey(projectId, branchName, resolved) {
|
|
1094
|
-
if (resolved.scope
|
|
1095
|
-
return
|
|
1185
|
+
if (resolved.scope !== "project") {
|
|
1186
|
+
return `${resolved.scope}:${import_node_path.default.normalize(resolved.absolutePath)}`;
|
|
1096
1187
|
}
|
|
1097
1188
|
return `project:${projectId}:${branchName}:${import_node_path.default.posix.normalize(resolved.repoRelativePath)}`;
|
|
1098
1189
|
}
|
|
1099
|
-
function readWorkerTextFile(branchPath, filePath, offset, limit) {
|
|
1100
|
-
const resolved = resolveWorkerFilePath(branchPath, filePath);
|
|
1190
|
+
function readWorkerTextFile(branchPath, filePath, offset, limit, builtInPaths) {
|
|
1191
|
+
const resolved = resolveWorkerFilePath(branchPath, filePath, builtInPaths);
|
|
1101
1192
|
if (!import_node_fs.default.existsSync(resolved.absolutePath) || !import_node_fs.default.statSync(resolved.absolutePath).isFile()) {
|
|
1102
1193
|
throw new Error(`File not found: ${resolved.displayPath}`);
|
|
1103
1194
|
}
|
|
@@ -1111,8 +1202,8 @@ function readWorkerTextFile(branchPath, filePath, offset, limit) {
|
|
|
1111
1202
|
...formatted
|
|
1112
1203
|
};
|
|
1113
1204
|
}
|
|
1114
|
-
function writeWorkerTextFile(branchPath, filePath, content) {
|
|
1115
|
-
const resolved = resolveWorkerFilePath(branchPath, filePath);
|
|
1205
|
+
function writeWorkerTextFile(branchPath, filePath, content, builtInPaths) {
|
|
1206
|
+
const resolved = resolveWorkerFilePath(branchPath, filePath, builtInPaths);
|
|
1116
1207
|
import_node_fs.default.mkdirSync(import_node_path.default.dirname(resolved.absolutePath), { recursive: true });
|
|
1117
1208
|
import_node_fs.default.writeFileSync(resolved.absolutePath, content, "utf8");
|
|
1118
1209
|
return {
|
|
@@ -1121,8 +1212,8 @@ function writeWorkerTextFile(branchPath, filePath, content) {
|
|
|
1121
1212
|
gitBlobHash: getGitBlobHashForContent(content)
|
|
1122
1213
|
};
|
|
1123
1214
|
}
|
|
1124
|
-
function editWorkerTextFile(branchPath, filePath, edits) {
|
|
1125
|
-
const resolved = resolveWorkerFilePath(branchPath, filePath);
|
|
1215
|
+
function editWorkerTextFile(branchPath, filePath, edits, builtInPaths) {
|
|
1216
|
+
const resolved = resolveWorkerFilePath(branchPath, filePath, builtInPaths);
|
|
1126
1217
|
if (!Array.isArray(edits) || edits.length === 0) {
|
|
1127
1218
|
throw new Error("edit requires at least one replacement");
|
|
1128
1219
|
}
|
|
@@ -1169,46 +1260,57 @@ function editWorkerTextFile(branchPath, filePath, edits) {
|
|
|
1169
1260
|
};
|
|
1170
1261
|
}
|
|
1171
1262
|
async function executeReadFileOperation(input) {
|
|
1172
|
-
const
|
|
1263
|
+
const builtInPaths = await prepareBuiltInToolPaths({
|
|
1173
1264
|
filePath: input.message.filePath,
|
|
1174
1265
|
baseUrl: input.baseUrl,
|
|
1175
1266
|
token: input.token,
|
|
1176
1267
|
projectId: input.projectId,
|
|
1177
1268
|
branchName: input.message.branchName,
|
|
1178
1269
|
sessionId: input.message.sessionId,
|
|
1179
|
-
|
|
1270
|
+
activePlanId: input.message.activePlanId,
|
|
1271
|
+
artifactRoot: input.artifactRoot,
|
|
1272
|
+
planRoot: input.planRoot,
|
|
1273
|
+
access: "read"
|
|
1180
1274
|
});
|
|
1181
|
-
if (artifact) {
|
|
1182
|
-
if (!import_node_fs.default.existsSync(artifact.absolutePath) || !import_node_fs.default.statSync(artifact.absolutePath).isFile()) {
|
|
1183
|
-
throw new Error(`File not found: ${artifact.virtualPath}`);
|
|
1184
|
-
}
|
|
1185
|
-
const buffer = import_node_fs.default.readFileSync(artifact.absolutePath);
|
|
1186
|
-
const formatted = formatLineNumberedContent(buffer.toString("utf8"), input.message.offset, input.message.limit);
|
|
1187
|
-
return {
|
|
1188
|
-
type: "read",
|
|
1189
|
-
kind: "text",
|
|
1190
|
-
file: artifact.virtualPath,
|
|
1191
|
-
gitBlobHash: getGitBlobHashForContent(buffer),
|
|
1192
|
-
...formatted
|
|
1193
|
-
};
|
|
1194
|
-
}
|
|
1195
1275
|
const workspace = ensureOperationBranch({ ...input, branchName: input.message.branchName });
|
|
1196
|
-
return readWorkerTextFile(workspace.branchPath, input.message.filePath, input.message.offset, input.message.limit);
|
|
1276
|
+
return readWorkerTextFile(workspace.branchPath, input.message.filePath, input.message.offset, input.message.limit, builtInPaths);
|
|
1197
1277
|
}
|
|
1198
1278
|
async function executeWriteFileOperation(input) {
|
|
1199
|
-
|
|
1279
|
+
const builtInPaths = await prepareBuiltInToolPaths({
|
|
1280
|
+
filePath: input.message.filePath,
|
|
1281
|
+
baseUrl: input.baseUrl,
|
|
1282
|
+
token: input.token,
|
|
1283
|
+
projectId: input.projectId,
|
|
1284
|
+
branchName: input.message.branchName,
|
|
1285
|
+
sessionId: input.message.sessionId,
|
|
1286
|
+
activePlanId: input.message.activePlanId,
|
|
1287
|
+
artifactRoot: input.artifactRoot,
|
|
1288
|
+
planRoot: input.planRoot,
|
|
1289
|
+
access: "write"
|
|
1290
|
+
});
|
|
1200
1291
|
const workspace = ensureOperationBranch({ ...input, branchName: input.message.branchName });
|
|
1201
|
-
const resolved = resolveWorkerFilePath(workspace.branchPath, input.message.filePath);
|
|
1292
|
+
const resolved = resolveWorkerFilePath(workspace.branchPath, input.message.filePath, builtInPaths);
|
|
1202
1293
|
return withFileMutationQueue(mutationQueueKey(input.projectId, input.message.branchName, resolved), async () => {
|
|
1203
|
-
return writeWorkerTextFile(workspace.branchPath, input.message.filePath, input.message.content);
|
|
1294
|
+
return writeWorkerTextFile(workspace.branchPath, input.message.filePath, input.message.content, builtInPaths);
|
|
1204
1295
|
});
|
|
1205
1296
|
}
|
|
1206
1297
|
async function executeEditFileOperation(input) {
|
|
1207
|
-
|
|
1298
|
+
const builtInPaths = await prepareBuiltInToolPaths({
|
|
1299
|
+
filePath: input.message.filePath,
|
|
1300
|
+
baseUrl: input.baseUrl,
|
|
1301
|
+
token: input.token,
|
|
1302
|
+
projectId: input.projectId,
|
|
1303
|
+
branchName: input.message.branchName,
|
|
1304
|
+
sessionId: input.message.sessionId,
|
|
1305
|
+
activePlanId: input.message.activePlanId,
|
|
1306
|
+
artifactRoot: input.artifactRoot,
|
|
1307
|
+
planRoot: input.planRoot,
|
|
1308
|
+
access: "write"
|
|
1309
|
+
});
|
|
1208
1310
|
const workspace = ensureOperationBranch({ ...input, branchName: input.message.branchName });
|
|
1209
|
-
const resolved = resolveWorkerFilePath(workspace.branchPath, input.message.filePath);
|
|
1311
|
+
const resolved = resolveWorkerFilePath(workspace.branchPath, input.message.filePath, builtInPaths);
|
|
1210
1312
|
return withFileMutationQueue(mutationQueueKey(input.projectId, input.message.branchName, resolved), async () => {
|
|
1211
|
-
return editWorkerTextFile(workspace.branchPath, input.message.filePath, input.message.edits);
|
|
1313
|
+
return editWorkerTextFile(workspace.branchPath, input.message.filePath, input.message.edits, builtInPaths);
|
|
1212
1314
|
});
|
|
1213
1315
|
}
|
|
1214
1316
|
const IGNORED_ENTRY_NAMES = /* @__PURE__ */ new Set([".git", "node_modules", "dist", "build", ".next", ".cache", "coverage", "target"]);
|
|
@@ -1229,16 +1331,20 @@ function normalizeFindPattern(pattern) {
|
|
|
1229
1331
|
function walkWorkerEntries(branchPath, start) {
|
|
1230
1332
|
const entries = [];
|
|
1231
1333
|
const visitedDirectories = /* @__PURE__ */ new Set();
|
|
1334
|
+
const realVirtualRoot = start.scope === "virtual" ? import_node_fs.default.realpathSync(start.virtualRootPath) : null;
|
|
1232
1335
|
const visit = (absolutePath, isRoot) => {
|
|
1233
1336
|
let stat;
|
|
1234
1337
|
try {
|
|
1235
1338
|
stat = import_node_fs.default.statSync(absolutePath);
|
|
1339
|
+
if (realVirtualRoot) {
|
|
1340
|
+
assertInsideRoot(realVirtualRoot, import_node_fs.default.realpathSync(absolutePath), `${start.displayPath} path`);
|
|
1341
|
+
}
|
|
1236
1342
|
} catch (error) {
|
|
1237
1343
|
if (isRoot) throw error;
|
|
1238
1344
|
return;
|
|
1239
1345
|
}
|
|
1240
|
-
const displayPath = start.scope === "project" ? toProjectDisplayPath(branchPath, absolutePath) : import_node_path.default.resolve(absolutePath);
|
|
1241
1346
|
const hostRelativePath = import_node_path.default.relative(start.absolutePath, absolutePath).split(import_node_path.default.sep).join(import_node_path.default.posix.sep);
|
|
1347
|
+
const displayPath = start.scope === "project" ? toProjectDisplayPath(branchPath, absolutePath) : start.scope === "virtual" ? hostRelativePath ? `${start.displayPath}/${hostRelativePath}` : start.displayPath : import_node_path.default.resolve(absolutePath);
|
|
1242
1348
|
const matchPath = start.scope === "project" ? displayPath : hostRelativePath || import_node_path.default.basename(start.absolutePath);
|
|
1243
1349
|
entries.push({ absolutePath, displayPath, matchPath, stat });
|
|
1244
1350
|
if (!stat.isDirectory()) return;
|
|
@@ -1269,8 +1375,8 @@ function walkWorkerEntries(branchPath, start) {
|
|
|
1269
1375
|
function isProbablyText(bytes) {
|
|
1270
1376
|
return !bytes.subarray(0, Math.min(bytes.length, 4096)).includes(0);
|
|
1271
1377
|
}
|
|
1272
|
-
function grepWorkerFiles(branchPath, input) {
|
|
1273
|
-
const start = resolveWorkerFilePath(branchPath, input.path ?? ".");
|
|
1378
|
+
function grepWorkerFiles(branchPath, input, builtInPaths) {
|
|
1379
|
+
const start = resolveWorkerFilePath(branchPath, input.path ?? ".", builtInPaths);
|
|
1274
1380
|
if (!import_node_fs.default.existsSync(start.absolutePath)) {
|
|
1275
1381
|
throw new Error(`Path not found: ${start.displayPath}`);
|
|
1276
1382
|
}
|
|
@@ -1316,17 +1422,34 @@ function grepWorkerFiles(branchPath, input) {
|
|
|
1316
1422
|
};
|
|
1317
1423
|
}
|
|
1318
1424
|
async function executeGrepOperation(input) {
|
|
1319
|
-
const
|
|
1320
|
-
|
|
1321
|
-
|
|
1322
|
-
|
|
1323
|
-
|
|
1324
|
-
|
|
1325
|
-
|
|
1425
|
+
const toolPath = input.message.path ?? ".";
|
|
1426
|
+
const builtInPaths = await prepareBuiltInToolPaths({
|
|
1427
|
+
filePath: toolPath,
|
|
1428
|
+
baseUrl: input.baseUrl,
|
|
1429
|
+
token: input.token,
|
|
1430
|
+
projectId: input.projectId,
|
|
1431
|
+
branchName: input.message.branchName,
|
|
1432
|
+
sessionId: input.message.sessionId,
|
|
1433
|
+
activePlanId: input.message.activePlanId,
|
|
1434
|
+
artifactRoot: input.artifactRoot,
|
|
1435
|
+
planRoot: input.planRoot,
|
|
1436
|
+
access: "read"
|
|
1326
1437
|
});
|
|
1438
|
+
const workspace = ensureOperationBranch({ ...input, branchName: input.message.branchName });
|
|
1439
|
+
return grepWorkerFiles(
|
|
1440
|
+
workspace.branchPath,
|
|
1441
|
+
{
|
|
1442
|
+
pattern: input.message.pattern,
|
|
1443
|
+
path: input.message.path,
|
|
1444
|
+
glob: input.message.glob,
|
|
1445
|
+
caseSensitive: input.message.caseSensitive,
|
|
1446
|
+
limit: input.message.limit
|
|
1447
|
+
},
|
|
1448
|
+
builtInPaths
|
|
1449
|
+
);
|
|
1327
1450
|
}
|
|
1328
|
-
function findWorkerFiles(branchPath, input) {
|
|
1329
|
-
const start = resolveWorkerFilePath(branchPath, input.path ?? ".");
|
|
1451
|
+
function findWorkerFiles(branchPath, input, builtInPaths) {
|
|
1452
|
+
const start = resolveWorkerFilePath(branchPath, input.path ?? ".", builtInPaths);
|
|
1330
1453
|
if (!import_node_fs.default.existsSync(start.absolutePath) || !import_node_fs.default.statSync(start.absolutePath).isDirectory()) {
|
|
1331
1454
|
throw new Error(`Directory not found: ${start.displayPath}`);
|
|
1332
1455
|
}
|
|
@@ -1360,16 +1483,33 @@ function findWorkerFiles(branchPath, input) {
|
|
|
1360
1483
|
};
|
|
1361
1484
|
}
|
|
1362
1485
|
async function executeFindOperation(input) {
|
|
1363
|
-
const
|
|
1364
|
-
|
|
1365
|
-
|
|
1366
|
-
|
|
1367
|
-
|
|
1368
|
-
|
|
1486
|
+
const toolPath = input.message.path ?? ".";
|
|
1487
|
+
const builtInPaths = await prepareBuiltInToolPaths({
|
|
1488
|
+
filePath: toolPath,
|
|
1489
|
+
baseUrl: input.baseUrl,
|
|
1490
|
+
token: input.token,
|
|
1491
|
+
projectId: input.projectId,
|
|
1492
|
+
branchName: input.message.branchName,
|
|
1493
|
+
sessionId: input.message.sessionId,
|
|
1494
|
+
activePlanId: input.message.activePlanId,
|
|
1495
|
+
artifactRoot: input.artifactRoot,
|
|
1496
|
+
planRoot: input.planRoot,
|
|
1497
|
+
access: "read"
|
|
1369
1498
|
});
|
|
1499
|
+
const workspace = ensureOperationBranch({ ...input, branchName: input.message.branchName });
|
|
1500
|
+
return findWorkerFiles(
|
|
1501
|
+
workspace.branchPath,
|
|
1502
|
+
{
|
|
1503
|
+
pattern: input.message.pattern,
|
|
1504
|
+
path: input.message.path,
|
|
1505
|
+
entryType: input.message.entryType,
|
|
1506
|
+
limit: input.message.limit
|
|
1507
|
+
},
|
|
1508
|
+
builtInPaths
|
|
1509
|
+
);
|
|
1370
1510
|
}
|
|
1371
|
-
function listWorkerDirectory(branchPath, inputPath = ".", inputLimit) {
|
|
1372
|
-
const resolved = resolveWorkerFilePath(branchPath, inputPath);
|
|
1511
|
+
function listWorkerDirectory(branchPath, inputPath = ".", inputLimit, builtInPaths) {
|
|
1512
|
+
const resolved = resolveWorkerFilePath(branchPath, inputPath, builtInPaths);
|
|
1373
1513
|
if (!import_node_fs.default.existsSync(resolved.absolutePath) || !import_node_fs.default.statSync(resolved.absolutePath).isDirectory()) {
|
|
1374
1514
|
throw new Error(`Directory not found: ${resolved.displayPath}`);
|
|
1375
1515
|
}
|
|
@@ -1387,11 +1527,24 @@ function listWorkerDirectory(branchPath, inputPath = ".", inputLimit) {
|
|
|
1387
1527
|
};
|
|
1388
1528
|
}
|
|
1389
1529
|
async function executeLsOperation(input) {
|
|
1530
|
+
const toolPath = input.message.path ?? ".";
|
|
1531
|
+
const builtInPaths = await prepareBuiltInToolPaths({
|
|
1532
|
+
filePath: toolPath,
|
|
1533
|
+
baseUrl: input.baseUrl,
|
|
1534
|
+
token: input.token,
|
|
1535
|
+
projectId: input.projectId,
|
|
1536
|
+
branchName: input.message.branchName,
|
|
1537
|
+
sessionId: input.message.sessionId,
|
|
1538
|
+
activePlanId: input.message.activePlanId,
|
|
1539
|
+
artifactRoot: input.artifactRoot,
|
|
1540
|
+
planRoot: input.planRoot,
|
|
1541
|
+
access: "read"
|
|
1542
|
+
});
|
|
1390
1543
|
const workspace = ensureOperationBranch({ ...input, branchName: input.message.branchName });
|
|
1391
|
-
return listWorkerDirectory(workspace.branchPath, input.message.path, input.message.limit);
|
|
1544
|
+
return listWorkerDirectory(workspace.branchPath, input.message.path, input.message.limit, builtInPaths);
|
|
1392
1545
|
}
|
|
1393
|
-
function readWorkerImageFile(branchPath, filePath) {
|
|
1394
|
-
const resolved = resolveWorkerFilePath(branchPath, filePath);
|
|
1546
|
+
function readWorkerImageFile(branchPath, filePath, builtInPaths) {
|
|
1547
|
+
const resolved = resolveWorkerFilePath(branchPath, filePath, builtInPaths);
|
|
1395
1548
|
if (!import_node_fs.default.existsSync(resolved.absolutePath) || !import_node_fs.default.statSync(resolved.absolutePath).isFile()) {
|
|
1396
1549
|
throw new Error(`File not found: ${resolved.displayPath}`);
|
|
1397
1550
|
}
|
|
@@ -1413,38 +1566,20 @@ function readWorkerImageFile(branchPath, filePath) {
|
|
|
1413
1566
|
};
|
|
1414
1567
|
}
|
|
1415
1568
|
async function executeViewFileBytesOperation(input) {
|
|
1416
|
-
const
|
|
1569
|
+
const builtInPaths = await prepareBuiltInToolPaths({
|
|
1417
1570
|
filePath: input.message.filePath,
|
|
1418
1571
|
baseUrl: input.baseUrl,
|
|
1419
1572
|
token: input.token,
|
|
1420
1573
|
projectId: input.projectId,
|
|
1421
1574
|
branchName: input.message.branchName,
|
|
1422
1575
|
sessionId: input.message.sessionId,
|
|
1423
|
-
|
|
1576
|
+
activePlanId: input.message.activePlanId,
|
|
1577
|
+
artifactRoot: input.artifactRoot,
|
|
1578
|
+
planRoot: input.planRoot,
|
|
1579
|
+
access: "read"
|
|
1424
1580
|
});
|
|
1425
|
-
if (artifact) {
|
|
1426
|
-
if (!import_node_fs.default.existsSync(artifact.absolutePath) || !import_node_fs.default.statSync(artifact.absolutePath).isFile()) {
|
|
1427
|
-
throw new Error(`File not found: ${artifact.virtualPath}`);
|
|
1428
|
-
}
|
|
1429
|
-
const extension = import_node_path.default.extname(artifact.absolutePath).toLowerCase();
|
|
1430
|
-
const mediaType = extension === ".jpg" || extension === ".jpeg" ? "image/jpeg" : extension === ".png" ? "image/png" : null;
|
|
1431
|
-
if (!mediaType) {
|
|
1432
|
-
throw new Error("read supports PNG and JPEG image inputs only");
|
|
1433
|
-
}
|
|
1434
|
-
const bytes = import_node_fs.default.readFileSync(artifact.absolutePath);
|
|
1435
|
-
const dimensions = readImageDimensions(bytes, mediaType);
|
|
1436
|
-
return {
|
|
1437
|
-
type: "view_file_bytes",
|
|
1438
|
-
filePath: artifact.virtualPath,
|
|
1439
|
-
mediaType,
|
|
1440
|
-
base64: bytes.toString("base64"),
|
|
1441
|
-
fileSizeBytes: bytes.length,
|
|
1442
|
-
width: dimensions.width,
|
|
1443
|
-
height: dimensions.height
|
|
1444
|
-
};
|
|
1445
|
-
}
|
|
1446
1581
|
const workspace = ensureOperationBranch({ ...input, branchName: input.message.branchName });
|
|
1447
|
-
return readWorkerImageFile(workspace.branchPath, input.message.filePath);
|
|
1582
|
+
return readWorkerImageFile(workspace.branchPath, input.message.filePath, builtInPaths);
|
|
1448
1583
|
}
|
|
1449
1584
|
async function executeOperation(input) {
|
|
1450
1585
|
switch (input.message.type) {
|
|
@@ -2083,6 +2218,7 @@ async function startWorker(options) {
|
|
|
2083
2218
|
import_node_fs.default.mkdirSync(artifactRoot, { recursive: true });
|
|
2084
2219
|
import_node_fs.default.mkdirSync(planRoot, { recursive: true });
|
|
2085
2220
|
const manifestByProjectId = /* @__PURE__ */ new Map();
|
|
2221
|
+
const pendingManifestCheckouts = /* @__PURE__ */ new Map();
|
|
2086
2222
|
const workspaceSyncSingleFlight = new import_workspace_sync.WorkspaceSyncSingleFlight();
|
|
2087
2223
|
let workspaceRemoteUrl = null;
|
|
2088
2224
|
let activeWorkspaceIncidentId = null;
|
|
@@ -2091,6 +2227,7 @@ async function startWorker(options) {
|
|
|
2091
2227
|
let periodicWorkspaceScanInFlight = false;
|
|
2092
2228
|
let terminalReplayTimer;
|
|
2093
2229
|
let workspaceSyncRequestsInFlight = 0;
|
|
2230
|
+
let sessionArtifactSyncRequestsInFlight = 0;
|
|
2094
2231
|
let cliUpdateInProgress = false;
|
|
2095
2232
|
let reloadAfterClose = false;
|
|
2096
2233
|
const workspaceSyncInput = (trigger, overrides = {}) => {
|
|
@@ -2110,37 +2247,64 @@ async function startWorker(options) {
|
|
|
2110
2247
|
const sendWorkspaceSyncResult = (requestId, result) => {
|
|
2111
2248
|
sendWorkerMessage(ws, { type: "workspace_sync_result", requestId, result });
|
|
2112
2249
|
};
|
|
2113
|
-
const
|
|
2250
|
+
const manifestCheckoutKey = (projectId, branchName) => `${projectId}\0${branchName}`;
|
|
2251
|
+
const allManifestCheckouts = () => [...manifestByProjectId.values()].flatMap(
|
|
2252
|
+
(manifest) => manifest.branches.map((branchName) => ({ projectId: manifest.projectId, branchName }))
|
|
2253
|
+
);
|
|
2254
|
+
const ensureVisibleWorkspaceCheckouts = (targets) => {
|
|
2114
2255
|
const created = [];
|
|
2115
|
-
for (const
|
|
2116
|
-
|
|
2117
|
-
|
|
2118
|
-
|
|
2119
|
-
|
|
2120
|
-
|
|
2121
|
-
projectRoot: projectRootFor(projectsRoot, manifest.projectId, manifestByProjectId),
|
|
2122
|
-
branchName,
|
|
2123
|
-
githubRemoteUrl,
|
|
2124
|
-
githubAuth: gitAuthForRemote(githubRemoteUrl, manifest.repoAuthHeader),
|
|
2125
|
-
githubAuthHeader: manifest.repoAuthHeader,
|
|
2126
|
-
defaultBranch: manifest.defaultBranch || "main",
|
|
2127
|
-
reconcileExistingOrigin: Boolean(manifest.repoHttpUrl)
|
|
2128
|
-
});
|
|
2129
|
-
if (!existed) created.push({ projectId: manifest.projectId, branchName });
|
|
2256
|
+
for (const target of targets) {
|
|
2257
|
+
const manifest = manifestByProjectId.get(target.projectId);
|
|
2258
|
+
const key = manifestCheckoutKey(target.projectId, target.branchName);
|
|
2259
|
+
if (!manifest || !manifest.branches.includes(target.branchName)) {
|
|
2260
|
+
pendingManifestCheckouts.delete(key);
|
|
2261
|
+
continue;
|
|
2130
2262
|
}
|
|
2263
|
+
const projectRoot = projectRootFor(projectsRoot, manifest.projectId, manifestByProjectId);
|
|
2264
|
+
const branchPath = import_node_path.default.join(projectRoot, target.branchName);
|
|
2265
|
+
const existed = hasNormalVisibleGitDir(branchPath);
|
|
2266
|
+
const githubRemoteUrl = githubRemoteUrlFor(baseUrl, manifest.projectId, manifest);
|
|
2267
|
+
ensureVisibleGitCheckout({
|
|
2268
|
+
projectRoot,
|
|
2269
|
+
branchName: target.branchName,
|
|
2270
|
+
githubRemoteUrl,
|
|
2271
|
+
githubAuth: gitAuthForRemote(githubRemoteUrl, manifest.repoAuthHeader),
|
|
2272
|
+
githubAuthHeader: manifest.repoAuthHeader,
|
|
2273
|
+
defaultBranch: manifest.defaultBranch || "main",
|
|
2274
|
+
reconcileExistingOrigin: Boolean(manifest.repoHttpUrl)
|
|
2275
|
+
});
|
|
2276
|
+
if (!existed) created.push(target);
|
|
2131
2277
|
}
|
|
2132
2278
|
return created;
|
|
2133
2279
|
};
|
|
2134
2280
|
const runRequestedWorkspaceSync = async (requestId, trigger, overrides = {}) => {
|
|
2135
2281
|
workspaceSyncRequestsInFlight += 1;
|
|
2136
2282
|
try {
|
|
2137
|
-
|
|
2138
|
-
const result = await workspaceSyncSingleFlight.
|
|
2139
|
-
|
|
2283
|
+
let pendingTargets = [];
|
|
2284
|
+
const result = await workspaceSyncSingleFlight.runPrepared(() => {
|
|
2285
|
+
const shouldEnsureCheckouts = !overrides.skipVisibleMirror && !overrides.resetToCanonical;
|
|
2286
|
+
let checkoutTargets = [];
|
|
2287
|
+
if (shouldEnsureCheckouts) {
|
|
2288
|
+
checkoutTargets = trigger.type === "connect" ? allManifestCheckouts() : [...pendingManifestCheckouts.values()];
|
|
2289
|
+
}
|
|
2290
|
+
pendingTargets = shouldEnsureCheckouts ? [...pendingManifestCheckouts.values()] : [];
|
|
2291
|
+
const createdCheckouts = ensureVisibleWorkspaceCheckouts(checkoutTargets);
|
|
2292
|
+
const newVisibleCheckoutByKey = /* @__PURE__ */ new Map();
|
|
2293
|
+
for (const target of [...overrides.newVisibleCheckouts ?? [], ...createdCheckouts, ...pendingTargets]) {
|
|
2294
|
+
newVisibleCheckoutByKey.set(manifestCheckoutKey(target.projectId, target.branchName), target);
|
|
2295
|
+
}
|
|
2296
|
+
const newVisibleCheckouts = [...newVisibleCheckoutByKey.values()];
|
|
2297
|
+
return workspaceSyncInput(trigger, {
|
|
2140
2298
|
...overrides,
|
|
2141
2299
|
...newVisibleCheckouts.length > 0 ? { newVisibleCheckouts } : {}
|
|
2142
|
-
})
|
|
2143
|
-
);
|
|
2300
|
+
});
|
|
2301
|
+
});
|
|
2302
|
+
if (result.outcome === "no_change" || result.outcome === "published" || result.outcome === "updated" || result.outcome === "conflict_reset") {
|
|
2303
|
+
for (const target of pendingTargets) {
|
|
2304
|
+
const key = manifestCheckoutKey(target.projectId, target.branchName);
|
|
2305
|
+
if (pendingManifestCheckouts.get(key) === target) pendingManifestCheckouts.delete(key);
|
|
2306
|
+
}
|
|
2307
|
+
}
|
|
2144
2308
|
previousPeriodicFingerprint = null;
|
|
2145
2309
|
sendWorkspaceSyncResult(requestId, result);
|
|
2146
2310
|
return result;
|
|
@@ -2233,9 +2397,26 @@ async function startWorker(options) {
|
|
|
2233
2397
|
configureGitHubAuth(message.githubCredential);
|
|
2234
2398
|
visibleGitIdentity = message.gitIdentity;
|
|
2235
2399
|
workspaceRemoteUrl = message.workspaceRemoteUrl;
|
|
2400
|
+
const previousCheckoutKeys = new Set(
|
|
2401
|
+
allManifestCheckouts().map(({ projectId, branchName }) => manifestCheckoutKey(projectId, branchName))
|
|
2402
|
+
);
|
|
2236
2403
|
const migratedProjectIds = (0, import_managed_paths.migrateLegacyProjectRoots)(projectsRoot, message.projects);
|
|
2237
2404
|
manifestByProjectId.clear();
|
|
2238
2405
|
for (const project of message.projects) manifestByProjectId.set(project.projectId, project);
|
|
2406
|
+
const currentCheckouts = allManifestCheckouts();
|
|
2407
|
+
const currentCheckoutKeys = new Set(
|
|
2408
|
+
currentCheckouts.map(({ projectId, branchName }) => manifestCheckoutKey(projectId, branchName))
|
|
2409
|
+
);
|
|
2410
|
+
for (const key of pendingManifestCheckouts.keys()) {
|
|
2411
|
+
if (!currentCheckoutKeys.has(key)) pendingManifestCheckouts.delete(key);
|
|
2412
|
+
}
|
|
2413
|
+
for (const checkout of currentCheckouts) {
|
|
2414
|
+
const key = manifestCheckoutKey(checkout.projectId, checkout.branchName);
|
|
2415
|
+
if (previousCheckoutKeys.has(key)) continue;
|
|
2416
|
+
const branchPath = import_node_path.default.join(projectRootFor(projectsRoot, checkout.projectId, manifestByProjectId), checkout.branchName);
|
|
2417
|
+
if (!hasNormalVisibleGitDir(branchPath)) pendingManifestCheckouts.set(key, checkout);
|
|
2418
|
+
}
|
|
2419
|
+
previousPeriodicFingerprint = null;
|
|
2239
2420
|
process.stdout.write(
|
|
2240
2421
|
`[r5d-worker] workspace manifest: ${message.projects.length} projects${migratedProjectIds.length > 0 ? `; migrated ${migratedProjectIds.length} project checkout root(s)` : ""}
|
|
2241
2422
|
`
|
|
@@ -2246,8 +2427,15 @@ async function startWorker(options) {
|
|
|
2246
2427
|
if (!workspaceRemoteUrl || activeWorkspaceIncidentId || cliUpdateInProgress || periodicWorkspaceScanInFlight) return;
|
|
2247
2428
|
periodicWorkspaceScanInFlight = true;
|
|
2248
2429
|
void (async () => {
|
|
2249
|
-
|
|
2250
|
-
|
|
2430
|
+
if (pendingManifestCheckouts.size > 0) {
|
|
2431
|
+
previousPeriodicFingerprint = null;
|
|
2432
|
+
await runRequestedWorkspaceSync(crypto.randomUUID(), {
|
|
2433
|
+
type: "periodic",
|
|
2434
|
+
detail: "workspace manifest additions"
|
|
2435
|
+
});
|
|
2436
|
+
return;
|
|
2437
|
+
}
|
|
2438
|
+
const fingerprint = await workspaceSyncSingleFlight.fingerprintPrepared(() => workspaceSyncInput({ type: "periodic" }));
|
|
2251
2439
|
if (fingerprint === emptyFingerprint) {
|
|
2252
2440
|
previousPeriodicFingerprint = null;
|
|
2253
2441
|
return;
|
|
@@ -2287,6 +2475,34 @@ async function startWorker(options) {
|
|
|
2287
2475
|
}
|
|
2288
2476
|
return;
|
|
2289
2477
|
}
|
|
2478
|
+
if (message.type === "sync_session_artifacts") {
|
|
2479
|
+
sessionArtifactSyncRequestsInFlight += 1;
|
|
2480
|
+
try {
|
|
2481
|
+
await syncSessionArtifacts({
|
|
2482
|
+
baseUrl,
|
|
2483
|
+
token,
|
|
2484
|
+
projectId: message.projectId,
|
|
2485
|
+
branchName: message.branchName,
|
|
2486
|
+
sessionId: message.sessionId,
|
|
2487
|
+
artifactRoot
|
|
2488
|
+
});
|
|
2489
|
+
sendWorkerMessage(ws, {
|
|
2490
|
+
type: "session_artifacts_sync_result",
|
|
2491
|
+
requestId: message.requestId,
|
|
2492
|
+
sessionId: message.sessionId
|
|
2493
|
+
});
|
|
2494
|
+
} catch (error) {
|
|
2495
|
+
sendWorkerMessage(ws, {
|
|
2496
|
+
type: "session_artifacts_sync_result",
|
|
2497
|
+
requestId: message.requestId,
|
|
2498
|
+
sessionId: message.sessionId,
|
|
2499
|
+
error: error instanceof Error ? error.message : String(error)
|
|
2500
|
+
});
|
|
2501
|
+
} finally {
|
|
2502
|
+
sessionArtifactSyncRequestsInFlight -= 1;
|
|
2503
|
+
}
|
|
2504
|
+
return;
|
|
2505
|
+
}
|
|
2290
2506
|
if (message.type === "workspace_incident_updated") {
|
|
2291
2507
|
activeWorkspaceIncidentId = message.status === "remediating" || message.status === "waiting_for_worker" ? message.incidentId : null;
|
|
2292
2508
|
previousPeriodicFingerprint = null;
|
|
@@ -2305,7 +2521,7 @@ async function startWorker(options) {
|
|
|
2305
2521
|
});
|
|
2306
2522
|
return;
|
|
2307
2523
|
}
|
|
2308
|
-
if (activeProcesses.size > 0 || activePtys.size > 0 || workspaceSyncRequestsInFlight > 0) {
|
|
2524
|
+
if (activeProcesses.size > 0 || activePtys.size > 0 || workspaceSyncRequestsInFlight > 0 || sessionArtifactSyncRequestsInFlight > 0) {
|
|
2309
2525
|
sendWorkerMessage(ws, {
|
|
2310
2526
|
type: "update_clis_result",
|
|
2311
2527
|
requestId: message.requestId,
|
|
@@ -2486,6 +2702,7 @@ async function startWorker(options) {
|
|
|
2486
2702
|
projectRoot,
|
|
2487
2703
|
syncRoot,
|
|
2488
2704
|
artifactRoot,
|
|
2705
|
+
planRoot,
|
|
2489
2706
|
manifest
|
|
2490
2707
|
});
|
|
2491
2708
|
ws.send(
|