@ricsam/r5d-worker 0.0.35 → 0.0.36

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 CHANGED
@@ -34,7 +34,6 @@ __export(main_exports, {
34
34
  findWorkerFiles: () => findWorkerFiles,
35
35
  githubCliEnv: () => githubCliEnv,
36
36
  grepWorkerFiles: () => grepWorkerFiles,
37
- inspectManagedWorkspace: () => inspectManagedWorkspace,
38
37
  isArtifactEnvPath: () => isArtifactEnvPath,
39
38
  listWorkerDirectory: () => listWorkerDirectory,
40
39
  prepareArtifactEnvForShell: () => prepareArtifactEnvForShell,
@@ -42,10 +41,8 @@ __export(main_exports, {
42
41
  readWorkerImageFile: () => readWorkerImageFile,
43
42
  readWorkerTextFile: () => readWorkerTextFile,
44
43
  resolveHostShell: () => resolveHostShell,
45
- resolveManagedCheckoutPath: () => resolveManagedCheckoutPath,
46
44
  resolveProjectFilePath: () => resolveProjectFilePath,
47
45
  resolveWorkerFilePath: () => resolveWorkerFilePath,
48
- selectManifestSyncTargets: () => selectManifestSyncTargets,
49
46
  syncManifestProjectsFromInternal: () => syncManifestProjectsFromInternal,
50
47
  syncProjectPlans: () => syncProjectPlans,
51
48
  syncSessionArtifacts: () => syncSessionArtifacts
@@ -746,11 +743,6 @@ function resolveWorkerFilePath(branchPath, inputPath) {
746
743
  const displayPath = toProjectDisplayPath(resolvedBranchPath, absolutePath2);
747
744
  const repoRelativePath2 = displayPath === "." ? "" : displayPath;
748
745
  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
- }
754
746
  return {
755
747
  absolutePath: absolutePath2,
756
748
  displayPath,
@@ -769,11 +761,6 @@ function resolveWorkerFilePath(branchPath, inputPath) {
769
761
  if (!isInsideBranchPath(resolvedBranchPath, absolutePath)) {
770
762
  throw new Error(`Invalid project file path: ${inputPath}`);
771
763
  }
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
- }
777
764
  return {
778
765
  absolutePath,
779
766
  displayPath: repoRelativePath || ".",
@@ -785,60 +772,11 @@ function resolveProjectFilePath(branchPath, inputPath) {
785
772
  const resolved = resolveWorkerFilePath(branchPath, inputPath);
786
773
  if (resolved.scope === "host") {
787
774
  throw new Error(
788
- `Project file writes must resolve inside the selected managed checkout: ${inputPath}`
775
+ `Project file writes must use a checkout-relative path. Absolute host paths are read-only in file tools: ${inputPath}`
789
776
  );
790
777
  }
791
778
  return resolved;
792
779
  }
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
- }
842
780
  function resolveRemoteUrl(baseUrl, remoteUrl) {
843
781
  if (/^https?:\/\//i.test(remoteUrl)) {
844
782
  return remoteUrl;
@@ -1207,9 +1145,6 @@ async function forceSyncManifestBranchFromInternal(input) {
1207
1145
  if (!target) {
1208
1146
  throw new Error(`Internal remote has neither ${input.branchName} nor main`);
1209
1147
  }
1210
- if (hasInternalWorktreeChanges(context)) {
1211
- return null;
1212
- }
1213
1148
  await runInternalGitAsync(context, ["reset", "--hard", target]);
1214
1149
  await runInternalGitAsync(context, ["clean", "-fd", "--", ".", ":(exclude).git"]);
1215
1150
  return getInternalCommitHash(context);
@@ -1217,34 +1152,21 @@ async function forceSyncManifestBranchFromInternal(input) {
1217
1152
  function findRepositorySyncBlockers(input) {
1218
1153
  const targetByKey = new Map(input.targets.map((target) => [`${target.projectId}:${target.branchName}`, target]));
1219
1154
  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
+ }
1220
1159
  for (const shell of input.shells) {
1221
1160
  const target = targetByKey.get(`${shell.projectId}:${shell.branchName}`);
1222
1161
  if (target) blockedBy.push({ ...target, kind: "shell", id: shell.id });
1223
1162
  }
1224
1163
  return blockedBy;
1225
1164
  }
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
- }
1244
1165
  async function syncManifestProjectsFromInternal(input) {
1245
- const targets = selectManifestSyncTargets({
1246
- manifests: input.manifests,
1247
- requestedTargets: input.requestedTargets
1166
+ const requestedProjectIds = input.projectIds ? new Set(input.projectIds) : null;
1167
+ const targets = input.manifests.filter((manifest) => !requestedProjectIds || requestedProjectIds.has(manifest.projectId)).flatMap((manifest) => {
1168
+ const branches = manifest.branches.length > 0 ? manifest.branches : [manifest.defaultBranch || "main"];
1169
+ return [...new Set(branches)].map((branchName) => ({ manifest, branchName }));
1248
1170
  });
1249
1171
  const blockedBy = findRepositorySyncBlockers({
1250
1172
  targets: targets.map(({ manifest, branchName }) => ({
@@ -1252,18 +1174,22 @@ async function syncManifestProjectsFromInternal(input) {
1252
1174
  projectPath: manifest.projectPath,
1253
1175
  branchName
1254
1176
  })),
1177
+ processes: [...activeProcesses].map(([id, active]) => ({
1178
+ id,
1179
+ projectId: active.projectId,
1180
+ branchName: active.branchName
1181
+ })),
1255
1182
  shells: [...activePtys].map(([id, active]) => ({
1256
1183
  id,
1257
1184
  projectId: active.projectId,
1258
1185
  branchName: active.branchName
1259
1186
  }))
1260
1187
  });
1261
- const blockedTargetKeys = new Set(blockedBy.map((blocker) => `${blocker.projectId}\0${blocker.branchName}`));
1188
+ if (blockedBy.length > 0) {
1189
+ return { status: "blocked", results: [], blockedBy };
1190
+ }
1262
1191
  const results = [];
1263
1192
  for (const { manifest, branchName } of targets) {
1264
- if (blockedTargetKeys.has(`${manifest.projectId}\0${branchName}`)) {
1265
- continue;
1266
- }
1267
1193
  try {
1268
1194
  const commitHash = await forceSyncManifestBranchFromInternal({
1269
1195
  projectId: manifest.projectId,
@@ -1274,16 +1200,6 @@ async function syncManifestProjectsFromInternal(input) {
1274
1200
  branchName,
1275
1201
  manifest
1276
1202
  });
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
- }
1287
1203
  results.push({
1288
1204
  projectId: manifest.projectId,
1289
1205
  projectPath: manifest.projectPath,
@@ -1301,11 +1217,10 @@ async function syncManifestProjectsFromInternal(input) {
1301
1217
  });
1302
1218
  }
1303
1219
  }
1304
- const hasFailures = results.some((result) => result.status === "failed");
1305
1220
  return {
1306
- status: blockedBy.length > 0 && results.length === 0 ? "blocked" : blockedBy.length > 0 || hasFailures ? "partial" : "completed",
1221
+ status: results.some((result) => result.status === "failed") ? "partial" : "completed",
1307
1222
  results,
1308
- blockedBy
1223
+ blockedBy: []
1309
1224
  };
1310
1225
  }
1311
1226
  function resolveCommandCwd(branchPath, cwd) {
@@ -1356,60 +1271,6 @@ async function streamCommandOutput(stream, onData) {
1356
1271
  function ensureOperationBranch(input) {
1357
1272
  return ensureBranchWorkspace(input);
1358
1273
  }
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
- }
1413
1274
  function internalGitProcessEnv(workspace, env) {
1414
1275
  if (env?.R5D_USE_INTERNAL_GIT !== "1") {
1415
1276
  return {};
@@ -1976,22 +1837,6 @@ function pullBranch(input) {
1976
1837
  }
1977
1838
  async function executeOperation(input) {
1978
1839
  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
- });
1995
1840
  case "read":
1996
1841
  return executeReadFileOperation({ ...input, message: input.message });
1997
1842
  case "write":
@@ -2095,7 +1940,8 @@ async function executeCommand(input) {
2095
1940
  sessionId: input.message.sessionId ?? "",
2096
1941
  branchName: input.message.branchName,
2097
1942
  mode: "foreground",
2098
- startCommitHash: "",
1943
+ pid: subprocess.pid,
1944
+ processGroupId: process.platform === "win32" ? void 0 : subprocess.pid,
2099
1945
  argv: input.message.argv,
2100
1946
  command: input.message.argv.join(" "),
2101
1947
  cwd,
@@ -2202,16 +2048,11 @@ async function executeStreamingCommand(input) {
2202
2048
  sessionId: input.message.sessionId,
2203
2049
  branchName: input.message.branchName,
2204
2050
  mode: input.message.mode,
2205
- startCommitHash: input.message.startCommitHash,
2206
- launchWorkspaceActionId: input.message.launchWorkspaceActionId,
2207
- credentialId: input.message.credentialId,
2208
2051
  pid: subprocess.pid,
2209
- processGroupId: subprocess.pid,
2052
+ processGroupId: process.platform === "win32" ? void 0 : subprocess.pid,
2053
+ credentialId: input.message.credentialId,
2210
2054
  argv: input.message.argv,
2211
2055
  command: input.message.command,
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
2056
  cwd,
2216
2057
  startedAt: (/* @__PURE__ */ new Date()).toISOString()
2217
2058
  });
@@ -2222,7 +2063,7 @@ async function executeStreamingCommand(input) {
2222
2063
  runId: input.message.runId,
2223
2064
  cwd,
2224
2065
  pid: subprocess.pid,
2225
- processGroupId: subprocess.pid
2066
+ ...process.platform === "win32" ? {} : { processGroupId: subprocess.pid }
2226
2067
  });
2227
2068
  if (input.message.timeoutMs) {
2228
2069
  timeout = setTimeout(() => {
@@ -2301,12 +2142,12 @@ function buildActiveProcessReports() {
2301
2142
  projectId: active.projectId,
2302
2143
  sessionId: active.sessionId,
2303
2144
  branchName: active.branchName,
2145
+ platform: process.platform,
2146
+ arch: process.arch,
2304
2147
  mode: active.mode,
2305
- startCommitHash: active.startCommitHash,
2306
- ...active.launchWorkspaceActionId ? { launchWorkspaceActionId: active.launchWorkspaceActionId } : {},
2307
- ...active.credentialId ? { credentialId: active.credentialId } : {},
2308
2148
  ...active.pid !== void 0 ? { pid: active.pid } : {},
2309
2149
  ...active.processGroupId !== void 0 ? { processGroupId: active.processGroupId } : {},
2150
+ ...active.credentialId ? { credentialId: active.credentialId } : {},
2310
2151
  argv: active.argv,
2311
2152
  command: active.command,
2312
2153
  ...active.cwd ? { cwd: active.cwd } : {},
@@ -2753,7 +2594,7 @@ async function startWorker(options) {
2753
2594
  projectsRoot,
2754
2595
  syncRoot,
2755
2596
  manifests: [...manifestByProjectId.values()],
2756
- requestedTargets: message.targets
2597
+ projectIds: message.projectIds
2757
2598
  });
2758
2599
  for (const result of syncResult.results) {
2759
2600
  if (result.status !== "synced") continue;
@@ -2803,7 +2644,6 @@ async function startWorker(options) {
2803
2644
  requestId: message.requestId,
2804
2645
  result: syncResult
2805
2646
  });
2806
- sendActiveProcessReport(ws);
2807
2647
  return;
2808
2648
  }
2809
2649
  if (message.type === "update_clis") {
@@ -2985,7 +2825,7 @@ async function startWorker(options) {
2985
2825
  }
2986
2826
  return;
2987
2827
  }
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") {
2828
+ 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") {
2989
2829
  try {
2990
2830
  await repositorySyncQueue;
2991
2831
  const manifest = manifestByProjectId.get(message.projectId);
@@ -2998,8 +2838,6 @@ async function startWorker(options) {
2998
2838
  projectRoot,
2999
2839
  syncRoot,
3000
2840
  artifactRoot,
3001
- projectsRoot,
3002
- manifests: [...manifestByProjectId.values()],
3003
2841
  manifest
3004
2842
  });
3005
2843
  ws.send(
@@ -3115,7 +2953,6 @@ if (isCliEntrypoint()) {
3115
2953
  findWorkerFiles,
3116
2954
  githubCliEnv,
3117
2955
  grepWorkerFiles,
3118
- inspectManagedWorkspace,
3119
2956
  isArtifactEnvPath,
3120
2957
  listWorkerDirectory,
3121
2958
  prepareArtifactEnvForShell,
@@ -3123,10 +2960,8 @@ if (isCliEntrypoint()) {
3123
2960
  readWorkerImageFile,
3124
2961
  readWorkerTextFile,
3125
2962
  resolveHostShell,
3126
- resolveManagedCheckoutPath,
3127
2963
  resolveProjectFilePath,
3128
2964
  resolveWorkerFilePath,
3129
- selectManifestSyncTargets,
3130
2965
  syncManifestProjectsFromInternal,
3131
2966
  syncProjectPlans,
3132
2967
  syncSessionArtifacts
@@ -1,5 +1,5 @@
1
1
  {
2
2
  "name": "@ricsam/r5d-worker",
3
- "version": "0.0.35",
3
+ "version": "0.0.36",
4
4
  "type": "commonjs"
5
5
  }
package/dist/mjs/main.mjs CHANGED
@@ -693,11 +693,6 @@ 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
- }
701
696
  return {
702
697
  absolutePath: absolutePath2,
703
698
  displayPath,
@@ -716,11 +711,6 @@ function resolveWorkerFilePath(branchPath, inputPath) {
716
711
  if (!isInsideBranchPath(resolvedBranchPath, absolutePath)) {
717
712
  throw new Error(`Invalid project file path: ${inputPath}`);
718
713
  }
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
- }
724
714
  return {
725
715
  absolutePath,
726
716
  displayPath: repoRelativePath || ".",
@@ -732,60 +722,11 @@ function resolveProjectFilePath(branchPath, inputPath) {
732
722
  const resolved = resolveWorkerFilePath(branchPath, inputPath);
733
723
  if (resolved.scope === "host") {
734
724
  throw new Error(
735
- `Project file writes must resolve inside the selected managed checkout: ${inputPath}`
725
+ `Project file writes must use a checkout-relative path. Absolute host paths are read-only in file tools: ${inputPath}`
736
726
  );
737
727
  }
738
728
  return resolved;
739
729
  }
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
- }
789
730
  function resolveRemoteUrl(baseUrl, remoteUrl) {
790
731
  if (/^https?:\/\//i.test(remoteUrl)) {
791
732
  return remoteUrl;
@@ -1154,9 +1095,6 @@ async function forceSyncManifestBranchFromInternal(input) {
1154
1095
  if (!target) {
1155
1096
  throw new Error(`Internal remote has neither ${input.branchName} nor main`);
1156
1097
  }
1157
- if (hasInternalWorktreeChanges(context)) {
1158
- return null;
1159
- }
1160
1098
  await runInternalGitAsync(context, ["reset", "--hard", target]);
1161
1099
  await runInternalGitAsync(context, ["clean", "-fd", "--", ".", ":(exclude).git"]);
1162
1100
  return getInternalCommitHash(context);
@@ -1164,34 +1102,21 @@ async function forceSyncManifestBranchFromInternal(input) {
1164
1102
  function findRepositorySyncBlockers(input) {
1165
1103
  const targetByKey = new Map(input.targets.map((target) => [`${target.projectId}:${target.branchName}`, target]));
1166
1104
  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
+ }
1167
1109
  for (const shell of input.shells) {
1168
1110
  const target = targetByKey.get(`${shell.projectId}:${shell.branchName}`);
1169
1111
  if (target) blockedBy.push({ ...target, kind: "shell", id: shell.id });
1170
1112
  }
1171
1113
  return blockedBy;
1172
1114
  }
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
- }
1191
1115
  async function syncManifestProjectsFromInternal(input) {
1192
- const targets = selectManifestSyncTargets({
1193
- manifests: input.manifests,
1194
- requestedTargets: input.requestedTargets
1116
+ const requestedProjectIds = input.projectIds ? new Set(input.projectIds) : null;
1117
+ const targets = input.manifests.filter((manifest) => !requestedProjectIds || requestedProjectIds.has(manifest.projectId)).flatMap((manifest) => {
1118
+ const branches = manifest.branches.length > 0 ? manifest.branches : [manifest.defaultBranch || "main"];
1119
+ return [...new Set(branches)].map((branchName) => ({ manifest, branchName }));
1195
1120
  });
1196
1121
  const blockedBy = findRepositorySyncBlockers({
1197
1122
  targets: targets.map(({ manifest, branchName }) => ({
@@ -1199,18 +1124,22 @@ async function syncManifestProjectsFromInternal(input) {
1199
1124
  projectPath: manifest.projectPath,
1200
1125
  branchName
1201
1126
  })),
1127
+ processes: [...activeProcesses].map(([id, active]) => ({
1128
+ id,
1129
+ projectId: active.projectId,
1130
+ branchName: active.branchName
1131
+ })),
1202
1132
  shells: [...activePtys].map(([id, active]) => ({
1203
1133
  id,
1204
1134
  projectId: active.projectId,
1205
1135
  branchName: active.branchName
1206
1136
  }))
1207
1137
  });
1208
- const blockedTargetKeys = new Set(blockedBy.map((blocker) => `${blocker.projectId}\0${blocker.branchName}`));
1138
+ if (blockedBy.length > 0) {
1139
+ return { status: "blocked", results: [], blockedBy };
1140
+ }
1209
1141
  const results = [];
1210
1142
  for (const { manifest, branchName } of targets) {
1211
- if (blockedTargetKeys.has(`${manifest.projectId}\0${branchName}`)) {
1212
- continue;
1213
- }
1214
1143
  try {
1215
1144
  const commitHash = await forceSyncManifestBranchFromInternal({
1216
1145
  projectId: manifest.projectId,
@@ -1221,16 +1150,6 @@ async function syncManifestProjectsFromInternal(input) {
1221
1150
  branchName,
1222
1151
  manifest
1223
1152
  });
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
- }
1234
1153
  results.push({
1235
1154
  projectId: manifest.projectId,
1236
1155
  projectPath: manifest.projectPath,
@@ -1248,11 +1167,10 @@ async function syncManifestProjectsFromInternal(input) {
1248
1167
  });
1249
1168
  }
1250
1169
  }
1251
- const hasFailures = results.some((result) => result.status === "failed");
1252
1170
  return {
1253
- status: blockedBy.length > 0 && results.length === 0 ? "blocked" : blockedBy.length > 0 || hasFailures ? "partial" : "completed",
1171
+ status: results.some((result) => result.status === "failed") ? "partial" : "completed",
1254
1172
  results,
1255
- blockedBy
1173
+ blockedBy: []
1256
1174
  };
1257
1175
  }
1258
1176
  function resolveCommandCwd(branchPath, cwd) {
@@ -1303,60 +1221,6 @@ async function streamCommandOutput(stream, onData) {
1303
1221
  function ensureOperationBranch(input) {
1304
1222
  return ensureBranchWorkspace(input);
1305
1223
  }
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
- }
1360
1224
  function internalGitProcessEnv(workspace, env) {
1361
1225
  if (env?.R5D_USE_INTERNAL_GIT !== "1") {
1362
1226
  return {};
@@ -1923,22 +1787,6 @@ function pullBranch(input) {
1923
1787
  }
1924
1788
  async function executeOperation(input) {
1925
1789
  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
- });
1942
1790
  case "read":
1943
1791
  return executeReadFileOperation({ ...input, message: input.message });
1944
1792
  case "write":
@@ -2042,7 +1890,8 @@ async function executeCommand(input) {
2042
1890
  sessionId: input.message.sessionId ?? "",
2043
1891
  branchName: input.message.branchName,
2044
1892
  mode: "foreground",
2045
- startCommitHash: "",
1893
+ pid: subprocess.pid,
1894
+ processGroupId: process.platform === "win32" ? void 0 : subprocess.pid,
2046
1895
  argv: input.message.argv,
2047
1896
  command: input.message.argv.join(" "),
2048
1897
  cwd,
@@ -2149,16 +1998,11 @@ async function executeStreamingCommand(input) {
2149
1998
  sessionId: input.message.sessionId,
2150
1999
  branchName: input.message.branchName,
2151
2000
  mode: input.message.mode,
2152
- startCommitHash: input.message.startCommitHash,
2153
- launchWorkspaceActionId: input.message.launchWorkspaceActionId,
2154
- credentialId: input.message.credentialId,
2155
2001
  pid: subprocess.pid,
2156
- processGroupId: subprocess.pid,
2002
+ processGroupId: process.platform === "win32" ? void 0 : subprocess.pid,
2003
+ credentialId: input.message.credentialId,
2157
2004
  argv: input.message.argv,
2158
2005
  command: input.message.command,
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
2006
  cwd,
2163
2007
  startedAt: (/* @__PURE__ */ new Date()).toISOString()
2164
2008
  });
@@ -2169,7 +2013,7 @@ async function executeStreamingCommand(input) {
2169
2013
  runId: input.message.runId,
2170
2014
  cwd,
2171
2015
  pid: subprocess.pid,
2172
- processGroupId: subprocess.pid
2016
+ ...process.platform === "win32" ? {} : { processGroupId: subprocess.pid }
2173
2017
  });
2174
2018
  if (input.message.timeoutMs) {
2175
2019
  timeout = setTimeout(() => {
@@ -2248,12 +2092,12 @@ function buildActiveProcessReports() {
2248
2092
  projectId: active.projectId,
2249
2093
  sessionId: active.sessionId,
2250
2094
  branchName: active.branchName,
2095
+ platform: process.platform,
2096
+ arch: process.arch,
2251
2097
  mode: active.mode,
2252
- startCommitHash: active.startCommitHash,
2253
- ...active.launchWorkspaceActionId ? { launchWorkspaceActionId: active.launchWorkspaceActionId } : {},
2254
- ...active.credentialId ? { credentialId: active.credentialId } : {},
2255
2098
  ...active.pid !== void 0 ? { pid: active.pid } : {},
2256
2099
  ...active.processGroupId !== void 0 ? { processGroupId: active.processGroupId } : {},
2100
+ ...active.credentialId ? { credentialId: active.credentialId } : {},
2257
2101
  argv: active.argv,
2258
2102
  command: active.command,
2259
2103
  ...active.cwd ? { cwd: active.cwd } : {},
@@ -2700,7 +2544,7 @@ async function startWorker(options) {
2700
2544
  projectsRoot,
2701
2545
  syncRoot,
2702
2546
  manifests: [...manifestByProjectId.values()],
2703
- requestedTargets: message.targets
2547
+ projectIds: message.projectIds
2704
2548
  });
2705
2549
  for (const result of syncResult.results) {
2706
2550
  if (result.status !== "synced") continue;
@@ -2750,7 +2594,6 @@ async function startWorker(options) {
2750
2594
  requestId: message.requestId,
2751
2595
  result: syncResult
2752
2596
  });
2753
- sendActiveProcessReport(ws);
2754
2597
  return;
2755
2598
  }
2756
2599
  if (message.type === "update_clis") {
@@ -2932,7 +2775,7 @@ async function startWorker(options) {
2932
2775
  }
2933
2776
  return;
2934
2777
  }
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") {
2778
+ 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") {
2936
2779
  try {
2937
2780
  await repositorySyncQueue;
2938
2781
  const manifest = manifestByProjectId.get(message.projectId);
@@ -2945,8 +2788,6 @@ async function startWorker(options) {
2945
2788
  projectRoot,
2946
2789
  syncRoot,
2947
2790
  artifactRoot,
2948
- projectsRoot,
2949
- manifests: [...manifestByProjectId.values()],
2950
2791
  manifest
2951
2792
  });
2952
2793
  ws.send(
@@ -3061,7 +2902,6 @@ export {
3061
2902
  findWorkerFiles,
3062
2903
  githubCliEnv,
3063
2904
  grepWorkerFiles,
3064
- inspectManagedWorkspace,
3065
2905
  isArtifactEnvPath,
3066
2906
  listWorkerDirectory,
3067
2907
  prepareArtifactEnvForShell,
@@ -3069,10 +2909,8 @@ export {
3069
2909
  readWorkerImageFile,
3070
2910
  readWorkerTextFile,
3071
2911
  resolveHostShell,
3072
- resolveManagedCheckoutPath,
3073
2912
  resolveProjectFilePath,
3074
2913
  resolveWorkerFilePath,
3075
- selectManifestSyncTargets,
3076
2914
  syncManifestProjectsFromInternal,
3077
2915
  syncProjectPlans,
3078
2916
  syncSessionArtifacts
@@ -1,5 +1,5 @@
1
1
  {
2
2
  "name": "@ricsam/r5d-worker",
3
- "version": "0.0.35",
3
+ "version": "0.0.36",
4
4
  "type": "module"
5
5
  }
@@ -26,10 +26,6 @@ type WorkerRepositorySyncTarget = {
26
26
  projectPath: string;
27
27
  branchName: string;
28
28
  };
29
- type WorkerRepositorySyncSelector = {
30
- projectId: string;
31
- branchName: string;
32
- };
33
29
  type WorkerRepositorySyncResult = {
34
30
  status: "completed" | "partial" | "blocked";
35
31
  results: Array<WorkerRepositorySyncTarget & ({
@@ -40,7 +36,7 @@ type WorkerRepositorySyncResult = {
40
36
  error: string;
41
37
  })>;
42
38
  blockedBy: Array<WorkerRepositorySyncTarget & {
43
- kind: "dirty_checkout" | "shell";
39
+ kind: "process" | "shell";
44
40
  id: string;
45
41
  }>;
46
42
  };
@@ -72,28 +68,6 @@ type WorkerViewFileBytesResult = {
72
68
  width: number;
73
69
  height: number;
74
70
  };
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
- };
97
71
  export declare function isArtifactEnvPath(filePath: string): boolean;
98
72
  export declare function syncSessionArtifacts(input: {
99
73
  baseUrl: string;
@@ -145,13 +119,6 @@ export declare function resolveWorkerFilePath(branchPath: string, inputPath: str
145
119
  export declare function resolveProjectFilePath(branchPath: string, inputPath: string): Extract<ResolvedWorkerFilePath, {
146
120
  scope: "project";
147
121
  }>;
148
- export declare function resolveManagedCheckoutPath(input: {
149
- projectsRoot: string;
150
- manifests: WorkerProjectManifestEntry[];
151
- originProjectId: string;
152
- originBranchName: string;
153
- inputPath: string;
154
- }): WorkerResolveManagedPathResult;
155
122
  export declare function githubCliEnv(token: string | null | undefined): Record<string, string>;
156
123
  export declare function ensureVisibleGitCheckout(input: {
157
124
  projectRoot: string;
@@ -164,34 +131,25 @@ export declare function ensureVisibleGitCheckout(input: {
164
131
  }): string;
165
132
  export declare function findRepositorySyncBlockers(input: {
166
133
  targets: WorkerRepositorySyncTarget[];
134
+ processes: Array<{
135
+ id: string;
136
+ projectId: string;
137
+ branchName: string;
138
+ }>;
167
139
  shells: Array<{
168
140
  id: string;
169
141
  projectId: string;
170
142
  branchName: string;
171
143
  }>;
172
144
  }): WorkerRepositorySyncResult["blockedBy"];
173
- export declare function selectManifestSyncTargets(input: {
174
- manifests: WorkerProjectManifestEntry[];
175
- requestedTargets?: WorkerRepositorySyncSelector[];
176
- }): Array<{
177
- manifest: WorkerProjectManifestEntry;
178
- branchName: string;
179
- }>;
180
145
  export declare function syncManifestProjectsFromInternal(input: {
181
146
  baseUrl: string;
182
147
  token: string;
183
148
  projectsRoot: string;
184
149
  syncRoot: string;
185
150
  manifests: WorkerProjectManifestEntry[];
186
- requestedTargets?: WorkerRepositorySyncSelector[];
151
+ projectIds?: string[];
187
152
  }): 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>;
195
153
  export declare function readWorkerTextFile(branchPath: string, filePath: string, offset?: number, limit?: number): WorkerReadFileResult;
196
154
  export declare function grepWorkerFiles(branchPath: string, input: {
197
155
  pattern: string;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ricsam/r5d-worker",
3
- "version": "0.0.35",
3
+ "version": "0.0.36",
4
4
  "type": "module",
5
5
  "main": "./dist/cjs/main.cjs",
6
6
  "module": "./dist/mjs/main.mjs",