@h-rig/server 0.0.6-alpha.10 → 0.0.6-alpha.12

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.
@@ -4,8 +4,8 @@ var __require = import.meta.require;
4
4
  // packages/server/src/server-helpers/http-router.ts
5
5
  import { randomUUID } from "crypto";
6
6
  import { spawnSync as spawnSync2 } from "child_process";
7
- import { basename, dirname as dirname6, isAbsolute as isAbsolute2, resolve as resolve11 } from "path";
8
- import { copyFileSync, existsSync as existsSync8, mkdirSync as mkdirSync7, readFileSync as readFileSync5, writeFileSync as writeFileSync7 } from "fs";
7
+ import { basename, dirname as dirname9, isAbsolute as isAbsolute3, resolve as resolve13 } from "path";
8
+ import { copyFileSync as copyFileSync2, existsSync as existsSync10, mkdirSync as mkdirSync9, readFileSync as readFileSync7, writeFileSync as writeFileSync9 } from "fs";
9
9
 
10
10
  // packages/server/src/server.ts
11
11
  import {
@@ -803,8 +803,8 @@ import {
803
803
 
804
804
  // packages/server/src/server-helpers/github-auth-store.ts
805
805
  import { randomBytes } from "crypto";
806
- import { chmodSync, existsSync as existsSync4, mkdirSync as mkdirSync3, readFileSync as readFileSync2, writeFileSync as writeFileSync3 } from "fs";
807
- import { resolve as resolve7 } from "path";
806
+ import { chmodSync, copyFileSync, existsSync as existsSync4, mkdirSync as mkdirSync3, readFileSync as readFileSync2, writeFileSync as writeFileSync3 } from "fs";
807
+ import { dirname as dirname3, resolve as resolve7 } from "path";
808
808
  function cleanString(value) {
809
809
  return typeof value === "string" && value.trim().length > 0 ? value.trim() : null;
810
810
  }
@@ -834,6 +834,26 @@ function parseApiSessions(value) {
834
834
  }];
835
835
  });
836
836
  }
837
+ function parsePendingDevice(value) {
838
+ if (!value || typeof value !== "object")
839
+ return null;
840
+ const record = value;
841
+ const pollId = cleanString(record.pollId);
842
+ const deviceCode = cleanString(record.deviceCode);
843
+ const expiresAt = cleanString(record.expiresAt);
844
+ const intervalSeconds = typeof record.intervalSeconds === "number" && Number.isFinite(record.intervalSeconds) ? Math.max(1, Math.floor(record.intervalSeconds)) : null;
845
+ if (!pollId || !deviceCode || !expiresAt || !intervalSeconds)
846
+ return null;
847
+ return { pollId, deviceCode, expiresAt, intervalSeconds };
848
+ }
849
+ function parsePendingDevices(value) {
850
+ if (!Array.isArray(value))
851
+ return [];
852
+ return value.flatMap((entry) => {
853
+ const pending = parsePendingDevice(entry);
854
+ return pending ? [pending] : [];
855
+ });
856
+ }
837
857
  function readStoredAuth(stateFile) {
838
858
  if (!existsSync4(stateFile))
839
859
  return {};
@@ -847,6 +867,7 @@ function readStoredAuth(stateFile) {
847
867
  selectedRepo: cleanString(parsed.selectedRepo),
848
868
  tokenSource: parsed.tokenSource === "oauth-device" || parsed.tokenSource === "manual-token" || parsed.tokenSource === "env" ? parsed.tokenSource : undefined,
849
869
  pendingDevice: parsePendingDevice(parsed.pendingDevice),
870
+ pendingDevices: parsePendingDevices(parsed.pendingDevices),
850
871
  apiSessions: parseApiSessions(parsed.apiSessions),
851
872
  updatedAt: cleanString(parsed.updatedAt) ?? undefined
852
873
  };
@@ -854,34 +875,36 @@ function readStoredAuth(stateFile) {
854
875
  return {};
855
876
  }
856
877
  }
857
- function parsePendingDevice(value) {
858
- if (!value || typeof value !== "object")
859
- return null;
860
- const record = value;
861
- const pollId = cleanString(record.pollId);
862
- const deviceCode = cleanString(record.deviceCode);
863
- const expiresAt = cleanString(record.expiresAt);
864
- const intervalSeconds = typeof record.intervalSeconds === "number" && Number.isFinite(record.intervalSeconds) ? Math.max(1, Math.floor(record.intervalSeconds)) : null;
865
- if (!pollId || !deviceCode || !expiresAt || !intervalSeconds)
866
- return null;
867
- return { pollId, deviceCode, expiresAt, intervalSeconds };
868
- }
869
878
  function newApiSessionToken() {
870
879
  return `rig_${randomBytes(32).toString("base64url")}`;
871
880
  }
872
881
  function writeStoredAuth(stateFile, payload) {
873
- mkdirSync3(resolve7(stateFile, ".."), { recursive: true });
882
+ mkdirSync3(dirname3(stateFile), { recursive: true });
874
883
  writeFileSync3(stateFile, `${JSON.stringify(payload, null, 2)}
875
884
  `, { encoding: "utf8", mode: 384 });
876
885
  try {
877
886
  chmodSync(stateFile, 384);
878
887
  } catch {}
879
888
  }
889
+ function localProjectAuthStateFile(projectRoot) {
890
+ return resolve7(projectRoot, ".rig", "state", "github-auth.json");
891
+ }
880
892
  function resolveGitHubAuthStateFile(projectRoot) {
881
893
  return resolve7(resolveServerAuthorityPaths(projectRoot).stateDir, "github-auth.json");
882
894
  }
883
- function createGitHubAuthStore(projectRoot) {
884
- const stateFile = resolveGitHubAuthStateFile(projectRoot);
895
+ function copyGitHubAuthStateToLocalProjectRoot(stateFile, projectRoot) {
896
+ const targetFile = localProjectAuthStateFile(projectRoot);
897
+ mkdirSync3(dirname3(targetFile), { recursive: true });
898
+ if (existsSync4(stateFile)) {
899
+ copyFileSync(stateFile, targetFile);
900
+ try {
901
+ chmodSync(targetFile, 384);
902
+ } catch {}
903
+ return;
904
+ }
905
+ writeStoredAuth(targetFile, {});
906
+ }
907
+ function createGitHubAuthStoreFromStateFile(stateFile) {
885
908
  return {
886
909
  stateFile,
887
910
  status(options) {
@@ -911,6 +934,7 @@ function createGitHubAuthStore(projectRoot) {
911
934
  scopes: input.scopes ?? [],
912
935
  selectedRepo: input.selectedRepo ?? previous.selectedRepo ?? null,
913
936
  pendingDevice: null,
937
+ pendingDevices: [],
914
938
  apiSessions: previous.apiSessions ?? [],
915
939
  updatedAt: new Date().toISOString()
916
940
  });
@@ -939,15 +963,24 @@ function createGitHubAuthStore(projectRoot) {
939
963
  const session = (previous.apiSessions ?? []).find((candidate) => candidate.token === clean);
940
964
  return session ? { login: cleanString(session.login), userId: cleanString(session.userId) } : null;
941
965
  },
942
- copyToProjectRoot(projectRoot2) {
943
- const targetFile = resolveGitHubAuthStateFile(projectRoot2);
966
+ copyToProjectRoot(projectRoot) {
967
+ const targetFile = resolveGitHubAuthStateFile(projectRoot);
944
968
  writeStoredAuth(targetFile, readStoredAuth(stateFile));
945
969
  },
970
+ copyToLocalProjectRoot(projectRoot) {
971
+ copyGitHubAuthStateToLocalProjectRoot(stateFile, projectRoot);
972
+ },
946
973
  savePendingDevice(input) {
947
974
  const previous = readStoredAuth(stateFile);
975
+ const pendingDevices = [
976
+ ...previous.pendingDevice ? [previous.pendingDevice] : [],
977
+ ...previous.pendingDevices ?? [],
978
+ input
979
+ ].filter((entry, index, entries) => entries.findIndex((candidate) => candidate.pollId === entry.pollId) === index);
948
980
  writeStoredAuth(stateFile, {
949
981
  ...previous,
950
- pendingDevice: input,
982
+ pendingDevice: null,
983
+ pendingDevices,
951
984
  updatedAt: new Date().toISOString()
952
985
  });
953
986
  },
@@ -960,23 +993,32 @@ function createGitHubAuthStore(projectRoot) {
960
993
  });
961
994
  },
962
995
  readPendingDevice(pollId) {
963
- const pending = readStoredAuth(stateFile).pendingDevice ?? null;
964
- if (!pending || pending.pollId !== pollId)
996
+ const previous = readStoredAuth(stateFile);
997
+ const pending = [
998
+ ...previous.pendingDevice ? [previous.pendingDevice] : [],
999
+ ...previous.pendingDevices ?? []
1000
+ ].find((entry) => entry.pollId === pollId) ?? null;
1001
+ if (!pending)
965
1002
  return null;
966
1003
  if (Date.parse(pending.expiresAt) <= Date.now())
967
1004
  return null;
968
1005
  return pending;
969
1006
  },
970
- clearPendingDevice() {
1007
+ clearPendingDevice(pollId) {
971
1008
  const previous = readStoredAuth(stateFile);
1009
+ const remaining = pollId ? (previous.pendingDevices ?? []).filter((entry) => entry.pollId !== pollId) : [];
972
1010
  writeStoredAuth(stateFile, {
973
1011
  ...previous,
974
1012
  pendingDevice: null,
1013
+ pendingDevices: remaining,
975
1014
  updatedAt: new Date().toISOString()
976
1015
  });
977
1016
  }
978
1017
  };
979
1018
  }
1019
+ function createGitHubAuthStore(projectRoot) {
1020
+ return createGitHubAuthStoreFromStateFile(resolveGitHubAuthStateFile(projectRoot));
1021
+ }
980
1022
 
981
1023
  // packages/server/src/server-helpers/github-projects.ts
982
1024
  function asRecord(value) {
@@ -1365,7 +1407,7 @@ import {
1365
1407
  } from "@rig/runtime/control-plane/remote";
1366
1408
 
1367
1409
  // packages/server/src/server-helpers/run-steering.ts
1368
- import { dirname as dirname3, resolve as resolve8 } from "path";
1410
+ import { dirname as dirname4, resolve as resolve8 } from "path";
1369
1411
  import { existsSync as existsSync5, mkdirSync as mkdirSync4, readFileSync as readFileSync3 } from "fs";
1370
1412
  import { appendJsonlRecord as appendJsonlRecord2, readAuthorityRun as readAuthorityRun7, resolveAuthorityRunDir as resolveAuthorityRunDir4 } from "@rig/runtime/control-plane/authority-files";
1371
1413
  var steeringSequence = 0;
@@ -1452,7 +1494,7 @@ function queueRunSteeringMessage(projectRoot, runId, input) {
1452
1494
  delivered: false
1453
1495
  };
1454
1496
  const path = runSteeringPath(projectRoot, runId);
1455
- mkdirSync4(dirname3(path), { recursive: true });
1497
+ mkdirSync4(dirname4(path), { recursive: true });
1456
1498
  appendJsonlRecord2(path, entry);
1457
1499
  appendRunTimelineEntry(projectRoot, runId, {
1458
1500
  id: entry.id,
@@ -1489,24 +1531,205 @@ import {
1489
1531
  updateConfiguredTaskSourceTask as updateConfiguredTaskSourceTask2
1490
1532
  } from "@rig/runtime/control-plane/tasks/source-lifecycle";
1491
1533
 
1534
+ // packages/server/src/server-helpers/github-api-session-index.ts
1535
+ import { chmodSync as chmodSync3, existsSync as existsSync7, mkdirSync as mkdirSync6, readFileSync as readFileSync5, writeFileSync as writeFileSync6 } from "fs";
1536
+ import { dirname as dirname6, resolve as resolve10 } from "path";
1537
+
1538
+ // packages/server/src/server-helpers/github-user-namespace.ts
1539
+ import { chmodSync as chmodSync2, existsSync as existsSync6, mkdirSync as mkdirSync5, readFileSync as readFileSync4, writeFileSync as writeFileSync5 } from "fs";
1540
+ import { dirname as dirname5, isAbsolute, relative, resolve as resolve9 } from "path";
1541
+ function cleanString3(value) {
1542
+ return typeof value === "string" && value.trim().length > 0 ? value.trim() : null;
1543
+ }
1544
+ function sanitizePathSegment(value) {
1545
+ return value.trim().toLowerCase().replace(/[^a-z0-9._-]/g, "-").replace(/^-+|-+$/g, "").slice(0, 96);
1546
+ }
1547
+ function deriveGitHubUserNamespaceKey(identity) {
1548
+ const userId = cleanString3(identity.userId);
1549
+ if (userId) {
1550
+ const safeId = sanitizePathSegment(userId);
1551
+ if (safeId)
1552
+ return `ghu-${safeId}`;
1553
+ }
1554
+ const login = cleanString3(identity.login);
1555
+ if (login) {
1556
+ const safeLogin = sanitizePathSegment(login);
1557
+ if (safeLogin)
1558
+ return `ghu-login-${safeLogin}`;
1559
+ }
1560
+ throw new Error("GitHub user namespace requires a user id or login");
1561
+ }
1562
+ function resolveRemoteUserNamespacesRoot(projectRoot) {
1563
+ const explicitRoot = cleanString3(process.env.RIG_REMOTE_USER_NAMESPACE_ROOT);
1564
+ if (explicitRoot)
1565
+ return resolve9(explicitRoot);
1566
+ const stateDir2 = cleanString3(process.env.RIG_STATE_DIR);
1567
+ if (stateDir2)
1568
+ return resolve9(dirname5(resolve9(stateDir2)), "users");
1569
+ return resolve9(projectRoot, ".rig", "users");
1570
+ }
1571
+ function resolveRemoteUserNamespace(projectRoot, identity) {
1572
+ const key = deriveGitHubUserNamespaceKey(identity);
1573
+ const root = resolve9(resolveRemoteUserNamespacesRoot(projectRoot), key);
1574
+ const stateDir2 = resolve9(root, ".rig", "state");
1575
+ return {
1576
+ key,
1577
+ userId: cleanString3(identity.userId),
1578
+ login: cleanString3(identity.login),
1579
+ root,
1580
+ stateDir: stateDir2,
1581
+ authStateFile: resolve9(stateDir2, "github-auth.json"),
1582
+ metadataFile: resolve9(stateDir2, "user-namespace.json"),
1583
+ checkoutBaseDir: resolve9(root, "remote-checkouts"),
1584
+ snapshotBaseDir: resolve9(root, "remote-snapshots")
1585
+ };
1586
+ }
1587
+ function serializeRemoteUserNamespace(namespace) {
1588
+ return {
1589
+ key: namespace.key,
1590
+ userId: namespace.userId,
1591
+ login: namespace.login,
1592
+ root: namespace.root,
1593
+ checkoutBaseDir: namespace.checkoutBaseDir,
1594
+ snapshotBaseDir: namespace.snapshotBaseDir
1595
+ };
1596
+ }
1597
+ function isPathInsideNamespace(namespaceRoot, candidatePath) {
1598
+ const root = resolve9(namespaceRoot);
1599
+ const candidate = resolve9(candidatePath);
1600
+ const rel = relative(root, candidate);
1601
+ return rel === "" || !rel.startsWith("..") && !isAbsolute(rel);
1602
+ }
1603
+ function writeRemoteUserNamespaceMetadata(namespace) {
1604
+ mkdirSync5(namespace.stateDir, { recursive: true });
1605
+ const previous = (() => {
1606
+ if (!existsSync6(namespace.metadataFile))
1607
+ return null;
1608
+ try {
1609
+ const parsed = JSON.parse(readFileSync4(namespace.metadataFile, "utf8"));
1610
+ return parsed && typeof parsed === "object" && !Array.isArray(parsed) ? parsed : null;
1611
+ } catch {
1612
+ return null;
1613
+ }
1614
+ })();
1615
+ const now = new Date().toISOString();
1616
+ writeFileSync5(namespace.metadataFile, `${JSON.stringify({
1617
+ key: namespace.key,
1618
+ userId: namespace.userId,
1619
+ login: namespace.login,
1620
+ root: namespace.root,
1621
+ checkoutBaseDir: namespace.checkoutBaseDir,
1622
+ snapshotBaseDir: namespace.snapshotBaseDir,
1623
+ createdAt: typeof previous?.createdAt === "string" ? previous.createdAt : now,
1624
+ updatedAt: now
1625
+ }, null, 2)}
1626
+ `, { encoding: "utf8", mode: 384 });
1627
+ try {
1628
+ chmodSync2(namespace.metadataFile, 384);
1629
+ } catch {}
1630
+ }
1631
+
1632
+ // packages/server/src/server-helpers/github-api-session-index.ts
1633
+ function cleanString4(value) {
1634
+ return typeof value === "string" && value.trim().length > 0 ? value.trim() : null;
1635
+ }
1636
+ function resolveGitHubApiSessionIndexFile(projectRoot) {
1637
+ return resolve10(resolveRemoteUserNamespacesRoot(projectRoot), ".api-sessions.json");
1638
+ }
1639
+ function parseEntry(value) {
1640
+ if (!value || typeof value !== "object" || Array.isArray(value))
1641
+ return null;
1642
+ const record = value;
1643
+ const token = cleanString4(record.token);
1644
+ const namespaceKey = cleanString4(record.namespaceKey);
1645
+ const namespaceRoot = cleanString4(record.namespaceRoot);
1646
+ const authStateFile = cleanString4(record.authStateFile);
1647
+ const checkoutBaseDir = cleanString4(record.checkoutBaseDir);
1648
+ const snapshotBaseDir = cleanString4(record.snapshotBaseDir);
1649
+ const createdAt = cleanString4(record.createdAt);
1650
+ if (!token || !namespaceKey || !namespaceRoot || !authStateFile || !checkoutBaseDir || !snapshotBaseDir || !createdAt)
1651
+ return null;
1652
+ return {
1653
+ token,
1654
+ namespaceKey,
1655
+ namespaceRoot,
1656
+ authStateFile,
1657
+ checkoutBaseDir,
1658
+ snapshotBaseDir,
1659
+ createdAt,
1660
+ login: cleanString4(record.login),
1661
+ userId: cleanString4(record.userId),
1662
+ selectedRepo: cleanString4(record.selectedRepo)
1663
+ };
1664
+ }
1665
+ function readIndex(indexFile) {
1666
+ if (!existsSync7(indexFile))
1667
+ return [];
1668
+ try {
1669
+ const parsed = JSON.parse(readFileSync5(indexFile, "utf8"));
1670
+ return Array.isArray(parsed.sessions) ? parsed.sessions.flatMap((entry) => {
1671
+ const parsedEntry = parseEntry(entry);
1672
+ return parsedEntry ? [parsedEntry] : [];
1673
+ }) : [];
1674
+ } catch {
1675
+ return [];
1676
+ }
1677
+ }
1678
+ function writeIndex(indexFile, sessions) {
1679
+ mkdirSync6(dirname6(indexFile), { recursive: true });
1680
+ writeFileSync6(indexFile, `${JSON.stringify({ sessions }, null, 2)}
1681
+ `, { encoding: "utf8", mode: 384 });
1682
+ try {
1683
+ chmodSync3(indexFile, 384);
1684
+ } catch {}
1685
+ }
1686
+ function registerGitHubApiSession(input) {
1687
+ const cleanToken = cleanString4(input.token);
1688
+ if (!cleanToken)
1689
+ throw new Error("GitHub API session token is required");
1690
+ const indexFile = resolveGitHubApiSessionIndexFile(input.projectRoot);
1691
+ const createdAt = new Date().toISOString();
1692
+ const entry = {
1693
+ token: cleanToken,
1694
+ login: input.namespace.login,
1695
+ userId: input.namespace.userId,
1696
+ namespaceKey: input.namespace.key,
1697
+ namespaceRoot: input.namespace.root,
1698
+ authStateFile: input.namespace.authStateFile,
1699
+ checkoutBaseDir: input.namespace.checkoutBaseDir,
1700
+ snapshotBaseDir: input.namespace.snapshotBaseDir,
1701
+ selectedRepo: cleanString4(input.selectedRepo),
1702
+ createdAt
1703
+ };
1704
+ const previous = readIndex(indexFile).filter((session) => session.token !== cleanToken);
1705
+ writeIndex(indexFile, [...previous.slice(-199), entry]);
1706
+ return entry;
1707
+ }
1708
+ function readGitHubApiSession(input) {
1709
+ const cleanToken = cleanString4(input.token);
1710
+ if (!cleanToken)
1711
+ return null;
1712
+ return readIndex(resolveGitHubApiSessionIndexFile(input.projectRoot)).find((entry) => entry.token === cleanToken) ?? null;
1713
+ }
1714
+
1492
1715
  // packages/server/src/server-helpers/project-registry.ts
1493
1716
  import { createHash as createHash2 } from "crypto";
1494
1717
  import { spawnSync } from "child_process";
1495
- import { existsSync as existsSync6, mkdirSync as mkdirSync5, readFileSync as readFileSync4, readdirSync, writeFileSync as writeFileSync5 } from "fs";
1496
- import { dirname as dirname4, resolve as resolve9 } from "path";
1718
+ import { existsSync as existsSync8, mkdirSync as mkdirSync7, readFileSync as readFileSync6, readdirSync, writeFileSync as writeFileSync7 } from "fs";
1719
+ import { dirname as dirname7, resolve as resolve11 } from "path";
1497
1720
  function normalizeRepoSlug(value) {
1498
1721
  const trimmed = value.trim();
1499
1722
  return /^[^/\s]+\/[^/\s]+$/.test(trimmed) ? trimmed : null;
1500
1723
  }
1501
1724
  function registryPath(projectRoot) {
1502
- return resolve9(projectRoot, ".rig", "state", "projects.json");
1725
+ return resolve11(projectRoot, ".rig", "state", "projects.json");
1503
1726
  }
1504
1727
  function readRegistry(projectRoot) {
1505
1728
  const path = registryPath(projectRoot);
1506
- if (!existsSync6(path))
1729
+ if (!existsSync8(path))
1507
1730
  return {};
1508
1731
  try {
1509
- const payload = JSON.parse(readFileSync4(path, "utf8"));
1732
+ const payload = JSON.parse(readFileSync6(path, "utf8"));
1510
1733
  if (!payload || typeof payload !== "object" || Array.isArray(payload))
1511
1734
  return {};
1512
1735
  const projects = payload.projects;
@@ -1517,14 +1740,14 @@ function readRegistry(projectRoot) {
1517
1740
  }
1518
1741
  function writeRegistry(projectRoot, projects) {
1519
1742
  const path = registryPath(projectRoot);
1520
- mkdirSync5(dirname4(path), { recursive: true });
1521
- writeFileSync5(path, `${JSON.stringify({ projects }, null, 2)}
1743
+ mkdirSync7(dirname7(path), { recursive: true });
1744
+ writeFileSync7(path, `${JSON.stringify({ projects }, null, 2)}
1522
1745
  `, "utf8");
1523
1746
  }
1524
1747
  function resolveConfigPath(projectRoot) {
1525
1748
  for (const name of ["rig.config.ts", "rig.config.mts", "rig.config.json"]) {
1526
- const path = resolve9(projectRoot, name);
1527
- if (existsSync6(path))
1749
+ const path = resolve11(projectRoot, name);
1750
+ if (existsSync8(path))
1528
1751
  return path;
1529
1752
  }
1530
1753
  return null;
@@ -1533,7 +1756,7 @@ function hashFile(path) {
1533
1756
  if (!path)
1534
1757
  return null;
1535
1758
  try {
1536
- return createHash2("sha256").update(readFileSync4(path)).digest("hex");
1759
+ return createHash2("sha256").update(readFileSync6(path)).digest("hex");
1537
1760
  } catch {
1538
1761
  return null;
1539
1762
  }
@@ -1549,11 +1772,11 @@ function readDefaultBranch(projectRoot) {
1549
1772
  return head.status === 0 && head.stdout.trim() && head.stdout.trim() !== "HEAD" ? head.stdout.trim() : null;
1550
1773
  }
1551
1774
  function buildRunSummary(projectRoot) {
1552
- const runsDir = resolve9(projectRoot, ".rig", "runs");
1775
+ const runsDir = resolve11(projectRoot, ".rig", "runs");
1553
1776
  try {
1554
1777
  const runs = readdirSync(runsDir, { withFileTypes: true }).filter((entry) => entry.isDirectory()).flatMap((entry) => {
1555
1778
  try {
1556
- const run = JSON.parse(readFileSync4(resolve9(runsDir, entry.name, "run.json"), "utf8"));
1779
+ const run = JSON.parse(readFileSync6(resolve11(runsDir, entry.name, "run.json"), "utf8"));
1557
1780
  return [{ runId: typeof run.runId === "string" ? run.runId : entry.name, status: typeof run.status === "string" ? run.status : "unknown", updatedAt: typeof run.updatedAt === "string" ? run.updatedAt : "" }];
1558
1781
  } catch {
1559
1782
  return [];
@@ -1605,10 +1828,14 @@ function upsertProjectRecord(projectRoot, input) {
1605
1828
  function linkProjectCheckout(projectRoot, repoSlug, checkout) {
1606
1829
  return upsertProjectRecord(projectRoot, { repoSlug, checkout });
1607
1830
  }
1831
+ function projectRegistryContainsCheckout(projectRoot, checkoutPath) {
1832
+ const target = resolve11(checkoutPath);
1833
+ return Object.values(readRegistry(projectRoot)).some((project) => project.checkouts.some((checkout) => checkout.path ? resolve11(checkout.path) === target : false));
1834
+ }
1608
1835
 
1609
1836
  // packages/server/src/server-helpers/remote-checkout.ts
1610
- import { existsSync as existsSync7, mkdirSync as mkdirSync6, writeFileSync as writeFileSync6 } from "fs";
1611
- import { dirname as dirname5, isAbsolute, relative, resolve as resolve10 } from "path";
1837
+ import { existsSync as existsSync9, mkdirSync as mkdirSync8, writeFileSync as writeFileSync8 } from "fs";
1838
+ import { dirname as dirname8, isAbsolute as isAbsolute2, relative as relative2, resolve as resolve12 } from "path";
1612
1839
  function safeSlugSegments(repoSlug) {
1613
1840
  const segments = repoSlug.split("/").map((part) => part.trim()).filter(Boolean);
1614
1841
  if (segments.length !== 2 || segments.some((segment) => segment === "." || segment === ".." || segment.includes("\\"))) {
@@ -1625,7 +1852,7 @@ function safeCheckoutKey(value) {
1625
1852
  }
1626
1853
  function repoSlugPath(baseDir, repoSlug, checkoutKey) {
1627
1854
  const key = safeCheckoutKey(checkoutKey);
1628
- return resolve10(baseDir, ...key ? [key] : [], ...safeSlugSegments(repoSlug));
1855
+ return resolve12(baseDir, ...key ? [key] : [], ...safeSlugSegments(repoSlug));
1629
1856
  }
1630
1857
  function sanitizeSnapshotId(value, fallback) {
1631
1858
  const raw = (value ?? fallback).trim();
@@ -1633,7 +1860,7 @@ function sanitizeSnapshotId(value, fallback) {
1633
1860
  return safe || fallback;
1634
1861
  }
1635
1862
  function assertWithinRoot(root, relativePath) {
1636
- if (!relativePath || isAbsolute(relativePath) || relativePath.includes("\x00")) {
1863
+ if (!relativePath || isAbsolute2(relativePath) || relativePath.includes("\x00")) {
1637
1864
  throw new Error(`Invalid snapshot file path: ${relativePath}`);
1638
1865
  }
1639
1866
  const normalizedRelative = relativePath.replace(/\\/g, "/");
@@ -1641,9 +1868,9 @@ function assertWithinRoot(root, relativePath) {
1641
1868
  if (segments.some((segment) => segment === "" || segment === "." || segment === "..")) {
1642
1869
  throw new Error(`Unsafe snapshot file path: ${relativePath}`);
1643
1870
  }
1644
- const target = resolve10(root, ...segments);
1645
- const rel = relative(root, target);
1646
- if (rel === ".." || rel.split(/[\\/]/)[0] === ".." || isAbsolute(rel)) {
1871
+ const target = resolve12(root, ...segments);
1872
+ const rel = relative2(root, target);
1873
+ if (rel === ".." || rel.split(/[\\/]/)[0] === ".." || isAbsolute2(rel)) {
1647
1874
  throw new Error(`Snapshot file path escapes checkout root: ${relativePath}`);
1648
1875
  }
1649
1876
  return target;
@@ -1661,17 +1888,17 @@ function parseSnapshotArchiveContentBase64(contentBase64) {
1661
1888
  function extractUploadedSnapshotArchive(input) {
1662
1889
  const archive = decodeSnapshotArchive(input.archive);
1663
1890
  const snapshotId = sanitizeSnapshotId(input.snapshotId, `snapshot-${(input.now?.() ?? new Date).toISOString().replace(/[:.]/g, "-")}`);
1664
- const checkoutPath = resolve10(repoSlugPath(input.baseDir, input.repoSlug, input.checkoutKey), snapshotId);
1665
- mkdirSync6(checkoutPath, { recursive: true });
1891
+ const checkoutPath = resolve12(repoSlugPath(input.baseDir, input.repoSlug, input.checkoutKey), snapshotId);
1892
+ mkdirSync8(checkoutPath, { recursive: true });
1666
1893
  for (const file of archive.files) {
1667
1894
  if (!file || typeof file.path !== "string" || typeof file.contentBase64 !== "string") {
1668
1895
  throw new Error("Invalid snapshot archive file entry");
1669
1896
  }
1670
1897
  const target = assertWithinRoot(checkoutPath, file.path);
1671
- mkdirSync6(dirname5(target), { recursive: true });
1672
- writeFileSync6(target, Buffer.from(file.contentBase64, "base64"));
1898
+ mkdirSync8(dirname8(target), { recursive: true });
1899
+ writeFileSync8(target, Buffer.from(file.contentBase64, "base64"));
1673
1900
  }
1674
- writeFileSync6(resolve10(checkoutPath, ".rig-uploaded-snapshot.json"), `${JSON.stringify({
1901
+ writeFileSync8(resolve12(checkoutPath, ".rig-uploaded-snapshot.json"), `${JSON.stringify({
1675
1902
  repoSlug: input.repoSlug,
1676
1903
  snapshotId,
1677
1904
  fileCount: archive.files.length,
@@ -1706,7 +1933,7 @@ function gitCredentialConfig(token) {
1706
1933
  };
1707
1934
  }
1708
1935
  async function prepareRemoteCheckout(input) {
1709
- const exists = input.exists ?? existsSync7;
1936
+ const exists = input.exists ?? existsSync9;
1710
1937
  const strategy = input.strategy;
1711
1938
  if (strategy.kind === "uploaded-snapshot") {
1712
1939
  return extractUploadedSnapshotArchive({
@@ -1718,7 +1945,7 @@ async function prepareRemoteCheckout(input) {
1718
1945
  });
1719
1946
  }
1720
1947
  if (strategy.kind === "existing-path") {
1721
- const checkoutPath2 = resolve10(strategy.path);
1948
+ const checkoutPath2 = resolve12(strategy.path);
1722
1949
  if (!exists(checkoutPath2)) {
1723
1950
  throw new Error(`Existing remote checkout path does not exist: ${checkoutPath2}`);
1724
1951
  }
@@ -1754,9 +1981,9 @@ function buildServerControlStatus() {
1754
1981
  };
1755
1982
  }
1756
1983
  function buildProjectConfigStatus(root) {
1757
- const hasConfigTs = existsSync8(resolve11(root, "rig.config.ts"));
1758
- const hasConfigJson = existsSync8(resolve11(root, "rig.config.json"));
1759
- const hasLegacyTaskConfig = existsSync8(resolve11(root, ".rig", "task-config.json"));
1984
+ const hasConfigTs = existsSync10(resolve13(root, "rig.config.ts"));
1985
+ const hasConfigJson = existsSync10(resolve13(root, "rig.config.json"));
1986
+ const hasLegacyTaskConfig = existsSync10(resolve13(root, ".rig", "task-config.json"));
1760
1987
  let kind = "missing";
1761
1988
  if (hasConfigTs)
1762
1989
  kind = "rig-config-ts";
@@ -1792,24 +2019,24 @@ function repoParts(repoSlug) {
1792
2019
  return { owner, repo, slug: `${owner}/${repo}` };
1793
2020
  }
1794
2021
  function repairDir(checkoutPath) {
1795
- const dir = resolve11(checkoutPath, ".rig", "state", "repairs", new Date().toISOString().replace(/[:.]/g, "-"));
1796
- mkdirSync7(dir, { recursive: true });
2022
+ const dir = resolve13(checkoutPath, ".rig", "state", "repairs", new Date().toISOString().replace(/[:.]/g, "-"));
2023
+ mkdirSync9(dir, { recursive: true });
1797
2024
  return dir;
1798
2025
  }
1799
2026
  function backupCheckoutFile(checkoutPath, relativePath) {
1800
- const source = resolve11(checkoutPath, relativePath);
1801
- const backupPath = resolve11(repairDir(checkoutPath), relativePath.replace(/[\\/]/g, "__"));
1802
- mkdirSync7(dirname6(backupPath), { recursive: true });
1803
- copyFileSync(source, backupPath);
2027
+ const source = resolve13(checkoutPath, relativePath);
2028
+ const backupPath = resolve13(repairDir(checkoutPath), relativePath.replace(/[\\/]/g, "__"));
2029
+ mkdirSync9(dirname9(backupPath), { recursive: true });
2030
+ copyFileSync2(source, backupPath);
1804
2031
  return backupPath;
1805
2032
  }
1806
2033
  function parsePackageJsonLosslessly(checkoutPath) {
1807
- const packagePath = resolve11(checkoutPath, "package.json");
1808
- if (!existsSync8(packagePath)) {
2034
+ const packagePath = resolve13(checkoutPath, "package.json");
2035
+ if (!existsSync10(packagePath)) {
1809
2036
  return { existed: false, packageJson: { name: basename(checkoutPath) || "rig-project", private: true } };
1810
2037
  }
1811
2038
  try {
1812
- const parsed = JSON.parse(readFileSync5(packagePath, "utf8"));
2039
+ const parsed = JSON.parse(readFileSync7(packagePath, "utf8"));
1813
2040
  return {
1814
2041
  existed: true,
1815
2042
  packageJson: parsed && typeof parsed === "object" && !Array.isArray(parsed) ? parsed : { name: basename(checkoutPath) || "rig-project", private: true }
@@ -1823,9 +2050,9 @@ function parsePackageJsonLosslessly(checkoutPath) {
1823
2050
  }
1824
2051
  }
1825
2052
  function ensureRemoteCheckoutRigPackageDeps(checkoutPath) {
1826
- const hasConfig = ["rig.config.ts", "rig.config.mts", "rig.config.json"].some((name) => existsSync8(resolve11(checkoutPath, name)));
1827
- const packagePath = resolve11(checkoutPath, "package.json");
1828
- if (!hasConfig && !existsSync8(packagePath)) {
2053
+ const hasConfig = ["rig.config.ts", "rig.config.mts", "rig.config.json"].some((name) => existsSync10(resolve13(checkoutPath, name)));
2054
+ const packagePath = resolve13(checkoutPath, "package.json");
2055
+ if (!hasConfig && !existsSync10(packagePath)) {
1829
2056
  return { skipped: true, reason: "package.json and rig.config missing" };
1830
2057
  }
1831
2058
  const parsed = parsePackageJsonLosslessly(checkoutPath);
@@ -1844,7 +2071,7 @@ function ensureRemoteCheckoutRigPackageDeps(checkoutPath) {
1844
2071
  }
1845
2072
  const changed = !parsed.existed || Boolean(parsed.backupPath) || added.length > 0 || updated.length > 0 || existingDevDependencies !== devDependencies && (!existingDevDependencies || typeof existingDevDependencies !== "object" || Array.isArray(existingDevDependencies));
1846
2073
  if (changed) {
1847
- writeFileSync7(packagePath, `${JSON.stringify({ ...parsed.packageJson, devDependencies }, null, 2)}
2074
+ writeFileSync9(packagePath, `${JSON.stringify({ ...parsed.packageJson, devDependencies }, null, 2)}
1848
2075
  `, "utf8");
1849
2076
  }
1850
2077
  return {
@@ -1870,11 +2097,11 @@ function configLooksStructurallyUsable(source) {
1870
2097
  return /taskSource\s*:/.test(source) && /workspace\s*:/.test(source) && /project\s*:/.test(source);
1871
2098
  }
1872
2099
  function ensureRemoteCheckoutRigConfig(checkoutPath, repoSlug, reason = "missing or incomplete rig config") {
1873
- const configPath = resolve11(checkoutPath, "rig.config.ts");
1874
- const existingConfigName = ["rig.config.ts", "rig.config.mts", "rig.config.json"].find((name) => existsSync8(resolve11(checkoutPath, name)));
2100
+ const configPath = resolve13(checkoutPath, "rig.config.ts");
2101
+ const existingConfigName = ["rig.config.ts", "rig.config.mts", "rig.config.json"].find((name) => existsSync10(resolve13(checkoutPath, name)));
1875
2102
  if (existingConfigName) {
1876
- const existingPath = resolve11(checkoutPath, existingConfigName);
1877
- const source = readFileSync5(existingPath, "utf8");
2103
+ const existingPath = resolve13(checkoutPath, existingConfigName);
2104
+ const source = readFileSync7(existingPath, "utf8");
1878
2105
  if (existingConfigName !== "rig.config.json" && configLooksStructurallyUsable(source)) {
1879
2106
  return { path: existingPath, changed: false, reason: "config structurally complete" };
1880
2107
  }
@@ -1888,7 +2115,7 @@ function ensureRemoteCheckoutRigConfig(checkoutPath, repoSlug, reason = "missing
1888
2115
  }
1889
2116
  }
1890
2117
  const backupPath = existingConfigName ? backupCheckoutFile(checkoutPath, existingConfigName) : undefined;
1891
- writeFileSync7(configPath, generatedRigConfigSource(repoSlug), "utf8");
2118
+ writeFileSync9(configPath, generatedRigConfigSource(repoSlug), "utf8");
1892
2119
  return {
1893
2120
  path: configPath,
1894
2121
  changed: true,
@@ -1897,7 +2124,7 @@ function ensureRemoteCheckoutRigConfig(checkoutPath, repoSlug, reason = "missing
1897
2124
  };
1898
2125
  }
1899
2126
  function validateRemoteCheckoutRigConfig(checkoutPath) {
1900
- const configFile = ["rig.config.ts", "rig.config.mts", "rig.config.json"].find((name) => existsSync8(resolve11(checkoutPath, name)));
2127
+ const configFile = ["rig.config.ts", "rig.config.mts", "rig.config.json"].find((name) => existsSync10(resolve13(checkoutPath, name)));
1901
2128
  if (!configFile)
1902
2129
  return { ok: false, error: "missing rig config" };
1903
2130
  if (process.env.RIG_TEST_SKIP_REMOTE_CHECKOUT_INSTALL === "1") {
@@ -1919,7 +2146,7 @@ function validateRemoteCheckoutRigConfig(checkoutPath) {
1919
2146
  return { ok: true, configFile };
1920
2147
  }
1921
2148
  function installRemoteCheckoutPackages(checkoutPath) {
1922
- if (!existsSync8(resolve11(checkoutPath, "package.json"))) {
2149
+ if (!existsSync10(resolve13(checkoutPath, "package.json"))) {
1923
2150
  return { skipped: true, reason: "package.json missing" };
1924
2151
  }
1925
2152
  if (process.env.RIG_TEST_SKIP_REMOTE_CHECKOUT_INSTALL === "1") {
@@ -1932,8 +2159,8 @@ function installRemoteCheckoutPackages(checkoutPath) {
1932
2159
  return { ok: true, command: "bun install", stdout: result.stdout?.trim() || undefined };
1933
2160
  }
1934
2161
  function repairRemoteCheckoutForRig(checkoutPath, repoSlug) {
1935
- const hasConfig = ["rig.config.ts", "rig.config.mts", "rig.config.json"].some((name) => existsSync8(resolve11(checkoutPath, name)));
1936
- const hasPackage = existsSync8(resolve11(checkoutPath, "package.json"));
2162
+ const hasConfig = ["rig.config.ts", "rig.config.mts", "rig.config.json"].some((name) => existsSync10(resolve13(checkoutPath, name)));
2163
+ const hasPackage = existsSync10(resolve13(checkoutPath, "package.json"));
1937
2164
  if (!hasConfig && !hasPackage) {
1938
2165
  return {
1939
2166
  packageJson: { skipped: true, reason: "package.json and rig.config missing" },
@@ -2031,26 +2258,26 @@ function buildRemoteRunLogEntry(body, identifiers) {
2031
2258
  }
2032
2259
  function readGitHeadCommit(projectRoot) {
2033
2260
  try {
2034
- let gitDir = resolve11(projectRoot, ".git");
2261
+ let gitDir = resolve13(projectRoot, ".git");
2035
2262
  try {
2036
- const dotGit = readFileSync5(gitDir, "utf8").trim();
2263
+ const dotGit = readFileSync7(gitDir, "utf8").trim();
2037
2264
  const gitDirPrefix = "gitdir:";
2038
2265
  if (dotGit.startsWith(gitDirPrefix)) {
2039
- gitDir = resolve11(projectRoot, dotGit.slice(gitDirPrefix.length).trim());
2266
+ gitDir = resolve13(projectRoot, dotGit.slice(gitDirPrefix.length).trim());
2040
2267
  }
2041
2268
  } catch {}
2042
- const head = readFileSync5(resolve11(gitDir, "HEAD"), "utf8").trim();
2269
+ const head = readFileSync7(resolve13(gitDir, "HEAD"), "utf8").trim();
2043
2270
  const refPrefix = "ref:";
2044
2271
  if (!head.startsWith(refPrefix)) {
2045
2272
  return normalizeCommit(head);
2046
2273
  }
2047
2274
  const ref = head.slice(refPrefix.length).trim();
2048
- const refPath = resolve11(gitDir, ref);
2049
- if (existsSync8(refPath)) {
2050
- return normalizeCommit(readFileSync5(refPath, "utf8").trim());
2275
+ const refPath = resolve13(gitDir, ref);
2276
+ if (existsSync10(refPath)) {
2277
+ return normalizeCommit(readFileSync7(refPath, "utf8").trim());
2051
2278
  }
2052
- const commonDir = normalizeString(readFileSync5(resolve11(gitDir, "commondir"), "utf8"));
2053
- return commonDir ? normalizeCommit(readFileSync5(resolve11(gitDir, commonDir, ref), "utf8").trim()) : null;
2279
+ const commonDir = normalizeString(readFileSync7(resolve13(gitDir, "commondir"), "utf8"));
2280
+ return commonDir ? normalizeCommit(readFileSync7(resolve13(gitDir, commonDir, ref), "utf8").trim()) : null;
2054
2281
  } catch {
2055
2282
  return null;
2056
2283
  }
@@ -2088,9 +2315,9 @@ function configuredRepoFromTaskSource(taskSource) {
2088
2315
  const repo = normalizeString(taskSource?.repo);
2089
2316
  return owner && repo ? `${owner}/${repo}` : null;
2090
2317
  }
2091
- async function buildTaskSourceStatus(state, config) {
2318
+ async function buildTaskSourceStatus(state, config, requestAuth) {
2092
2319
  const diagnostics = state.snapshotService.getTaskSourceErrors();
2093
- const selectedRepo = createGitHubAuthStore(state.projectRoot).status({
2320
+ const selectedRepo = requestScopedAuthStore(state.projectRoot, requestAuth).status({
2094
2321
  oauthConfigured: Boolean(process.env.RIG_GITHUB_OAUTH_CLIENT_ID?.trim())
2095
2322
  }).selectedRepo;
2096
2323
  try {
@@ -2188,31 +2415,83 @@ function normalizePrMode(value) {
2188
2415
  const mode = normalizeString(value);
2189
2416
  return mode === "auto" || mode === "ask" || mode === "off" ? mode : undefined;
2190
2417
  }
2418
+ function requestAuthResult(input) {
2419
+ return {
2420
+ authorized: input.authorized,
2421
+ actor: input.actor ?? null,
2422
+ reason: input.reason,
2423
+ login: input.login ?? null,
2424
+ userId: input.userId ?? null,
2425
+ userNamespace: input.userNamespace ?? null,
2426
+ authStateFile: input.authStateFile ?? null
2427
+ };
2428
+ }
2429
+ function namespaceFromSessionIndex(entry) {
2430
+ const stateDir2 = dirname9(entry.authStateFile);
2431
+ return {
2432
+ key: entry.namespaceKey,
2433
+ userId: entry.userId,
2434
+ login: entry.login,
2435
+ root: entry.namespaceRoot,
2436
+ stateDir: stateDir2,
2437
+ authStateFile: entry.authStateFile,
2438
+ metadataFile: resolve13(stateDir2, "user-namespace.json"),
2439
+ checkoutBaseDir: entry.checkoutBaseDir,
2440
+ snapshotBaseDir: entry.snapshotBaseDir
2441
+ };
2442
+ }
2191
2443
  function authorizeRigHttpRequest(input) {
2192
2444
  if (input.legacyAuthorized) {
2193
- return { authorized: true, actor: "rig-local-server", reason: "server-token" };
2445
+ return requestAuthResult({ authorized: true, actor: "rig-local-server", reason: "server-token" });
2194
2446
  }
2195
2447
  const bearer = bearerTokenFromRequest(input.req);
2196
2448
  const store = createGitHubAuthStore(input.projectRoot);
2197
2449
  const storedToken = store.readToken();
2198
2450
  const session = bearer ? store.readApiSession(bearer) : null;
2199
2451
  if (session) {
2200
- return { authorized: true, actor: session.login ?? "github-operator", reason: "github-session" };
2452
+ return requestAuthResult({
2453
+ authorized: true,
2454
+ actor: session.login ?? "github-operator",
2455
+ reason: "github-session",
2456
+ login: session.login,
2457
+ userId: session.userId,
2458
+ authStateFile: store.stateFile
2459
+ });
2201
2460
  }
2202
2461
  if (bearer && storedToken && bearer === storedToken) {
2203
2462
  const status = store.status({ oauthConfigured: Boolean(process.env.RIG_GITHUB_OAUTH_CLIENT_ID?.trim()) });
2204
- return { authorized: true, actor: status.login ?? "github-operator", reason: "github-token" };
2463
+ return requestAuthResult({
2464
+ authorized: true,
2465
+ actor: status.login ?? "github-operator",
2466
+ reason: "github-token",
2467
+ login: status.login,
2468
+ userId: status.userId,
2469
+ authStateFile: store.stateFile
2470
+ });
2471
+ }
2472
+ const indexedSession = readGitHubApiSession({ projectRoot: input.projectRoot, token: bearer });
2473
+ if (indexedSession) {
2474
+ const userNamespace = namespaceFromSessionIndex(indexedSession);
2475
+ return requestAuthResult({
2476
+ authorized: true,
2477
+ actor: indexedSession.login ?? "github-operator",
2478
+ reason: "github-user-session",
2479
+ login: indexedSession.login,
2480
+ userId: indexedSession.userId,
2481
+ userNamespace,
2482
+ authStateFile: indexedSession.authStateFile
2483
+ });
2205
2484
  }
2206
2485
  if (isPublicRigAuthBootstrapRoute(input.pathname)) {
2207
- return { authorized: true, actor: null, reason: "public-bootstrap" };
2486
+ return requestAuthResult({ authorized: true, actor: null, reason: "public-bootstrap" });
2208
2487
  }
2209
2488
  if (!input.serverAuthToken && !storedToken) {
2210
2489
  if (isLoopbackRequest(input.req)) {
2211
- return { authorized: true, actor: null, reason: "loopback-dev-no-auth" };
2490
+ return requestAuthResult({ authorized: true, actor: null, reason: "loopback-dev-no-auth" });
2212
2491
  }
2213
- return { authorized: false, actor: null, reason: "auth-required" };
2492
+ return requestAuthResult({ authorized: false, actor: null, reason: "auth-required" });
2214
2493
  }
2215
- return { authorized: false, actor: null, reason: storedToken ? "github-token-required" : "auth-required" };
2494
+ return requestAuthResult({ authorized: false, actor: null, reason: storedToken ? "github-token-required" : "auth-required" });
2216
2495
  }
2217
2496
  async function fetchGitHubUserInfo(token) {
2218
2497
  const response = await fetch("https://api.github.com/user", {
@@ -2232,6 +2511,67 @@ async function fetchGitHubUserInfo(token) {
2232
2511
  scopes: cleanHeaderScopes(response.headers.get("x-oauth-scopes"))
2233
2512
  };
2234
2513
  }
2514
+ function shouldWriteRootAuthCompat(projectRoot) {
2515
+ if (process.env.RIG_REMOTE_USER_NAMESPACE_ROOT?.trim())
2516
+ return false;
2517
+ const stateDir2 = normalizeString(process.env.RIG_STATE_DIR);
2518
+ if (!stateDir2)
2519
+ return true;
2520
+ return resolve13(stateDir2) === resolve13(projectRoot, ".rig", "state");
2521
+ }
2522
+ function requestScopedRegistryRoot(stateProjectRoot, requestAuth) {
2523
+ return requestAuth.userNamespace?.root ?? stateProjectRoot;
2524
+ }
2525
+ function requestScopedAuthStore(stateProjectRoot, requestAuth) {
2526
+ return requestAuth.authStateFile ? createGitHubAuthStoreFromStateFile(requestAuth.authStateFile) : createGitHubAuthStore(stateProjectRoot);
2527
+ }
2528
+ function userNamespaceResponse(namespace) {
2529
+ return namespace ? serializeRemoteUserNamespace(namespace) : undefined;
2530
+ }
2531
+ function resolveNamespacedBaseDir(input) {
2532
+ if (input.explicitBaseDir)
2533
+ return input.explicitBaseDir;
2534
+ const envBase = normalizeString(process.env[input.envName]);
2535
+ if (input.userNamespace) {
2536
+ return envBase ? resolve13(envBase, input.userNamespace.key) : input.userNamespace[input.legacySubdir === "remote-checkouts" ? "checkoutBaseDir" : "snapshotBaseDir"];
2537
+ }
2538
+ return envBase ?? (normalizeString(process.env.RIG_STATE_DIR) ? resolve13(normalizeString(process.env.RIG_STATE_DIR), input.legacySubdir) : resolve13(input.legacyProjectRoot, ".rig", input.legacySubdir));
2539
+ }
2540
+ function explicitCheckoutKey(body, checkoutInput, requestAuth) {
2541
+ return normalizeString(body.checkoutKey) ?? normalizeString(checkoutInput.checkoutKey) ?? normalizeString(checkoutInput.key) ?? (requestAuth.userNamespace ? undefined : "default");
2542
+ }
2543
+ function saveGitHubTokenForRemoteUser(input) {
2544
+ const namespace = resolveRemoteUserNamespace(input.projectRoot, { userId: input.user.userId, login: input.user.login });
2545
+ writeRemoteUserNamespaceMetadata(namespace);
2546
+ const store = createGitHubAuthStoreFromStateFile(namespace.authStateFile);
2547
+ store.saveToken({
2548
+ token: input.token,
2549
+ tokenSource: input.tokenSource,
2550
+ login: input.user.login,
2551
+ userId: input.user.userId,
2552
+ scopes: input.user.scopes,
2553
+ selectedRepo: input.selectedRepo
2554
+ });
2555
+ const apiSession = store.createApiSession();
2556
+ registerGitHubApiSession({ projectRoot: input.projectRoot, token: apiSession.token, namespace, selectedRepo: input.selectedRepo });
2557
+ const requestedRoot = normalizeString(input.requestedProjectRoot);
2558
+ if (requestedRoot && isAbsolute3(requestedRoot) && existsSync10(resolve13(requestedRoot))) {
2559
+ copyGitHubAuthStateToLocalProjectRoot(namespace.authStateFile, resolve13(requestedRoot));
2560
+ }
2561
+ if (shouldWriteRootAuthCompat(input.projectRoot)) {
2562
+ const rootStore = createGitHubAuthStore(input.projectRoot);
2563
+ rootStore.saveToken({
2564
+ token: input.token,
2565
+ tokenSource: input.tokenSource,
2566
+ login: input.user.login,
2567
+ userId: input.user.userId,
2568
+ scopes: input.user.scopes,
2569
+ selectedRepo: input.selectedRepo
2570
+ });
2571
+ rootStore.createApiSession();
2572
+ }
2573
+ return { store, namespace, apiSessionToken: apiSession.token };
2574
+ }
2235
2575
  async function postGitHubForm(endpoint, body) {
2236
2576
  const response = await fetch(endpoint, {
2237
2577
  method: "POST",
@@ -2249,11 +2589,11 @@ function resolveRequestedProjectRoot(currentRoot, rawRoot) {
2249
2589
  const requestedRoot = normalizeString(rawRoot);
2250
2590
  if (!requestedRoot)
2251
2591
  return currentRoot;
2252
- if (!isAbsolute2(requestedRoot)) {
2592
+ if (!isAbsolute3(requestedRoot)) {
2253
2593
  throw new Error("projectRoot must be an absolute path on the Rig server host");
2254
2594
  }
2255
- const normalizedRoot = resolve11(requestedRoot);
2256
- if (!existsSync8(normalizedRoot)) {
2595
+ const normalizedRoot = resolve13(requestedRoot);
2596
+ if (!existsSync10(normalizedRoot)) {
2257
2597
  throw new Error("projectRoot does not exist on the Rig server host");
2258
2598
  }
2259
2599
  return normalizedRoot;
@@ -2914,7 +3254,7 @@ data: ${JSON.stringify({ connectedAt: new Date().toISOString() })}
2914
3254
  }
2915
3255
  if (url.pathname === "/api/server/status") {
2916
3256
  const config = buildProjectConfigStatus(state.projectRoot);
2917
- const taskSource = await buildTaskSourceStatus(state, config);
3257
+ const taskSource = await buildTaskSourceStatus(state, config, requestAuth);
2918
3258
  return deps.jsonResponse({
2919
3259
  ok: true,
2920
3260
  projectRoot: state.projectRoot,
@@ -2938,8 +3278,9 @@ data: ${JSON.stringify({ connectedAt: new Date().toISOString() })}
2938
3278
  path: normalizeString(rawCheckout?.path) ?? state.projectRoot,
2939
3279
  ref: normalizeString(rawCheckout?.ref) ?? undefined
2940
3280
  } : undefined;
2941
- const record = upsertProjectRecord(state.projectRoot, { repoSlug, checkout });
2942
- return deps.jsonResponse({ ok: true, project: record });
3281
+ const registryRoot = requestScopedRegistryRoot(state.projectRoot, requestAuth);
3282
+ const record = upsertProjectRecord(registryRoot, { repoSlug, checkout });
3283
+ return deps.jsonResponse({ ok: true, project: record, userNamespace: userNamespaceResponse(requestAuth.userNamespace) });
2943
3284
  }
2944
3285
  const snapshotUploadMatch = url.pathname.match(/^\/api\/projects\/(.+?)\/upload-snapshot$/);
2945
3286
  if (snapshotUploadMatch && req.method === "POST") {
@@ -2952,8 +3293,15 @@ data: ${JSON.stringify({ connectedAt: new Date().toISOString() })}
2952
3293
  if (!archiveContentBase64) {
2953
3294
  return deps.badRequest("archiveContentBase64 is required");
2954
3295
  }
2955
- const baseDir = normalizeString(body.baseDir) ?? normalizeString(process.env.RIG_REMOTE_SNAPSHOT_BASE_DIR) ?? (normalizeString(process.env.RIG_STATE_DIR) ? resolve11(normalizeString(process.env.RIG_STATE_DIR), "remote-snapshots") : resolve11(state.projectRoot, ".rig", "remote-snapshots"));
2956
- const checkoutKey = normalizeString(body.checkoutKey) ?? "default";
3296
+ const registryRoot = requestScopedRegistryRoot(state.projectRoot, requestAuth);
3297
+ const baseDir = resolveNamespacedBaseDir({
3298
+ explicitBaseDir: normalizeString(body.baseDir),
3299
+ envName: "RIG_REMOTE_SNAPSHOT_BASE_DIR",
3300
+ userNamespace: requestAuth.userNamespace,
3301
+ legacyProjectRoot: state.projectRoot,
3302
+ legacySubdir: "remote-snapshots"
3303
+ });
3304
+ const checkoutKey = explicitCheckoutKey(body, body, requestAuth);
2957
3305
  try {
2958
3306
  const archive = parseSnapshotArchiveContentBase64(archiveContentBase64);
2959
3307
  const checkout = extractUploadedSnapshotArchive({
@@ -2966,14 +3314,14 @@ data: ${JSON.stringify({ connectedAt: new Date().toISOString() })}
2966
3314
  const checkoutRepair = repairRemoteCheckoutForRig(checkout.path, repoSlug);
2967
3315
  const packageInstall = installRemoteCheckoutPackages(checkout.path);
2968
3316
  const postInstallConfigValidation = validateRemoteCheckoutRigConfig(checkout.path);
2969
- const project = linkProjectCheckout(state.projectRoot, repoSlug, {
3317
+ const project = linkProjectCheckout(registryRoot, repoSlug, {
2970
3318
  kind: "uploaded-snapshot",
2971
3319
  path: checkout.path,
2972
3320
  ref: checkout.snapshotId
2973
3321
  });
2974
3322
  deps.snapshotService.invalidate("uploaded-snapshot-checkout");
2975
3323
  deps.broadcastSnapshotInvalidation(state, "uploaded-snapshot-checkout");
2976
- return deps.jsonResponse({ ok: true, checkout, project, checkoutRepair, packageInstall, postInstallConfigValidation });
3324
+ return deps.jsonResponse({ ok: true, checkout, project, checkoutRepair, packageInstall, postInstallConfigValidation, userNamespace: userNamespaceResponse(requestAuth.userNamespace) });
2977
3325
  } catch (error) {
2978
3326
  return deps.jsonResponse({
2979
3327
  ok: false,
@@ -2993,10 +3341,17 @@ data: ${JSON.stringify({ connectedAt: new Date().toISOString() })}
2993
3341
  if (kind !== "managed-clone" && kind !== "current-ref" && kind !== "existing-path") {
2994
3342
  return deps.jsonResponse({ ok: false, error: "checkout kind must be managed-clone, current-ref, or existing-path" }, 400);
2995
3343
  }
2996
- const baseDir = normalizeString(body.baseDir) ?? normalizeString(checkoutInput.baseDir) ?? normalizeString(process.env.RIG_REMOTE_CHECKOUT_BASE_DIR) ?? (normalizeString(process.env.RIG_STATE_DIR) ? resolve11(normalizeString(process.env.RIG_STATE_DIR), "remote-checkouts") : resolve11(state.projectRoot, ".rig", "remote-checkouts"));
2997
- const checkoutKey = normalizeString(body.checkoutKey) ?? normalizeString(checkoutInput.checkoutKey) ?? normalizeString(checkoutInput.key) ?? "default";
3344
+ const registryRoot = requestScopedRegistryRoot(state.projectRoot, requestAuth);
3345
+ const baseDir = resolveNamespacedBaseDir({
3346
+ explicitBaseDir: normalizeString(body.baseDir) ?? normalizeString(checkoutInput.baseDir),
3347
+ envName: "RIG_REMOTE_CHECKOUT_BASE_DIR",
3348
+ userNamespace: requestAuth.userNamespace,
3349
+ legacyProjectRoot: state.projectRoot,
3350
+ legacySubdir: "remote-checkouts"
3351
+ });
3352
+ const checkoutKey = explicitCheckoutKey(body, checkoutInput, requestAuth);
2998
3353
  const repoUrl = normalizeString(body.repoUrl) ?? normalizeString(checkoutInput.repoUrl) ?? `https://github.com/${repoSlug}.git`;
2999
- const credentialToken = createGitHubAuthStore(state.projectRoot).readToken();
3354
+ const credentialToken = requestScopedAuthStore(state.projectRoot, requestAuth).readToken();
3000
3355
  try {
3001
3356
  const checkout = await prepareRemoteCheckout({
3002
3357
  command: runRemoteCheckoutCommand,
@@ -3005,14 +3360,14 @@ data: ${JSON.stringify({ connectedAt: new Date().toISOString() })}
3005
3360
  const checkoutRepair = repairRemoteCheckoutForRig(checkout.path, repoSlug);
3006
3361
  const packageInstall = installRemoteCheckoutPackages(checkout.path);
3007
3362
  const postInstallConfigValidation = validateRemoteCheckoutRigConfig(checkout.path);
3008
- const project = linkProjectCheckout(state.projectRoot, repoSlug, {
3363
+ const project = linkProjectCheckout(registryRoot, repoSlug, {
3009
3364
  kind: checkout.kind,
3010
3365
  path: checkout.path,
3011
3366
  ref: checkout.ref ?? checkout.snapshotId ?? undefined
3012
3367
  });
3013
3368
  deps.snapshotService.invalidate("remote-checkout-prepared");
3014
3369
  deps.broadcastSnapshotInvalidation(state, "remote-checkout-prepared");
3015
- return deps.jsonResponse({ ok: true, checkout, project, checkoutRepair, packageInstall, postInstallConfigValidation });
3370
+ return deps.jsonResponse({ ok: true, checkout, project, checkoutRepair, packageInstall, postInstallConfigValidation, userNamespace: userNamespaceResponse(requestAuth.userNamespace) });
3016
3371
  } catch (error) {
3017
3372
  return deps.jsonResponse({ ok: false, error: error instanceof Error ? error.message : String(error) }, 400);
3018
3373
  }
@@ -3029,16 +3384,18 @@ data: ${JSON.stringify({ connectedAt: new Date().toISOString() })}
3029
3384
  if (kind !== "local" && kind !== "managed-clone" && kind !== "current-ref" && kind !== "uploaded-snapshot" && kind !== "existing-path") {
3030
3385
  return deps.jsonResponse({ ok: false, error: "checkout kind is required" }, 400);
3031
3386
  }
3032
- const project = linkProjectCheckout(state.projectRoot, repoSlug, {
3387
+ const registryRoot = requestScopedRegistryRoot(state.projectRoot, requestAuth);
3388
+ const project = linkProjectCheckout(registryRoot, repoSlug, {
3033
3389
  kind,
3034
3390
  path: normalizeString(body.path) ?? state.projectRoot,
3035
3391
  ref: normalizeString(body.ref) ?? undefined
3036
3392
  });
3037
- return deps.jsonResponse({ ok: true, project });
3393
+ return deps.jsonResponse({ ok: true, project, userNamespace: userNamespaceResponse(requestAuth.userNamespace) });
3038
3394
  }
3039
3395
  if (req.method === "GET") {
3040
- const project = getProjectRecord(state.projectRoot, repoSlug);
3041
- return project ? deps.jsonResponse({ ok: true, project }) : deps.notFound();
3396
+ const registryRoot = requestScopedRegistryRoot(state.projectRoot, requestAuth);
3397
+ const project = getProjectRecord(registryRoot, repoSlug);
3398
+ return project ? deps.jsonResponse({ ok: true, project, userNamespace: userNamespaceResponse(requestAuth.userNamespace) }) : deps.notFound();
3042
3399
  }
3043
3400
  }
3044
3401
  if (url.pathname === "/api/server/project-root" && req.method === "POST") {
@@ -3047,13 +3404,26 @@ data: ${JSON.stringify({ connectedAt: new Date().toISOString() })}
3047
3404
  if (!requestedRoot) {
3048
3405
  return deps.badRequest("projectRoot is required");
3049
3406
  }
3050
- if (!isAbsolute2(requestedRoot)) {
3407
+ if (!isAbsolute3(requestedRoot)) {
3051
3408
  return deps.badRequest("projectRoot must be an absolute path on the Rig server host");
3052
3409
  }
3053
- const normalizedRoot = resolve11(requestedRoot);
3054
- const exists = existsSync8(normalizedRoot);
3055
- if (exists) {
3056
- createGitHubAuthStore(state.projectRoot).copyToProjectRoot(normalizedRoot);
3410
+ const normalizedRoot = resolve13(requestedRoot);
3411
+ const exists = existsSync10(normalizedRoot);
3412
+ if (exists && requestAuth.userNamespace) {
3413
+ const allowedByNamespace = isPathInsideNamespace(requestAuth.userNamespace.root, normalizedRoot);
3414
+ const allowedByRegistry = projectRegistryContainsCheckout(requestAuth.userNamespace.root, normalizedRoot);
3415
+ if (!allowedByNamespace && !allowedByRegistry) {
3416
+ return deps.jsonResponse({
3417
+ ok: false,
3418
+ error: "Requested project root is outside the authenticated GitHub user namespace.",
3419
+ projectRoot: state.projectRoot,
3420
+ requestedProjectRoot: normalizedRoot,
3421
+ userNamespace: userNamespaceResponse(requestAuth.userNamespace)
3422
+ }, 403);
3423
+ }
3424
+ copyGitHubAuthStateToLocalProjectRoot(requestAuth.userNamespace.authStateFile, normalizedRoot);
3425
+ } else if (exists) {
3426
+ createGitHubAuthStore(state.projectRoot).copyToLocalProjectRoot(normalizedRoot);
3057
3427
  }
3058
3428
  const control = buildServerControlStatus();
3059
3429
  const switchCommand = process.env.RIG_PROJECT_ROOT_SWITCH_COMMAND?.trim();
@@ -3068,7 +3438,7 @@ data: ${JSON.stringify({ connectedAt: new Date().toISOString() })}
3068
3438
  message: "Requested project root does not exist on the Rig server host."
3069
3439
  }, 404);
3070
3440
  }
3071
- if (!existsSync8(resolve11(normalizedRoot, "rig.config.ts")) && !existsSync8(resolve11(normalizedRoot, "rig.config.json"))) {
3441
+ if (!existsSync10(resolve13(normalizedRoot, "rig.config.ts")) && !existsSync10(resolve13(normalizedRoot, "rig.config.json"))) {
3072
3442
  return deps.jsonResponse({
3073
3443
  ok: false,
3074
3444
  projectRoot: state.projectRoot,
@@ -3103,6 +3473,7 @@ data: ${JSON.stringify({ connectedAt: new Date().toISOString() })}
3103
3473
  exists,
3104
3474
  control,
3105
3475
  requiresRestart: false,
3476
+ userNamespace: userNamespaceResponse(requestAuth.userNamespace),
3106
3477
  message: "Project-root switch accepted. Rig server restart has been scheduled."
3107
3478
  }, 202);
3108
3479
  }
@@ -3117,11 +3488,11 @@ data: ${JSON.stringify({ connectedAt: new Date().toISOString() })}
3117
3488
  }, 409);
3118
3489
  }
3119
3490
  if (url.pathname === "/api/github/auth/status") {
3120
- const store = createGitHubAuthStore(state.projectRoot);
3121
- return deps.jsonResponse({ ok: true, ...store.status({ oauthConfigured: Boolean(process.env.RIG_GITHUB_OAUTH_CLIENT_ID?.trim()) }) });
3491
+ const store = requestScopedAuthStore(state.projectRoot, requestAuth);
3492
+ return deps.jsonResponse({ ok: true, ...store.status({ oauthConfigured: Boolean(process.env.RIG_GITHUB_OAUTH_CLIENT_ID?.trim()) }), userNamespace: userNamespaceResponse(requestAuth.userNamespace) });
3122
3493
  }
3123
3494
  if (url.pathname === "/api/github/repo/permissions") {
3124
- const store = createGitHubAuthStore(state.projectRoot);
3495
+ const store = requestScopedAuthStore(state.projectRoot, requestAuth);
3125
3496
  const auth = store.status({ oauthConfigured: Boolean(process.env.RIG_GITHUB_OAUTH_CLIENT_ID?.trim()) });
3126
3497
  if (!auth.signedIn) {
3127
3498
  return deps.jsonResponse({ ok: false, signedIn: false, canOpenPullRequest: false, reason: "not-authenticated" }, 401);
@@ -3149,24 +3520,20 @@ data: ${JSON.stringify({ connectedAt: new Date().toISOString() })}
3149
3520
  }
3150
3521
  try {
3151
3522
  const user = await fetchGitHubUserInfo(token);
3152
- const storeRoots = [
3153
- state.projectRoot,
3154
- ...requestedProjectRoot && isAbsolute2(requestedProjectRoot) && existsSync8(resolve11(requestedProjectRoot)) ? [resolve11(requestedProjectRoot)] : []
3155
- ].filter((root, index, roots) => roots.indexOf(root) === index);
3156
- const stores = storeRoots.map((root) => createGitHubAuthStore(root));
3157
- for (const store2 of stores) {
3158
- store2.saveToken({
3159
- token,
3160
- tokenSource: "manual-token",
3161
- login: user.login,
3162
- userId: user.userId,
3163
- scopes: user.scopes,
3164
- selectedRepo
3165
- });
3166
- }
3167
- const store = stores[stores.length - 1] ?? createGitHubAuthStore(state.projectRoot);
3168
- const apiSession = store.createApiSession();
3169
- return deps.jsonResponse({ ok: true, ...store.status({ oauthConfigured: Boolean(process.env.RIG_GITHUB_OAUTH_CLIENT_ID?.trim()) }), apiSessionToken: apiSession.token });
3523
+ const saved = saveGitHubTokenForRemoteUser({
3524
+ projectRoot: state.projectRoot,
3525
+ token,
3526
+ tokenSource: "manual-token",
3527
+ user,
3528
+ selectedRepo,
3529
+ requestedProjectRoot
3530
+ });
3531
+ return deps.jsonResponse({
3532
+ ok: true,
3533
+ ...saved.store.status({ oauthConfigured: Boolean(process.env.RIG_GITHUB_OAUTH_CLIENT_ID?.trim()) }),
3534
+ apiSessionToken: saved.apiSessionToken,
3535
+ userNamespace: userNamespaceResponse(saved.namespace)
3536
+ });
3170
3537
  } catch (error) {
3171
3538
  const message = error instanceof Error ? error.message : String(error);
3172
3539
  return deps.jsonResponse({ ok: false, error: message }, 400);
@@ -3233,9 +3600,21 @@ data: ${JSON.stringify({ connectedAt: new Date().toISOString() })}
3233
3600
  }
3234
3601
  const token = result.payload.access_token;
3235
3602
  const user = await fetchGitHubUserInfo(token);
3236
- store.saveToken({ token, tokenSource: "oauth-device", login: user.login, userId: user.userId, scopes: user.scopes });
3237
- const apiSession = store.createApiSession();
3238
- return deps.jsonResponse({ ok: true, status: "signed-in", ...store.status({ oauthConfigured: true }), apiSessionToken: apiSession.token });
3603
+ const saved = saveGitHubTokenForRemoteUser({
3604
+ projectRoot: state.projectRoot,
3605
+ token,
3606
+ tokenSource: "oauth-device",
3607
+ user,
3608
+ selectedRepo: null
3609
+ });
3610
+ store.clearPendingDevice(pollId);
3611
+ return deps.jsonResponse({
3612
+ ok: true,
3613
+ status: "signed-in",
3614
+ ...saved.store.status({ oauthConfigured: true }),
3615
+ apiSessionToken: saved.apiSessionToken,
3616
+ userNamespace: userNamespaceResponse(saved.namespace)
3617
+ });
3239
3618
  }
3240
3619
  if (url.pathname === "/api/github/repo/probe" && req.method === "POST") {
3241
3620
  const body = await deps.readJsonBody(req);
@@ -3244,7 +3623,7 @@ data: ${JSON.stringify({ connectedAt: new Date().toISOString() })}
3244
3623
  if (!owner || !repo) {
3245
3624
  return deps.badRequest("owner and repo are required");
3246
3625
  }
3247
- const store = createGitHubAuthStore(state.projectRoot);
3626
+ const store = requestScopedAuthStore(state.projectRoot, requestAuth);
3248
3627
  const authStatus = store.status({ oauthConfigured: Boolean(process.env.RIG_GITHUB_OAUTH_CLIENT_ID?.trim()) });
3249
3628
  const probe = await probeGitHubRepository({ owner, repo, token: store.readToken(), scopes: authStatus.scopes });
3250
3629
  return deps.jsonResponse({ ok: probe.ok, probe }, probe.ok ? 200 : 400);
@@ -3265,7 +3644,7 @@ data: ${JSON.stringify({ connectedAt: new Date().toISOString() })}
3265
3644
  return deps.badRequest(error instanceof Error ? error.message : String(error));
3266
3645
  }
3267
3646
  const authStatus = createGitHubAuthStore(state.projectRoot).status({ oauthConfigured: Boolean(process.env.RIG_GITHUB_OAUTH_CLIENT_ID?.trim()) });
3268
- const configPath = resolve11(targetRoot, "rig.config.ts");
3647
+ const configPath = resolve13(targetRoot, "rig.config.ts");
3269
3648
  const source = buildGitHubProjectConfigSource({
3270
3649
  projectName: rawProjectName,
3271
3650
  owner,
@@ -3277,8 +3656,8 @@ data: ${JSON.stringify({ connectedAt: new Date().toISOString() })}
3277
3656
  ok: true,
3278
3657
  projectRoot: targetRoot,
3279
3658
  configPath,
3280
- exists: existsSync8(configPath),
3281
- requiresOverwrite: existsSync8(configPath),
3659
+ exists: existsSync10(configPath),
3660
+ requiresOverwrite: existsSync10(configPath),
3282
3661
  source,
3283
3662
  owner,
3284
3663
  repo,
@@ -3314,8 +3693,8 @@ data: ${JSON.stringify({ connectedAt: new Date().toISOString() })}
3314
3693
  assignee,
3315
3694
  githubUserId: authStatus.userId ?? authStatus.login
3316
3695
  });
3317
- const configPath = resolve11(targetRoot, "rig.config.ts");
3318
- if (existsSync8(configPath) && !overwrite) {
3696
+ const configPath = resolve13(targetRoot, "rig.config.ts");
3697
+ if (existsSync10(configPath) && !overwrite) {
3319
3698
  return deps.jsonResponse({
3320
3699
  ok: false,
3321
3700
  error: "rig.config.ts already exists. Confirm overwrite to replace it; Rig will create a backup first.",
@@ -3331,11 +3710,11 @@ data: ${JSON.stringify({ connectedAt: new Date().toISOString() })}
3331
3710
  return deps.jsonResponse({ ok: false, error: repoProbe.message, repoProbe }, 400);
3332
3711
  }
3333
3712
  let backupPath = null;
3334
- if (existsSync8(configPath)) {
3713
+ if (existsSync10(configPath)) {
3335
3714
  backupPath = backupConfigPath(configPath);
3336
- copyFileSync(configPath, backupPath);
3715
+ copyFileSync2(configPath, backupPath);
3337
3716
  }
3338
- writeFileSync7(configPath, source, "utf8");
3717
+ writeFileSync9(configPath, source, "utf8");
3339
3718
  const selectedRepo = `${owner}/${repo}`;
3340
3719
  store.saveSelectedRepo(selectedRepo);
3341
3720
  const targetStore = createGitHubAuthStore(targetRoot);
@@ -3621,7 +4000,7 @@ data: ${JSON.stringify({ connectedAt: new Date().toISOString() })}
3621
4000
  }
3622
4001
  if (url.pathname === "/api/pi-rig/install" && req.method === "POST") {
3623
4002
  const configuredPackageSource = normalizeString(process.env.RIG_PI_RIG_PACKAGE_SOURCE);
3624
- const packageSource = configuredPackageSource ?? [process.env.RIG_HOST_PROJECT_ROOT, process.cwd(), state.projectRoot].map((root) => normalizeString(root)).filter((root) => Boolean(root)).map((root) => resolve11(root, "packages", "pi-rig")).find((candidate) => existsSync8(resolve11(candidate, "package.json"))) ?? "npm:@rig/pi-rig";
4003
+ const packageSource = configuredPackageSource ?? [process.env.RIG_HOST_PROJECT_ROOT, process.cwd(), state.projectRoot].map((root) => normalizeString(root)).filter((root) => Boolean(root)).map((root) => resolve13(root, "packages", "pi-rig")).find((candidate) => existsSync10(resolve13(candidate, "package.json"))) ?? "npm:@rig/pi-rig";
3625
4004
  if (process.env.RIG_TEST_FAKE_PI_INSTALL === "1") {
3626
4005
  return deps.jsonResponse({ ok: true, installed: true, piOk: true, piRigOk: true, extensionPath: "remote:~/.pi/agent/extensions/pi-rig", packageSource });
3627
4006
  }
@@ -3937,9 +4316,9 @@ data: ${JSON.stringify({ connectedAt: new Date().toISOString() })}
3937
4316
  } catch {
3938
4317
  return deps.badRequest("Invalid artifact path");
3939
4318
  }
3940
- mkdirSync7(dirname6(artifactPath), { recursive: true });
4319
+ mkdirSync9(dirname9(artifactPath), { recursive: true });
3941
4320
  const bytes = Buffer.from(contentBase64, "base64");
3942
- writeFileSync7(artifactPath, bytes);
4321
+ writeFileSync9(artifactPath, bytes);
3943
4322
  writeJsonFile4(`${artifactPath}.json`, {
3944
4323
  workspaceId: normalizeString(body.workspaceId) ?? RIG_WORKSPACE_ID,
3945
4324
  runId,
@@ -3982,7 +4361,6 @@ data: ${JSON.stringify({ connectedAt: new Date().toISOString() })}
3982
4361
  hostId,
3983
4362
  endpointId: leaseId
3984
4363
  });
3985
- await updateRemoteRunTaskSourceLifecycle(state.projectRoot, { ...run, status: "completed", completedAt, hostId, endpointId: leaseId }, "closed", "Remote Rig task run completed and closed this task.");
3986
4364
  await deps.enqueueRunLinearEvent(state.projectRoot, {
3987
4365
  type: "run.completed",
3988
4366
  runId,
@@ -4101,12 +4479,12 @@ data: ${JSON.stringify({ connectedAt: new Date().toISOString() })}
4101
4479
  try {
4102
4480
  const runsRoot = resolveAuthorityPaths(state.projectRoot).runsDir;
4103
4481
  const runRoot = deps.normalizeRelativePath(runsRoot, runId);
4104
- const artifactsRoot = resolve11(runRoot, "remote-artifacts");
4482
+ const artifactsRoot = resolve13(runRoot, "remote-artifacts");
4105
4483
  artifactPath = deps.normalizeRelativePath(artifactsRoot, fileName);
4106
4484
  } catch {
4107
4485
  return deps.badRequest("Invalid artifact path");
4108
4486
  }
4109
- if (!existsSync8(artifactPath)) {
4487
+ if (!existsSync10(artifactPath)) {
4110
4488
  return deps.notFound();
4111
4489
  }
4112
4490
  return new Response(Bun.file(artifactPath));