@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,9 +4,9 @@ var __require = import.meta.require;
4
4
 
5
5
  // packages/server/src/server.ts
6
6
  import { spawn as spawn5 } from "child_process";
7
- import { existsSync as existsSync17, readdirSync as readdirSync5, readFileSync as readFileSync12, statSync as statSync6 } from "fs";
7
+ import { existsSync as existsSync19, readdirSync as readdirSync5, readFileSync as readFileSync14, statSync as statSync6 } from "fs";
8
8
  import { open } from "fs/promises";
9
- import { dirname as dirname17, resolve as resolve22 } from "path";
9
+ import { dirname as dirname20, resolve as resolve24 } from "path";
10
10
  import {
11
11
  listAuthorityArtifactRoots,
12
12
  listAuthorityRuns as listAuthorityRuns7,
@@ -3201,7 +3201,7 @@ function applyOrchestrationCommand(state, command) {
3201
3201
  import { spawn as spawn3 } from "child_process";
3202
3202
  import { loadConfig } from "@rig/core/load-config";
3203
3203
  import { existsSync as existsSync7, mkdirSync as mkdirSync7, readFileSync as readFileSync4, statSync as statSync5, writeFileSync as writeFileSync6 } from "fs";
3204
- import { dirname as dirname8, relative as relative2, resolve as resolve14 } from "path";
3204
+ import { dirname as dirname9, relative as relative2, resolve as resolve14 } from "path";
3205
3205
  import {
3206
3206
  listAuthorityRuns as listAuthorityRuns4,
3207
3207
  readAuthorityRun as readAuthorityRun4,
@@ -3325,8 +3325,8 @@ function summarizeRunValidationFailure(projectRoot, run) {
3325
3325
 
3326
3326
  // packages/server/src/server-helpers/github-auth-store.ts
3327
3327
  import { randomBytes } from "crypto";
3328
- import { chmodSync, existsSync as existsSync6, mkdirSync as mkdirSync6, readFileSync as readFileSync3, writeFileSync as writeFileSync5 } from "fs";
3329
- import { resolve as resolve13 } from "path";
3328
+ import { chmodSync, copyFileSync, existsSync as existsSync6, mkdirSync as mkdirSync6, readFileSync as readFileSync3, writeFileSync as writeFileSync5 } from "fs";
3329
+ import { dirname as dirname8, resolve as resolve13 } from "path";
3330
3330
  function cleanString(value) {
3331
3331
  return typeof value === "string" && value.trim().length > 0 ? value.trim() : null;
3332
3332
  }
@@ -3356,6 +3356,26 @@ function parseApiSessions(value) {
3356
3356
  }];
3357
3357
  });
3358
3358
  }
3359
+ function parsePendingDevice(value) {
3360
+ if (!value || typeof value !== "object")
3361
+ return null;
3362
+ const record = value;
3363
+ const pollId = cleanString(record.pollId);
3364
+ const deviceCode = cleanString(record.deviceCode);
3365
+ const expiresAt = cleanString(record.expiresAt);
3366
+ const intervalSeconds = typeof record.intervalSeconds === "number" && Number.isFinite(record.intervalSeconds) ? Math.max(1, Math.floor(record.intervalSeconds)) : null;
3367
+ if (!pollId || !deviceCode || !expiresAt || !intervalSeconds)
3368
+ return null;
3369
+ return { pollId, deviceCode, expiresAt, intervalSeconds };
3370
+ }
3371
+ function parsePendingDevices(value) {
3372
+ if (!Array.isArray(value))
3373
+ return [];
3374
+ return value.flatMap((entry) => {
3375
+ const pending = parsePendingDevice(entry);
3376
+ return pending ? [pending] : [];
3377
+ });
3378
+ }
3359
3379
  function readStoredAuth(stateFile) {
3360
3380
  if (!existsSync6(stateFile))
3361
3381
  return {};
@@ -3369,6 +3389,7 @@ function readStoredAuth(stateFile) {
3369
3389
  selectedRepo: cleanString(parsed.selectedRepo),
3370
3390
  tokenSource: parsed.tokenSource === "oauth-device" || parsed.tokenSource === "manual-token" || parsed.tokenSource === "env" ? parsed.tokenSource : undefined,
3371
3391
  pendingDevice: parsePendingDevice(parsed.pendingDevice),
3392
+ pendingDevices: parsePendingDevices(parsed.pendingDevices),
3372
3393
  apiSessions: parseApiSessions(parsed.apiSessions),
3373
3394
  updatedAt: cleanString(parsed.updatedAt) ?? undefined
3374
3395
  };
@@ -3376,34 +3397,36 @@ function readStoredAuth(stateFile) {
3376
3397
  return {};
3377
3398
  }
3378
3399
  }
3379
- function parsePendingDevice(value) {
3380
- if (!value || typeof value !== "object")
3381
- return null;
3382
- const record = value;
3383
- const pollId = cleanString(record.pollId);
3384
- const deviceCode = cleanString(record.deviceCode);
3385
- const expiresAt = cleanString(record.expiresAt);
3386
- const intervalSeconds = typeof record.intervalSeconds === "number" && Number.isFinite(record.intervalSeconds) ? Math.max(1, Math.floor(record.intervalSeconds)) : null;
3387
- if (!pollId || !deviceCode || !expiresAt || !intervalSeconds)
3388
- return null;
3389
- return { pollId, deviceCode, expiresAt, intervalSeconds };
3390
- }
3391
3400
  function newApiSessionToken() {
3392
3401
  return `rig_${randomBytes(32).toString("base64url")}`;
3393
3402
  }
3394
3403
  function writeStoredAuth(stateFile, payload) {
3395
- mkdirSync6(resolve13(stateFile, ".."), { recursive: true });
3404
+ mkdirSync6(dirname8(stateFile), { recursive: true });
3396
3405
  writeFileSync5(stateFile, `${JSON.stringify(payload, null, 2)}
3397
3406
  `, { encoding: "utf8", mode: 384 });
3398
3407
  try {
3399
3408
  chmodSync(stateFile, 384);
3400
3409
  } catch {}
3401
3410
  }
3411
+ function localProjectAuthStateFile(projectRoot) {
3412
+ return resolve13(projectRoot, ".rig", "state", "github-auth.json");
3413
+ }
3402
3414
  function resolveGitHubAuthStateFile(projectRoot) {
3403
3415
  return resolve13(resolveServerAuthorityPaths(projectRoot).stateDir, "github-auth.json");
3404
3416
  }
3405
- function createGitHubAuthStore(projectRoot) {
3406
- const stateFile = resolveGitHubAuthStateFile(projectRoot);
3417
+ function copyGitHubAuthStateToLocalProjectRoot(stateFile, projectRoot) {
3418
+ const targetFile = localProjectAuthStateFile(projectRoot);
3419
+ mkdirSync6(dirname8(targetFile), { recursive: true });
3420
+ if (existsSync6(stateFile)) {
3421
+ copyFileSync(stateFile, targetFile);
3422
+ try {
3423
+ chmodSync(targetFile, 384);
3424
+ } catch {}
3425
+ return;
3426
+ }
3427
+ writeStoredAuth(targetFile, {});
3428
+ }
3429
+ function createGitHubAuthStoreFromStateFile(stateFile) {
3407
3430
  return {
3408
3431
  stateFile,
3409
3432
  status(options) {
@@ -3433,6 +3456,7 @@ function createGitHubAuthStore(projectRoot) {
3433
3456
  scopes: input.scopes ?? [],
3434
3457
  selectedRepo: input.selectedRepo ?? previous.selectedRepo ?? null,
3435
3458
  pendingDevice: null,
3459
+ pendingDevices: [],
3436
3460
  apiSessions: previous.apiSessions ?? [],
3437
3461
  updatedAt: new Date().toISOString()
3438
3462
  });
@@ -3461,15 +3485,24 @@ function createGitHubAuthStore(projectRoot) {
3461
3485
  const session = (previous.apiSessions ?? []).find((candidate) => candidate.token === clean);
3462
3486
  return session ? { login: cleanString(session.login), userId: cleanString(session.userId) } : null;
3463
3487
  },
3464
- copyToProjectRoot(projectRoot2) {
3465
- const targetFile = resolveGitHubAuthStateFile(projectRoot2);
3488
+ copyToProjectRoot(projectRoot) {
3489
+ const targetFile = resolveGitHubAuthStateFile(projectRoot);
3466
3490
  writeStoredAuth(targetFile, readStoredAuth(stateFile));
3467
3491
  },
3492
+ copyToLocalProjectRoot(projectRoot) {
3493
+ copyGitHubAuthStateToLocalProjectRoot(stateFile, projectRoot);
3494
+ },
3468
3495
  savePendingDevice(input) {
3469
3496
  const previous = readStoredAuth(stateFile);
3497
+ const pendingDevices = [
3498
+ ...previous.pendingDevice ? [previous.pendingDevice] : [],
3499
+ ...previous.pendingDevices ?? [],
3500
+ input
3501
+ ].filter((entry, index, entries) => entries.findIndex((candidate) => candidate.pollId === entry.pollId) === index);
3470
3502
  writeStoredAuth(stateFile, {
3471
3503
  ...previous,
3472
- pendingDevice: input,
3504
+ pendingDevice: null,
3505
+ pendingDevices,
3473
3506
  updatedAt: new Date().toISOString()
3474
3507
  });
3475
3508
  },
@@ -3482,23 +3515,32 @@ function createGitHubAuthStore(projectRoot) {
3482
3515
  });
3483
3516
  },
3484
3517
  readPendingDevice(pollId) {
3485
- const pending = readStoredAuth(stateFile).pendingDevice ?? null;
3486
- if (!pending || pending.pollId !== pollId)
3518
+ const previous = readStoredAuth(stateFile);
3519
+ const pending = [
3520
+ ...previous.pendingDevice ? [previous.pendingDevice] : [],
3521
+ ...previous.pendingDevices ?? []
3522
+ ].find((entry) => entry.pollId === pollId) ?? null;
3523
+ if (!pending)
3487
3524
  return null;
3488
3525
  if (Date.parse(pending.expiresAt) <= Date.now())
3489
3526
  return null;
3490
3527
  return pending;
3491
3528
  },
3492
- clearPendingDevice() {
3529
+ clearPendingDevice(pollId) {
3493
3530
  const previous = readStoredAuth(stateFile);
3531
+ const remaining = pollId ? (previous.pendingDevices ?? []).filter((entry) => entry.pollId !== pollId) : [];
3494
3532
  writeStoredAuth(stateFile, {
3495
3533
  ...previous,
3496
3534
  pendingDevice: null,
3535
+ pendingDevices: remaining,
3497
3536
  updatedAt: new Date().toISOString()
3498
3537
  });
3499
3538
  }
3500
3539
  };
3501
3540
  }
3541
+ function createGitHubAuthStore(projectRoot) {
3542
+ return createGitHubAuthStoreFromStateFile(resolveGitHubAuthStateFile(projectRoot));
3543
+ }
3502
3544
 
3503
3545
  // packages/server/src/server-helpers/github-projects.ts
3504
3546
  function asRecord(value) {
@@ -4237,7 +4279,7 @@ function resolveLocalRunCliProjectRoot(projectRoot) {
4237
4279
  }
4238
4280
  try {
4239
4281
  const monorepoRoot = resolveMonorepoRoot3(projectRoot);
4240
- const outerProjectRoot = dirname8(dirname8(monorepoRoot));
4282
+ const outerProjectRoot = dirname9(dirname9(monorepoRoot));
4241
4283
  if (existsSync7(resolve14(outerProjectRoot, "packages/cli/bin/rig.ts"))) {
4242
4284
  return outerProjectRoot;
4243
4285
  }
@@ -4451,8 +4493,8 @@ async function reconcileScheduler(state, reason) {
4451
4493
  // packages/server/src/server-helpers/http-router.ts
4452
4494
  import { randomUUID } from "crypto";
4453
4495
  import { spawnSync as spawnSync3 } from "child_process";
4454
- import { basename, dirname as dirname12, isAbsolute as isAbsolute3, resolve as resolve18 } from "path";
4455
- import { copyFileSync, existsSync as existsSync11, mkdirSync as mkdirSync11, readFileSync as readFileSync7, writeFileSync as writeFileSync10 } from "fs";
4496
+ import { basename, dirname as dirname15, isAbsolute as isAbsolute4, resolve as resolve20 } from "path";
4497
+ import { copyFileSync as copyFileSync2, existsSync as existsSync13, mkdirSync as mkdirSync13, readFileSync as readFileSync9, writeFileSync as writeFileSync12 } from "fs";
4456
4498
  import {
4457
4499
  listAuthorityRuns as listAuthorityRuns5,
4458
4500
  readAuthorityRun as readAuthorityRun6,
@@ -4476,7 +4518,7 @@ import {
4476
4518
  } from "@rig/runtime/control-plane/remote";
4477
4519
 
4478
4520
  // packages/server/src/server-helpers/run-steering.ts
4479
- import { dirname as dirname9, resolve as resolve15 } from "path";
4521
+ import { dirname as dirname10, resolve as resolve15 } from "path";
4480
4522
  import { existsSync as existsSync8, mkdirSync as mkdirSync8, readFileSync as readFileSync5 } from "fs";
4481
4523
  import { appendJsonlRecord as appendJsonlRecord2, readAuthorityRun as readAuthorityRun5, resolveAuthorityRunDir as resolveAuthorityRunDir4 } from "@rig/runtime/control-plane/authority-files";
4482
4524
  var steeringSequence = 0;
@@ -4563,7 +4605,7 @@ function queueRunSteeringMessage(projectRoot, runId, input) {
4563
4605
  delivered: false
4564
4606
  };
4565
4607
  const path = runSteeringPath(projectRoot, runId);
4566
- mkdirSync8(dirname9(path), { recursive: true });
4608
+ mkdirSync8(dirname10(path), { recursive: true });
4567
4609
  appendJsonlRecord2(path, entry);
4568
4610
  appendRunTimelineEntry(projectRoot, runId, {
4569
4611
  id: entry.id,
@@ -4600,6 +4642,187 @@ import {
4600
4642
  updateConfiguredTaskSourceTask as updateConfiguredTaskSourceTask2
4601
4643
  } from "@rig/runtime/control-plane/tasks/source-lifecycle";
4602
4644
 
4645
+ // packages/server/src/server-helpers/github-api-session-index.ts
4646
+ import { chmodSync as chmodSync3, existsSync as existsSync10, mkdirSync as mkdirSync10, readFileSync as readFileSync7, writeFileSync as writeFileSync9 } from "fs";
4647
+ import { dirname as dirname12, resolve as resolve17 } from "path";
4648
+
4649
+ // packages/server/src/server-helpers/github-user-namespace.ts
4650
+ import { chmodSync as chmodSync2, existsSync as existsSync9, mkdirSync as mkdirSync9, readFileSync as readFileSync6, writeFileSync as writeFileSync8 } from "fs";
4651
+ import { dirname as dirname11, isAbsolute as isAbsolute2, relative as relative3, resolve as resolve16 } from "path";
4652
+ function cleanString3(value) {
4653
+ return typeof value === "string" && value.trim().length > 0 ? value.trim() : null;
4654
+ }
4655
+ function sanitizePathSegment(value) {
4656
+ return value.trim().toLowerCase().replace(/[^a-z0-9._-]/g, "-").replace(/^-+|-+$/g, "").slice(0, 96);
4657
+ }
4658
+ function deriveGitHubUserNamespaceKey(identity) {
4659
+ const userId = cleanString3(identity.userId);
4660
+ if (userId) {
4661
+ const safeId = sanitizePathSegment(userId);
4662
+ if (safeId)
4663
+ return `ghu-${safeId}`;
4664
+ }
4665
+ const login = cleanString3(identity.login);
4666
+ if (login) {
4667
+ const safeLogin = sanitizePathSegment(login);
4668
+ if (safeLogin)
4669
+ return `ghu-login-${safeLogin}`;
4670
+ }
4671
+ throw new Error("GitHub user namespace requires a user id or login");
4672
+ }
4673
+ function resolveRemoteUserNamespacesRoot(projectRoot) {
4674
+ const explicitRoot = cleanString3(process.env.RIG_REMOTE_USER_NAMESPACE_ROOT);
4675
+ if (explicitRoot)
4676
+ return resolve16(explicitRoot);
4677
+ const stateDir2 = cleanString3(process.env.RIG_STATE_DIR);
4678
+ if (stateDir2)
4679
+ return resolve16(dirname11(resolve16(stateDir2)), "users");
4680
+ return resolve16(projectRoot, ".rig", "users");
4681
+ }
4682
+ function resolveRemoteUserNamespace(projectRoot, identity) {
4683
+ const key = deriveGitHubUserNamespaceKey(identity);
4684
+ const root = resolve16(resolveRemoteUserNamespacesRoot(projectRoot), key);
4685
+ const stateDir2 = resolve16(root, ".rig", "state");
4686
+ return {
4687
+ key,
4688
+ userId: cleanString3(identity.userId),
4689
+ login: cleanString3(identity.login),
4690
+ root,
4691
+ stateDir: stateDir2,
4692
+ authStateFile: resolve16(stateDir2, "github-auth.json"),
4693
+ metadataFile: resolve16(stateDir2, "user-namespace.json"),
4694
+ checkoutBaseDir: resolve16(root, "remote-checkouts"),
4695
+ snapshotBaseDir: resolve16(root, "remote-snapshots")
4696
+ };
4697
+ }
4698
+ function serializeRemoteUserNamespace(namespace) {
4699
+ return {
4700
+ key: namespace.key,
4701
+ userId: namespace.userId,
4702
+ login: namespace.login,
4703
+ root: namespace.root,
4704
+ checkoutBaseDir: namespace.checkoutBaseDir,
4705
+ snapshotBaseDir: namespace.snapshotBaseDir
4706
+ };
4707
+ }
4708
+ function isPathInsideNamespace(namespaceRoot, candidatePath) {
4709
+ const root = resolve16(namespaceRoot);
4710
+ const candidate = resolve16(candidatePath);
4711
+ const rel = relative3(root, candidate);
4712
+ return rel === "" || !rel.startsWith("..") && !isAbsolute2(rel);
4713
+ }
4714
+ function writeRemoteUserNamespaceMetadata(namespace) {
4715
+ mkdirSync9(namespace.stateDir, { recursive: true });
4716
+ const previous = (() => {
4717
+ if (!existsSync9(namespace.metadataFile))
4718
+ return null;
4719
+ try {
4720
+ const parsed = JSON.parse(readFileSync6(namespace.metadataFile, "utf8"));
4721
+ return parsed && typeof parsed === "object" && !Array.isArray(parsed) ? parsed : null;
4722
+ } catch {
4723
+ return null;
4724
+ }
4725
+ })();
4726
+ const now = new Date().toISOString();
4727
+ writeFileSync8(namespace.metadataFile, `${JSON.stringify({
4728
+ key: namespace.key,
4729
+ userId: namespace.userId,
4730
+ login: namespace.login,
4731
+ root: namespace.root,
4732
+ checkoutBaseDir: namespace.checkoutBaseDir,
4733
+ snapshotBaseDir: namespace.snapshotBaseDir,
4734
+ createdAt: typeof previous?.createdAt === "string" ? previous.createdAt : now,
4735
+ updatedAt: now
4736
+ }, null, 2)}
4737
+ `, { encoding: "utf8", mode: 384 });
4738
+ try {
4739
+ chmodSync2(namespace.metadataFile, 384);
4740
+ } catch {}
4741
+ }
4742
+
4743
+ // packages/server/src/server-helpers/github-api-session-index.ts
4744
+ function cleanString4(value) {
4745
+ return typeof value === "string" && value.trim().length > 0 ? value.trim() : null;
4746
+ }
4747
+ function resolveGitHubApiSessionIndexFile(projectRoot) {
4748
+ return resolve17(resolveRemoteUserNamespacesRoot(projectRoot), ".api-sessions.json");
4749
+ }
4750
+ function parseEntry(value) {
4751
+ if (!value || typeof value !== "object" || Array.isArray(value))
4752
+ return null;
4753
+ const record = value;
4754
+ const token = cleanString4(record.token);
4755
+ const namespaceKey = cleanString4(record.namespaceKey);
4756
+ const namespaceRoot = cleanString4(record.namespaceRoot);
4757
+ const authStateFile = cleanString4(record.authStateFile);
4758
+ const checkoutBaseDir = cleanString4(record.checkoutBaseDir);
4759
+ const snapshotBaseDir = cleanString4(record.snapshotBaseDir);
4760
+ const createdAt = cleanString4(record.createdAt);
4761
+ if (!token || !namespaceKey || !namespaceRoot || !authStateFile || !checkoutBaseDir || !snapshotBaseDir || !createdAt)
4762
+ return null;
4763
+ return {
4764
+ token,
4765
+ namespaceKey,
4766
+ namespaceRoot,
4767
+ authStateFile,
4768
+ checkoutBaseDir,
4769
+ snapshotBaseDir,
4770
+ createdAt,
4771
+ login: cleanString4(record.login),
4772
+ userId: cleanString4(record.userId),
4773
+ selectedRepo: cleanString4(record.selectedRepo)
4774
+ };
4775
+ }
4776
+ function readIndex(indexFile) {
4777
+ if (!existsSync10(indexFile))
4778
+ return [];
4779
+ try {
4780
+ const parsed = JSON.parse(readFileSync7(indexFile, "utf8"));
4781
+ return Array.isArray(parsed.sessions) ? parsed.sessions.flatMap((entry) => {
4782
+ const parsedEntry = parseEntry(entry);
4783
+ return parsedEntry ? [parsedEntry] : [];
4784
+ }) : [];
4785
+ } catch {
4786
+ return [];
4787
+ }
4788
+ }
4789
+ function writeIndex(indexFile, sessions) {
4790
+ mkdirSync10(dirname12(indexFile), { recursive: true });
4791
+ writeFileSync9(indexFile, `${JSON.stringify({ sessions }, null, 2)}
4792
+ `, { encoding: "utf8", mode: 384 });
4793
+ try {
4794
+ chmodSync3(indexFile, 384);
4795
+ } catch {}
4796
+ }
4797
+ function registerGitHubApiSession(input) {
4798
+ const cleanToken = cleanString4(input.token);
4799
+ if (!cleanToken)
4800
+ throw new Error("GitHub API session token is required");
4801
+ const indexFile = resolveGitHubApiSessionIndexFile(input.projectRoot);
4802
+ const createdAt = new Date().toISOString();
4803
+ const entry = {
4804
+ token: cleanToken,
4805
+ login: input.namespace.login,
4806
+ userId: input.namespace.userId,
4807
+ namespaceKey: input.namespace.key,
4808
+ namespaceRoot: input.namespace.root,
4809
+ authStateFile: input.namespace.authStateFile,
4810
+ checkoutBaseDir: input.namespace.checkoutBaseDir,
4811
+ snapshotBaseDir: input.namespace.snapshotBaseDir,
4812
+ selectedRepo: cleanString4(input.selectedRepo),
4813
+ createdAt
4814
+ };
4815
+ const previous = readIndex(indexFile).filter((session) => session.token !== cleanToken);
4816
+ writeIndex(indexFile, [...previous.slice(-199), entry]);
4817
+ return entry;
4818
+ }
4819
+ function readGitHubApiSession(input) {
4820
+ const cleanToken = cleanString4(input.token);
4821
+ if (!cleanToken)
4822
+ return null;
4823
+ return readIndex(resolveGitHubApiSessionIndexFile(input.projectRoot)).find((entry) => entry.token === cleanToken) ?? null;
4824
+ }
4825
+
4603
4826
  // packages/server/src/server-helpers/inspector-agent-lifecycle.ts
4604
4827
  function createInspectorAgentLifecycleController(options) {
4605
4828
  const initialDelayMs = Math.max(1, options.initialDelayMs ?? 5000);
@@ -4703,21 +4926,21 @@ function inspectorAgentLifecycleSnapshot(input) {
4703
4926
  // packages/server/src/server-helpers/project-registry.ts
4704
4927
  import { createHash as createHash2 } from "crypto";
4705
4928
  import { spawnSync as spawnSync2 } from "child_process";
4706
- import { existsSync as existsSync9, mkdirSync as mkdirSync9, readFileSync as readFileSync6, readdirSync as readdirSync3, writeFileSync as writeFileSync8 } from "fs";
4707
- import { dirname as dirname10, resolve as resolve16 } from "path";
4929
+ import { existsSync as existsSync11, mkdirSync as mkdirSync11, readFileSync as readFileSync8, readdirSync as readdirSync3, writeFileSync as writeFileSync10 } from "fs";
4930
+ import { dirname as dirname13, resolve as resolve18 } from "path";
4708
4931
  function normalizeRepoSlug(value) {
4709
4932
  const trimmed = value.trim();
4710
4933
  return /^[^/\s]+\/[^/\s]+$/.test(trimmed) ? trimmed : null;
4711
4934
  }
4712
4935
  function registryPath(projectRoot) {
4713
- return resolve16(projectRoot, ".rig", "state", "projects.json");
4936
+ return resolve18(projectRoot, ".rig", "state", "projects.json");
4714
4937
  }
4715
4938
  function readRegistry(projectRoot) {
4716
4939
  const path = registryPath(projectRoot);
4717
- if (!existsSync9(path))
4940
+ if (!existsSync11(path))
4718
4941
  return {};
4719
4942
  try {
4720
- const payload = JSON.parse(readFileSync6(path, "utf8"));
4943
+ const payload = JSON.parse(readFileSync8(path, "utf8"));
4721
4944
  if (!payload || typeof payload !== "object" || Array.isArray(payload))
4722
4945
  return {};
4723
4946
  const projects = payload.projects;
@@ -4728,14 +4951,14 @@ function readRegistry(projectRoot) {
4728
4951
  }
4729
4952
  function writeRegistry(projectRoot, projects) {
4730
4953
  const path = registryPath(projectRoot);
4731
- mkdirSync9(dirname10(path), { recursive: true });
4732
- writeFileSync8(path, `${JSON.stringify({ projects }, null, 2)}
4954
+ mkdirSync11(dirname13(path), { recursive: true });
4955
+ writeFileSync10(path, `${JSON.stringify({ projects }, null, 2)}
4733
4956
  `, "utf8");
4734
4957
  }
4735
4958
  function resolveConfigPath(projectRoot) {
4736
4959
  for (const name of ["rig.config.ts", "rig.config.mts", "rig.config.json"]) {
4737
- const path = resolve16(projectRoot, name);
4738
- if (existsSync9(path))
4960
+ const path = resolve18(projectRoot, name);
4961
+ if (existsSync11(path))
4739
4962
  return path;
4740
4963
  }
4741
4964
  return null;
@@ -4744,7 +4967,7 @@ function hashFile(path) {
4744
4967
  if (!path)
4745
4968
  return null;
4746
4969
  try {
4747
- return createHash2("sha256").update(readFileSync6(path)).digest("hex");
4970
+ return createHash2("sha256").update(readFileSync8(path)).digest("hex");
4748
4971
  } catch {
4749
4972
  return null;
4750
4973
  }
@@ -4760,11 +4983,11 @@ function readDefaultBranch(projectRoot) {
4760
4983
  return head.status === 0 && head.stdout.trim() && head.stdout.trim() !== "HEAD" ? head.stdout.trim() : null;
4761
4984
  }
4762
4985
  function buildRunSummary(projectRoot) {
4763
- const runsDir = resolve16(projectRoot, ".rig", "runs");
4986
+ const runsDir = resolve18(projectRoot, ".rig", "runs");
4764
4987
  try {
4765
4988
  const runs = readdirSync3(runsDir, { withFileTypes: true }).filter((entry) => entry.isDirectory()).flatMap((entry) => {
4766
4989
  try {
4767
- const run = JSON.parse(readFileSync6(resolve16(runsDir, entry.name, "run.json"), "utf8"));
4990
+ const run = JSON.parse(readFileSync8(resolve18(runsDir, entry.name, "run.json"), "utf8"));
4768
4991
  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 : "" }];
4769
4992
  } catch {
4770
4993
  return [];
@@ -4816,10 +5039,14 @@ function upsertProjectRecord(projectRoot, input) {
4816
5039
  function linkProjectCheckout(projectRoot, repoSlug, checkout) {
4817
5040
  return upsertProjectRecord(projectRoot, { repoSlug, checkout });
4818
5041
  }
5042
+ function projectRegistryContainsCheckout(projectRoot, checkoutPath) {
5043
+ const target = resolve18(checkoutPath);
5044
+ return Object.values(readRegistry(projectRoot)).some((project) => project.checkouts.some((checkout) => checkout.path ? resolve18(checkout.path) === target : false));
5045
+ }
4819
5046
 
4820
5047
  // packages/server/src/server-helpers/remote-checkout.ts
4821
- import { existsSync as existsSync10, mkdirSync as mkdirSync10, writeFileSync as writeFileSync9 } from "fs";
4822
- import { dirname as dirname11, isAbsolute as isAbsolute2, relative as relative3, resolve as resolve17 } from "path";
5048
+ import { existsSync as existsSync12, mkdirSync as mkdirSync12, writeFileSync as writeFileSync11 } from "fs";
5049
+ import { dirname as dirname14, isAbsolute as isAbsolute3, relative as relative4, resolve as resolve19 } from "path";
4823
5050
  function safeSlugSegments(repoSlug) {
4824
5051
  const segments = repoSlug.split("/").map((part) => part.trim()).filter(Boolean);
4825
5052
  if (segments.length !== 2 || segments.some((segment) => segment === "." || segment === ".." || segment.includes("\\"))) {
@@ -4836,7 +5063,7 @@ function safeCheckoutKey(value) {
4836
5063
  }
4837
5064
  function repoSlugPath(baseDir, repoSlug, checkoutKey) {
4838
5065
  const key = safeCheckoutKey(checkoutKey);
4839
- return resolve17(baseDir, ...key ? [key] : [], ...safeSlugSegments(repoSlug));
5066
+ return resolve19(baseDir, ...key ? [key] : [], ...safeSlugSegments(repoSlug));
4840
5067
  }
4841
5068
  function sanitizeSnapshotId(value, fallback) {
4842
5069
  const raw = (value ?? fallback).trim();
@@ -4844,7 +5071,7 @@ function sanitizeSnapshotId(value, fallback) {
4844
5071
  return safe || fallback;
4845
5072
  }
4846
5073
  function assertWithinRoot(root, relativePath) {
4847
- if (!relativePath || isAbsolute2(relativePath) || relativePath.includes("\x00")) {
5074
+ if (!relativePath || isAbsolute3(relativePath) || relativePath.includes("\x00")) {
4848
5075
  throw new Error(`Invalid snapshot file path: ${relativePath}`);
4849
5076
  }
4850
5077
  const normalizedRelative = relativePath.replace(/\\/g, "/");
@@ -4852,9 +5079,9 @@ function assertWithinRoot(root, relativePath) {
4852
5079
  if (segments.some((segment) => segment === "" || segment === "." || segment === "..")) {
4853
5080
  throw new Error(`Unsafe snapshot file path: ${relativePath}`);
4854
5081
  }
4855
- const target = resolve17(root, ...segments);
4856
- const rel = relative3(root, target);
4857
- if (rel === ".." || rel.split(/[\\/]/)[0] === ".." || isAbsolute2(rel)) {
5082
+ const target = resolve19(root, ...segments);
5083
+ const rel = relative4(root, target);
5084
+ if (rel === ".." || rel.split(/[\\/]/)[0] === ".." || isAbsolute3(rel)) {
4858
5085
  throw new Error(`Snapshot file path escapes checkout root: ${relativePath}`);
4859
5086
  }
4860
5087
  return target;
@@ -4872,17 +5099,17 @@ function parseSnapshotArchiveContentBase64(contentBase64) {
4872
5099
  function extractUploadedSnapshotArchive(input) {
4873
5100
  const archive = decodeSnapshotArchive(input.archive);
4874
5101
  const snapshotId = sanitizeSnapshotId(input.snapshotId, `snapshot-${(input.now?.() ?? new Date).toISOString().replace(/[:.]/g, "-")}`);
4875
- const checkoutPath = resolve17(repoSlugPath(input.baseDir, input.repoSlug, input.checkoutKey), snapshotId);
4876
- mkdirSync10(checkoutPath, { recursive: true });
5102
+ const checkoutPath = resolve19(repoSlugPath(input.baseDir, input.repoSlug, input.checkoutKey), snapshotId);
5103
+ mkdirSync12(checkoutPath, { recursive: true });
4877
5104
  for (const file of archive.files) {
4878
5105
  if (!file || typeof file.path !== "string" || typeof file.contentBase64 !== "string") {
4879
5106
  throw new Error("Invalid snapshot archive file entry");
4880
5107
  }
4881
5108
  const target = assertWithinRoot(checkoutPath, file.path);
4882
- mkdirSync10(dirname11(target), { recursive: true });
4883
- writeFileSync9(target, Buffer.from(file.contentBase64, "base64"));
5109
+ mkdirSync12(dirname14(target), { recursive: true });
5110
+ writeFileSync11(target, Buffer.from(file.contentBase64, "base64"));
4884
5111
  }
4885
- writeFileSync9(resolve17(checkoutPath, ".rig-uploaded-snapshot.json"), `${JSON.stringify({
5112
+ writeFileSync11(resolve19(checkoutPath, ".rig-uploaded-snapshot.json"), `${JSON.stringify({
4886
5113
  repoSlug: input.repoSlug,
4887
5114
  snapshotId,
4888
5115
  fileCount: archive.files.length,
@@ -4917,7 +5144,7 @@ function gitCredentialConfig(token) {
4917
5144
  };
4918
5145
  }
4919
5146
  async function prepareRemoteCheckout(input) {
4920
- const exists = input.exists ?? existsSync10;
5147
+ const exists = input.exists ?? existsSync12;
4921
5148
  const strategy = input.strategy;
4922
5149
  if (strategy.kind === "uploaded-snapshot") {
4923
5150
  return extractUploadedSnapshotArchive({
@@ -4929,7 +5156,7 @@ async function prepareRemoteCheckout(input) {
4929
5156
  });
4930
5157
  }
4931
5158
  if (strategy.kind === "existing-path") {
4932
- const checkoutPath2 = resolve17(strategy.path);
5159
+ const checkoutPath2 = resolve19(strategy.path);
4933
5160
  if (!exists(checkoutPath2)) {
4934
5161
  throw new Error(`Existing remote checkout path does not exist: ${checkoutPath2}`);
4935
5162
  }
@@ -4965,9 +5192,9 @@ function buildServerControlStatus() {
4965
5192
  };
4966
5193
  }
4967
5194
  function buildProjectConfigStatus(root) {
4968
- const hasConfigTs = existsSync11(resolve18(root, "rig.config.ts"));
4969
- const hasConfigJson = existsSync11(resolve18(root, "rig.config.json"));
4970
- const hasLegacyTaskConfig = existsSync11(resolve18(root, ".rig", "task-config.json"));
5195
+ const hasConfigTs = existsSync13(resolve20(root, "rig.config.ts"));
5196
+ const hasConfigJson = existsSync13(resolve20(root, "rig.config.json"));
5197
+ const hasLegacyTaskConfig = existsSync13(resolve20(root, ".rig", "task-config.json"));
4971
5198
  let kind = "missing";
4972
5199
  if (hasConfigTs)
4973
5200
  kind = "rig-config-ts";
@@ -5003,24 +5230,24 @@ function repoParts(repoSlug) {
5003
5230
  return { owner, repo, slug: `${owner}/${repo}` };
5004
5231
  }
5005
5232
  function repairDir(checkoutPath) {
5006
- const dir = resolve18(checkoutPath, ".rig", "state", "repairs", new Date().toISOString().replace(/[:.]/g, "-"));
5007
- mkdirSync11(dir, { recursive: true });
5233
+ const dir = resolve20(checkoutPath, ".rig", "state", "repairs", new Date().toISOString().replace(/[:.]/g, "-"));
5234
+ mkdirSync13(dir, { recursive: true });
5008
5235
  return dir;
5009
5236
  }
5010
5237
  function backupCheckoutFile(checkoutPath, relativePath) {
5011
- const source = resolve18(checkoutPath, relativePath);
5012
- const backupPath = resolve18(repairDir(checkoutPath), relativePath.replace(/[\\/]/g, "__"));
5013
- mkdirSync11(dirname12(backupPath), { recursive: true });
5014
- copyFileSync(source, backupPath);
5238
+ const source = resolve20(checkoutPath, relativePath);
5239
+ const backupPath = resolve20(repairDir(checkoutPath), relativePath.replace(/[\\/]/g, "__"));
5240
+ mkdirSync13(dirname15(backupPath), { recursive: true });
5241
+ copyFileSync2(source, backupPath);
5015
5242
  return backupPath;
5016
5243
  }
5017
5244
  function parsePackageJsonLosslessly(checkoutPath) {
5018
- const packagePath = resolve18(checkoutPath, "package.json");
5019
- if (!existsSync11(packagePath)) {
5245
+ const packagePath = resolve20(checkoutPath, "package.json");
5246
+ if (!existsSync13(packagePath)) {
5020
5247
  return { existed: false, packageJson: { name: basename(checkoutPath) || "rig-project", private: true } };
5021
5248
  }
5022
5249
  try {
5023
- const parsed = JSON.parse(readFileSync7(packagePath, "utf8"));
5250
+ const parsed = JSON.parse(readFileSync9(packagePath, "utf8"));
5024
5251
  return {
5025
5252
  existed: true,
5026
5253
  packageJson: parsed && typeof parsed === "object" && !Array.isArray(parsed) ? parsed : { name: basename(checkoutPath) || "rig-project", private: true }
@@ -5034,9 +5261,9 @@ function parsePackageJsonLosslessly(checkoutPath) {
5034
5261
  }
5035
5262
  }
5036
5263
  function ensureRemoteCheckoutRigPackageDeps(checkoutPath) {
5037
- const hasConfig = ["rig.config.ts", "rig.config.mts", "rig.config.json"].some((name) => existsSync11(resolve18(checkoutPath, name)));
5038
- const packagePath = resolve18(checkoutPath, "package.json");
5039
- if (!hasConfig && !existsSync11(packagePath)) {
5264
+ const hasConfig = ["rig.config.ts", "rig.config.mts", "rig.config.json"].some((name) => existsSync13(resolve20(checkoutPath, name)));
5265
+ const packagePath = resolve20(checkoutPath, "package.json");
5266
+ if (!hasConfig && !existsSync13(packagePath)) {
5040
5267
  return { skipped: true, reason: "package.json and rig.config missing" };
5041
5268
  }
5042
5269
  const parsed = parsePackageJsonLosslessly(checkoutPath);
@@ -5055,7 +5282,7 @@ function ensureRemoteCheckoutRigPackageDeps(checkoutPath) {
5055
5282
  }
5056
5283
  const changed = !parsed.existed || Boolean(parsed.backupPath) || added.length > 0 || updated.length > 0 || existingDevDependencies !== devDependencies && (!existingDevDependencies || typeof existingDevDependencies !== "object" || Array.isArray(existingDevDependencies));
5057
5284
  if (changed) {
5058
- writeFileSync10(packagePath, `${JSON.stringify({ ...parsed.packageJson, devDependencies }, null, 2)}
5285
+ writeFileSync12(packagePath, `${JSON.stringify({ ...parsed.packageJson, devDependencies }, null, 2)}
5059
5286
  `, "utf8");
5060
5287
  }
5061
5288
  return {
@@ -5081,11 +5308,11 @@ function configLooksStructurallyUsable(source) {
5081
5308
  return /taskSource\s*:/.test(source) && /workspace\s*:/.test(source) && /project\s*:/.test(source);
5082
5309
  }
5083
5310
  function ensureRemoteCheckoutRigConfig(checkoutPath, repoSlug, reason = "missing or incomplete rig config") {
5084
- const configPath = resolve18(checkoutPath, "rig.config.ts");
5085
- const existingConfigName = ["rig.config.ts", "rig.config.mts", "rig.config.json"].find((name) => existsSync11(resolve18(checkoutPath, name)));
5311
+ const configPath = resolve20(checkoutPath, "rig.config.ts");
5312
+ const existingConfigName = ["rig.config.ts", "rig.config.mts", "rig.config.json"].find((name) => existsSync13(resolve20(checkoutPath, name)));
5086
5313
  if (existingConfigName) {
5087
- const existingPath = resolve18(checkoutPath, existingConfigName);
5088
- const source = readFileSync7(existingPath, "utf8");
5314
+ const existingPath = resolve20(checkoutPath, existingConfigName);
5315
+ const source = readFileSync9(existingPath, "utf8");
5089
5316
  if (existingConfigName !== "rig.config.json" && configLooksStructurallyUsable(source)) {
5090
5317
  return { path: existingPath, changed: false, reason: "config structurally complete" };
5091
5318
  }
@@ -5099,7 +5326,7 @@ function ensureRemoteCheckoutRigConfig(checkoutPath, repoSlug, reason = "missing
5099
5326
  }
5100
5327
  }
5101
5328
  const backupPath = existingConfigName ? backupCheckoutFile(checkoutPath, existingConfigName) : undefined;
5102
- writeFileSync10(configPath, generatedRigConfigSource(repoSlug), "utf8");
5329
+ writeFileSync12(configPath, generatedRigConfigSource(repoSlug), "utf8");
5103
5330
  return {
5104
5331
  path: configPath,
5105
5332
  changed: true,
@@ -5108,7 +5335,7 @@ function ensureRemoteCheckoutRigConfig(checkoutPath, repoSlug, reason = "missing
5108
5335
  };
5109
5336
  }
5110
5337
  function validateRemoteCheckoutRigConfig(checkoutPath) {
5111
- const configFile = ["rig.config.ts", "rig.config.mts", "rig.config.json"].find((name) => existsSync11(resolve18(checkoutPath, name)));
5338
+ const configFile = ["rig.config.ts", "rig.config.mts", "rig.config.json"].find((name) => existsSync13(resolve20(checkoutPath, name)));
5112
5339
  if (!configFile)
5113
5340
  return { ok: false, error: "missing rig config" };
5114
5341
  if (process.env.RIG_TEST_SKIP_REMOTE_CHECKOUT_INSTALL === "1") {
@@ -5130,7 +5357,7 @@ function validateRemoteCheckoutRigConfig(checkoutPath) {
5130
5357
  return { ok: true, configFile };
5131
5358
  }
5132
5359
  function installRemoteCheckoutPackages(checkoutPath) {
5133
- if (!existsSync11(resolve18(checkoutPath, "package.json"))) {
5360
+ if (!existsSync13(resolve20(checkoutPath, "package.json"))) {
5134
5361
  return { skipped: true, reason: "package.json missing" };
5135
5362
  }
5136
5363
  if (process.env.RIG_TEST_SKIP_REMOTE_CHECKOUT_INSTALL === "1") {
@@ -5143,8 +5370,8 @@ function installRemoteCheckoutPackages(checkoutPath) {
5143
5370
  return { ok: true, command: "bun install", stdout: result.stdout?.trim() || undefined };
5144
5371
  }
5145
5372
  function repairRemoteCheckoutForRig(checkoutPath, repoSlug) {
5146
- const hasConfig = ["rig.config.ts", "rig.config.mts", "rig.config.json"].some((name) => existsSync11(resolve18(checkoutPath, name)));
5147
- const hasPackage = existsSync11(resolve18(checkoutPath, "package.json"));
5373
+ const hasConfig = ["rig.config.ts", "rig.config.mts", "rig.config.json"].some((name) => existsSync13(resolve20(checkoutPath, name)));
5374
+ const hasPackage = existsSync13(resolve20(checkoutPath, "package.json"));
5148
5375
  if (!hasConfig && !hasPackage) {
5149
5376
  return {
5150
5377
  packageJson: { skipped: true, reason: "package.json and rig.config missing" },
@@ -5242,26 +5469,26 @@ function buildRemoteRunLogEntry(body, identifiers) {
5242
5469
  }
5243
5470
  function readGitHeadCommit(projectRoot) {
5244
5471
  try {
5245
- let gitDir = resolve18(projectRoot, ".git");
5472
+ let gitDir = resolve20(projectRoot, ".git");
5246
5473
  try {
5247
- const dotGit = readFileSync7(gitDir, "utf8").trim();
5474
+ const dotGit = readFileSync9(gitDir, "utf8").trim();
5248
5475
  const gitDirPrefix = "gitdir:";
5249
5476
  if (dotGit.startsWith(gitDirPrefix)) {
5250
- gitDir = resolve18(projectRoot, dotGit.slice(gitDirPrefix.length).trim());
5477
+ gitDir = resolve20(projectRoot, dotGit.slice(gitDirPrefix.length).trim());
5251
5478
  }
5252
5479
  } catch {}
5253
- const head = readFileSync7(resolve18(gitDir, "HEAD"), "utf8").trim();
5480
+ const head = readFileSync9(resolve20(gitDir, "HEAD"), "utf8").trim();
5254
5481
  const refPrefix = "ref:";
5255
5482
  if (!head.startsWith(refPrefix)) {
5256
5483
  return normalizeCommit(head);
5257
5484
  }
5258
5485
  const ref = head.slice(refPrefix.length).trim();
5259
- const refPath = resolve18(gitDir, ref);
5260
- if (existsSync11(refPath)) {
5261
- return normalizeCommit(readFileSync7(refPath, "utf8").trim());
5486
+ const refPath = resolve20(gitDir, ref);
5487
+ if (existsSync13(refPath)) {
5488
+ return normalizeCommit(readFileSync9(refPath, "utf8").trim());
5262
5489
  }
5263
- const commonDir = normalizeString(readFileSync7(resolve18(gitDir, "commondir"), "utf8"));
5264
- return commonDir ? normalizeCommit(readFileSync7(resolve18(gitDir, commonDir, ref), "utf8").trim()) : null;
5490
+ const commonDir = normalizeString(readFileSync9(resolve20(gitDir, "commondir"), "utf8"));
5491
+ return commonDir ? normalizeCommit(readFileSync9(resolve20(gitDir, commonDir, ref), "utf8").trim()) : null;
5265
5492
  } catch {
5266
5493
  return null;
5267
5494
  }
@@ -5299,9 +5526,9 @@ function configuredRepoFromTaskSource(taskSource) {
5299
5526
  const repo = normalizeString(taskSource?.repo);
5300
5527
  return owner && repo ? `${owner}/${repo}` : null;
5301
5528
  }
5302
- async function buildTaskSourceStatus(state, config) {
5529
+ async function buildTaskSourceStatus(state, config, requestAuth) {
5303
5530
  const diagnostics = state.snapshotService.getTaskSourceErrors();
5304
- const selectedRepo = createGitHubAuthStore(state.projectRoot).status({
5531
+ const selectedRepo = requestScopedAuthStore(state.projectRoot, requestAuth).status({
5305
5532
  oauthConfigured: Boolean(process.env.RIG_GITHUB_OAUTH_CLIENT_ID?.trim())
5306
5533
  }).selectedRepo;
5307
5534
  try {
@@ -5399,31 +5626,83 @@ function normalizePrMode(value) {
5399
5626
  const mode = normalizeString(value);
5400
5627
  return mode === "auto" || mode === "ask" || mode === "off" ? mode : undefined;
5401
5628
  }
5629
+ function requestAuthResult(input) {
5630
+ return {
5631
+ authorized: input.authorized,
5632
+ actor: input.actor ?? null,
5633
+ reason: input.reason,
5634
+ login: input.login ?? null,
5635
+ userId: input.userId ?? null,
5636
+ userNamespace: input.userNamespace ?? null,
5637
+ authStateFile: input.authStateFile ?? null
5638
+ };
5639
+ }
5640
+ function namespaceFromSessionIndex(entry) {
5641
+ const stateDir2 = dirname15(entry.authStateFile);
5642
+ return {
5643
+ key: entry.namespaceKey,
5644
+ userId: entry.userId,
5645
+ login: entry.login,
5646
+ root: entry.namespaceRoot,
5647
+ stateDir: stateDir2,
5648
+ authStateFile: entry.authStateFile,
5649
+ metadataFile: resolve20(stateDir2, "user-namespace.json"),
5650
+ checkoutBaseDir: entry.checkoutBaseDir,
5651
+ snapshotBaseDir: entry.snapshotBaseDir
5652
+ };
5653
+ }
5402
5654
  function authorizeRigHttpRequest(input) {
5403
5655
  if (input.legacyAuthorized) {
5404
- return { authorized: true, actor: "rig-local-server", reason: "server-token" };
5656
+ return requestAuthResult({ authorized: true, actor: "rig-local-server", reason: "server-token" });
5405
5657
  }
5406
5658
  const bearer = bearerTokenFromRequest(input.req);
5407
5659
  const store = createGitHubAuthStore(input.projectRoot);
5408
5660
  const storedToken = store.readToken();
5409
5661
  const session = bearer ? store.readApiSession(bearer) : null;
5410
5662
  if (session) {
5411
- return { authorized: true, actor: session.login ?? "github-operator", reason: "github-session" };
5663
+ return requestAuthResult({
5664
+ authorized: true,
5665
+ actor: session.login ?? "github-operator",
5666
+ reason: "github-session",
5667
+ login: session.login,
5668
+ userId: session.userId,
5669
+ authStateFile: store.stateFile
5670
+ });
5412
5671
  }
5413
5672
  if (bearer && storedToken && bearer === storedToken) {
5414
5673
  const status = store.status({ oauthConfigured: Boolean(process.env.RIG_GITHUB_OAUTH_CLIENT_ID?.trim()) });
5415
- return { authorized: true, actor: status.login ?? "github-operator", reason: "github-token" };
5674
+ return requestAuthResult({
5675
+ authorized: true,
5676
+ actor: status.login ?? "github-operator",
5677
+ reason: "github-token",
5678
+ login: status.login,
5679
+ userId: status.userId,
5680
+ authStateFile: store.stateFile
5681
+ });
5682
+ }
5683
+ const indexedSession = readGitHubApiSession({ projectRoot: input.projectRoot, token: bearer });
5684
+ if (indexedSession) {
5685
+ const userNamespace = namespaceFromSessionIndex(indexedSession);
5686
+ return requestAuthResult({
5687
+ authorized: true,
5688
+ actor: indexedSession.login ?? "github-operator",
5689
+ reason: "github-user-session",
5690
+ login: indexedSession.login,
5691
+ userId: indexedSession.userId,
5692
+ userNamespace,
5693
+ authStateFile: indexedSession.authStateFile
5694
+ });
5416
5695
  }
5417
5696
  if (isPublicRigAuthBootstrapRoute(input.pathname)) {
5418
- return { authorized: true, actor: null, reason: "public-bootstrap" };
5697
+ return requestAuthResult({ authorized: true, actor: null, reason: "public-bootstrap" });
5419
5698
  }
5420
5699
  if (!input.serverAuthToken && !storedToken) {
5421
5700
  if (isLoopbackRequest(input.req)) {
5422
- return { authorized: true, actor: null, reason: "loopback-dev-no-auth" };
5701
+ return requestAuthResult({ authorized: true, actor: null, reason: "loopback-dev-no-auth" });
5423
5702
  }
5424
- return { authorized: false, actor: null, reason: "auth-required" };
5703
+ return requestAuthResult({ authorized: false, actor: null, reason: "auth-required" });
5425
5704
  }
5426
- return { authorized: false, actor: null, reason: storedToken ? "github-token-required" : "auth-required" };
5705
+ return requestAuthResult({ authorized: false, actor: null, reason: storedToken ? "github-token-required" : "auth-required" });
5427
5706
  }
5428
5707
  async function fetchGitHubUserInfo(token) {
5429
5708
  const response = await fetch("https://api.github.com/user", {
@@ -5443,6 +5722,67 @@ async function fetchGitHubUserInfo(token) {
5443
5722
  scopes: cleanHeaderScopes(response.headers.get("x-oauth-scopes"))
5444
5723
  };
5445
5724
  }
5725
+ function shouldWriteRootAuthCompat(projectRoot) {
5726
+ if (process.env.RIG_REMOTE_USER_NAMESPACE_ROOT?.trim())
5727
+ return false;
5728
+ const stateDir2 = normalizeString(process.env.RIG_STATE_DIR);
5729
+ if (!stateDir2)
5730
+ return true;
5731
+ return resolve20(stateDir2) === resolve20(projectRoot, ".rig", "state");
5732
+ }
5733
+ function requestScopedRegistryRoot(stateProjectRoot, requestAuth) {
5734
+ return requestAuth.userNamespace?.root ?? stateProjectRoot;
5735
+ }
5736
+ function requestScopedAuthStore(stateProjectRoot, requestAuth) {
5737
+ return requestAuth.authStateFile ? createGitHubAuthStoreFromStateFile(requestAuth.authStateFile) : createGitHubAuthStore(stateProjectRoot);
5738
+ }
5739
+ function userNamespaceResponse(namespace) {
5740
+ return namespace ? serializeRemoteUserNamespace(namespace) : undefined;
5741
+ }
5742
+ function resolveNamespacedBaseDir(input) {
5743
+ if (input.explicitBaseDir)
5744
+ return input.explicitBaseDir;
5745
+ const envBase = normalizeString(process.env[input.envName]);
5746
+ if (input.userNamespace) {
5747
+ return envBase ? resolve20(envBase, input.userNamespace.key) : input.userNamespace[input.legacySubdir === "remote-checkouts" ? "checkoutBaseDir" : "snapshotBaseDir"];
5748
+ }
5749
+ return envBase ?? (normalizeString(process.env.RIG_STATE_DIR) ? resolve20(normalizeString(process.env.RIG_STATE_DIR), input.legacySubdir) : resolve20(input.legacyProjectRoot, ".rig", input.legacySubdir));
5750
+ }
5751
+ function explicitCheckoutKey(body, checkoutInput, requestAuth) {
5752
+ return normalizeString(body.checkoutKey) ?? normalizeString(checkoutInput.checkoutKey) ?? normalizeString(checkoutInput.key) ?? (requestAuth.userNamespace ? undefined : "default");
5753
+ }
5754
+ function saveGitHubTokenForRemoteUser(input) {
5755
+ const namespace = resolveRemoteUserNamespace(input.projectRoot, { userId: input.user.userId, login: input.user.login });
5756
+ writeRemoteUserNamespaceMetadata(namespace);
5757
+ const store = createGitHubAuthStoreFromStateFile(namespace.authStateFile);
5758
+ store.saveToken({
5759
+ token: input.token,
5760
+ tokenSource: input.tokenSource,
5761
+ login: input.user.login,
5762
+ userId: input.user.userId,
5763
+ scopes: input.user.scopes,
5764
+ selectedRepo: input.selectedRepo
5765
+ });
5766
+ const apiSession = store.createApiSession();
5767
+ registerGitHubApiSession({ projectRoot: input.projectRoot, token: apiSession.token, namespace, selectedRepo: input.selectedRepo });
5768
+ const requestedRoot = normalizeString(input.requestedProjectRoot);
5769
+ if (requestedRoot && isAbsolute4(requestedRoot) && existsSync13(resolve20(requestedRoot))) {
5770
+ copyGitHubAuthStateToLocalProjectRoot(namespace.authStateFile, resolve20(requestedRoot));
5771
+ }
5772
+ if (shouldWriteRootAuthCompat(input.projectRoot)) {
5773
+ const rootStore = createGitHubAuthStore(input.projectRoot);
5774
+ rootStore.saveToken({
5775
+ token: input.token,
5776
+ tokenSource: input.tokenSource,
5777
+ login: input.user.login,
5778
+ userId: input.user.userId,
5779
+ scopes: input.user.scopes,
5780
+ selectedRepo: input.selectedRepo
5781
+ });
5782
+ rootStore.createApiSession();
5783
+ }
5784
+ return { store, namespace, apiSessionToken: apiSession.token };
5785
+ }
5446
5786
  async function postGitHubForm(endpoint, body) {
5447
5787
  const response = await fetch(endpoint, {
5448
5788
  method: "POST",
@@ -5460,11 +5800,11 @@ function resolveRequestedProjectRoot(currentRoot, rawRoot) {
5460
5800
  const requestedRoot = normalizeString(rawRoot);
5461
5801
  if (!requestedRoot)
5462
5802
  return currentRoot;
5463
- if (!isAbsolute3(requestedRoot)) {
5803
+ if (!isAbsolute4(requestedRoot)) {
5464
5804
  throw new Error("projectRoot must be an absolute path on the Rig server host");
5465
5805
  }
5466
- const normalizedRoot = resolve18(requestedRoot);
5467
- if (!existsSync11(normalizedRoot)) {
5806
+ const normalizedRoot = resolve20(requestedRoot);
5807
+ if (!existsSync13(normalizedRoot)) {
5468
5808
  throw new Error("projectRoot does not exist on the Rig server host");
5469
5809
  }
5470
5810
  return normalizedRoot;
@@ -6125,7 +6465,7 @@ data: ${JSON.stringify({ connectedAt: new Date().toISOString() })}
6125
6465
  }
6126
6466
  if (url.pathname === "/api/server/status") {
6127
6467
  const config = buildProjectConfigStatus(state.projectRoot);
6128
- const taskSource = await buildTaskSourceStatus(state, config);
6468
+ const taskSource = await buildTaskSourceStatus(state, config, requestAuth);
6129
6469
  return deps.jsonResponse({
6130
6470
  ok: true,
6131
6471
  projectRoot: state.projectRoot,
@@ -6149,8 +6489,9 @@ data: ${JSON.stringify({ connectedAt: new Date().toISOString() })}
6149
6489
  path: normalizeString(rawCheckout?.path) ?? state.projectRoot,
6150
6490
  ref: normalizeString(rawCheckout?.ref) ?? undefined
6151
6491
  } : undefined;
6152
- const record = upsertProjectRecord(state.projectRoot, { repoSlug, checkout });
6153
- return deps.jsonResponse({ ok: true, project: record });
6492
+ const registryRoot = requestScopedRegistryRoot(state.projectRoot, requestAuth);
6493
+ const record = upsertProjectRecord(registryRoot, { repoSlug, checkout });
6494
+ return deps.jsonResponse({ ok: true, project: record, userNamespace: userNamespaceResponse(requestAuth.userNamespace) });
6154
6495
  }
6155
6496
  const snapshotUploadMatch = url.pathname.match(/^\/api\/projects\/(.+?)\/upload-snapshot$/);
6156
6497
  if (snapshotUploadMatch && req.method === "POST") {
@@ -6163,8 +6504,15 @@ data: ${JSON.stringify({ connectedAt: new Date().toISOString() })}
6163
6504
  if (!archiveContentBase64) {
6164
6505
  return deps.badRequest("archiveContentBase64 is required");
6165
6506
  }
6166
- const baseDir = normalizeString(body.baseDir) ?? normalizeString(process.env.RIG_REMOTE_SNAPSHOT_BASE_DIR) ?? (normalizeString(process.env.RIG_STATE_DIR) ? resolve18(normalizeString(process.env.RIG_STATE_DIR), "remote-snapshots") : resolve18(state.projectRoot, ".rig", "remote-snapshots"));
6167
- const checkoutKey = normalizeString(body.checkoutKey) ?? "default";
6507
+ const registryRoot = requestScopedRegistryRoot(state.projectRoot, requestAuth);
6508
+ const baseDir = resolveNamespacedBaseDir({
6509
+ explicitBaseDir: normalizeString(body.baseDir),
6510
+ envName: "RIG_REMOTE_SNAPSHOT_BASE_DIR",
6511
+ userNamespace: requestAuth.userNamespace,
6512
+ legacyProjectRoot: state.projectRoot,
6513
+ legacySubdir: "remote-snapshots"
6514
+ });
6515
+ const checkoutKey = explicitCheckoutKey(body, body, requestAuth);
6168
6516
  try {
6169
6517
  const archive = parseSnapshotArchiveContentBase64(archiveContentBase64);
6170
6518
  const checkout = extractUploadedSnapshotArchive({
@@ -6177,14 +6525,14 @@ data: ${JSON.stringify({ connectedAt: new Date().toISOString() })}
6177
6525
  const checkoutRepair = repairRemoteCheckoutForRig(checkout.path, repoSlug);
6178
6526
  const packageInstall = installRemoteCheckoutPackages(checkout.path);
6179
6527
  const postInstallConfigValidation = validateRemoteCheckoutRigConfig(checkout.path);
6180
- const project = linkProjectCheckout(state.projectRoot, repoSlug, {
6528
+ const project = linkProjectCheckout(registryRoot, repoSlug, {
6181
6529
  kind: "uploaded-snapshot",
6182
6530
  path: checkout.path,
6183
6531
  ref: checkout.snapshotId
6184
6532
  });
6185
6533
  deps.snapshotService.invalidate("uploaded-snapshot-checkout");
6186
6534
  deps.broadcastSnapshotInvalidation(state, "uploaded-snapshot-checkout");
6187
- return deps.jsonResponse({ ok: true, checkout, project, checkoutRepair, packageInstall, postInstallConfigValidation });
6535
+ return deps.jsonResponse({ ok: true, checkout, project, checkoutRepair, packageInstall, postInstallConfigValidation, userNamespace: userNamespaceResponse(requestAuth.userNamespace) });
6188
6536
  } catch (error) {
6189
6537
  return deps.jsonResponse({
6190
6538
  ok: false,
@@ -6204,10 +6552,17 @@ data: ${JSON.stringify({ connectedAt: new Date().toISOString() })}
6204
6552
  if (kind !== "managed-clone" && kind !== "current-ref" && kind !== "existing-path") {
6205
6553
  return deps.jsonResponse({ ok: false, error: "checkout kind must be managed-clone, current-ref, or existing-path" }, 400);
6206
6554
  }
6207
- const baseDir = normalizeString(body.baseDir) ?? normalizeString(checkoutInput.baseDir) ?? normalizeString(process.env.RIG_REMOTE_CHECKOUT_BASE_DIR) ?? (normalizeString(process.env.RIG_STATE_DIR) ? resolve18(normalizeString(process.env.RIG_STATE_DIR), "remote-checkouts") : resolve18(state.projectRoot, ".rig", "remote-checkouts"));
6208
- const checkoutKey = normalizeString(body.checkoutKey) ?? normalizeString(checkoutInput.checkoutKey) ?? normalizeString(checkoutInput.key) ?? "default";
6555
+ const registryRoot = requestScopedRegistryRoot(state.projectRoot, requestAuth);
6556
+ const baseDir = resolveNamespacedBaseDir({
6557
+ explicitBaseDir: normalizeString(body.baseDir) ?? normalizeString(checkoutInput.baseDir),
6558
+ envName: "RIG_REMOTE_CHECKOUT_BASE_DIR",
6559
+ userNamespace: requestAuth.userNamespace,
6560
+ legacyProjectRoot: state.projectRoot,
6561
+ legacySubdir: "remote-checkouts"
6562
+ });
6563
+ const checkoutKey = explicitCheckoutKey(body, checkoutInput, requestAuth);
6209
6564
  const repoUrl = normalizeString(body.repoUrl) ?? normalizeString(checkoutInput.repoUrl) ?? `https://github.com/${repoSlug}.git`;
6210
- const credentialToken = createGitHubAuthStore(state.projectRoot).readToken();
6565
+ const credentialToken = requestScopedAuthStore(state.projectRoot, requestAuth).readToken();
6211
6566
  try {
6212
6567
  const checkout = await prepareRemoteCheckout({
6213
6568
  command: runRemoteCheckoutCommand,
@@ -6216,14 +6571,14 @@ data: ${JSON.stringify({ connectedAt: new Date().toISOString() })}
6216
6571
  const checkoutRepair = repairRemoteCheckoutForRig(checkout.path, repoSlug);
6217
6572
  const packageInstall = installRemoteCheckoutPackages(checkout.path);
6218
6573
  const postInstallConfigValidation = validateRemoteCheckoutRigConfig(checkout.path);
6219
- const project = linkProjectCheckout(state.projectRoot, repoSlug, {
6574
+ const project = linkProjectCheckout(registryRoot, repoSlug, {
6220
6575
  kind: checkout.kind,
6221
6576
  path: checkout.path,
6222
6577
  ref: checkout.ref ?? checkout.snapshotId ?? undefined
6223
6578
  });
6224
6579
  deps.snapshotService.invalidate("remote-checkout-prepared");
6225
6580
  deps.broadcastSnapshotInvalidation(state, "remote-checkout-prepared");
6226
- return deps.jsonResponse({ ok: true, checkout, project, checkoutRepair, packageInstall, postInstallConfigValidation });
6581
+ return deps.jsonResponse({ ok: true, checkout, project, checkoutRepair, packageInstall, postInstallConfigValidation, userNamespace: userNamespaceResponse(requestAuth.userNamespace) });
6227
6582
  } catch (error) {
6228
6583
  return deps.jsonResponse({ ok: false, error: error instanceof Error ? error.message : String(error) }, 400);
6229
6584
  }
@@ -6240,16 +6595,18 @@ data: ${JSON.stringify({ connectedAt: new Date().toISOString() })}
6240
6595
  if (kind !== "local" && kind !== "managed-clone" && kind !== "current-ref" && kind !== "uploaded-snapshot" && kind !== "existing-path") {
6241
6596
  return deps.jsonResponse({ ok: false, error: "checkout kind is required" }, 400);
6242
6597
  }
6243
- const project = linkProjectCheckout(state.projectRoot, repoSlug, {
6598
+ const registryRoot = requestScopedRegistryRoot(state.projectRoot, requestAuth);
6599
+ const project = linkProjectCheckout(registryRoot, repoSlug, {
6244
6600
  kind,
6245
6601
  path: normalizeString(body.path) ?? state.projectRoot,
6246
6602
  ref: normalizeString(body.ref) ?? undefined
6247
6603
  });
6248
- return deps.jsonResponse({ ok: true, project });
6604
+ return deps.jsonResponse({ ok: true, project, userNamespace: userNamespaceResponse(requestAuth.userNamespace) });
6249
6605
  }
6250
6606
  if (req.method === "GET") {
6251
- const project = getProjectRecord(state.projectRoot, repoSlug);
6252
- return project ? deps.jsonResponse({ ok: true, project }) : deps.notFound();
6607
+ const registryRoot = requestScopedRegistryRoot(state.projectRoot, requestAuth);
6608
+ const project = getProjectRecord(registryRoot, repoSlug);
6609
+ return project ? deps.jsonResponse({ ok: true, project, userNamespace: userNamespaceResponse(requestAuth.userNamespace) }) : deps.notFound();
6253
6610
  }
6254
6611
  }
6255
6612
  if (url.pathname === "/api/server/project-root" && req.method === "POST") {
@@ -6258,13 +6615,26 @@ data: ${JSON.stringify({ connectedAt: new Date().toISOString() })}
6258
6615
  if (!requestedRoot) {
6259
6616
  return deps.badRequest("projectRoot is required");
6260
6617
  }
6261
- if (!isAbsolute3(requestedRoot)) {
6618
+ if (!isAbsolute4(requestedRoot)) {
6262
6619
  return deps.badRequest("projectRoot must be an absolute path on the Rig server host");
6263
6620
  }
6264
- const normalizedRoot = resolve18(requestedRoot);
6265
- const exists = existsSync11(normalizedRoot);
6266
- if (exists) {
6267
- createGitHubAuthStore(state.projectRoot).copyToProjectRoot(normalizedRoot);
6621
+ const normalizedRoot = resolve20(requestedRoot);
6622
+ const exists = existsSync13(normalizedRoot);
6623
+ if (exists && requestAuth.userNamespace) {
6624
+ const allowedByNamespace = isPathInsideNamespace(requestAuth.userNamespace.root, normalizedRoot);
6625
+ const allowedByRegistry = projectRegistryContainsCheckout(requestAuth.userNamespace.root, normalizedRoot);
6626
+ if (!allowedByNamespace && !allowedByRegistry) {
6627
+ return deps.jsonResponse({
6628
+ ok: false,
6629
+ error: "Requested project root is outside the authenticated GitHub user namespace.",
6630
+ projectRoot: state.projectRoot,
6631
+ requestedProjectRoot: normalizedRoot,
6632
+ userNamespace: userNamespaceResponse(requestAuth.userNamespace)
6633
+ }, 403);
6634
+ }
6635
+ copyGitHubAuthStateToLocalProjectRoot(requestAuth.userNamespace.authStateFile, normalizedRoot);
6636
+ } else if (exists) {
6637
+ createGitHubAuthStore(state.projectRoot).copyToLocalProjectRoot(normalizedRoot);
6268
6638
  }
6269
6639
  const control = buildServerControlStatus();
6270
6640
  const switchCommand = process.env.RIG_PROJECT_ROOT_SWITCH_COMMAND?.trim();
@@ -6279,7 +6649,7 @@ data: ${JSON.stringify({ connectedAt: new Date().toISOString() })}
6279
6649
  message: "Requested project root does not exist on the Rig server host."
6280
6650
  }, 404);
6281
6651
  }
6282
- if (!existsSync11(resolve18(normalizedRoot, "rig.config.ts")) && !existsSync11(resolve18(normalizedRoot, "rig.config.json"))) {
6652
+ if (!existsSync13(resolve20(normalizedRoot, "rig.config.ts")) && !existsSync13(resolve20(normalizedRoot, "rig.config.json"))) {
6283
6653
  return deps.jsonResponse({
6284
6654
  ok: false,
6285
6655
  projectRoot: state.projectRoot,
@@ -6314,6 +6684,7 @@ data: ${JSON.stringify({ connectedAt: new Date().toISOString() })}
6314
6684
  exists,
6315
6685
  control,
6316
6686
  requiresRestart: false,
6687
+ userNamespace: userNamespaceResponse(requestAuth.userNamespace),
6317
6688
  message: "Project-root switch accepted. Rig server restart has been scheduled."
6318
6689
  }, 202);
6319
6690
  }
@@ -6328,11 +6699,11 @@ data: ${JSON.stringify({ connectedAt: new Date().toISOString() })}
6328
6699
  }, 409);
6329
6700
  }
6330
6701
  if (url.pathname === "/api/github/auth/status") {
6331
- const store = createGitHubAuthStore(state.projectRoot);
6332
- return deps.jsonResponse({ ok: true, ...store.status({ oauthConfigured: Boolean(process.env.RIG_GITHUB_OAUTH_CLIENT_ID?.trim()) }) });
6702
+ const store = requestScopedAuthStore(state.projectRoot, requestAuth);
6703
+ return deps.jsonResponse({ ok: true, ...store.status({ oauthConfigured: Boolean(process.env.RIG_GITHUB_OAUTH_CLIENT_ID?.trim()) }), userNamespace: userNamespaceResponse(requestAuth.userNamespace) });
6333
6704
  }
6334
6705
  if (url.pathname === "/api/github/repo/permissions") {
6335
- const store = createGitHubAuthStore(state.projectRoot);
6706
+ const store = requestScopedAuthStore(state.projectRoot, requestAuth);
6336
6707
  const auth = store.status({ oauthConfigured: Boolean(process.env.RIG_GITHUB_OAUTH_CLIENT_ID?.trim()) });
6337
6708
  if (!auth.signedIn) {
6338
6709
  return deps.jsonResponse({ ok: false, signedIn: false, canOpenPullRequest: false, reason: "not-authenticated" }, 401);
@@ -6360,24 +6731,20 @@ data: ${JSON.stringify({ connectedAt: new Date().toISOString() })}
6360
6731
  }
6361
6732
  try {
6362
6733
  const user = await fetchGitHubUserInfo(token);
6363
- const storeRoots = [
6364
- state.projectRoot,
6365
- ...requestedProjectRoot && isAbsolute3(requestedProjectRoot) && existsSync11(resolve18(requestedProjectRoot)) ? [resolve18(requestedProjectRoot)] : []
6366
- ].filter((root, index, roots) => roots.indexOf(root) === index);
6367
- const stores = storeRoots.map((root) => createGitHubAuthStore(root));
6368
- for (const store2 of stores) {
6369
- store2.saveToken({
6370
- token,
6371
- tokenSource: "manual-token",
6372
- login: user.login,
6373
- userId: user.userId,
6374
- scopes: user.scopes,
6375
- selectedRepo
6376
- });
6377
- }
6378
- const store = stores[stores.length - 1] ?? createGitHubAuthStore(state.projectRoot);
6379
- const apiSession = store.createApiSession();
6380
- return deps.jsonResponse({ ok: true, ...store.status({ oauthConfigured: Boolean(process.env.RIG_GITHUB_OAUTH_CLIENT_ID?.trim()) }), apiSessionToken: apiSession.token });
6734
+ const saved = saveGitHubTokenForRemoteUser({
6735
+ projectRoot: state.projectRoot,
6736
+ token,
6737
+ tokenSource: "manual-token",
6738
+ user,
6739
+ selectedRepo,
6740
+ requestedProjectRoot
6741
+ });
6742
+ return deps.jsonResponse({
6743
+ ok: true,
6744
+ ...saved.store.status({ oauthConfigured: Boolean(process.env.RIG_GITHUB_OAUTH_CLIENT_ID?.trim()) }),
6745
+ apiSessionToken: saved.apiSessionToken,
6746
+ userNamespace: userNamespaceResponse(saved.namespace)
6747
+ });
6381
6748
  } catch (error) {
6382
6749
  const message = error instanceof Error ? error.message : String(error);
6383
6750
  return deps.jsonResponse({ ok: false, error: message }, 400);
@@ -6444,9 +6811,21 @@ data: ${JSON.stringify({ connectedAt: new Date().toISOString() })}
6444
6811
  }
6445
6812
  const token = result.payload.access_token;
6446
6813
  const user = await fetchGitHubUserInfo(token);
6447
- store.saveToken({ token, tokenSource: "oauth-device", login: user.login, userId: user.userId, scopes: user.scopes });
6448
- const apiSession = store.createApiSession();
6449
- return deps.jsonResponse({ ok: true, status: "signed-in", ...store.status({ oauthConfigured: true }), apiSessionToken: apiSession.token });
6814
+ const saved = saveGitHubTokenForRemoteUser({
6815
+ projectRoot: state.projectRoot,
6816
+ token,
6817
+ tokenSource: "oauth-device",
6818
+ user,
6819
+ selectedRepo: null
6820
+ });
6821
+ store.clearPendingDevice(pollId);
6822
+ return deps.jsonResponse({
6823
+ ok: true,
6824
+ status: "signed-in",
6825
+ ...saved.store.status({ oauthConfigured: true }),
6826
+ apiSessionToken: saved.apiSessionToken,
6827
+ userNamespace: userNamespaceResponse(saved.namespace)
6828
+ });
6450
6829
  }
6451
6830
  if (url.pathname === "/api/github/repo/probe" && req.method === "POST") {
6452
6831
  const body = await deps.readJsonBody(req);
@@ -6455,7 +6834,7 @@ data: ${JSON.stringify({ connectedAt: new Date().toISOString() })}
6455
6834
  if (!owner || !repo) {
6456
6835
  return deps.badRequest("owner and repo are required");
6457
6836
  }
6458
- const store = createGitHubAuthStore(state.projectRoot);
6837
+ const store = requestScopedAuthStore(state.projectRoot, requestAuth);
6459
6838
  const authStatus = store.status({ oauthConfigured: Boolean(process.env.RIG_GITHUB_OAUTH_CLIENT_ID?.trim()) });
6460
6839
  const probe = await probeGitHubRepository({ owner, repo, token: store.readToken(), scopes: authStatus.scopes });
6461
6840
  return deps.jsonResponse({ ok: probe.ok, probe }, probe.ok ? 200 : 400);
@@ -6476,7 +6855,7 @@ data: ${JSON.stringify({ connectedAt: new Date().toISOString() })}
6476
6855
  return deps.badRequest(error instanceof Error ? error.message : String(error));
6477
6856
  }
6478
6857
  const authStatus = createGitHubAuthStore(state.projectRoot).status({ oauthConfigured: Boolean(process.env.RIG_GITHUB_OAUTH_CLIENT_ID?.trim()) });
6479
- const configPath = resolve18(targetRoot, "rig.config.ts");
6858
+ const configPath = resolve20(targetRoot, "rig.config.ts");
6480
6859
  const source = buildGitHubProjectConfigSource({
6481
6860
  projectName: rawProjectName,
6482
6861
  owner,
@@ -6488,8 +6867,8 @@ data: ${JSON.stringify({ connectedAt: new Date().toISOString() })}
6488
6867
  ok: true,
6489
6868
  projectRoot: targetRoot,
6490
6869
  configPath,
6491
- exists: existsSync11(configPath),
6492
- requiresOverwrite: existsSync11(configPath),
6870
+ exists: existsSync13(configPath),
6871
+ requiresOverwrite: existsSync13(configPath),
6493
6872
  source,
6494
6873
  owner,
6495
6874
  repo,
@@ -6525,8 +6904,8 @@ data: ${JSON.stringify({ connectedAt: new Date().toISOString() })}
6525
6904
  assignee,
6526
6905
  githubUserId: authStatus.userId ?? authStatus.login
6527
6906
  });
6528
- const configPath = resolve18(targetRoot, "rig.config.ts");
6529
- if (existsSync11(configPath) && !overwrite) {
6907
+ const configPath = resolve20(targetRoot, "rig.config.ts");
6908
+ if (existsSync13(configPath) && !overwrite) {
6530
6909
  return deps.jsonResponse({
6531
6910
  ok: false,
6532
6911
  error: "rig.config.ts already exists. Confirm overwrite to replace it; Rig will create a backup first.",
@@ -6542,11 +6921,11 @@ data: ${JSON.stringify({ connectedAt: new Date().toISOString() })}
6542
6921
  return deps.jsonResponse({ ok: false, error: repoProbe.message, repoProbe }, 400);
6543
6922
  }
6544
6923
  let backupPath = null;
6545
- if (existsSync11(configPath)) {
6924
+ if (existsSync13(configPath)) {
6546
6925
  backupPath = backupConfigPath(configPath);
6547
- copyFileSync(configPath, backupPath);
6926
+ copyFileSync2(configPath, backupPath);
6548
6927
  }
6549
- writeFileSync10(configPath, source, "utf8");
6928
+ writeFileSync12(configPath, source, "utf8");
6550
6929
  const selectedRepo = `${owner}/${repo}`;
6551
6930
  store.saveSelectedRepo(selectedRepo);
6552
6931
  const targetStore = createGitHubAuthStore(targetRoot);
@@ -6832,7 +7211,7 @@ data: ${JSON.stringify({ connectedAt: new Date().toISOString() })}
6832
7211
  }
6833
7212
  if (url.pathname === "/api/pi-rig/install" && req.method === "POST") {
6834
7213
  const configuredPackageSource = normalizeString(process.env.RIG_PI_RIG_PACKAGE_SOURCE);
6835
- const packageSource = configuredPackageSource ?? [process.env.RIG_HOST_PROJECT_ROOT, process.cwd(), state.projectRoot].map((root) => normalizeString(root)).filter((root) => Boolean(root)).map((root) => resolve18(root, "packages", "pi-rig")).find((candidate) => existsSync11(resolve18(candidate, "package.json"))) ?? "npm:@rig/pi-rig";
7214
+ const packageSource = configuredPackageSource ?? [process.env.RIG_HOST_PROJECT_ROOT, process.cwd(), state.projectRoot].map((root) => normalizeString(root)).filter((root) => Boolean(root)).map((root) => resolve20(root, "packages", "pi-rig")).find((candidate) => existsSync13(resolve20(candidate, "package.json"))) ?? "npm:@rig/pi-rig";
6836
7215
  if (process.env.RIG_TEST_FAKE_PI_INSTALL === "1") {
6837
7216
  return deps.jsonResponse({ ok: true, installed: true, piOk: true, piRigOk: true, extensionPath: "remote:~/.pi/agent/extensions/pi-rig", packageSource });
6838
7217
  }
@@ -7148,9 +7527,9 @@ data: ${JSON.stringify({ connectedAt: new Date().toISOString() })}
7148
7527
  } catch {
7149
7528
  return deps.badRequest("Invalid artifact path");
7150
7529
  }
7151
- mkdirSync11(dirname12(artifactPath), { recursive: true });
7530
+ mkdirSync13(dirname15(artifactPath), { recursive: true });
7152
7531
  const bytes = Buffer.from(contentBase64, "base64");
7153
- writeFileSync10(artifactPath, bytes);
7532
+ writeFileSync12(artifactPath, bytes);
7154
7533
  writeJsonFile4(`${artifactPath}.json`, {
7155
7534
  workspaceId: normalizeString(body.workspaceId) ?? RIG_WORKSPACE_ID,
7156
7535
  runId,
@@ -7193,7 +7572,6 @@ data: ${JSON.stringify({ connectedAt: new Date().toISOString() })}
7193
7572
  hostId,
7194
7573
  endpointId: leaseId
7195
7574
  });
7196
- await updateRemoteRunTaskSourceLifecycle(state.projectRoot, { ...run, status: "completed", completedAt, hostId, endpointId: leaseId }, "closed", "Remote Rig task run completed and closed this task.");
7197
7575
  await deps.enqueueRunLinearEvent(state.projectRoot, {
7198
7576
  type: "run.completed",
7199
7577
  runId,
@@ -7312,12 +7690,12 @@ data: ${JSON.stringify({ connectedAt: new Date().toISOString() })}
7312
7690
  try {
7313
7691
  const runsRoot = resolveAuthorityPaths(state.projectRoot).runsDir;
7314
7692
  const runRoot = deps.normalizeRelativePath(runsRoot, runId);
7315
- const artifactsRoot = resolve18(runRoot, "remote-artifacts");
7693
+ const artifactsRoot = resolve20(runRoot, "remote-artifacts");
7316
7694
  artifactPath = deps.normalizeRelativePath(artifactsRoot, fileName);
7317
7695
  } catch {
7318
7696
  return deps.badRequest("Invalid artifact path");
7319
7697
  }
7320
- if (!existsSync11(artifactPath)) {
7698
+ if (!existsSync13(artifactPath)) {
7321
7699
  return deps.notFound();
7322
7700
  }
7323
7701
  return new Response(Bun.file(artifactPath));
@@ -8210,8 +8588,8 @@ async function routeWebSocketRequest(state, deps, request) {
8210
8588
  }
8211
8589
 
8212
8590
  // packages/server/src/server-helpers/inspector-jobs.ts
8213
- import { existsSync as existsSync15, mkdirSync as mkdirSync14, readFileSync as readFileSync10, writeFileSync as writeFileSync13 } from "fs";
8214
- import { dirname as dirname15, resolve as resolve21 } from "path";
8591
+ import { existsSync as existsSync17, mkdirSync as mkdirSync16, readFileSync as readFileSync12, writeFileSync as writeFileSync15 } from "fs";
8592
+ import { dirname as dirname18, resolve as resolve23 } from "path";
8215
8593
  import { readJsonFile as readJsonFile3 } from "@rig/runtime/control-plane/authority-files";
8216
8594
  import { resolveMonorepoRoot as resolveMonorepoRoot5 } from "@rig/runtime/control-plane/native/utils";
8217
8595
  import { normalizeTaskLifecycleStatus as normalizeTaskLifecycleStatus2 } from "@rig/runtime/control-plane/state-sync/types";
@@ -8319,8 +8697,8 @@ import { randomUUID as randomUUID3 } from "crypto";
8319
8697
 
8320
8698
  // packages/server/src/inspector/mission.ts
8321
8699
  import { randomUUID as randomUUID2 } from "crypto";
8322
- import { appendFileSync, existsSync as existsSync12, mkdirSync as mkdirSync12, readFileSync as readFileSync8, readdirSync as readdirSync4, renameSync, writeFileSync as writeFileSync11 } from "fs";
8323
- import { dirname as dirname13, join, resolve as resolve19 } from "path";
8700
+ import { appendFileSync, existsSync as existsSync14, mkdirSync as mkdirSync14, readFileSync as readFileSync10, readdirSync as readdirSync4, renameSync, writeFileSync as writeFileSync13 } from "fs";
8701
+ import { dirname as dirname16, join, resolve as resolve21 } from "path";
8324
8702
  function isJsonValue(value) {
8325
8703
  if (value === null)
8326
8704
  return true;
@@ -8360,7 +8738,7 @@ function isRecord2(value) {
8360
8738
  }
8361
8739
  function readJsonRecord(path) {
8362
8740
  try {
8363
- const parsed = JSON.parse(readFileSync8(path, "utf8"));
8741
+ const parsed = JSON.parse(readFileSync10(path, "utf8"));
8364
8742
  if (!isRecord2(parsed)) {
8365
8743
  return { ok: false, error: `Mission file ${path} does not contain an object` };
8366
8744
  }
@@ -8440,14 +8818,14 @@ function missionActionDetails(mission) {
8440
8818
  };
8441
8819
  }
8442
8820
  function writeJsonFile5(path, value) {
8443
- mkdirSync12(dirname13(path), { recursive: true });
8821
+ mkdirSync14(dirname16(path), { recursive: true });
8444
8822
  const tempPath = `${path}.${process.pid}.${Date.now()}.tmp`;
8445
- writeFileSync11(tempPath, `${JSON.stringify(value, null, 2)}
8823
+ writeFileSync13(tempPath, `${JSON.stringify(value, null, 2)}
8446
8824
  `, "utf8");
8447
8825
  renameSync(tempPath, path);
8448
8826
  }
8449
8827
  function resolveInspectorMissionPaths(projectRoot) {
8450
- const inspectorDir = resolve19(resolveRigServerPaths(projectRoot).stateDir, "inspector");
8828
+ const inspectorDir = resolve21(resolveRigServerPaths(projectRoot).stateDir, "inspector");
8451
8829
  return {
8452
8830
  inspectorDir,
8453
8831
  missionsDir: join(inspectorDir, "missions"),
@@ -8456,8 +8834,8 @@ function resolveInspectorMissionPaths(projectRoot) {
8456
8834
  }
8457
8835
  function createInspectorMissionController(options) {
8458
8836
  const paths = resolveInspectorMissionPaths(options.projectRoot);
8459
- mkdirSync12(paths.missionsDir, { recursive: true });
8460
- mkdirSync12(paths.journalsDir, { recursive: true });
8837
+ mkdirSync14(paths.missionsDir, { recursive: true });
8838
+ mkdirSync14(paths.journalsDir, { recursive: true });
8461
8839
  const now = options.now ?? (() => new Date().toISOString());
8462
8840
  const nextId = options.idGenerator ?? (() => `mission:${randomUUID2()}`);
8463
8841
  function missionPath(missionId) {
@@ -8467,15 +8845,15 @@ function createInspectorMissionController(options) {
8467
8845
  return join(paths.journalsDir, `${missionId}.jsonl`);
8468
8846
  }
8469
8847
  function appendMissionJournal(entry) {
8470
- mkdirSync12(paths.journalsDir, { recursive: true });
8848
+ mkdirSync14(paths.journalsDir, { recursive: true });
8471
8849
  appendFileSync(journalPath(entry.missionId), `${JSON.stringify(entry)}
8472
8850
  `, "utf8");
8473
8851
  }
8474
8852
  function listMissionJournal(missionId) {
8475
8853
  const path = journalPath(missionId);
8476
- if (!existsSync12(path))
8854
+ if (!existsSync14(path))
8477
8855
  return [];
8478
- return readFileSync8(path, "utf8").split(`
8856
+ return readFileSync10(path, "utf8").split(`
8479
8857
  `).filter((line) => line.trim().length > 0).map((line) => JSON.parse(line)).filter(isRecord2).map((entry) => ({
8480
8858
  id: typeof entry.id === "string" ? entry.id : `journal:${randomUUID2()}`,
8481
8859
  missionId,
@@ -8491,7 +8869,7 @@ function createInspectorMissionController(options) {
8491
8869
  }
8492
8870
  function readMissionOnly(missionId) {
8493
8871
  const path = missionPath(missionId);
8494
- if (!existsSync12(path)) {
8872
+ if (!existsSync14(path)) {
8495
8873
  return { ok: false, error: `Mission ${missionId} was not found` };
8496
8874
  }
8497
8875
  const read = readJsonRecord(path);
@@ -8542,7 +8920,7 @@ function createInspectorMissionController(options) {
8542
8920
  const source = cloneJsonRecord(input.sourceTask);
8543
8921
  const missionId = nextId();
8544
8922
  const path = missionPath(missionId);
8545
- if (existsSync12(path)) {
8923
+ if (existsSync14(path)) {
8546
8924
  const existing = readMissionOnly(missionId);
8547
8925
  if (!existing.ok)
8548
8926
  return existing;
@@ -10142,8 +10520,8 @@ function createCodexInspectorTransport(options) {
10142
10520
  const sendRequest = async (method, params) => {
10143
10521
  const id = nextRequestId;
10144
10522
  nextRequestId += 1;
10145
- const response = new Promise((resolve20, reject) => {
10146
- pendingResponses.set(id, { resolve: resolve20, reject });
10523
+ const response = new Promise((resolve22, reject) => {
10524
+ pendingResponses.set(id, { resolve: resolve22, reject });
10147
10525
  });
10148
10526
  response.catch(() => {});
10149
10527
  try {
@@ -10453,9 +10831,9 @@ function createCodexInspectorTransport(options) {
10453
10831
  }
10454
10832
  lastAssistantMessage = null;
10455
10833
  lastError = null;
10456
- const turnResult = new Promise((resolve20, reject) => {
10834
+ const turnResult = new Promise((resolve22, reject) => {
10457
10835
  currentTurn = {
10458
- resolve: resolve20,
10836
+ resolve: resolve22,
10459
10837
  reject,
10460
10838
  events: []
10461
10839
  };
@@ -10515,13 +10893,13 @@ function createCodexInspectorTransport(options) {
10515
10893
  };
10516
10894
  }
10517
10895
  function writeChildLine(child, line) {
10518
- return new Promise((resolve20, reject) => {
10896
+ return new Promise((resolve22, reject) => {
10519
10897
  child.stdin.write(line, (error) => {
10520
10898
  if (error) {
10521
10899
  reject(error);
10522
10900
  return;
10523
10901
  }
10524
- resolve20();
10902
+ resolve22();
10525
10903
  });
10526
10904
  });
10527
10905
  }
@@ -10534,10 +10912,10 @@ function terminateChild(child) {
10534
10912
  } catch {}
10535
10913
  }
10536
10914
  async function waitForChildSpawn(child) {
10537
- await new Promise((resolve20, reject) => {
10915
+ await new Promise((resolve22, reject) => {
10538
10916
  const onSpawn = () => {
10539
10917
  cleanup();
10540
- resolve20();
10918
+ resolve22();
10541
10919
  };
10542
10920
  const onError = (error) => {
10543
10921
  cleanup();
@@ -11049,8 +11427,8 @@ function createGlobalInspectorService(options) {
11049
11427
 
11050
11428
  // packages/server/src/inspector/upstream-sync.ts
11051
11429
  import { spawnSync as spawnSync4 } from "child_process";
11052
- import { existsSync as existsSync13, mkdirSync as mkdirSync13, readFileSync as readFileSync9, writeFileSync as writeFileSync12 } from "fs";
11053
- import { dirname as dirname14, resolve as resolve20 } from "path";
11430
+ import { existsSync as existsSync15, mkdirSync as mkdirSync15, readFileSync as readFileSync11, writeFileSync as writeFileSync14 } from "fs";
11431
+ import { dirname as dirname17, resolve as resolve22 } from "path";
11054
11432
  import { resolveMonorepoRoot as resolveMonorepoRoot4 } from "@rig/runtime/control-plane/native/utils";
11055
11433
  var UPSTREAM_VALIDATION_DESCRIPTIONS = {
11056
11434
  "integration:hg-auth-backport": "Preserves the upstream auth hardening cluster: nonce-backed node-client login, signature-aware JWT verification, shared-token onboarding semantics, and regression coverage.",
@@ -11188,34 +11566,34 @@ function defaultGitRunner(repoRoot, args) {
11188
11566
  }
11189
11567
  function upstreamStatePath(projectRoot, override) {
11190
11568
  if (override) {
11191
- return resolve20(override);
11569
+ return resolve22(override);
11192
11570
  }
11193
- return resolve20(resolveRigServerPaths(projectRoot).stateDir, "inspector", "upstream-sync.json");
11571
+ return resolve22(resolveRigServerPaths(projectRoot).stateDir, "inspector", "upstream-sync.json");
11194
11572
  }
11195
11573
  function readUpstreamState(projectRoot, statePath) {
11196
11574
  const path = upstreamStatePath(projectRoot, statePath);
11197
- if (!existsSync13(path)) {
11575
+ if (!existsSync15(path)) {
11198
11576
  return null;
11199
11577
  }
11200
11578
  try {
11201
- return JSON.parse(readFileSync9(path, "utf-8"));
11579
+ return JSON.parse(readFileSync11(path, "utf-8"));
11202
11580
  } catch {
11203
11581
  return null;
11204
11582
  }
11205
11583
  }
11206
11584
  function writeUpstreamState(projectRoot, state, statePath) {
11207
11585
  const path = upstreamStatePath(projectRoot, statePath);
11208
- mkdirSync13(dirname14(path), { recursive: true });
11209
- writeFileSync12(path, `${JSON.stringify(state, null, 2)}
11586
+ mkdirSync15(dirname17(path), { recursive: true });
11587
+ writeFileSync14(path, `${JSON.stringify(state, null, 2)}
11210
11588
  `, "utf8");
11211
11589
  }
11212
11590
  function readImportedRevision(projectRoot, upstreamsDocPath) {
11213
11591
  const monorepoRoot = resolveMonorepoRoot4(projectRoot);
11214
- const docPath = upstreamsDocPath ? resolve20(upstreamsDocPath) : resolve20(monorepoRoot, "docs", "UPSTREAMS.md");
11215
- if (!existsSync13(docPath)) {
11592
+ const docPath = upstreamsDocPath ? resolve22(upstreamsDocPath) : resolve22(monorepoRoot, "docs", "UPSTREAMS.md");
11593
+ if (!existsSync15(docPath)) {
11216
11594
  throw new Error(`UPSTREAMS.md not found at ${docPath}`);
11217
11595
  }
11218
- const docContent = readFileSync9(docPath, "utf-8");
11596
+ const docContent = readFileSync11(docPath, "utf-8");
11219
11597
  const revision = parseImportedUpstreamRevision(docContent, "upstream") ?? parseImportedUpstreamRevision(docContent, "humoongate");
11220
11598
  if (!revision) {
11221
11599
  throw new Error(`Failed to parse upstream imported revision from ${docPath}`);
@@ -11237,7 +11615,7 @@ function resolveRemoteBranch(repoRoot, remote, gitRunner) {
11237
11615
  return null;
11238
11616
  }
11239
11617
  function isGitCheckout(path, gitRunner) {
11240
- if (!existsSync13(resolve20(path, ".git"))) {
11618
+ if (!existsSync15(resolve22(path, ".git"))) {
11241
11619
  return false;
11242
11620
  }
11243
11621
  const result = gitRunner(path, ["rev-parse", "--is-inside-work-tree"]);
@@ -11246,12 +11624,12 @@ function isGitCheckout(path, gitRunner) {
11246
11624
  function resolveUpstreamCheckout(projectRoot, explicitCheckout, gitRunner) {
11247
11625
  const monorepoRoot = resolveMonorepoRoot4(projectRoot);
11248
11626
  const candidates = [
11249
- explicitCheckout ? resolve20(explicitCheckout) : "",
11250
- process.env.UPSTREAM_CHECKOUT?.trim() ? resolve20(process.env.UPSTREAM_CHECKOUT.trim()) : "",
11251
- process.env.HUMOONGATE_UPSTREAM_CHECKOUT?.trim() ? resolve20(process.env.HUMOONGATE_UPSTREAM_CHECKOUT.trim()) : "",
11252
- resolve20(projectRoot, "..", "humoongate"),
11253
- resolve20(monorepoRoot, "..", "humoongate"),
11254
- resolve20(monorepoRoot, "humoongate")
11627
+ explicitCheckout ? resolve22(explicitCheckout) : "",
11628
+ process.env.UPSTREAM_CHECKOUT?.trim() ? resolve22(process.env.UPSTREAM_CHECKOUT.trim()) : "",
11629
+ process.env.HUMOONGATE_UPSTREAM_CHECKOUT?.trim() ? resolve22(process.env.HUMOONGATE_UPSTREAM_CHECKOUT.trim()) : "",
11630
+ resolve22(projectRoot, "..", "humoongate"),
11631
+ resolve22(monorepoRoot, "..", "humoongate"),
11632
+ resolve22(monorepoRoot, "humoongate")
11255
11633
  ].filter(Boolean);
11256
11634
  for (const candidate of candidates) {
11257
11635
  if (isGitCheckout(candidate, gitRunner)) {
@@ -11487,10 +11865,10 @@ async function runUpstreamSyncScan(options) {
11487
11865
  }
11488
11866
 
11489
11867
  // packages/server/src/server-helpers/task-config.ts
11490
- import { existsSync as existsSync14 } from "fs";
11868
+ import { existsSync as existsSync16 } from "fs";
11491
11869
  async function readTaskConfig(projectRoot) {
11492
11870
  const taskConfigPath = resolveRigServerPaths(projectRoot).taskConfigPath;
11493
- if (!existsSync14(taskConfigPath)) {
11871
+ if (!existsSync16(taskConfigPath)) {
11494
11872
  return {};
11495
11873
  }
11496
11874
  try {
@@ -11526,11 +11904,11 @@ function resolveFollowupSourceCommit(input) {
11526
11904
  }
11527
11905
  async function createInspectorFollowupTask(projectRoot, input) {
11528
11906
  const monorepoRoot = resolveMonorepoRoot5(projectRoot);
11529
- const issuesPath = resolve21(monorepoRoot, ".beads", "issues.jsonl");
11530
- const taskStatePath = resolve21(monorepoRoot, ".beads", "task-state.json");
11531
- const taskConfigPath = resolve21(monorepoRoot, ".rig", "task-config.json");
11532
- mkdirSync14(dirname15(issuesPath), { recursive: true });
11533
- mkdirSync14(dirname15(taskConfigPath), { recursive: true });
11907
+ const issuesPath = resolve23(monorepoRoot, ".beads", "issues.jsonl");
11908
+ const taskStatePath = resolve23(monorepoRoot, ".beads", "task-state.json");
11909
+ const taskConfigPath = resolve23(monorepoRoot, ".rig", "task-config.json");
11910
+ mkdirSync16(dirname18(issuesPath), { recursive: true });
11911
+ mkdirSync16(dirname18(taskConfigPath), { recursive: true });
11534
11912
  const summary = normalizeString(input.summary) ?? "Inspector follow-up";
11535
11913
  const description = normalizeString(input.description) ?? normalizeString(input.details?.description) ?? `Created by the global inspector: ${summary}`;
11536
11914
  const acceptanceCriteria = normalizeString(input.acceptanceCriteria) ?? "Investigate the detected drift and port the relevant changes into Rig.";
@@ -11549,7 +11927,7 @@ async function createInspectorFollowupTask(projectRoot, input) {
11549
11927
  const sourceKey = normalizeString(input.sourceKey) ?? normalizeString(input.details?.sourceKey);
11550
11928
  const createdAt = normalizeString(input.createdAt) ?? new Date().toISOString();
11551
11929
  const status = normalizeTaskLifecycleStatus2(normalizeString(input.status) ?? "open") ?? "open";
11552
- const existingIssueLines = existsSync15(issuesPath) ? readFileSync10(issuesPath, "utf8").split(/\r?\n/).map((line) => line.trim()).filter(Boolean) : [];
11930
+ const existingIssueLines = existsSync17(issuesPath) ? readFileSync12(issuesPath, "utf8").split(/\r?\n/).map((line) => line.trim()).filter(Boolean) : [];
11553
11931
  const existingIssues = existingIssueLines.map((line) => {
11554
11932
  try {
11555
11933
  return JSON.parse(line);
@@ -11558,7 +11936,7 @@ async function createInspectorFollowupTask(projectRoot, input) {
11558
11936
  }
11559
11937
  }).filter((value) => value !== null);
11560
11938
  const existingIds = new Set(existingIssues.map((issue) => typeof issue.id === "string" ? issue.id : null).filter((value) => value !== null));
11561
- const rawTaskState = existsSync15(taskStatePath) ? readJsonFile3(taskStatePath, {}) : {};
11939
+ const rawTaskState = existsSync17(taskStatePath) ? readJsonFile3(taskStatePath, {}) : {};
11562
11940
  const tasks = rawTaskState.tasks && typeof rawTaskState.tasks === "object" && !Array.isArray(rawTaskState.tasks) ? rawTaskState.tasks : {};
11563
11941
  const existingTaskIdFromSourceKey = sourceKey == null ? null : Object.entries(tasks).find(([, metadata]) => {
11564
11942
  if (!metadata || typeof metadata !== "object" || Array.isArray(metadata)) {
@@ -11584,7 +11962,7 @@ async function createInspectorFollowupTask(projectRoot, input) {
11584
11962
  updated_at: createdAt,
11585
11963
  labels: mergedLabels
11586
11964
  };
11587
- writeFileSync13(issuesPath, existingIssueLines.length > 0 ? `${existingIssueLines.join(`
11965
+ writeFileSync15(issuesPath, existingIssueLines.length > 0 ? `${existingIssueLines.join(`
11588
11966
  `)}
11589
11967
  ${JSON.stringify(issueRecord)}
11590
11968
  ` : `${JSON.stringify(issueRecord)}
@@ -11602,7 +11980,7 @@ ${JSON.stringify(issueRecord)}
11602
11980
  labels: mergedLabels
11603
11981
  };
11604
11982
  });
11605
- writeFileSync13(issuesPath, `${updatedIssues.map((issue) => JSON.stringify(issue)).join(`
11983
+ writeFileSync15(issuesPath, `${updatedIssues.map((issue) => JSON.stringify(issue)).join(`
11606
11984
  `)}
11607
11985
  `, "utf8");
11608
11986
  }
@@ -11625,14 +12003,14 @@ ${JSON.stringify(issueRecord)}
11625
12003
  }
11626
12004
  };
11627
12005
  }
11628
- writeFileSync13(taskConfigPath, `${JSON.stringify(taskConfig, null, 2)}
12006
+ writeFileSync15(taskConfigPath, `${JSON.stringify(taskConfig, null, 2)}
11629
12007
  `, "utf8");
11630
12008
  tasks[taskId] = {
11631
12009
  status,
11632
12010
  sourceCommit: resolveFollowupSourceCommit(input),
11633
12011
  ...sourceKey ? { sourceKey } : {}
11634
12012
  };
11635
- writeFileSync13(taskStatePath, `${JSON.stringify({
12013
+ writeFileSync15(taskStatePath, `${JSON.stringify({
11636
12014
  schemaVersion: 1,
11637
12015
  baseTrackerCommit: typeof rawTaskState.baseTrackerCommit === "string" ? rawTaskState.baseTrackerCommit : null,
11638
12016
  tasks
@@ -11940,12 +12318,12 @@ function isAuthorizedLinearWebhookRequest(req) {
11940
12318
  }
11941
12319
 
11942
12320
  // packages/server/src/server-helpers/notifications.ts
11943
- import { existsSync as existsSync16, mkdirSync as mkdirSync15, readFileSync as readFileSync11 } from "fs";
11944
- import { dirname as dirname16 } from "path";
12321
+ import { existsSync as existsSync18, mkdirSync as mkdirSync17, readFileSync as readFileSync13 } from "fs";
12322
+ import { dirname as dirname19 } from "path";
11945
12323
  async function loadNotificationConfig(path) {
11946
- if (!existsSync16(path)) {
12324
+ if (!existsSync18(path)) {
11947
12325
  const defaultConfig = { targets: [] };
11948
- mkdirSync15(dirname16(path), { recursive: true });
12326
+ mkdirSync17(dirname19(path), { recursive: true });
11949
12327
  await Bun.write(path, `${JSON.stringify(defaultConfig, null, 2)}
11950
12328
  `);
11951
12329
  return defaultConfig;
@@ -11960,10 +12338,10 @@ async function loadNotificationConfig(path) {
11960
12338
  }
11961
12339
  }
11962
12340
  function readRecentEvents(file, limit) {
11963
- if (!existsSync16(file)) {
12341
+ if (!existsSync18(file)) {
11964
12342
  return [];
11965
12343
  }
11966
- const lines = readFileSync11(file, "utf-8").split(/\r?\n/).map((line) => line.trim()).filter(Boolean).slice(-limit);
12344
+ const lines = readFileSync13(file, "utf-8").split(/\r?\n/).map((line) => line.trim()).filter(Boolean).slice(-limit);
11967
12345
  const events = [];
11968
12346
  for (const line of lines) {
11969
12347
  try {
@@ -12058,11 +12436,11 @@ function extractObjectLiteralBlock(source, property) {
12058
12436
  }
12059
12437
  function readFallbackIssueAnalysisConfig(projectRoot) {
12060
12438
  for (const fileName of ["rig.config.ts", "rig.config.json"]) {
12061
- const path = resolve22(projectRoot, fileName);
12062
- if (!existsSync17(path))
12439
+ const path = resolve24(projectRoot, fileName);
12440
+ if (!existsSync19(path))
12063
12441
  continue;
12064
12442
  try {
12065
- const source = readFileSync12(path, "utf8");
12443
+ const source = readFileSync14(path, "utf8");
12066
12444
  if (fileName.endsWith(".json"))
12067
12445
  return JSON.parse(source);
12068
12446
  const issueBlock = extractObjectLiteralBlock(source, "issueAnalysis");
@@ -12195,8 +12573,8 @@ async function createIssueAnalysisRunnerForServerState(state, input) {
12195
12573
  async function withServerPathEnv(projectRoot, fn) {
12196
12574
  const waitForTurn = serverPathEnvQueue;
12197
12575
  let releaseTurn;
12198
- serverPathEnvQueue = new Promise((resolve23) => {
12199
- releaseTurn = resolve23;
12576
+ serverPathEnvQueue = new Promise((resolve25) => {
12577
+ releaseTurn = resolve25;
12200
12578
  });
12201
12579
  await waitForTurn;
12202
12580
  const paths = resolveServerAuthorityPaths(projectRoot);
@@ -12232,9 +12610,9 @@ async function withServerAuthorityEnvIfNeeded(projectRoot, fn) {
12232
12610
  return withServerPathEnv(projectRoot, fn);
12233
12611
  }
12234
12612
  async function readWorkspaceTasks(projectRoot) {
12235
- const issuesPath = resolve22(resolveMonorepoRoot6(projectRoot), ".beads", "issues.jsonl");
12613
+ const issuesPath = resolve24(resolveMonorepoRoot6(projectRoot), ".beads", "issues.jsonl");
12236
12614
  const taskConfig = await readTaskConfig(projectRoot);
12237
- if (!existsSync17(issuesPath)) {
12615
+ if (!existsSync19(issuesPath)) {
12238
12616
  return [];
12239
12617
  }
12240
12618
  const latestById = new Map;
@@ -12308,11 +12686,11 @@ function resolveTaskArtifactDirsFromRuns(projectRoot, taskId, knownRuns) {
12308
12686
  continue;
12309
12687
  add(run.artifactRoot);
12310
12688
  if (run.worktreePath) {
12311
- add(resolve22(run.worktreePath, "artifacts", taskId));
12689
+ add(resolve24(run.worktreePath, "artifacts", taskId));
12312
12690
  }
12313
12691
  }
12314
12692
  for (const artifactsRoot of listAuthorityArtifactRoots(projectRoot)) {
12315
- add(resolve22(artifactsRoot, taskId));
12693
+ add(resolve24(artifactsRoot, taskId));
12316
12694
  }
12317
12695
  return candidates;
12318
12696
  }
@@ -12326,7 +12704,7 @@ async function listArtifactSummaries(projectRoot, taskId, knownTaskIds, knownRun
12326
12704
  }
12327
12705
  }
12328
12706
  return taskIds.flatMap((currentTaskId) => {
12329
- const currentRoot = resolveTaskArtifactDirsFromRuns(projectRoot, currentTaskId, runs).find((path) => existsSync17(path));
12707
+ const currentRoot = resolveTaskArtifactDirsFromRuns(projectRoot, currentTaskId, runs).find((path) => existsSync19(path));
12330
12708
  if (!currentRoot) {
12331
12709
  return [];
12332
12710
  }
@@ -12338,7 +12716,7 @@ async function listArtifactSummaries(projectRoot, taskId, knownTaskIds, knownRun
12338
12716
  taskId: currentTaskId,
12339
12717
  kind: "file",
12340
12718
  label: fileName,
12341
- path: resolve22(currentRoot, fileName),
12719
+ path: resolve24(currentRoot, fileName),
12342
12720
  url: null,
12343
12721
  metadata: {
12344
12722
  fileName
@@ -12381,11 +12759,11 @@ function buildInspectorStreamPayload(state, sequence) {
12381
12759
  }
12382
12760
  function listRemoteRunArtifacts(projectRoot, runId) {
12383
12761
  const root = remoteArtifactsRoot(projectRoot, runId);
12384
- if (!existsSync17(root)) {
12762
+ if (!existsSync19(root)) {
12385
12763
  return [];
12386
12764
  }
12387
12765
  return readdirSync5(root, { withFileTypes: true }).filter((entry) => entry.isFile()).filter((entry) => !entry.name.endsWith(".json")).map((entry) => {
12388
- const artifactPath = resolve22(root, entry.name);
12766
+ const artifactPath = resolve24(root, entry.name);
12389
12767
  const stat = statSync6(artifactPath);
12390
12768
  const meta = readJsonFile4(`${artifactPath}.json`, null);
12391
12769
  return {
@@ -12627,8 +13005,8 @@ function fileStats(path) {
12627
13005
  }
12628
13006
  }
12629
13007
  function runFileCursor(projectRoot, run) {
12630
- const runDir = dirname17(runLogsPath(projectRoot, run.runId));
12631
- const runJson = fileStats(resolve22(runDir, "run.json"));
13008
+ const runDir = dirname20(runLogsPath(projectRoot, run.runId));
13009
+ const runJson = fileStats(resolve24(runDir, "run.json"));
12632
13010
  const timeline = fileStats(runTimelinePath(projectRoot, run.runId));
12633
13011
  const logs = fileStats(runLogsPath(projectRoot, run.runId));
12634
13012
  return {
@@ -12678,10 +13056,10 @@ function startRunFileWatcher(state, pollMs) {
12678
13056
  }, Math.max(250, Math.min(pollMs, 1000)));
12679
13057
  }
12680
13058
  function startPoller(state, pollMs) {
12681
- let offset = existsSync17(state.eventsFile) ? statSync6(state.eventsFile).size : 0;
13059
+ let offset = existsSync19(state.eventsFile) ? statSync6(state.eventsFile).size : 0;
12682
13060
  return setInterval(async () => {
12683
13061
  try {
12684
- if (!existsSync17(state.eventsFile)) {
13062
+ if (!existsSync19(state.eventsFile)) {
12685
13063
  return;
12686
13064
  }
12687
13065
  const file = await open(state.eventsFile, "r");
@@ -12809,7 +13187,7 @@ function resolveProjectRoot() {
12809
13187
  return resolveRigProjectRoot({
12810
13188
  envProjectRoot: process.env.PROJECT_RIG_ROOT ?? null,
12811
13189
  cwd: process.cwd(),
12812
- fallbackRoot: resolve22(import.meta.dir, "../..")
13190
+ fallbackRoot: resolve24(import.meta.dir, "../..")
12813
13191
  });
12814
13192
  }
12815
13193
  var __testOnly = {