@ricsam/r5d-worker 0.0.33 → 0.0.35
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 +203 -24
- package/dist/cjs/package.json +1 -1
- package/dist/mjs/main.mjs +200 -24
- package/dist/mjs/package.json +1 -1
- package/dist/types/main.d.ts +49 -7
- package/package.json +1 -1
package/dist/cjs/main.cjs
CHANGED
|
@@ -34,6 +34,7 @@ __export(main_exports, {
|
|
|
34
34
|
findWorkerFiles: () => findWorkerFiles,
|
|
35
35
|
githubCliEnv: () => githubCliEnv,
|
|
36
36
|
grepWorkerFiles: () => grepWorkerFiles,
|
|
37
|
+
inspectManagedWorkspace: () => inspectManagedWorkspace,
|
|
37
38
|
isArtifactEnvPath: () => isArtifactEnvPath,
|
|
38
39
|
listWorkerDirectory: () => listWorkerDirectory,
|
|
39
40
|
prepareArtifactEnvForShell: () => prepareArtifactEnvForShell,
|
|
@@ -41,8 +42,10 @@ __export(main_exports, {
|
|
|
41
42
|
readWorkerImageFile: () => readWorkerImageFile,
|
|
42
43
|
readWorkerTextFile: () => readWorkerTextFile,
|
|
43
44
|
resolveHostShell: () => resolveHostShell,
|
|
45
|
+
resolveManagedCheckoutPath: () => resolveManagedCheckoutPath,
|
|
44
46
|
resolveProjectFilePath: () => resolveProjectFilePath,
|
|
45
47
|
resolveWorkerFilePath: () => resolveWorkerFilePath,
|
|
48
|
+
selectManifestSyncTargets: () => selectManifestSyncTargets,
|
|
46
49
|
syncManifestProjectsFromInternal: () => syncManifestProjectsFromInternal,
|
|
47
50
|
syncProjectPlans: () => syncProjectPlans,
|
|
48
51
|
syncSessionArtifacts: () => syncSessionArtifacts
|
|
@@ -743,6 +746,11 @@ function resolveWorkerFilePath(branchPath, inputPath) {
|
|
|
743
746
|
const displayPath = toProjectDisplayPath(resolvedBranchPath, absolutePath2);
|
|
744
747
|
const repoRelativePath2 = displayPath === "." ? "" : displayPath;
|
|
745
748
|
assertAllowedProjectPath(repoRelativePath2, inputPath);
|
|
749
|
+
const realBranchPath2 = import_node_fs.default.existsSync(resolvedBranchPath) ? import_node_fs.default.realpathSync(resolvedBranchPath) : resolvedBranchPath;
|
|
750
|
+
const realTargetPath2 = resolveThroughExistingAncestor(absolutePath2);
|
|
751
|
+
if (!isInsideBranchPath(realBranchPath2, realTargetPath2)) {
|
|
752
|
+
throw new Error(`Project file path escapes its checkout through a symbolic link: ${inputPath}`);
|
|
753
|
+
}
|
|
746
754
|
return {
|
|
747
755
|
absolutePath: absolutePath2,
|
|
748
756
|
displayPath,
|
|
@@ -761,6 +769,11 @@ function resolveWorkerFilePath(branchPath, inputPath) {
|
|
|
761
769
|
if (!isInsideBranchPath(resolvedBranchPath, absolutePath)) {
|
|
762
770
|
throw new Error(`Invalid project file path: ${inputPath}`);
|
|
763
771
|
}
|
|
772
|
+
const realBranchPath = import_node_fs.default.existsSync(resolvedBranchPath) ? import_node_fs.default.realpathSync(resolvedBranchPath) : resolvedBranchPath;
|
|
773
|
+
const realTargetPath = resolveThroughExistingAncestor(absolutePath);
|
|
774
|
+
if (!isInsideBranchPath(realBranchPath, realTargetPath)) {
|
|
775
|
+
throw new Error(`Project file path escapes its checkout through a symbolic link: ${inputPath}`);
|
|
776
|
+
}
|
|
764
777
|
return {
|
|
765
778
|
absolutePath,
|
|
766
779
|
displayPath: repoRelativePath || ".",
|
|
@@ -772,11 +785,60 @@ function resolveProjectFilePath(branchPath, inputPath) {
|
|
|
772
785
|
const resolved = resolveWorkerFilePath(branchPath, inputPath);
|
|
773
786
|
if (resolved.scope === "host") {
|
|
774
787
|
throw new Error(
|
|
775
|
-
`Project file writes must
|
|
788
|
+
`Project file writes must resolve inside the selected managed checkout: ${inputPath}`
|
|
776
789
|
);
|
|
777
790
|
}
|
|
778
791
|
return resolved;
|
|
779
792
|
}
|
|
793
|
+
function resolveThroughExistingAncestor(inputPath) {
|
|
794
|
+
const suffix = [];
|
|
795
|
+
let cursor = import_node_path.default.resolve(inputPath);
|
|
796
|
+
while (!import_node_fs.default.existsSync(cursor)) {
|
|
797
|
+
const parent = import_node_path.default.dirname(cursor);
|
|
798
|
+
if (parent === cursor) return import_node_path.default.resolve(inputPath);
|
|
799
|
+
suffix.unshift(import_node_path.default.basename(cursor));
|
|
800
|
+
cursor = parent;
|
|
801
|
+
}
|
|
802
|
+
return import_node_path.default.resolve(import_node_fs.default.realpathSync(cursor), ...suffix);
|
|
803
|
+
}
|
|
804
|
+
function resolveManagedCheckoutPath(input) {
|
|
805
|
+
const originManifest = input.manifests.find((manifest) => manifest.projectId === input.originProjectId);
|
|
806
|
+
const candidate = import_node_path.default.isAbsolute(input.inputPath) ? import_node_path.default.resolve(input.inputPath) : import_node_path.default.resolve(
|
|
807
|
+
input.projectsRoot,
|
|
808
|
+
originManifest?.repoSlug ?? input.originProjectId,
|
|
809
|
+
input.originBranchName,
|
|
810
|
+
input.inputPath
|
|
811
|
+
);
|
|
812
|
+
const resolvedCandidate = resolveThroughExistingAncestor(candidate);
|
|
813
|
+
for (const manifest of input.manifests) {
|
|
814
|
+
const branches = manifest.branches.length > 0 ? manifest.branches : [manifest.defaultBranch || "main"];
|
|
815
|
+
for (const branchName of branches) {
|
|
816
|
+
const checkoutPath = import_node_path.default.resolve(input.projectsRoot, manifest.repoSlug || manifest.projectId, branchName);
|
|
817
|
+
if (!hasNormalVisibleGitDir(checkoutPath)) continue;
|
|
818
|
+
const resolvedCheckoutPath = import_node_fs.default.existsSync(checkoutPath) ? import_node_fs.default.realpathSync(checkoutPath) : checkoutPath;
|
|
819
|
+
const matchesLexically = isInsideBranchPath(checkoutPath, candidate);
|
|
820
|
+
if (!matchesLexically) continue;
|
|
821
|
+
if (!isInsideBranchPath(resolvedCheckoutPath, resolvedCandidate)) {
|
|
822
|
+
throw new Error(`Managed path escapes its checkout through a symbolic link: ${input.inputPath}`);
|
|
823
|
+
}
|
|
824
|
+
const repoRelativePath = toProjectDisplayPath(resolvedCheckoutPath, resolvedCandidate);
|
|
825
|
+
assertAllowedProjectPath(repoRelativePath === "." ? "" : repoRelativePath, input.inputPath);
|
|
826
|
+
return {
|
|
827
|
+
type: "resolve_managed_path",
|
|
828
|
+
resolved: {
|
|
829
|
+
projectId: manifest.projectId,
|
|
830
|
+
projectPath: manifest.projectPath,
|
|
831
|
+
branchName,
|
|
832
|
+
checkoutPath,
|
|
833
|
+
inputPath: input.inputPath,
|
|
834
|
+
absolutePath: candidate,
|
|
835
|
+
repoRelativePath: repoRelativePath === "." ? "" : repoRelativePath
|
|
836
|
+
}
|
|
837
|
+
};
|
|
838
|
+
}
|
|
839
|
+
}
|
|
840
|
+
return { type: "resolve_managed_path", resolved: null };
|
|
841
|
+
}
|
|
780
842
|
function resolveRemoteUrl(baseUrl, remoteUrl) {
|
|
781
843
|
if (/^https?:\/\//i.test(remoteUrl)) {
|
|
782
844
|
return remoteUrl;
|
|
@@ -1145,6 +1207,9 @@ async function forceSyncManifestBranchFromInternal(input) {
|
|
|
1145
1207
|
if (!target) {
|
|
1146
1208
|
throw new Error(`Internal remote has neither ${input.branchName} nor main`);
|
|
1147
1209
|
}
|
|
1210
|
+
if (hasInternalWorktreeChanges(context)) {
|
|
1211
|
+
return null;
|
|
1212
|
+
}
|
|
1148
1213
|
await runInternalGitAsync(context, ["reset", "--hard", target]);
|
|
1149
1214
|
await runInternalGitAsync(context, ["clean", "-fd", "--", ".", ":(exclude).git"]);
|
|
1150
1215
|
return getInternalCommitHash(context);
|
|
@@ -1152,21 +1217,34 @@ async function forceSyncManifestBranchFromInternal(input) {
|
|
|
1152
1217
|
function findRepositorySyncBlockers(input) {
|
|
1153
1218
|
const targetByKey = new Map(input.targets.map((target) => [`${target.projectId}:${target.branchName}`, target]));
|
|
1154
1219
|
const blockedBy = [];
|
|
1155
|
-
for (const process2 of input.processes) {
|
|
1156
|
-
const target = targetByKey.get(`${process2.projectId}:${process2.branchName}`);
|
|
1157
|
-
if (target) blockedBy.push({ ...target, kind: "process", id: process2.id });
|
|
1158
|
-
}
|
|
1159
1220
|
for (const shell of input.shells) {
|
|
1160
1221
|
const target = targetByKey.get(`${shell.projectId}:${shell.branchName}`);
|
|
1161
1222
|
if (target) blockedBy.push({ ...target, kind: "shell", id: shell.id });
|
|
1162
1223
|
}
|
|
1163
1224
|
return blockedBy;
|
|
1164
1225
|
}
|
|
1226
|
+
function selectManifestSyncTargets(input) {
|
|
1227
|
+
if (!input.requestedTargets) {
|
|
1228
|
+
return input.manifests.flatMap((manifest) => {
|
|
1229
|
+
const branches = manifest.branches.length > 0 ? manifest.branches : [manifest.defaultBranch || "main"];
|
|
1230
|
+
return [...new Set(branches)].map((branchName) => ({ manifest, branchName }));
|
|
1231
|
+
});
|
|
1232
|
+
}
|
|
1233
|
+
const manifestsByProjectId = new Map(input.manifests.map((manifest) => [manifest.projectId, manifest]));
|
|
1234
|
+
const selected = /* @__PURE__ */ new Map();
|
|
1235
|
+
for (const target of input.requestedTargets) {
|
|
1236
|
+
const manifest = manifestsByProjectId.get(target.projectId);
|
|
1237
|
+
if (!manifest) {
|
|
1238
|
+
continue;
|
|
1239
|
+
}
|
|
1240
|
+
selected.set(`${target.projectId}\0${target.branchName}`, { manifest, branchName: target.branchName });
|
|
1241
|
+
}
|
|
1242
|
+
return [...selected.values()];
|
|
1243
|
+
}
|
|
1165
1244
|
async function syncManifestProjectsFromInternal(input) {
|
|
1166
|
-
const
|
|
1167
|
-
|
|
1168
|
-
|
|
1169
|
-
return [...new Set(branches)].map((branchName) => ({ manifest, branchName }));
|
|
1245
|
+
const targets = selectManifestSyncTargets({
|
|
1246
|
+
manifests: input.manifests,
|
|
1247
|
+
requestedTargets: input.requestedTargets
|
|
1170
1248
|
});
|
|
1171
1249
|
const blockedBy = findRepositorySyncBlockers({
|
|
1172
1250
|
targets: targets.map(({ manifest, branchName }) => ({
|
|
@@ -1174,22 +1252,18 @@ async function syncManifestProjectsFromInternal(input) {
|
|
|
1174
1252
|
projectPath: manifest.projectPath,
|
|
1175
1253
|
branchName
|
|
1176
1254
|
})),
|
|
1177
|
-
processes: [...activeProcesses].map(([id, active]) => ({
|
|
1178
|
-
id,
|
|
1179
|
-
projectId: active.projectId,
|
|
1180
|
-
branchName: active.branchName
|
|
1181
|
-
})),
|
|
1182
1255
|
shells: [...activePtys].map(([id, active]) => ({
|
|
1183
1256
|
id,
|
|
1184
1257
|
projectId: active.projectId,
|
|
1185
1258
|
branchName: active.branchName
|
|
1186
1259
|
}))
|
|
1187
1260
|
});
|
|
1188
|
-
|
|
1189
|
-
return { status: "blocked", results: [], blockedBy };
|
|
1190
|
-
}
|
|
1261
|
+
const blockedTargetKeys = new Set(blockedBy.map((blocker) => `${blocker.projectId}\0${blocker.branchName}`));
|
|
1191
1262
|
const results = [];
|
|
1192
1263
|
for (const { manifest, branchName } of targets) {
|
|
1264
|
+
if (blockedTargetKeys.has(`${manifest.projectId}\0${branchName}`)) {
|
|
1265
|
+
continue;
|
|
1266
|
+
}
|
|
1193
1267
|
try {
|
|
1194
1268
|
const commitHash = await forceSyncManifestBranchFromInternal({
|
|
1195
1269
|
projectId: manifest.projectId,
|
|
@@ -1200,6 +1274,16 @@ async function syncManifestProjectsFromInternal(input) {
|
|
|
1200
1274
|
branchName,
|
|
1201
1275
|
manifest
|
|
1202
1276
|
});
|
|
1277
|
+
if (commitHash === null) {
|
|
1278
|
+
blockedBy.push({
|
|
1279
|
+
projectId: manifest.projectId,
|
|
1280
|
+
projectPath: manifest.projectPath,
|
|
1281
|
+
branchName,
|
|
1282
|
+
kind: "dirty_checkout",
|
|
1283
|
+
id: `${manifest.projectId}:${branchName}`
|
|
1284
|
+
});
|
|
1285
|
+
continue;
|
|
1286
|
+
}
|
|
1203
1287
|
results.push({
|
|
1204
1288
|
projectId: manifest.projectId,
|
|
1205
1289
|
projectPath: manifest.projectPath,
|
|
@@ -1217,10 +1301,11 @@ async function syncManifestProjectsFromInternal(input) {
|
|
|
1217
1301
|
});
|
|
1218
1302
|
}
|
|
1219
1303
|
}
|
|
1304
|
+
const hasFailures = results.some((result) => result.status === "failed");
|
|
1220
1305
|
return {
|
|
1221
|
-
status:
|
|
1306
|
+
status: blockedBy.length > 0 && results.length === 0 ? "blocked" : blockedBy.length > 0 || hasFailures ? "partial" : "completed",
|
|
1222
1307
|
results,
|
|
1223
|
-
blockedBy
|
|
1308
|
+
blockedBy
|
|
1224
1309
|
};
|
|
1225
1310
|
}
|
|
1226
1311
|
function resolveCommandCwd(branchPath, cwd) {
|
|
@@ -1271,6 +1356,60 @@ async function streamCommandOutput(stream, onData) {
|
|
|
1271
1356
|
function ensureOperationBranch(input) {
|
|
1272
1357
|
return ensureBranchWorkspace(input);
|
|
1273
1358
|
}
|
|
1359
|
+
async function inspectManagedWorkspace(input) {
|
|
1360
|
+
const candidates = input.manifests.flatMap((manifest) => {
|
|
1361
|
+
const branches = manifest.branches.length > 0 ? manifest.branches : [manifest.defaultBranch || "main"];
|
|
1362
|
+
return branches.map((branchName) => ({
|
|
1363
|
+
manifest,
|
|
1364
|
+
branchName,
|
|
1365
|
+
checkoutPath: import_node_path.default.resolve(input.projectsRoot, manifest.repoSlug || manifest.projectId, branchName)
|
|
1366
|
+
}));
|
|
1367
|
+
});
|
|
1368
|
+
const inspected = await Promise.all(
|
|
1369
|
+
candidates.map(async ({ manifest, branchName, checkoutPath }) => {
|
|
1370
|
+
const gitDir = internalGitDirFor(input.syncRoot, manifest.projectId, branchName);
|
|
1371
|
+
if (!hasNormalVisibleGitDir(checkoutPath) || !import_node_fs.default.existsSync(gitDir)) return null;
|
|
1372
|
+
const context = {
|
|
1373
|
+
gitDir,
|
|
1374
|
+
workTree: checkoutPath,
|
|
1375
|
+
auth: internalGitAuth(input.baseUrl, input.token)
|
|
1376
|
+
};
|
|
1377
|
+
try {
|
|
1378
|
+
const status = await runInternalGitAsync(context, ["status", "--porcelain", "--", ".", ":(exclude).git"]);
|
|
1379
|
+
let commitHash = null;
|
|
1380
|
+
for (const revision of [
|
|
1381
|
+
"HEAD",
|
|
1382
|
+
`refs/remotes/${R5D_REMOTE_NAME}/${branchName}`,
|
|
1383
|
+
`refs/remotes/${R5D_REMOTE_NAME}/main`
|
|
1384
|
+
]) {
|
|
1385
|
+
try {
|
|
1386
|
+
commitHash = await runInternalGitAsync(context, ["rev-parse", "--verify", revision]);
|
|
1387
|
+
break;
|
|
1388
|
+
} catch {
|
|
1389
|
+
}
|
|
1390
|
+
}
|
|
1391
|
+
if (!commitHash) return null;
|
|
1392
|
+
return {
|
|
1393
|
+
projectId: manifest.projectId,
|
|
1394
|
+
projectPath: manifest.projectPath,
|
|
1395
|
+
branchName,
|
|
1396
|
+
checkoutPath,
|
|
1397
|
+
commitHash,
|
|
1398
|
+
status,
|
|
1399
|
+
dirty: status.trim().length > 0
|
|
1400
|
+
};
|
|
1401
|
+
} catch {
|
|
1402
|
+
return null;
|
|
1403
|
+
}
|
|
1404
|
+
})
|
|
1405
|
+
);
|
|
1406
|
+
return {
|
|
1407
|
+
type: "workspace_status",
|
|
1408
|
+
checkouts: inspected.filter((checkout) => checkout !== null).sort(
|
|
1409
|
+
(left, right) => left.projectPath.localeCompare(right.projectPath) || left.branchName.localeCompare(right.branchName)
|
|
1410
|
+
)
|
|
1411
|
+
};
|
|
1412
|
+
}
|
|
1274
1413
|
function internalGitProcessEnv(workspace, env) {
|
|
1275
1414
|
if (env?.R5D_USE_INTERNAL_GIT !== "1") {
|
|
1276
1415
|
return {};
|
|
@@ -1837,6 +1976,22 @@ function pullBranch(input) {
|
|
|
1837
1976
|
}
|
|
1838
1977
|
async function executeOperation(input) {
|
|
1839
1978
|
switch (input.message.type) {
|
|
1979
|
+
case "resolve_managed_path":
|
|
1980
|
+
return resolveManagedCheckoutPath({
|
|
1981
|
+
projectsRoot: input.projectsRoot,
|
|
1982
|
+
manifests: input.manifests,
|
|
1983
|
+
originProjectId: input.message.projectId,
|
|
1984
|
+
originBranchName: input.message.branchName,
|
|
1985
|
+
inputPath: input.message.inputPath
|
|
1986
|
+
});
|
|
1987
|
+
case "workspace_status":
|
|
1988
|
+
return await inspectManagedWorkspace({
|
|
1989
|
+
baseUrl: input.baseUrl,
|
|
1990
|
+
token: input.token,
|
|
1991
|
+
projectsRoot: input.projectsRoot,
|
|
1992
|
+
syncRoot: input.syncRoot,
|
|
1993
|
+
manifests: input.manifests
|
|
1994
|
+
});
|
|
1840
1995
|
case "read":
|
|
1841
1996
|
return executeReadFileOperation({ ...input, message: input.message });
|
|
1842
1997
|
case "write":
|
|
@@ -1939,9 +2094,11 @@ async function executeCommand(input) {
|
|
|
1939
2094
|
projectId: input.projectId,
|
|
1940
2095
|
sessionId: input.message.sessionId ?? "",
|
|
1941
2096
|
branchName: input.message.branchName,
|
|
2097
|
+
mode: "foreground",
|
|
2098
|
+
startCommitHash: "",
|
|
1942
2099
|
argv: input.message.argv,
|
|
1943
2100
|
command: input.message.argv.join(" "),
|
|
1944
|
-
cwd
|
|
2101
|
+
cwd,
|
|
1945
2102
|
startedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
1946
2103
|
});
|
|
1947
2104
|
if (input.message.timeoutMs) {
|
|
@@ -2044,17 +2201,28 @@ async function executeStreamingCommand(input) {
|
|
|
2044
2201
|
projectId: input.projectId,
|
|
2045
2202
|
sessionId: input.message.sessionId,
|
|
2046
2203
|
branchName: input.message.branchName,
|
|
2204
|
+
mode: input.message.mode,
|
|
2205
|
+
startCommitHash: input.message.startCommitHash,
|
|
2206
|
+
launchWorkspaceActionId: input.message.launchWorkspaceActionId,
|
|
2047
2207
|
credentialId: input.message.credentialId,
|
|
2208
|
+
pid: subprocess.pid,
|
|
2209
|
+
processGroupId: subprocess.pid,
|
|
2048
2210
|
argv: input.message.argv,
|
|
2049
2211
|
command: input.message.command,
|
|
2050
|
-
cwd
|
|
2212
|
+
// Persist the resolved absolute cwd so reconnect reports and process
|
|
2213
|
+
// history retain the actual execution location, including cross-project
|
|
2214
|
+
// managed paths.
|
|
2215
|
+
cwd,
|
|
2051
2216
|
startedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
2052
2217
|
});
|
|
2053
2218
|
started = true;
|
|
2054
2219
|
sendWorkerMessage(input.ws, {
|
|
2055
2220
|
type: "exec_started",
|
|
2056
2221
|
requestId: input.message.requestId,
|
|
2057
|
-
runId: input.message.runId
|
|
2222
|
+
runId: input.message.runId,
|
|
2223
|
+
cwd,
|
|
2224
|
+
pid: subprocess.pid,
|
|
2225
|
+
processGroupId: subprocess.pid
|
|
2058
2226
|
});
|
|
2059
2227
|
if (input.message.timeoutMs) {
|
|
2060
2228
|
timeout = setTimeout(() => {
|
|
@@ -2133,7 +2301,12 @@ function buildActiveProcessReports() {
|
|
|
2133
2301
|
projectId: active.projectId,
|
|
2134
2302
|
sessionId: active.sessionId,
|
|
2135
2303
|
branchName: active.branchName,
|
|
2304
|
+
mode: active.mode,
|
|
2305
|
+
startCommitHash: active.startCommitHash,
|
|
2306
|
+
...active.launchWorkspaceActionId ? { launchWorkspaceActionId: active.launchWorkspaceActionId } : {},
|
|
2136
2307
|
...active.credentialId ? { credentialId: active.credentialId } : {},
|
|
2308
|
+
...active.pid !== void 0 ? { pid: active.pid } : {},
|
|
2309
|
+
...active.processGroupId !== void 0 ? { processGroupId: active.processGroupId } : {},
|
|
2137
2310
|
argv: active.argv,
|
|
2138
2311
|
command: active.command,
|
|
2139
2312
|
...active.cwd ? { cwd: active.cwd } : {},
|
|
@@ -2580,7 +2753,7 @@ async function startWorker(options) {
|
|
|
2580
2753
|
projectsRoot,
|
|
2581
2754
|
syncRoot,
|
|
2582
2755
|
manifests: [...manifestByProjectId.values()],
|
|
2583
|
-
|
|
2756
|
+
requestedTargets: message.targets
|
|
2584
2757
|
});
|
|
2585
2758
|
for (const result of syncResult.results) {
|
|
2586
2759
|
if (result.status !== "synced") continue;
|
|
@@ -2630,6 +2803,7 @@ async function startWorker(options) {
|
|
|
2630
2803
|
requestId: message.requestId,
|
|
2631
2804
|
result: syncResult
|
|
2632
2805
|
});
|
|
2806
|
+
sendActiveProcessReport(ws);
|
|
2633
2807
|
return;
|
|
2634
2808
|
}
|
|
2635
2809
|
if (message.type === "update_clis") {
|
|
@@ -2811,7 +2985,7 @@ async function startWorker(options) {
|
|
|
2811
2985
|
}
|
|
2812
2986
|
return;
|
|
2813
2987
|
}
|
|
2814
|
-
if (message.type === "read" || message.type === "write" || message.type === "edit" || message.type === "grep" || message.type === "find" || message.type === "ls" || message.type === "view_file_bytes" || message.type === "sync" || message.type === "confirm_large_diff" || message.type === "pull_branch") {
|
|
2988
|
+
if (message.type === "resolve_managed_path" || message.type === "workspace_status" || message.type === "read" || message.type === "write" || message.type === "edit" || message.type === "grep" || message.type === "find" || message.type === "ls" || message.type === "view_file_bytes" || message.type === "sync" || message.type === "confirm_large_diff" || message.type === "pull_branch") {
|
|
2815
2989
|
try {
|
|
2816
2990
|
await repositorySyncQueue;
|
|
2817
2991
|
const manifest = manifestByProjectId.get(message.projectId);
|
|
@@ -2824,6 +2998,8 @@ async function startWorker(options) {
|
|
|
2824
2998
|
projectRoot,
|
|
2825
2999
|
syncRoot,
|
|
2826
3000
|
artifactRoot,
|
|
3001
|
+
projectsRoot,
|
|
3002
|
+
manifests: [...manifestByProjectId.values()],
|
|
2827
3003
|
manifest
|
|
2828
3004
|
});
|
|
2829
3005
|
ws.send(
|
|
@@ -2939,6 +3115,7 @@ if (isCliEntrypoint()) {
|
|
|
2939
3115
|
findWorkerFiles,
|
|
2940
3116
|
githubCliEnv,
|
|
2941
3117
|
grepWorkerFiles,
|
|
3118
|
+
inspectManagedWorkspace,
|
|
2942
3119
|
isArtifactEnvPath,
|
|
2943
3120
|
listWorkerDirectory,
|
|
2944
3121
|
prepareArtifactEnvForShell,
|
|
@@ -2946,8 +3123,10 @@ if (isCliEntrypoint()) {
|
|
|
2946
3123
|
readWorkerImageFile,
|
|
2947
3124
|
readWorkerTextFile,
|
|
2948
3125
|
resolveHostShell,
|
|
3126
|
+
resolveManagedCheckoutPath,
|
|
2949
3127
|
resolveProjectFilePath,
|
|
2950
3128
|
resolveWorkerFilePath,
|
|
3129
|
+
selectManifestSyncTargets,
|
|
2951
3130
|
syncManifestProjectsFromInternal,
|
|
2952
3131
|
syncProjectPlans,
|
|
2953
3132
|
syncSessionArtifacts
|
package/dist/cjs/package.json
CHANGED
package/dist/mjs/main.mjs
CHANGED
|
@@ -693,6 +693,11 @@ function resolveWorkerFilePath(branchPath, inputPath) {
|
|
|
693
693
|
const displayPath = toProjectDisplayPath(resolvedBranchPath, absolutePath2);
|
|
694
694
|
const repoRelativePath2 = displayPath === "." ? "" : displayPath;
|
|
695
695
|
assertAllowedProjectPath(repoRelativePath2, inputPath);
|
|
696
|
+
const realBranchPath2 = fs.existsSync(resolvedBranchPath) ? fs.realpathSync(resolvedBranchPath) : resolvedBranchPath;
|
|
697
|
+
const realTargetPath2 = resolveThroughExistingAncestor(absolutePath2);
|
|
698
|
+
if (!isInsideBranchPath(realBranchPath2, realTargetPath2)) {
|
|
699
|
+
throw new Error(`Project file path escapes its checkout through a symbolic link: ${inputPath}`);
|
|
700
|
+
}
|
|
696
701
|
return {
|
|
697
702
|
absolutePath: absolutePath2,
|
|
698
703
|
displayPath,
|
|
@@ -711,6 +716,11 @@ function resolveWorkerFilePath(branchPath, inputPath) {
|
|
|
711
716
|
if (!isInsideBranchPath(resolvedBranchPath, absolutePath)) {
|
|
712
717
|
throw new Error(`Invalid project file path: ${inputPath}`);
|
|
713
718
|
}
|
|
719
|
+
const realBranchPath = fs.existsSync(resolvedBranchPath) ? fs.realpathSync(resolvedBranchPath) : resolvedBranchPath;
|
|
720
|
+
const realTargetPath = resolveThroughExistingAncestor(absolutePath);
|
|
721
|
+
if (!isInsideBranchPath(realBranchPath, realTargetPath)) {
|
|
722
|
+
throw new Error(`Project file path escapes its checkout through a symbolic link: ${inputPath}`);
|
|
723
|
+
}
|
|
714
724
|
return {
|
|
715
725
|
absolutePath,
|
|
716
726
|
displayPath: repoRelativePath || ".",
|
|
@@ -722,11 +732,60 @@ function resolveProjectFilePath(branchPath, inputPath) {
|
|
|
722
732
|
const resolved = resolveWorkerFilePath(branchPath, inputPath);
|
|
723
733
|
if (resolved.scope === "host") {
|
|
724
734
|
throw new Error(
|
|
725
|
-
`Project file writes must
|
|
735
|
+
`Project file writes must resolve inside the selected managed checkout: ${inputPath}`
|
|
726
736
|
);
|
|
727
737
|
}
|
|
728
738
|
return resolved;
|
|
729
739
|
}
|
|
740
|
+
function resolveThroughExistingAncestor(inputPath) {
|
|
741
|
+
const suffix = [];
|
|
742
|
+
let cursor = path.resolve(inputPath);
|
|
743
|
+
while (!fs.existsSync(cursor)) {
|
|
744
|
+
const parent = path.dirname(cursor);
|
|
745
|
+
if (parent === cursor) return path.resolve(inputPath);
|
|
746
|
+
suffix.unshift(path.basename(cursor));
|
|
747
|
+
cursor = parent;
|
|
748
|
+
}
|
|
749
|
+
return path.resolve(fs.realpathSync(cursor), ...suffix);
|
|
750
|
+
}
|
|
751
|
+
function resolveManagedCheckoutPath(input) {
|
|
752
|
+
const originManifest = input.manifests.find((manifest) => manifest.projectId === input.originProjectId);
|
|
753
|
+
const candidate = path.isAbsolute(input.inputPath) ? path.resolve(input.inputPath) : path.resolve(
|
|
754
|
+
input.projectsRoot,
|
|
755
|
+
originManifest?.repoSlug ?? input.originProjectId,
|
|
756
|
+
input.originBranchName,
|
|
757
|
+
input.inputPath
|
|
758
|
+
);
|
|
759
|
+
const resolvedCandidate = resolveThroughExistingAncestor(candidate);
|
|
760
|
+
for (const manifest of input.manifests) {
|
|
761
|
+
const branches = manifest.branches.length > 0 ? manifest.branches : [manifest.defaultBranch || "main"];
|
|
762
|
+
for (const branchName of branches) {
|
|
763
|
+
const checkoutPath = path.resolve(input.projectsRoot, manifest.repoSlug || manifest.projectId, branchName);
|
|
764
|
+
if (!hasNormalVisibleGitDir(checkoutPath)) continue;
|
|
765
|
+
const resolvedCheckoutPath = fs.existsSync(checkoutPath) ? fs.realpathSync(checkoutPath) : checkoutPath;
|
|
766
|
+
const matchesLexically = isInsideBranchPath(checkoutPath, candidate);
|
|
767
|
+
if (!matchesLexically) continue;
|
|
768
|
+
if (!isInsideBranchPath(resolvedCheckoutPath, resolvedCandidate)) {
|
|
769
|
+
throw new Error(`Managed path escapes its checkout through a symbolic link: ${input.inputPath}`);
|
|
770
|
+
}
|
|
771
|
+
const repoRelativePath = toProjectDisplayPath(resolvedCheckoutPath, resolvedCandidate);
|
|
772
|
+
assertAllowedProjectPath(repoRelativePath === "." ? "" : repoRelativePath, input.inputPath);
|
|
773
|
+
return {
|
|
774
|
+
type: "resolve_managed_path",
|
|
775
|
+
resolved: {
|
|
776
|
+
projectId: manifest.projectId,
|
|
777
|
+
projectPath: manifest.projectPath,
|
|
778
|
+
branchName,
|
|
779
|
+
checkoutPath,
|
|
780
|
+
inputPath: input.inputPath,
|
|
781
|
+
absolutePath: candidate,
|
|
782
|
+
repoRelativePath: repoRelativePath === "." ? "" : repoRelativePath
|
|
783
|
+
}
|
|
784
|
+
};
|
|
785
|
+
}
|
|
786
|
+
}
|
|
787
|
+
return { type: "resolve_managed_path", resolved: null };
|
|
788
|
+
}
|
|
730
789
|
function resolveRemoteUrl(baseUrl, remoteUrl) {
|
|
731
790
|
if (/^https?:\/\//i.test(remoteUrl)) {
|
|
732
791
|
return remoteUrl;
|
|
@@ -1095,6 +1154,9 @@ async function forceSyncManifestBranchFromInternal(input) {
|
|
|
1095
1154
|
if (!target) {
|
|
1096
1155
|
throw new Error(`Internal remote has neither ${input.branchName} nor main`);
|
|
1097
1156
|
}
|
|
1157
|
+
if (hasInternalWorktreeChanges(context)) {
|
|
1158
|
+
return null;
|
|
1159
|
+
}
|
|
1098
1160
|
await runInternalGitAsync(context, ["reset", "--hard", target]);
|
|
1099
1161
|
await runInternalGitAsync(context, ["clean", "-fd", "--", ".", ":(exclude).git"]);
|
|
1100
1162
|
return getInternalCommitHash(context);
|
|
@@ -1102,21 +1164,34 @@ async function forceSyncManifestBranchFromInternal(input) {
|
|
|
1102
1164
|
function findRepositorySyncBlockers(input) {
|
|
1103
1165
|
const targetByKey = new Map(input.targets.map((target) => [`${target.projectId}:${target.branchName}`, target]));
|
|
1104
1166
|
const blockedBy = [];
|
|
1105
|
-
for (const process2 of input.processes) {
|
|
1106
|
-
const target = targetByKey.get(`${process2.projectId}:${process2.branchName}`);
|
|
1107
|
-
if (target) blockedBy.push({ ...target, kind: "process", id: process2.id });
|
|
1108
|
-
}
|
|
1109
1167
|
for (const shell of input.shells) {
|
|
1110
1168
|
const target = targetByKey.get(`${shell.projectId}:${shell.branchName}`);
|
|
1111
1169
|
if (target) blockedBy.push({ ...target, kind: "shell", id: shell.id });
|
|
1112
1170
|
}
|
|
1113
1171
|
return blockedBy;
|
|
1114
1172
|
}
|
|
1173
|
+
function selectManifestSyncTargets(input) {
|
|
1174
|
+
if (!input.requestedTargets) {
|
|
1175
|
+
return input.manifests.flatMap((manifest) => {
|
|
1176
|
+
const branches = manifest.branches.length > 0 ? manifest.branches : [manifest.defaultBranch || "main"];
|
|
1177
|
+
return [...new Set(branches)].map((branchName) => ({ manifest, branchName }));
|
|
1178
|
+
});
|
|
1179
|
+
}
|
|
1180
|
+
const manifestsByProjectId = new Map(input.manifests.map((manifest) => [manifest.projectId, manifest]));
|
|
1181
|
+
const selected = /* @__PURE__ */ new Map();
|
|
1182
|
+
for (const target of input.requestedTargets) {
|
|
1183
|
+
const manifest = manifestsByProjectId.get(target.projectId);
|
|
1184
|
+
if (!manifest) {
|
|
1185
|
+
continue;
|
|
1186
|
+
}
|
|
1187
|
+
selected.set(`${target.projectId}\0${target.branchName}`, { manifest, branchName: target.branchName });
|
|
1188
|
+
}
|
|
1189
|
+
return [...selected.values()];
|
|
1190
|
+
}
|
|
1115
1191
|
async function syncManifestProjectsFromInternal(input) {
|
|
1116
|
-
const
|
|
1117
|
-
|
|
1118
|
-
|
|
1119
|
-
return [...new Set(branches)].map((branchName) => ({ manifest, branchName }));
|
|
1192
|
+
const targets = selectManifestSyncTargets({
|
|
1193
|
+
manifests: input.manifests,
|
|
1194
|
+
requestedTargets: input.requestedTargets
|
|
1120
1195
|
});
|
|
1121
1196
|
const blockedBy = findRepositorySyncBlockers({
|
|
1122
1197
|
targets: targets.map(({ manifest, branchName }) => ({
|
|
@@ -1124,22 +1199,18 @@ async function syncManifestProjectsFromInternal(input) {
|
|
|
1124
1199
|
projectPath: manifest.projectPath,
|
|
1125
1200
|
branchName
|
|
1126
1201
|
})),
|
|
1127
|
-
processes: [...activeProcesses].map(([id, active]) => ({
|
|
1128
|
-
id,
|
|
1129
|
-
projectId: active.projectId,
|
|
1130
|
-
branchName: active.branchName
|
|
1131
|
-
})),
|
|
1132
1202
|
shells: [...activePtys].map(([id, active]) => ({
|
|
1133
1203
|
id,
|
|
1134
1204
|
projectId: active.projectId,
|
|
1135
1205
|
branchName: active.branchName
|
|
1136
1206
|
}))
|
|
1137
1207
|
});
|
|
1138
|
-
|
|
1139
|
-
return { status: "blocked", results: [], blockedBy };
|
|
1140
|
-
}
|
|
1208
|
+
const blockedTargetKeys = new Set(blockedBy.map((blocker) => `${blocker.projectId}\0${blocker.branchName}`));
|
|
1141
1209
|
const results = [];
|
|
1142
1210
|
for (const { manifest, branchName } of targets) {
|
|
1211
|
+
if (blockedTargetKeys.has(`${manifest.projectId}\0${branchName}`)) {
|
|
1212
|
+
continue;
|
|
1213
|
+
}
|
|
1143
1214
|
try {
|
|
1144
1215
|
const commitHash = await forceSyncManifestBranchFromInternal({
|
|
1145
1216
|
projectId: manifest.projectId,
|
|
@@ -1150,6 +1221,16 @@ async function syncManifestProjectsFromInternal(input) {
|
|
|
1150
1221
|
branchName,
|
|
1151
1222
|
manifest
|
|
1152
1223
|
});
|
|
1224
|
+
if (commitHash === null) {
|
|
1225
|
+
blockedBy.push({
|
|
1226
|
+
projectId: manifest.projectId,
|
|
1227
|
+
projectPath: manifest.projectPath,
|
|
1228
|
+
branchName,
|
|
1229
|
+
kind: "dirty_checkout",
|
|
1230
|
+
id: `${manifest.projectId}:${branchName}`
|
|
1231
|
+
});
|
|
1232
|
+
continue;
|
|
1233
|
+
}
|
|
1153
1234
|
results.push({
|
|
1154
1235
|
projectId: manifest.projectId,
|
|
1155
1236
|
projectPath: manifest.projectPath,
|
|
@@ -1167,10 +1248,11 @@ async function syncManifestProjectsFromInternal(input) {
|
|
|
1167
1248
|
});
|
|
1168
1249
|
}
|
|
1169
1250
|
}
|
|
1251
|
+
const hasFailures = results.some((result) => result.status === "failed");
|
|
1170
1252
|
return {
|
|
1171
|
-
status:
|
|
1253
|
+
status: blockedBy.length > 0 && results.length === 0 ? "blocked" : blockedBy.length > 0 || hasFailures ? "partial" : "completed",
|
|
1172
1254
|
results,
|
|
1173
|
-
blockedBy
|
|
1255
|
+
blockedBy
|
|
1174
1256
|
};
|
|
1175
1257
|
}
|
|
1176
1258
|
function resolveCommandCwd(branchPath, cwd) {
|
|
@@ -1221,6 +1303,60 @@ async function streamCommandOutput(stream, onData) {
|
|
|
1221
1303
|
function ensureOperationBranch(input) {
|
|
1222
1304
|
return ensureBranchWorkspace(input);
|
|
1223
1305
|
}
|
|
1306
|
+
async function inspectManagedWorkspace(input) {
|
|
1307
|
+
const candidates = input.manifests.flatMap((manifest) => {
|
|
1308
|
+
const branches = manifest.branches.length > 0 ? manifest.branches : [manifest.defaultBranch || "main"];
|
|
1309
|
+
return branches.map((branchName) => ({
|
|
1310
|
+
manifest,
|
|
1311
|
+
branchName,
|
|
1312
|
+
checkoutPath: path.resolve(input.projectsRoot, manifest.repoSlug || manifest.projectId, branchName)
|
|
1313
|
+
}));
|
|
1314
|
+
});
|
|
1315
|
+
const inspected = await Promise.all(
|
|
1316
|
+
candidates.map(async ({ manifest, branchName, checkoutPath }) => {
|
|
1317
|
+
const gitDir = internalGitDirFor(input.syncRoot, manifest.projectId, branchName);
|
|
1318
|
+
if (!hasNormalVisibleGitDir(checkoutPath) || !fs.existsSync(gitDir)) return null;
|
|
1319
|
+
const context = {
|
|
1320
|
+
gitDir,
|
|
1321
|
+
workTree: checkoutPath,
|
|
1322
|
+
auth: internalGitAuth(input.baseUrl, input.token)
|
|
1323
|
+
};
|
|
1324
|
+
try {
|
|
1325
|
+
const status = await runInternalGitAsync(context, ["status", "--porcelain", "--", ".", ":(exclude).git"]);
|
|
1326
|
+
let commitHash = null;
|
|
1327
|
+
for (const revision of [
|
|
1328
|
+
"HEAD",
|
|
1329
|
+
`refs/remotes/${R5D_REMOTE_NAME}/${branchName}`,
|
|
1330
|
+
`refs/remotes/${R5D_REMOTE_NAME}/main`
|
|
1331
|
+
]) {
|
|
1332
|
+
try {
|
|
1333
|
+
commitHash = await runInternalGitAsync(context, ["rev-parse", "--verify", revision]);
|
|
1334
|
+
break;
|
|
1335
|
+
} catch {
|
|
1336
|
+
}
|
|
1337
|
+
}
|
|
1338
|
+
if (!commitHash) return null;
|
|
1339
|
+
return {
|
|
1340
|
+
projectId: manifest.projectId,
|
|
1341
|
+
projectPath: manifest.projectPath,
|
|
1342
|
+
branchName,
|
|
1343
|
+
checkoutPath,
|
|
1344
|
+
commitHash,
|
|
1345
|
+
status,
|
|
1346
|
+
dirty: status.trim().length > 0
|
|
1347
|
+
};
|
|
1348
|
+
} catch {
|
|
1349
|
+
return null;
|
|
1350
|
+
}
|
|
1351
|
+
})
|
|
1352
|
+
);
|
|
1353
|
+
return {
|
|
1354
|
+
type: "workspace_status",
|
|
1355
|
+
checkouts: inspected.filter((checkout) => checkout !== null).sort(
|
|
1356
|
+
(left, right) => left.projectPath.localeCompare(right.projectPath) || left.branchName.localeCompare(right.branchName)
|
|
1357
|
+
)
|
|
1358
|
+
};
|
|
1359
|
+
}
|
|
1224
1360
|
function internalGitProcessEnv(workspace, env) {
|
|
1225
1361
|
if (env?.R5D_USE_INTERNAL_GIT !== "1") {
|
|
1226
1362
|
return {};
|
|
@@ -1787,6 +1923,22 @@ function pullBranch(input) {
|
|
|
1787
1923
|
}
|
|
1788
1924
|
async function executeOperation(input) {
|
|
1789
1925
|
switch (input.message.type) {
|
|
1926
|
+
case "resolve_managed_path":
|
|
1927
|
+
return resolveManagedCheckoutPath({
|
|
1928
|
+
projectsRoot: input.projectsRoot,
|
|
1929
|
+
manifests: input.manifests,
|
|
1930
|
+
originProjectId: input.message.projectId,
|
|
1931
|
+
originBranchName: input.message.branchName,
|
|
1932
|
+
inputPath: input.message.inputPath
|
|
1933
|
+
});
|
|
1934
|
+
case "workspace_status":
|
|
1935
|
+
return await inspectManagedWorkspace({
|
|
1936
|
+
baseUrl: input.baseUrl,
|
|
1937
|
+
token: input.token,
|
|
1938
|
+
projectsRoot: input.projectsRoot,
|
|
1939
|
+
syncRoot: input.syncRoot,
|
|
1940
|
+
manifests: input.manifests
|
|
1941
|
+
});
|
|
1790
1942
|
case "read":
|
|
1791
1943
|
return executeReadFileOperation({ ...input, message: input.message });
|
|
1792
1944
|
case "write":
|
|
@@ -1889,9 +2041,11 @@ async function executeCommand(input) {
|
|
|
1889
2041
|
projectId: input.projectId,
|
|
1890
2042
|
sessionId: input.message.sessionId ?? "",
|
|
1891
2043
|
branchName: input.message.branchName,
|
|
2044
|
+
mode: "foreground",
|
|
2045
|
+
startCommitHash: "",
|
|
1892
2046
|
argv: input.message.argv,
|
|
1893
2047
|
command: input.message.argv.join(" "),
|
|
1894
|
-
cwd
|
|
2048
|
+
cwd,
|
|
1895
2049
|
startedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
1896
2050
|
});
|
|
1897
2051
|
if (input.message.timeoutMs) {
|
|
@@ -1994,17 +2148,28 @@ async function executeStreamingCommand(input) {
|
|
|
1994
2148
|
projectId: input.projectId,
|
|
1995
2149
|
sessionId: input.message.sessionId,
|
|
1996
2150
|
branchName: input.message.branchName,
|
|
2151
|
+
mode: input.message.mode,
|
|
2152
|
+
startCommitHash: input.message.startCommitHash,
|
|
2153
|
+
launchWorkspaceActionId: input.message.launchWorkspaceActionId,
|
|
1997
2154
|
credentialId: input.message.credentialId,
|
|
2155
|
+
pid: subprocess.pid,
|
|
2156
|
+
processGroupId: subprocess.pid,
|
|
1998
2157
|
argv: input.message.argv,
|
|
1999
2158
|
command: input.message.command,
|
|
2000
|
-
cwd
|
|
2159
|
+
// Persist the resolved absolute cwd so reconnect reports and process
|
|
2160
|
+
// history retain the actual execution location, including cross-project
|
|
2161
|
+
// managed paths.
|
|
2162
|
+
cwd,
|
|
2001
2163
|
startedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
2002
2164
|
});
|
|
2003
2165
|
started = true;
|
|
2004
2166
|
sendWorkerMessage(input.ws, {
|
|
2005
2167
|
type: "exec_started",
|
|
2006
2168
|
requestId: input.message.requestId,
|
|
2007
|
-
runId: input.message.runId
|
|
2169
|
+
runId: input.message.runId,
|
|
2170
|
+
cwd,
|
|
2171
|
+
pid: subprocess.pid,
|
|
2172
|
+
processGroupId: subprocess.pid
|
|
2008
2173
|
});
|
|
2009
2174
|
if (input.message.timeoutMs) {
|
|
2010
2175
|
timeout = setTimeout(() => {
|
|
@@ -2083,7 +2248,12 @@ function buildActiveProcessReports() {
|
|
|
2083
2248
|
projectId: active.projectId,
|
|
2084
2249
|
sessionId: active.sessionId,
|
|
2085
2250
|
branchName: active.branchName,
|
|
2251
|
+
mode: active.mode,
|
|
2252
|
+
startCommitHash: active.startCommitHash,
|
|
2253
|
+
...active.launchWorkspaceActionId ? { launchWorkspaceActionId: active.launchWorkspaceActionId } : {},
|
|
2086
2254
|
...active.credentialId ? { credentialId: active.credentialId } : {},
|
|
2255
|
+
...active.pid !== void 0 ? { pid: active.pid } : {},
|
|
2256
|
+
...active.processGroupId !== void 0 ? { processGroupId: active.processGroupId } : {},
|
|
2087
2257
|
argv: active.argv,
|
|
2088
2258
|
command: active.command,
|
|
2089
2259
|
...active.cwd ? { cwd: active.cwd } : {},
|
|
@@ -2530,7 +2700,7 @@ async function startWorker(options) {
|
|
|
2530
2700
|
projectsRoot,
|
|
2531
2701
|
syncRoot,
|
|
2532
2702
|
manifests: [...manifestByProjectId.values()],
|
|
2533
|
-
|
|
2703
|
+
requestedTargets: message.targets
|
|
2534
2704
|
});
|
|
2535
2705
|
for (const result of syncResult.results) {
|
|
2536
2706
|
if (result.status !== "synced") continue;
|
|
@@ -2580,6 +2750,7 @@ async function startWorker(options) {
|
|
|
2580
2750
|
requestId: message.requestId,
|
|
2581
2751
|
result: syncResult
|
|
2582
2752
|
});
|
|
2753
|
+
sendActiveProcessReport(ws);
|
|
2583
2754
|
return;
|
|
2584
2755
|
}
|
|
2585
2756
|
if (message.type === "update_clis") {
|
|
@@ -2761,7 +2932,7 @@ async function startWorker(options) {
|
|
|
2761
2932
|
}
|
|
2762
2933
|
return;
|
|
2763
2934
|
}
|
|
2764
|
-
if (message.type === "read" || message.type === "write" || message.type === "edit" || message.type === "grep" || message.type === "find" || message.type === "ls" || message.type === "view_file_bytes" || message.type === "sync" || message.type === "confirm_large_diff" || message.type === "pull_branch") {
|
|
2935
|
+
if (message.type === "resolve_managed_path" || message.type === "workspace_status" || message.type === "read" || message.type === "write" || message.type === "edit" || message.type === "grep" || message.type === "find" || message.type === "ls" || message.type === "view_file_bytes" || message.type === "sync" || message.type === "confirm_large_diff" || message.type === "pull_branch") {
|
|
2765
2936
|
try {
|
|
2766
2937
|
await repositorySyncQueue;
|
|
2767
2938
|
const manifest = manifestByProjectId.get(message.projectId);
|
|
@@ -2774,6 +2945,8 @@ async function startWorker(options) {
|
|
|
2774
2945
|
projectRoot,
|
|
2775
2946
|
syncRoot,
|
|
2776
2947
|
artifactRoot,
|
|
2948
|
+
projectsRoot,
|
|
2949
|
+
manifests: [...manifestByProjectId.values()],
|
|
2777
2950
|
manifest
|
|
2778
2951
|
});
|
|
2779
2952
|
ws.send(
|
|
@@ -2888,6 +3061,7 @@ export {
|
|
|
2888
3061
|
findWorkerFiles,
|
|
2889
3062
|
githubCliEnv,
|
|
2890
3063
|
grepWorkerFiles,
|
|
3064
|
+
inspectManagedWorkspace,
|
|
2891
3065
|
isArtifactEnvPath,
|
|
2892
3066
|
listWorkerDirectory,
|
|
2893
3067
|
prepareArtifactEnvForShell,
|
|
@@ -2895,8 +3069,10 @@ export {
|
|
|
2895
3069
|
readWorkerImageFile,
|
|
2896
3070
|
readWorkerTextFile,
|
|
2897
3071
|
resolveHostShell,
|
|
3072
|
+
resolveManagedCheckoutPath,
|
|
2898
3073
|
resolveProjectFilePath,
|
|
2899
3074
|
resolveWorkerFilePath,
|
|
3075
|
+
selectManifestSyncTargets,
|
|
2900
3076
|
syncManifestProjectsFromInternal,
|
|
2901
3077
|
syncProjectPlans,
|
|
2902
3078
|
syncSessionArtifacts
|
package/dist/mjs/package.json
CHANGED
package/dist/types/main.d.ts
CHANGED
|
@@ -26,6 +26,10 @@ type WorkerRepositorySyncTarget = {
|
|
|
26
26
|
projectPath: string;
|
|
27
27
|
branchName: string;
|
|
28
28
|
};
|
|
29
|
+
type WorkerRepositorySyncSelector = {
|
|
30
|
+
projectId: string;
|
|
31
|
+
branchName: string;
|
|
32
|
+
};
|
|
29
33
|
type WorkerRepositorySyncResult = {
|
|
30
34
|
status: "completed" | "partial" | "blocked";
|
|
31
35
|
results: Array<WorkerRepositorySyncTarget & ({
|
|
@@ -36,7 +40,7 @@ type WorkerRepositorySyncResult = {
|
|
|
36
40
|
error: string;
|
|
37
41
|
})>;
|
|
38
42
|
blockedBy: Array<WorkerRepositorySyncTarget & {
|
|
39
|
-
kind: "
|
|
43
|
+
kind: "dirty_checkout" | "shell";
|
|
40
44
|
id: string;
|
|
41
45
|
}>;
|
|
42
46
|
};
|
|
@@ -68,6 +72,28 @@ type WorkerViewFileBytesResult = {
|
|
|
68
72
|
width: number;
|
|
69
73
|
height: number;
|
|
70
74
|
};
|
|
75
|
+
type WorkerManagedCheckout = {
|
|
76
|
+
projectId: string;
|
|
77
|
+
projectPath: string;
|
|
78
|
+
branchName: string;
|
|
79
|
+
checkoutPath: string;
|
|
80
|
+
};
|
|
81
|
+
type WorkerResolveManagedPathResult = {
|
|
82
|
+
type: "resolve_managed_path";
|
|
83
|
+
resolved: (WorkerManagedCheckout & {
|
|
84
|
+
inputPath: string;
|
|
85
|
+
absolutePath: string;
|
|
86
|
+
repoRelativePath: string;
|
|
87
|
+
}) | null;
|
|
88
|
+
};
|
|
89
|
+
type WorkerWorkspaceStatusResult = {
|
|
90
|
+
type: "workspace_status";
|
|
91
|
+
checkouts: Array<WorkerManagedCheckout & {
|
|
92
|
+
commitHash: string;
|
|
93
|
+
status: string;
|
|
94
|
+
dirty: boolean;
|
|
95
|
+
}>;
|
|
96
|
+
};
|
|
71
97
|
export declare function isArtifactEnvPath(filePath: string): boolean;
|
|
72
98
|
export declare function syncSessionArtifacts(input: {
|
|
73
99
|
baseUrl: string;
|
|
@@ -119,6 +145,13 @@ export declare function resolveWorkerFilePath(branchPath: string, inputPath: str
|
|
|
119
145
|
export declare function resolveProjectFilePath(branchPath: string, inputPath: string): Extract<ResolvedWorkerFilePath, {
|
|
120
146
|
scope: "project";
|
|
121
147
|
}>;
|
|
148
|
+
export declare function resolveManagedCheckoutPath(input: {
|
|
149
|
+
projectsRoot: string;
|
|
150
|
+
manifests: WorkerProjectManifestEntry[];
|
|
151
|
+
originProjectId: string;
|
|
152
|
+
originBranchName: string;
|
|
153
|
+
inputPath: string;
|
|
154
|
+
}): WorkerResolveManagedPathResult;
|
|
122
155
|
export declare function githubCliEnv(token: string | null | undefined): Record<string, string>;
|
|
123
156
|
export declare function ensureVisibleGitCheckout(input: {
|
|
124
157
|
projectRoot: string;
|
|
@@ -131,25 +164,34 @@ export declare function ensureVisibleGitCheckout(input: {
|
|
|
131
164
|
}): string;
|
|
132
165
|
export declare function findRepositorySyncBlockers(input: {
|
|
133
166
|
targets: WorkerRepositorySyncTarget[];
|
|
134
|
-
processes: Array<{
|
|
135
|
-
id: string;
|
|
136
|
-
projectId: string;
|
|
137
|
-
branchName: string;
|
|
138
|
-
}>;
|
|
139
167
|
shells: Array<{
|
|
140
168
|
id: string;
|
|
141
169
|
projectId: string;
|
|
142
170
|
branchName: string;
|
|
143
171
|
}>;
|
|
144
172
|
}): WorkerRepositorySyncResult["blockedBy"];
|
|
173
|
+
export declare function selectManifestSyncTargets(input: {
|
|
174
|
+
manifests: WorkerProjectManifestEntry[];
|
|
175
|
+
requestedTargets?: WorkerRepositorySyncSelector[];
|
|
176
|
+
}): Array<{
|
|
177
|
+
manifest: WorkerProjectManifestEntry;
|
|
178
|
+
branchName: string;
|
|
179
|
+
}>;
|
|
145
180
|
export declare function syncManifestProjectsFromInternal(input: {
|
|
146
181
|
baseUrl: string;
|
|
147
182
|
token: string;
|
|
148
183
|
projectsRoot: string;
|
|
149
184
|
syncRoot: string;
|
|
150
185
|
manifests: WorkerProjectManifestEntry[];
|
|
151
|
-
|
|
186
|
+
requestedTargets?: WorkerRepositorySyncSelector[];
|
|
152
187
|
}): Promise<WorkerRepositorySyncResult>;
|
|
188
|
+
export declare function inspectManagedWorkspace(input: {
|
|
189
|
+
baseUrl: string;
|
|
190
|
+
token: string;
|
|
191
|
+
projectsRoot: string;
|
|
192
|
+
syncRoot: string;
|
|
193
|
+
manifests: WorkerProjectManifestEntry[];
|
|
194
|
+
}): Promise<WorkerWorkspaceStatusResult>;
|
|
153
195
|
export declare function readWorkerTextFile(branchPath: string, filePath: string, offset?: number, limit?: number): WorkerReadFileResult;
|
|
154
196
|
export declare function grepWorkerFiles(branchPath: string, input: {
|
|
155
197
|
pattern: string;
|