@ricsam/r5d-worker 0.0.48 → 0.0.49

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
@@ -29,6 +29,7 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
29
29
  var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
30
30
  var main_exports = {};
31
31
  __export(main_exports, {
32
+ describeWorkerSessionTarget: () => describeWorkerSessionTarget,
32
33
  editWorkerTextFile: () => editWorkerTextFile,
33
34
  ensureVisibleGitCheckout: () => ensureVisibleGitCheckout,
34
35
  findWorkerFiles: () => findWorkerFiles,
@@ -37,11 +38,14 @@ __export(main_exports, {
37
38
  isArtifactEnvPath: () => isArtifactEnvPath,
38
39
  listWorkerDirectory: () => listWorkerDirectory,
39
40
  prepareArtifactEnvForShell: () => prepareArtifactEnvForShell,
41
+ prepareBuiltInToolPathsForTarget: () => prepareBuiltInToolPathsForTarget,
40
42
  preparePlanEnvForShell: () => preparePlanEnvForShell,
43
+ prepareShellEnvForTarget: () => prepareShellEnvForTarget,
41
44
  readWorkerImageFile: () => readWorkerImageFile,
42
45
  readWorkerTextFile: () => readWorkerTextFile,
43
46
  resolveHostShell: () => resolveHostShell,
44
47
  resolveWorkerFilePath: () => resolveWorkerFilePath,
48
+ resolveWorkerSessionTarget: () => resolveWorkerSessionTarget,
45
49
  syncSessionArtifacts: () => syncSessionArtifacts,
46
50
  visibleCheckoutGitTestHarness: () => visibleCheckoutGitTestHarness,
47
51
  visibleCheckoutRemoteFor: () => visibleCheckoutRemoteFor,
@@ -333,8 +337,15 @@ function projectPlanDir(planRoot, projectId, branchName) {
333
337
  validateBranchName(branchName);
334
338
  return import_node_path.default.join(planRoot, projectId, branchName);
335
339
  }
336
- function planEnv(planRoot, projectId, branchName, activePlanId) {
337
- const plansDir = projectPlanDir(planRoot, projectId, branchName);
340
+ function targetPlanDir(planRoot, target) {
341
+ if (target.type === "project") return projectPlanDir(planRoot, target.projectId, target.branchName);
342
+ if (target.rootProfile === "canonical_sync") {
343
+ throw new Error("Ordinary plans are unavailable in canonical synchronization sessions");
344
+ }
345
+ return import_node_path.default.join(planRoot, "workspace");
346
+ }
347
+ function planEnv(planRoot, target, activePlanId) {
348
+ const plansDir = targetPlanDir(planRoot, target);
338
349
  const env = {
339
350
  [R5D_PLANS_DIR_ENV]: plansDir
340
351
  };
@@ -352,14 +363,14 @@ function isArtifactEnvPath(filePath) {
352
363
  return false;
353
364
  }
354
365
  }
355
- function artifactApiBasePath(projectId, branchName, sessionId) {
356
- return `/preview-api/${encodeURIComponent(projectId)}/${encodeURIComponent(branchName)}/artifacts/${encodeURIComponent(sessionId)}`;
366
+ function artifactApiBasePath(sessionId) {
367
+ return `/preview-api/artifacts/${encodeURIComponent(sessionId)}`;
357
368
  }
358
- function artifactManifestUrl(baseUrl, projectId, branchName, sessionId) {
359
- return new URL(artifactApiBasePath(projectId, branchName, sessionId), baseUrl);
369
+ function artifactManifestUrl(baseUrl, sessionId) {
370
+ return new URL(artifactApiBasePath(sessionId), baseUrl);
360
371
  }
361
- function artifactDownloadUrl(baseUrl, projectId, branchName, sessionId, filename) {
362
- return new URL(`${artifactApiBasePath(projectId, branchName, sessionId)}/${encodeURIComponent(filename)}`, baseUrl);
372
+ function artifactDownloadUrl(baseUrl, sessionId, filename) {
373
+ return new URL(`${artifactApiBasePath(sessionId)}/${encodeURIComponent(filename)}`, baseUrl);
363
374
  }
364
375
  function sha256Path(filePath) {
365
376
  return (0, import_node_crypto.createHash)("sha256").update(import_node_fs.default.readFileSync(filePath)).digest("hex");
@@ -451,7 +462,7 @@ async function syncRemoteFileSet(input) {
451
462
  }
452
463
  async function fetchSessionArtifactManifest(input) {
453
464
  return fetchRemoteFileManifest({
454
- url: artifactManifestUrl(input.baseUrl, input.projectId, input.branchName, input.sessionId),
465
+ url: artifactManifestUrl(input.baseUrl, input.sessionId),
455
466
  token: input.token,
456
467
  collectionKey: "artifacts",
457
468
  label: "artifact"
@@ -469,7 +480,7 @@ async function syncSessionArtifacts(input) {
469
480
  files: artifacts,
470
481
  label: "Artifact path",
471
482
  download: (artifact) => downloadRemoteSyncedFile({
472
- url: artifactDownloadUrl(input.baseUrl, input.projectId, input.branchName, input.sessionId, artifact.filename),
483
+ url: artifactDownloadUrl(input.baseUrl, input.sessionId, artifact.filename),
473
484
  token: input.token,
474
485
  targetDir,
475
486
  file: artifact,
@@ -502,14 +513,12 @@ async function prepareBuiltInToolPaths(input) {
502
513
  artifactsDir: await syncSessionArtifacts({
503
514
  baseUrl: input.baseUrl,
504
515
  token: input.token,
505
- projectId: input.projectId,
506
- branchName: input.branchName,
507
516
  sessionId: input.sessionId,
508
517
  artifactRoot: input.artifactRoot
509
518
  })
510
519
  };
511
520
  }
512
- const plansDir = projectPlanDir(input.planRoot, input.projectId, input.branchName);
521
+ const plansDir = targetPlanDir(input.planRoot, input.target);
513
522
  import_node_fs.default.mkdirSync(plansDir, { recursive: true });
514
523
  if (parsed.ref === R5D_PLANS_DIR_REF) {
515
524
  return { plansDir };
@@ -531,8 +540,6 @@ async function prepareArtifactEnvForShell(input) {
531
540
  await syncSessionArtifacts({
532
541
  baseUrl: input.baseUrl,
533
542
  token: input.token,
534
- projectId: input.projectId,
535
- branchName: input.branchName,
536
543
  sessionId: input.sessionId,
537
544
  artifactRoot: input.artifactRoot
538
545
  });
@@ -549,9 +556,9 @@ async function prepareArtifactEnvForShell(input) {
549
556
  };
550
557
  }
551
558
  function preparePlanEnvForShell(input) {
552
- const plansDir = projectPlanDir(input.planRoot, input.projectId, input.branchName);
559
+ const plansDir = targetPlanDir(input.planRoot, input.target);
553
560
  import_node_fs.default.mkdirSync(plansDir, { recursive: true });
554
- return planEnv(input.planRoot, input.projectId, input.branchName, input.activePlanId);
561
+ return planEnv(input.planRoot, input.target, input.activePlanId);
555
562
  }
556
563
  async function verifyR5dctlAuth(baseUrl, token) {
557
564
  const statusUrl = new URL("/api/r5dctl/auth/status", baseUrl);
@@ -1166,6 +1173,32 @@ function ensureBranchWorkspace(input) {
1166
1173
  })
1167
1174
  };
1168
1175
  }
1176
+ function describeWorkerSessionTarget(target) {
1177
+ return target.type === "project" ? `${target.projectId}/${target.branchName}` : `${target.ownerUserId}/${target.rootProfile}`;
1178
+ }
1179
+ function resolveWorkerSessionTarget(input) {
1180
+ if (input.target.type === "workspace") {
1181
+ if (input.target.rootProfile === "visible_projects") {
1182
+ return { target: input.target, rootPath: input.projectsRoot };
1183
+ }
1184
+ if (!import_node_fs.default.existsSync(import_node_path.default.join(input.workspaceShadowRoot, ".git"))) {
1185
+ throw new Error("The canonical workspace checkout is not initialized on this worker");
1186
+ }
1187
+ return { target: input.target, rootPath: input.workspaceShadowRoot };
1188
+ }
1189
+ const manifest = input.manifestByProjectId.get(input.target.projectId);
1190
+ const projectRoot = projectRootFor(input.projectsRoot, input.target.projectId, input.manifestByProjectId);
1191
+ const workspace = ensureBranchWorkspace({
1192
+ projectId: input.target.projectId,
1193
+ baseUrl: input.baseUrl,
1194
+ token: input.token,
1195
+ projectRoot,
1196
+ syncRoot: input.syncRoot,
1197
+ branchName: input.target.branchName,
1198
+ manifest
1199
+ });
1200
+ return { target: input.target, rootPath: workspace.branchPath, manifest };
1201
+ }
1169
1202
  function workspaceSyncCheckoutTargets(input) {
1170
1203
  const selected = /* @__PURE__ */ new Map();
1171
1204
  const add = (target) => {
@@ -1194,7 +1227,7 @@ function resolveCommandCwd(branchPath, cwd) {
1194
1227
  const resolved = import_node_path.default.resolve(branchPath, cwd);
1195
1228
  const relative = import_node_path.default.relative(branchPath, resolved);
1196
1229
  if (relative === ".." || relative.startsWith(`..${import_node_path.default.sep}`) || import_node_path.default.isAbsolute(relative)) {
1197
- throw new Error(`Relative cwd escapes the project branch: ${cwd}`);
1230
+ throw new Error(`Relative cwd escapes the execution root: ${cwd}`);
1198
1231
  }
1199
1232
  return resolved;
1200
1233
  }
@@ -1229,8 +1262,8 @@ async function streamCommandOutput(stream, onData) {
1229
1262
  reader.releaseLock();
1230
1263
  }
1231
1264
  }
1232
- function ensureOperationBranch(input) {
1233
- return ensureBranchWorkspace(input);
1265
+ async function prepareBuiltInToolPathsForTarget(input) {
1266
+ return prepareBuiltInToolPaths(input);
1234
1267
  }
1235
1268
  function formatLineNumberedContent(content, offset, limit) {
1236
1269
  const allLines = content.split("\n");
@@ -1356,11 +1389,11 @@ async function withFileMutationQueue(key, operation) {
1356
1389
  }
1357
1390
  }
1358
1391
  }
1359
- function mutationQueueKey(projectId, branchName, resolved) {
1392
+ function mutationQueueKey(target, resolved) {
1360
1393
  if (resolved.scope !== "project") {
1361
1394
  return `${resolved.scope}:${import_node_path.default.normalize(resolved.absolutePath)}`;
1362
1395
  }
1363
- return `project:${projectId}:${branchName}:${import_node_path.default.posix.normalize(resolved.repoRelativePath)}`;
1396
+ return `${describeWorkerSessionTarget(target)}:${import_node_path.default.posix.normalize(resolved.repoRelativePath)}`;
1364
1397
  }
1365
1398
  function readWorkerTextFile(branchPath, filePath, offset, limit, builtInPaths) {
1366
1399
  const resolved = resolveWorkerFilePath(branchPath, filePath, builtInPaths);
@@ -1435,57 +1468,51 @@ function editWorkerTextFile(branchPath, filePath, edits, builtInPaths) {
1435
1468
  };
1436
1469
  }
1437
1470
  async function executeReadFileOperation(input) {
1438
- const builtInPaths = await prepareBuiltInToolPaths({
1471
+ const builtInPaths = await prepareBuiltInToolPathsForTarget({
1472
+ target: input.resolvedTarget.target,
1439
1473
  filePath: input.message.filePath,
1440
1474
  baseUrl: input.baseUrl,
1441
1475
  token: input.token,
1442
- projectId: input.projectId,
1443
- branchName: input.message.branchName,
1444
1476
  sessionId: input.message.sessionId,
1445
1477
  activePlanId: input.message.activePlanId,
1446
1478
  artifactRoot: input.artifactRoot,
1447
1479
  planRoot: input.planRoot,
1448
1480
  access: "read"
1449
1481
  });
1450
- const workspace = ensureOperationBranch({ ...input, branchName: input.message.branchName });
1451
- return readWorkerTextFile(workspace.branchPath, input.message.filePath, input.message.offset, input.message.limit, builtInPaths);
1482
+ return readWorkerTextFile(input.resolvedTarget.rootPath, input.message.filePath, input.message.offset, input.message.limit, builtInPaths);
1452
1483
  }
1453
1484
  async function executeWriteFileOperation(input) {
1454
- const builtInPaths = await prepareBuiltInToolPaths({
1485
+ const builtInPaths = await prepareBuiltInToolPathsForTarget({
1486
+ target: input.resolvedTarget.target,
1455
1487
  filePath: input.message.filePath,
1456
1488
  baseUrl: input.baseUrl,
1457
1489
  token: input.token,
1458
- projectId: input.projectId,
1459
- branchName: input.message.branchName,
1460
1490
  sessionId: input.message.sessionId,
1461
1491
  activePlanId: input.message.activePlanId,
1462
1492
  artifactRoot: input.artifactRoot,
1463
1493
  planRoot: input.planRoot,
1464
1494
  access: "write"
1465
1495
  });
1466
- const workspace = ensureOperationBranch({ ...input, branchName: input.message.branchName });
1467
- const resolved = resolveWorkerFilePath(workspace.branchPath, input.message.filePath, builtInPaths);
1468
- return withFileMutationQueue(mutationQueueKey(input.projectId, input.message.branchName, resolved), async () => {
1469
- return writeWorkerTextFile(workspace.branchPath, input.message.filePath, input.message.content, builtInPaths);
1496
+ const resolved = resolveWorkerFilePath(input.resolvedTarget.rootPath, input.message.filePath, builtInPaths);
1497
+ return withFileMutationQueue(mutationQueueKey(input.resolvedTarget.target, resolved), async () => {
1498
+ return writeWorkerTextFile(input.resolvedTarget.rootPath, input.message.filePath, input.message.content, builtInPaths);
1470
1499
  });
1471
1500
  }
1472
1501
  async function executeEditFileOperation(input) {
1473
- const builtInPaths = await prepareBuiltInToolPaths({
1502
+ const builtInPaths = await prepareBuiltInToolPathsForTarget({
1503
+ target: input.resolvedTarget.target,
1474
1504
  filePath: input.message.filePath,
1475
1505
  baseUrl: input.baseUrl,
1476
1506
  token: input.token,
1477
- projectId: input.projectId,
1478
- branchName: input.message.branchName,
1479
1507
  sessionId: input.message.sessionId,
1480
1508
  activePlanId: input.message.activePlanId,
1481
1509
  artifactRoot: input.artifactRoot,
1482
1510
  planRoot: input.planRoot,
1483
1511
  access: "write"
1484
1512
  });
1485
- const workspace = ensureOperationBranch({ ...input, branchName: input.message.branchName });
1486
- const resolved = resolveWorkerFilePath(workspace.branchPath, input.message.filePath, builtInPaths);
1487
- return withFileMutationQueue(mutationQueueKey(input.projectId, input.message.branchName, resolved), async () => {
1488
- return editWorkerTextFile(workspace.branchPath, input.message.filePath, input.message.edits, builtInPaths);
1513
+ const resolved = resolveWorkerFilePath(input.resolvedTarget.rootPath, input.message.filePath, builtInPaths);
1514
+ return withFileMutationQueue(mutationQueueKey(input.resolvedTarget.target, resolved), async () => {
1515
+ return editWorkerTextFile(input.resolvedTarget.rootPath, input.message.filePath, input.message.edits, builtInPaths);
1489
1516
  });
1490
1517
  }
1491
1518
  const IGNORED_ENTRY_NAMES = /* @__PURE__ */ new Set([".git", "node_modules", "dist", "build", ".next", ".cache", "coverage", "target"]);
@@ -1598,21 +1625,19 @@ function grepWorkerFiles(branchPath, input, builtInPaths) {
1598
1625
  }
1599
1626
  async function executeGrepOperation(input) {
1600
1627
  const toolPath = input.message.path ?? ".";
1601
- const builtInPaths = await prepareBuiltInToolPaths({
1628
+ const builtInPaths = await prepareBuiltInToolPathsForTarget({
1629
+ target: input.resolvedTarget.target,
1602
1630
  filePath: toolPath,
1603
1631
  baseUrl: input.baseUrl,
1604
1632
  token: input.token,
1605
- projectId: input.projectId,
1606
- branchName: input.message.branchName,
1607
1633
  sessionId: input.message.sessionId,
1608
1634
  activePlanId: input.message.activePlanId,
1609
1635
  artifactRoot: input.artifactRoot,
1610
1636
  planRoot: input.planRoot,
1611
1637
  access: "read"
1612
1638
  });
1613
- const workspace = ensureOperationBranch({ ...input, branchName: input.message.branchName });
1614
1639
  return grepWorkerFiles(
1615
- workspace.branchPath,
1640
+ input.resolvedTarget.rootPath,
1616
1641
  {
1617
1642
  pattern: input.message.pattern,
1618
1643
  path: input.message.path,
@@ -1659,21 +1684,19 @@ function findWorkerFiles(branchPath, input, builtInPaths) {
1659
1684
  }
1660
1685
  async function executeFindOperation(input) {
1661
1686
  const toolPath = input.message.path ?? ".";
1662
- const builtInPaths = await prepareBuiltInToolPaths({
1687
+ const builtInPaths = await prepareBuiltInToolPathsForTarget({
1688
+ target: input.resolvedTarget.target,
1663
1689
  filePath: toolPath,
1664
1690
  baseUrl: input.baseUrl,
1665
1691
  token: input.token,
1666
- projectId: input.projectId,
1667
- branchName: input.message.branchName,
1668
1692
  sessionId: input.message.sessionId,
1669
1693
  activePlanId: input.message.activePlanId,
1670
1694
  artifactRoot: input.artifactRoot,
1671
1695
  planRoot: input.planRoot,
1672
1696
  access: "read"
1673
1697
  });
1674
- const workspace = ensureOperationBranch({ ...input, branchName: input.message.branchName });
1675
1698
  return findWorkerFiles(
1676
- workspace.branchPath,
1699
+ input.resolvedTarget.rootPath,
1677
1700
  {
1678
1701
  pattern: input.message.pattern,
1679
1702
  path: input.message.path,
@@ -1703,20 +1726,18 @@ function listWorkerDirectory(branchPath, inputPath = ".", inputLimit, builtInPat
1703
1726
  }
1704
1727
  async function executeLsOperation(input) {
1705
1728
  const toolPath = input.message.path ?? ".";
1706
- const builtInPaths = await prepareBuiltInToolPaths({
1729
+ const builtInPaths = await prepareBuiltInToolPathsForTarget({
1730
+ target: input.resolvedTarget.target,
1707
1731
  filePath: toolPath,
1708
1732
  baseUrl: input.baseUrl,
1709
1733
  token: input.token,
1710
- projectId: input.projectId,
1711
- branchName: input.message.branchName,
1712
1734
  sessionId: input.message.sessionId,
1713
1735
  activePlanId: input.message.activePlanId,
1714
1736
  artifactRoot: input.artifactRoot,
1715
1737
  planRoot: input.planRoot,
1716
1738
  access: "read"
1717
1739
  });
1718
- const workspace = ensureOperationBranch({ ...input, branchName: input.message.branchName });
1719
- return listWorkerDirectory(workspace.branchPath, input.message.path, input.message.limit, builtInPaths);
1740
+ return listWorkerDirectory(input.resolvedTarget.rootPath, input.message.path, input.message.limit, builtInPaths);
1720
1741
  }
1721
1742
  function readWorkerImageFile(branchPath, filePath, builtInPaths) {
1722
1743
  const resolved = resolveWorkerFilePath(branchPath, filePath, builtInPaths);
@@ -1741,20 +1762,18 @@ function readWorkerImageFile(branchPath, filePath, builtInPaths) {
1741
1762
  };
1742
1763
  }
1743
1764
  async function executeViewFileBytesOperation(input) {
1744
- const builtInPaths = await prepareBuiltInToolPaths({
1765
+ const builtInPaths = await prepareBuiltInToolPathsForTarget({
1766
+ target: input.resolvedTarget.target,
1745
1767
  filePath: input.message.filePath,
1746
1768
  baseUrl: input.baseUrl,
1747
1769
  token: input.token,
1748
- projectId: input.projectId,
1749
- branchName: input.message.branchName,
1750
1770
  sessionId: input.message.sessionId,
1751
1771
  activePlanId: input.message.activePlanId,
1752
1772
  artifactRoot: input.artifactRoot,
1753
1773
  planRoot: input.planRoot,
1754
1774
  access: "read"
1755
1775
  });
1756
- const workspace = ensureOperationBranch({ ...input, branchName: input.message.branchName });
1757
- return readWorkerImageFile(workspace.branchPath, input.message.filePath, builtInPaths);
1776
+ return readWorkerImageFile(input.resolvedTarget.rootPath, input.message.filePath, builtInPaths);
1758
1777
  }
1759
1778
  async function executeOperation(input) {
1760
1779
  switch (input.message.type) {
@@ -1774,38 +1793,56 @@ async function executeOperation(input) {
1774
1793
  return executeViewFileBytesOperation({ ...input, message: input.message });
1775
1794
  }
1776
1795
  }
1796
+ function targetIdentityEnv(target) {
1797
+ return target.type === "project" ? {
1798
+ R5D_PROJECT_ID: target.projectId,
1799
+ R5D_BRANCH_NAME: target.branchName,
1800
+ R5D_WORKSPACE_OWNER_ID: "",
1801
+ R5D_WORKSPACE_ROOT_PROFILE: ""
1802
+ } : {
1803
+ R5D_WORKSPACE_OWNER_ID: target.ownerUserId,
1804
+ R5D_WORKSPACE_ROOT_PROFILE: target.rootProfile,
1805
+ R5D_PROJECT_ID: "",
1806
+ R5D_BRANCH_NAME: ""
1807
+ };
1808
+ }
1809
+ async function prepareShellEnvForTarget(input) {
1810
+ const artifactProcessEnv = await prepareArtifactEnvForShell({
1811
+ baseUrl: input.baseUrl,
1812
+ token: input.token,
1813
+ sessionId: input.sessionId,
1814
+ artifactRoot: input.artifactRoot
1815
+ });
1816
+ if (input.target.type === "workspace" && input.target.rootProfile === "canonical_sync") {
1817
+ return {
1818
+ ...artifactProcessEnv,
1819
+ ...targetIdentityEnv(input.target)
1820
+ };
1821
+ }
1822
+ return {
1823
+ ...artifactProcessEnv,
1824
+ ...preparePlanEnvForShell({
1825
+ target: input.target,
1826
+ planRoot: input.planRoot,
1827
+ activePlanId: input.activePlanId
1828
+ }),
1829
+ ...targetIdentityEnv(input.target)
1830
+ };
1831
+ }
1777
1832
  async function executeCommand(input) {
1778
1833
  const startedAt = Date.now();
1779
1834
  let timeout;
1780
1835
  try {
1781
- const workspace = ensureBranchWorkspace({
1782
- projectId: input.projectId,
1783
- baseUrl: input.baseUrl,
1784
- token: input.token,
1785
- projectRoot: input.projectRoot,
1786
- syncRoot: input.syncRoot,
1787
- branchName: input.message.branchName,
1788
- manifest: input.manifest
1789
- });
1790
- const artifactProcessEnv = await prepareArtifactEnvForShell({
1836
+ const targetProcessEnv = await prepareShellEnvForTarget({
1837
+ target: input.resolvedTarget.target,
1791
1838
  baseUrl: input.baseUrl,
1792
1839
  token: input.token,
1793
- projectId: input.projectId,
1794
- branchName: input.message.branchName,
1795
1840
  sessionId: input.message.sessionId,
1796
- artifactRoot: input.artifactRoot
1797
- });
1798
- const planProcessEnv = preparePlanEnvForShell({
1799
- projectId: input.projectId,
1800
- branchName: input.message.branchName,
1841
+ artifactRoot: input.artifactRoot,
1801
1842
  planRoot: input.planRoot,
1802
1843
  activePlanId: input.message.env?.R5D_ACTIVE_PLAN_ID
1803
1844
  });
1804
- const commandRoot = input.message.env?.R5D_USE_WORKSPACE_GIT === "1" ? input.workspaceShadowRoot : workspace.branchPath;
1805
- if (input.message.env?.R5D_USE_WORKSPACE_GIT === "1" && !import_node_fs.default.existsSync(import_node_path.default.join(commandRoot, ".git"))) {
1806
- throw new Error("The canonical workspace checkout is not initialized on this worker");
1807
- }
1808
- const cwd = resolveCommandCwd(commandRoot, input.message.cwd);
1845
+ const cwd = resolveCommandCwd(input.resolvedTarget.rootPath, input.message.cwd);
1809
1846
  const subprocess = Bun.spawn(input.message.argv, {
1810
1847
  cwd,
1811
1848
  stdout: "pipe",
@@ -1815,15 +1852,13 @@ async function executeCommand(input) {
1815
1852
  ...process.env,
1816
1853
  ...githubProcessEnv(),
1817
1854
  ...input.message.env ?? {},
1818
- ...artifactProcessEnv,
1819
- ...planProcessEnv
1855
+ ...targetProcessEnv
1820
1856
  }
1821
1857
  });
1822
1858
  activeProcesses.set(input.message.runId, {
1823
1859
  process: subprocess,
1824
- projectId: input.projectId,
1860
+ target: input.resolvedTarget.target,
1825
1861
  sessionId: input.message.sessionId ?? "",
1826
- branchName: input.message.branchName,
1827
1862
  mode: "foreground",
1828
1863
  pid: subprocess.pid,
1829
1864
  processGroupId: process.platform === "win32" ? void 0 : subprocess.pid,
@@ -1887,34 +1922,16 @@ async function executeStreamingCommand(input) {
1887
1922
  });
1888
1923
  return;
1889
1924
  }
1890
- const workspace = ensureBranchWorkspace({
1891
- projectId: input.projectId,
1892
- baseUrl: input.baseUrl,
1893
- token: input.token,
1894
- projectRoot: input.projectRoot,
1895
- syncRoot: input.syncRoot,
1896
- branchName: input.message.branchName,
1897
- manifest: input.manifest
1898
- });
1899
- const artifactProcessEnv = await prepareArtifactEnvForShell({
1925
+ const targetProcessEnv = await prepareShellEnvForTarget({
1926
+ target: input.resolvedTarget.target,
1900
1927
  baseUrl: input.baseUrl,
1901
1928
  token: input.token,
1902
- projectId: input.projectId,
1903
- branchName: input.message.branchName,
1904
1929
  sessionId: input.message.sessionId,
1905
- artifactRoot: input.artifactRoot
1906
- });
1907
- const planProcessEnv = preparePlanEnvForShell({
1908
- projectId: input.projectId,
1909
- branchName: input.message.branchName,
1930
+ artifactRoot: input.artifactRoot,
1910
1931
  planRoot: input.planRoot,
1911
1932
  activePlanId: input.message.env?.R5D_ACTIVE_PLAN_ID
1912
1933
  });
1913
- const commandRoot = input.message.env?.R5D_USE_WORKSPACE_GIT === "1" ? input.workspaceShadowRoot : workspace.branchPath;
1914
- if (input.message.env?.R5D_USE_WORKSPACE_GIT === "1" && !import_node_fs.default.existsSync(import_node_path.default.join(commandRoot, ".git"))) {
1915
- throw new Error("The canonical workspace checkout is not initialized on this worker");
1916
- }
1917
- const cwd = resolveCommandCwd(commandRoot, input.message.cwd);
1934
+ const cwd = resolveCommandCwd(input.resolvedTarget.rootPath, input.message.cwd);
1918
1935
  const subprocess = Bun.spawn(input.message.argv, {
1919
1936
  cwd,
1920
1937
  stdout: "pipe",
@@ -1924,15 +1941,13 @@ async function executeStreamingCommand(input) {
1924
1941
  ...process.env,
1925
1942
  ...githubProcessEnv(),
1926
1943
  ...input.message.env ?? {},
1927
- ...artifactProcessEnv,
1928
- ...planProcessEnv
1944
+ ...targetProcessEnv
1929
1945
  }
1930
1946
  });
1931
1947
  activeProcesses.set(input.message.runId, {
1932
1948
  process: subprocess,
1933
- projectId: input.projectId,
1949
+ target: input.resolvedTarget.target,
1934
1950
  sessionId: input.message.sessionId,
1935
- branchName: input.message.branchName,
1936
1951
  mode: input.message.mode,
1937
1952
  pid: subprocess.pid,
1938
1953
  processGroupId: process.platform === "win32" ? void 0 : subprocess.pid,
@@ -2029,9 +2044,8 @@ function sendWorkerMessage(ws, message) {
2029
2044
  function buildActiveProcessReports() {
2030
2045
  return Array.from(activeProcesses.entries()).filter(([, active]) => active.sessionId.length > 0).map(([runId, active]) => ({
2031
2046
  runId,
2032
- projectId: active.projectId,
2047
+ target: active.target,
2033
2048
  sessionId: active.sessionId,
2034
- branchName: active.branchName,
2035
2049
  platform: process.platform,
2036
2050
  arch: process.arch,
2037
2051
  mode: active.mode,
@@ -2239,21 +2253,12 @@ function resolveHostShell(command, platform = process.platform) {
2239
2253
  }
2240
2254
  async function openPty(input) {
2241
2255
  try {
2242
- const branchPath = ensureBranchWorkspace({
2243
- projectId: input.projectId,
2244
- baseUrl: input.baseUrl,
2245
- token: input.token,
2246
- projectRoot: input.projectRoot,
2247
- syncRoot: input.syncRoot,
2248
- branchName: input.message.branchName,
2249
- manifest: input.manifest
2250
- });
2251
- const planProcessEnv = preparePlanEnvForShell({
2252
- projectId: input.projectId,
2253
- branchName: input.message.branchName,
2256
+ const planProcessEnv = !(input.resolvedTarget.target.type === "workspace" && input.resolvedTarget.target.rootProfile === "canonical_sync") ? preparePlanEnvForShell({
2257
+ target: input.resolvedTarget.target,
2254
2258
  planRoot: input.planRoot,
2255
2259
  activePlanId: input.message.env?.R5D_ACTIVE_PLAN_ID
2256
- });
2260
+ }) : {};
2261
+ const targetProcessEnv = targetIdentityEnv(input.resolvedTarget.target);
2257
2262
  const shell = resolveHostShell(input.message.command);
2258
2263
  const ptyProcess = createNodePtyBridge({
2259
2264
  file: shell.file,
@@ -2262,14 +2267,13 @@ async function openPty(input) {
2262
2267
  name: "xterm-256color",
2263
2268
  cols: Math.max(1, Math.min(Math.floor(input.message.cols || 80), 500)),
2264
2269
  rows: Math.max(1, Math.min(Math.floor(input.message.rows || 24), 500)),
2265
- cwd: branchPath.branchPath,
2270
+ cwd: input.resolvedTarget.rootPath,
2266
2271
  env: {
2267
2272
  ...process.env,
2268
2273
  ...githubProcessEnv(),
2269
2274
  ...input.message.env ?? {},
2270
2275
  ...planProcessEnv,
2271
- R5D_PROJECT_ID: input.projectId,
2272
- R5D_BRANCH_NAME: input.message.branchName
2276
+ ...targetProcessEnv
2273
2277
  }
2274
2278
  },
2275
2279
  onOpened: () => {
@@ -2307,8 +2311,7 @@ async function openPty(input) {
2307
2311
  });
2308
2312
  activePtys.set(input.message.ptyId, {
2309
2313
  ...ptyProcess,
2310
- projectId: input.projectId,
2311
- branchName: input.message.branchName
2314
+ target: input.resolvedTarget.target
2312
2315
  });
2313
2316
  } catch (error) {
2314
2317
  sendWorkerMessage(input.ws, {
@@ -2427,6 +2430,15 @@ async function startWorker(options) {
2427
2430
  const allManifestCheckouts = () => [...manifestByProjectId.values()].flatMap(
2428
2431
  (manifest) => manifest.branches.map((branchName) => ({ projectId: manifest.projectId, branchName }))
2429
2432
  );
2433
+ const resolveMessageTarget = (target) => resolveWorkerSessionTarget({
2434
+ target,
2435
+ baseUrl,
2436
+ token,
2437
+ projectsRoot,
2438
+ syncRoot,
2439
+ workspaceShadowRoot,
2440
+ manifestByProjectId
2441
+ });
2430
2442
  const ensureVisibleWorkspaceCheckouts = (targets, options2 = {}) => {
2431
2443
  const created = [];
2432
2444
  for (const target of targets) {
@@ -2682,7 +2694,11 @@ async function startWorker(options) {
2682
2694
  }
2683
2695
  if (message.type === "workspace_head_updated") {
2684
2696
  if (!activeWorkspaceIncidentId) {
2685
- await runRequestedWorkspaceSync(crypto.randomUUID(), { type: "inbound_head", detail: message.commitHash });
2697
+ await runRequestedWorkspaceSync(
2698
+ crypto.randomUUID(),
2699
+ { type: "inbound_head", detail: message.commitHash },
2700
+ { skipVisibleMirror: true }
2701
+ );
2686
2702
  }
2687
2703
  return;
2688
2704
  }
@@ -2692,8 +2708,6 @@ async function startWorker(options) {
2692
2708
  await syncSessionArtifacts({
2693
2709
  baseUrl,
2694
2710
  token,
2695
- projectId: message.projectId,
2696
- branchName: message.branchName,
2697
2711
  sessionId: message.sessionId,
2698
2712
  artifactRoot
2699
2713
  });
@@ -2813,11 +2827,19 @@ async function startWorker(options) {
2813
2827
  });
2814
2828
  return;
2815
2829
  }
2816
- const manifest = manifestByProjectId.get(message.projectId);
2817
- const projectRoot = projectRootFor(projectsRoot, message.projectId, manifestByProjectId);
2818
- process.stdout.write(`[r5d-worker] pty ${message.ptyId}: ${message.projectId}/${message.branchName}
2830
+ try {
2831
+ const resolvedTarget = resolveMessageTarget(message.target);
2832
+ process.stdout.write(`[r5d-worker] pty ${message.ptyId}: ${describeWorkerSessionTarget(message.target)}
2819
2833
  `);
2820
- await openPty({ ws, message, projectId: message.projectId, baseUrl, token, projectRoot, syncRoot, planRoot, manifest });
2834
+ await openPty({ ws, message, resolvedTarget, planRoot });
2835
+ } catch (error) {
2836
+ sendWorkerMessage(ws, {
2837
+ type: "pty_error",
2838
+ requestId: message.requestId,
2839
+ ptyId: message.ptyId,
2840
+ error: error instanceof Error ? error.message : String(error)
2841
+ });
2842
+ }
2821
2843
  return;
2822
2844
  }
2823
2845
  if (message.type === "pty_input") {
@@ -2843,22 +2865,22 @@ async function startWorker(options) {
2843
2865
  });
2844
2866
  return;
2845
2867
  }
2846
- const manifest = manifestByProjectId.get(message.projectId);
2847
- const projectRoot = projectRootFor(projectsRoot, message.projectId, manifestByProjectId);
2848
- process.stdout.write(`[r5d-worker] exec ${message.runId}: ${message.argv.join(" ")}
2868
+ let result;
2869
+ try {
2870
+ const resolvedTarget = resolveMessageTarget(message.target);
2871
+ process.stdout.write(`[r5d-worker] exec ${message.runId}: ${message.argv.join(" ")}
2849
2872
  `);
2850
- const result = await executeCommand({
2851
- message,
2852
- projectId: message.projectId,
2853
- baseUrl,
2854
- token,
2855
- projectRoot,
2856
- syncRoot,
2857
- artifactRoot,
2858
- planRoot,
2859
- workspaceShadowRoot,
2860
- manifest
2861
- });
2873
+ result = await executeCommand({ message, resolvedTarget, baseUrl, token, artifactRoot, planRoot });
2874
+ } catch (error) {
2875
+ result = {
2876
+ type: "exec_result",
2877
+ requestId: message.requestId,
2878
+ stdout: "",
2879
+ stderr: "",
2880
+ exitCode: 1,
2881
+ error: error instanceof Error ? error.message : String(error)
2882
+ };
2883
+ }
2862
2884
  ws.send(JSON.stringify(result));
2863
2885
  return;
2864
2886
  }
@@ -2878,22 +2900,17 @@ async function startWorker(options) {
2878
2900
  runId: message.runId
2879
2901
  });
2880
2902
  try {
2881
- const manifest = manifestByProjectId.get(message.projectId);
2882
- const projectRoot = projectRootFor(projectsRoot, message.projectId, manifestByProjectId);
2903
+ const resolvedTarget = resolveMessageTarget(message.target);
2883
2904
  process.stdout.write(`[r5d-worker] exec_start ${message.runId}: ${message.argv.join(" ")}
2884
2905
  `);
2885
2906
  void executeStreamingCommand({
2886
2907
  ws,
2887
2908
  message,
2888
- projectId: message.projectId,
2909
+ resolvedTarget,
2889
2910
  baseUrl,
2890
2911
  token,
2891
- projectRoot,
2892
- syncRoot,
2893
2912
  artifactRoot,
2894
- planRoot,
2895
- workspaceShadowRoot,
2896
- manifest
2913
+ planRoot
2897
2914
  }).catch((error) => {
2898
2915
  sendWorkerMessage(ws, {
2899
2916
  type: "exec_start_error",
@@ -2914,18 +2931,14 @@ async function startWorker(options) {
2914
2931
  }
2915
2932
  if (message.type === "read" || message.type === "write" || message.type === "edit" || message.type === "grep" || message.type === "find" || message.type === "ls" || message.type === "view_file_bytes") {
2916
2933
  try {
2917
- const manifest = manifestByProjectId.get(message.projectId);
2918
- const projectRoot = projectRootFor(projectsRoot, message.projectId, manifestByProjectId);
2934
+ const resolvedTarget = resolveMessageTarget(message.target);
2919
2935
  const result = await executeOperation({
2920
2936
  message,
2921
- projectId: message.projectId,
2937
+ resolvedTarget,
2922
2938
  baseUrl,
2923
2939
  token,
2924
- projectRoot,
2925
- syncRoot,
2926
2940
  artifactRoot,
2927
- planRoot,
2928
- manifest
2941
+ planRoot
2929
2942
  });
2930
2943
  ws.send(
2931
2944
  JSON.stringify({
@@ -3053,6 +3066,7 @@ if (isCliEntrypoint()) {
3053
3066
  }
3054
3067
  // Annotate the CommonJS export names for ESM import in node:
3055
3068
  0 && (module.exports = {
3069
+ describeWorkerSessionTarget,
3056
3070
  editWorkerTextFile,
3057
3071
  ensureVisibleGitCheckout,
3058
3072
  findWorkerFiles,
@@ -3061,11 +3075,14 @@ if (isCliEntrypoint()) {
3061
3075
  isArtifactEnvPath,
3062
3076
  listWorkerDirectory,
3063
3077
  prepareArtifactEnvForShell,
3078
+ prepareBuiltInToolPathsForTarget,
3064
3079
  preparePlanEnvForShell,
3080
+ prepareShellEnvForTarget,
3065
3081
  readWorkerImageFile,
3066
3082
  readWorkerTextFile,
3067
3083
  resolveHostShell,
3068
3084
  resolveWorkerFilePath,
3085
+ resolveWorkerSessionTarget,
3069
3086
  syncSessionArtifacts,
3070
3087
  visibleCheckoutGitTestHarness,
3071
3088
  visibleCheckoutRemoteFor,