@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.
package/dist/src/index.js CHANGED
@@ -663,9 +663,9 @@ function createRemoteOrchestrationSummary(input) {
663
663
  }
664
664
  // packages/server/src/server.ts
665
665
  import { spawn as spawn5 } from "child_process";
666
- import { existsSync as existsSync17, readdirSync as readdirSync5, readFileSync as readFileSync12, statSync as statSync6 } from "fs";
666
+ import { existsSync as existsSync19, readdirSync as readdirSync5, readFileSync as readFileSync14, statSync as statSync6 } from "fs";
667
667
  import { open } from "fs/promises";
668
- import { dirname as dirname17, resolve as resolve22 } from "path";
668
+ import { dirname as dirname20, resolve as resolve24 } from "path";
669
669
  import {
670
670
  listAuthorityArtifactRoots,
671
671
  listAuthorityRuns as listAuthorityRuns7,
@@ -3707,7 +3707,7 @@ function applyOrchestrationCommand2(state, command) {
3707
3707
  import { spawn as spawn3 } from "child_process";
3708
3708
  import { loadConfig } from "@rig/core/load-config";
3709
3709
  import { existsSync as existsSync7, mkdirSync as mkdirSync7, readFileSync as readFileSync4, statSync as statSync5, writeFileSync as writeFileSync6 } from "fs";
3710
- import { dirname as dirname8, relative as relative2, resolve as resolve14 } from "path";
3710
+ import { dirname as dirname9, relative as relative2, resolve as resolve14 } from "path";
3711
3711
  import {
3712
3712
  listAuthorityRuns as listAuthorityRuns4,
3713
3713
  readAuthorityRun as readAuthorityRun4,
@@ -3831,8 +3831,8 @@ function summarizeRunValidationFailure(projectRoot, run) {
3831
3831
 
3832
3832
  // packages/server/src/server-helpers/github-auth-store.ts
3833
3833
  import { randomBytes } from "crypto";
3834
- import { chmodSync, existsSync as existsSync6, mkdirSync as mkdirSync6, readFileSync as readFileSync3, writeFileSync as writeFileSync5 } from "fs";
3835
- import { resolve as resolve13 } from "path";
3834
+ import { chmodSync, copyFileSync, existsSync as existsSync6, mkdirSync as mkdirSync6, readFileSync as readFileSync3, writeFileSync as writeFileSync5 } from "fs";
3835
+ import { dirname as dirname8, resolve as resolve13 } from "path";
3836
3836
  function cleanString(value) {
3837
3837
  return typeof value === "string" && value.trim().length > 0 ? value.trim() : null;
3838
3838
  }
@@ -3862,6 +3862,26 @@ function parseApiSessions(value) {
3862
3862
  }];
3863
3863
  });
3864
3864
  }
3865
+ function parsePendingDevice(value) {
3866
+ if (!value || typeof value !== "object")
3867
+ return null;
3868
+ const record = value;
3869
+ const pollId = cleanString(record.pollId);
3870
+ const deviceCode = cleanString(record.deviceCode);
3871
+ const expiresAt = cleanString(record.expiresAt);
3872
+ const intervalSeconds = typeof record.intervalSeconds === "number" && Number.isFinite(record.intervalSeconds) ? Math.max(1, Math.floor(record.intervalSeconds)) : null;
3873
+ if (!pollId || !deviceCode || !expiresAt || !intervalSeconds)
3874
+ return null;
3875
+ return { pollId, deviceCode, expiresAt, intervalSeconds };
3876
+ }
3877
+ function parsePendingDevices(value) {
3878
+ if (!Array.isArray(value))
3879
+ return [];
3880
+ return value.flatMap((entry) => {
3881
+ const pending = parsePendingDevice(entry);
3882
+ return pending ? [pending] : [];
3883
+ });
3884
+ }
3865
3885
  function readStoredAuth(stateFile) {
3866
3886
  if (!existsSync6(stateFile))
3867
3887
  return {};
@@ -3875,6 +3895,7 @@ function readStoredAuth(stateFile) {
3875
3895
  selectedRepo: cleanString(parsed.selectedRepo),
3876
3896
  tokenSource: parsed.tokenSource === "oauth-device" || parsed.tokenSource === "manual-token" || parsed.tokenSource === "env" ? parsed.tokenSource : undefined,
3877
3897
  pendingDevice: parsePendingDevice(parsed.pendingDevice),
3898
+ pendingDevices: parsePendingDevices(parsed.pendingDevices),
3878
3899
  apiSessions: parseApiSessions(parsed.apiSessions),
3879
3900
  updatedAt: cleanString(parsed.updatedAt) ?? undefined
3880
3901
  };
@@ -3882,34 +3903,36 @@ function readStoredAuth(stateFile) {
3882
3903
  return {};
3883
3904
  }
3884
3905
  }
3885
- function parsePendingDevice(value) {
3886
- if (!value || typeof value !== "object")
3887
- return null;
3888
- const record = value;
3889
- const pollId = cleanString(record.pollId);
3890
- const deviceCode = cleanString(record.deviceCode);
3891
- const expiresAt = cleanString(record.expiresAt);
3892
- const intervalSeconds = typeof record.intervalSeconds === "number" && Number.isFinite(record.intervalSeconds) ? Math.max(1, Math.floor(record.intervalSeconds)) : null;
3893
- if (!pollId || !deviceCode || !expiresAt || !intervalSeconds)
3894
- return null;
3895
- return { pollId, deviceCode, expiresAt, intervalSeconds };
3896
- }
3897
3906
  function newApiSessionToken() {
3898
3907
  return `rig_${randomBytes(32).toString("base64url")}`;
3899
3908
  }
3900
3909
  function writeStoredAuth(stateFile, payload) {
3901
- mkdirSync6(resolve13(stateFile, ".."), { recursive: true });
3910
+ mkdirSync6(dirname8(stateFile), { recursive: true });
3902
3911
  writeFileSync5(stateFile, `${JSON.stringify(payload, null, 2)}
3903
3912
  `, { encoding: "utf8", mode: 384 });
3904
3913
  try {
3905
3914
  chmodSync(stateFile, 384);
3906
3915
  } catch {}
3907
3916
  }
3917
+ function localProjectAuthStateFile(projectRoot) {
3918
+ return resolve13(projectRoot, ".rig", "state", "github-auth.json");
3919
+ }
3908
3920
  function resolveGitHubAuthStateFile(projectRoot) {
3909
3921
  return resolve13(resolveServerAuthorityPaths(projectRoot).stateDir, "github-auth.json");
3910
3922
  }
3911
- function createGitHubAuthStore(projectRoot) {
3912
- const stateFile = resolveGitHubAuthStateFile(projectRoot);
3923
+ function copyGitHubAuthStateToLocalProjectRoot(stateFile, projectRoot) {
3924
+ const targetFile = localProjectAuthStateFile(projectRoot);
3925
+ mkdirSync6(dirname8(targetFile), { recursive: true });
3926
+ if (existsSync6(stateFile)) {
3927
+ copyFileSync(stateFile, targetFile);
3928
+ try {
3929
+ chmodSync(targetFile, 384);
3930
+ } catch {}
3931
+ return;
3932
+ }
3933
+ writeStoredAuth(targetFile, {});
3934
+ }
3935
+ function createGitHubAuthStoreFromStateFile(stateFile) {
3913
3936
  return {
3914
3937
  stateFile,
3915
3938
  status(options) {
@@ -3939,6 +3962,7 @@ function createGitHubAuthStore(projectRoot) {
3939
3962
  scopes: input.scopes ?? [],
3940
3963
  selectedRepo: input.selectedRepo ?? previous.selectedRepo ?? null,
3941
3964
  pendingDevice: null,
3965
+ pendingDevices: [],
3942
3966
  apiSessions: previous.apiSessions ?? [],
3943
3967
  updatedAt: new Date().toISOString()
3944
3968
  });
@@ -3967,15 +3991,24 @@ function createGitHubAuthStore(projectRoot) {
3967
3991
  const session = (previous.apiSessions ?? []).find((candidate) => candidate.token === clean);
3968
3992
  return session ? { login: cleanString(session.login), userId: cleanString(session.userId) } : null;
3969
3993
  },
3970
- copyToProjectRoot(projectRoot2) {
3971
- const targetFile = resolveGitHubAuthStateFile(projectRoot2);
3994
+ copyToProjectRoot(projectRoot) {
3995
+ const targetFile = resolveGitHubAuthStateFile(projectRoot);
3972
3996
  writeStoredAuth(targetFile, readStoredAuth(stateFile));
3973
3997
  },
3998
+ copyToLocalProjectRoot(projectRoot) {
3999
+ copyGitHubAuthStateToLocalProjectRoot(stateFile, projectRoot);
4000
+ },
3974
4001
  savePendingDevice(input) {
3975
4002
  const previous = readStoredAuth(stateFile);
4003
+ const pendingDevices = [
4004
+ ...previous.pendingDevice ? [previous.pendingDevice] : [],
4005
+ ...previous.pendingDevices ?? [],
4006
+ input
4007
+ ].filter((entry, index, entries) => entries.findIndex((candidate) => candidate.pollId === entry.pollId) === index);
3976
4008
  writeStoredAuth(stateFile, {
3977
4009
  ...previous,
3978
- pendingDevice: input,
4010
+ pendingDevice: null,
4011
+ pendingDevices,
3979
4012
  updatedAt: new Date().toISOString()
3980
4013
  });
3981
4014
  },
@@ -3988,23 +4021,32 @@ function createGitHubAuthStore(projectRoot) {
3988
4021
  });
3989
4022
  },
3990
4023
  readPendingDevice(pollId) {
3991
- const pending = readStoredAuth(stateFile).pendingDevice ?? null;
3992
- if (!pending || pending.pollId !== pollId)
4024
+ const previous = readStoredAuth(stateFile);
4025
+ const pending = [
4026
+ ...previous.pendingDevice ? [previous.pendingDevice] : [],
4027
+ ...previous.pendingDevices ?? []
4028
+ ].find((entry) => entry.pollId === pollId) ?? null;
4029
+ if (!pending)
3993
4030
  return null;
3994
4031
  if (Date.parse(pending.expiresAt) <= Date.now())
3995
4032
  return null;
3996
4033
  return pending;
3997
4034
  },
3998
- clearPendingDevice() {
4035
+ clearPendingDevice(pollId) {
3999
4036
  const previous = readStoredAuth(stateFile);
4037
+ const remaining = pollId ? (previous.pendingDevices ?? []).filter((entry) => entry.pollId !== pollId) : [];
4000
4038
  writeStoredAuth(stateFile, {
4001
4039
  ...previous,
4002
4040
  pendingDevice: null,
4041
+ pendingDevices: remaining,
4003
4042
  updatedAt: new Date().toISOString()
4004
4043
  });
4005
4044
  }
4006
4045
  };
4007
4046
  }
4047
+ function createGitHubAuthStore(projectRoot) {
4048
+ return createGitHubAuthStoreFromStateFile(resolveGitHubAuthStateFile(projectRoot));
4049
+ }
4008
4050
 
4009
4051
  // packages/server/src/server-helpers/github-projects.ts
4010
4052
  function asRecord(value) {
@@ -4743,7 +4785,7 @@ function resolveLocalRunCliProjectRoot(projectRoot) {
4743
4785
  }
4744
4786
  try {
4745
4787
  const monorepoRoot = resolveMonorepoRoot3(projectRoot);
4746
- const outerProjectRoot = dirname8(dirname8(monorepoRoot));
4788
+ const outerProjectRoot = dirname9(dirname9(monorepoRoot));
4747
4789
  if (existsSync7(resolve14(outerProjectRoot, "packages/cli/bin/rig.ts"))) {
4748
4790
  return outerProjectRoot;
4749
4791
  }
@@ -4957,8 +4999,8 @@ async function reconcileScheduler(state, reason) {
4957
4999
  // packages/server/src/server-helpers/http-router.ts
4958
5000
  import { randomUUID } from "crypto";
4959
5001
  import { spawnSync as spawnSync3 } from "child_process";
4960
- import { basename, dirname as dirname12, isAbsolute as isAbsolute3, resolve as resolve18 } from "path";
4961
- import { copyFileSync, existsSync as existsSync11, mkdirSync as mkdirSync11, readFileSync as readFileSync7, writeFileSync as writeFileSync10 } from "fs";
5002
+ import { basename, dirname as dirname15, isAbsolute as isAbsolute4, resolve as resolve20 } from "path";
5003
+ import { copyFileSync as copyFileSync2, existsSync as existsSync13, mkdirSync as mkdirSync13, readFileSync as readFileSync9, writeFileSync as writeFileSync12 } from "fs";
4962
5004
  import {
4963
5005
  listAuthorityRuns as listAuthorityRuns5,
4964
5006
  readAuthorityRun as readAuthorityRun6,
@@ -4982,7 +5024,7 @@ import {
4982
5024
  } from "@rig/runtime/control-plane/remote";
4983
5025
 
4984
5026
  // packages/server/src/server-helpers/run-steering.ts
4985
- import { dirname as dirname9, resolve as resolve15 } from "path";
5027
+ import { dirname as dirname10, resolve as resolve15 } from "path";
4986
5028
  import { existsSync as existsSync8, mkdirSync as mkdirSync8, readFileSync as readFileSync5 } from "fs";
4987
5029
  import { appendJsonlRecord as appendJsonlRecord2, readAuthorityRun as readAuthorityRun5, resolveAuthorityRunDir as resolveAuthorityRunDir4 } from "@rig/runtime/control-plane/authority-files";
4988
5030
  var steeringSequence = 0;
@@ -5069,7 +5111,7 @@ function queueRunSteeringMessage(projectRoot, runId, input) {
5069
5111
  delivered: false
5070
5112
  };
5071
5113
  const path = runSteeringPath(projectRoot, runId);
5072
- mkdirSync8(dirname9(path), { recursive: true });
5114
+ mkdirSync8(dirname10(path), { recursive: true });
5073
5115
  appendJsonlRecord2(path, entry);
5074
5116
  appendRunTimelineEntry(projectRoot, runId, {
5075
5117
  id: entry.id,
@@ -5106,6 +5148,187 @@ import {
5106
5148
  updateConfiguredTaskSourceTask as updateConfiguredTaskSourceTask2
5107
5149
  } from "@rig/runtime/control-plane/tasks/source-lifecycle";
5108
5150
 
5151
+ // packages/server/src/server-helpers/github-api-session-index.ts
5152
+ import { chmodSync as chmodSync3, existsSync as existsSync10, mkdirSync as mkdirSync10, readFileSync as readFileSync7, writeFileSync as writeFileSync9 } from "fs";
5153
+ import { dirname as dirname12, resolve as resolve17 } from "path";
5154
+
5155
+ // packages/server/src/server-helpers/github-user-namespace.ts
5156
+ import { chmodSync as chmodSync2, existsSync as existsSync9, mkdirSync as mkdirSync9, readFileSync as readFileSync6, writeFileSync as writeFileSync8 } from "fs";
5157
+ import { dirname as dirname11, isAbsolute as isAbsolute2, relative as relative3, resolve as resolve16 } from "path";
5158
+ function cleanString3(value) {
5159
+ return typeof value === "string" && value.trim().length > 0 ? value.trim() : null;
5160
+ }
5161
+ function sanitizePathSegment(value) {
5162
+ return value.trim().toLowerCase().replace(/[^a-z0-9._-]/g, "-").replace(/^-+|-+$/g, "").slice(0, 96);
5163
+ }
5164
+ function deriveGitHubUserNamespaceKey(identity) {
5165
+ const userId = cleanString3(identity.userId);
5166
+ if (userId) {
5167
+ const safeId = sanitizePathSegment(userId);
5168
+ if (safeId)
5169
+ return `ghu-${safeId}`;
5170
+ }
5171
+ const login = cleanString3(identity.login);
5172
+ if (login) {
5173
+ const safeLogin = sanitizePathSegment(login);
5174
+ if (safeLogin)
5175
+ return `ghu-login-${safeLogin}`;
5176
+ }
5177
+ throw new Error("GitHub user namespace requires a user id or login");
5178
+ }
5179
+ function resolveRemoteUserNamespacesRoot(projectRoot) {
5180
+ const explicitRoot = cleanString3(process.env.RIG_REMOTE_USER_NAMESPACE_ROOT);
5181
+ if (explicitRoot)
5182
+ return resolve16(explicitRoot);
5183
+ const stateDir2 = cleanString3(process.env.RIG_STATE_DIR);
5184
+ if (stateDir2)
5185
+ return resolve16(dirname11(resolve16(stateDir2)), "users");
5186
+ return resolve16(projectRoot, ".rig", "users");
5187
+ }
5188
+ function resolveRemoteUserNamespace(projectRoot, identity) {
5189
+ const key = deriveGitHubUserNamespaceKey(identity);
5190
+ const root = resolve16(resolveRemoteUserNamespacesRoot(projectRoot), key);
5191
+ const stateDir2 = resolve16(root, ".rig", "state");
5192
+ return {
5193
+ key,
5194
+ userId: cleanString3(identity.userId),
5195
+ login: cleanString3(identity.login),
5196
+ root,
5197
+ stateDir: stateDir2,
5198
+ authStateFile: resolve16(stateDir2, "github-auth.json"),
5199
+ metadataFile: resolve16(stateDir2, "user-namespace.json"),
5200
+ checkoutBaseDir: resolve16(root, "remote-checkouts"),
5201
+ snapshotBaseDir: resolve16(root, "remote-snapshots")
5202
+ };
5203
+ }
5204
+ function serializeRemoteUserNamespace(namespace) {
5205
+ return {
5206
+ key: namespace.key,
5207
+ userId: namespace.userId,
5208
+ login: namespace.login,
5209
+ root: namespace.root,
5210
+ checkoutBaseDir: namespace.checkoutBaseDir,
5211
+ snapshotBaseDir: namespace.snapshotBaseDir
5212
+ };
5213
+ }
5214
+ function isPathInsideNamespace(namespaceRoot, candidatePath) {
5215
+ const root = resolve16(namespaceRoot);
5216
+ const candidate = resolve16(candidatePath);
5217
+ const rel = relative3(root, candidate);
5218
+ return rel === "" || !rel.startsWith("..") && !isAbsolute2(rel);
5219
+ }
5220
+ function writeRemoteUserNamespaceMetadata(namespace) {
5221
+ mkdirSync9(namespace.stateDir, { recursive: true });
5222
+ const previous = (() => {
5223
+ if (!existsSync9(namespace.metadataFile))
5224
+ return null;
5225
+ try {
5226
+ const parsed = JSON.parse(readFileSync6(namespace.metadataFile, "utf8"));
5227
+ return parsed && typeof parsed === "object" && !Array.isArray(parsed) ? parsed : null;
5228
+ } catch {
5229
+ return null;
5230
+ }
5231
+ })();
5232
+ const now = new Date().toISOString();
5233
+ writeFileSync8(namespace.metadataFile, `${JSON.stringify({
5234
+ key: namespace.key,
5235
+ userId: namespace.userId,
5236
+ login: namespace.login,
5237
+ root: namespace.root,
5238
+ checkoutBaseDir: namespace.checkoutBaseDir,
5239
+ snapshotBaseDir: namespace.snapshotBaseDir,
5240
+ createdAt: typeof previous?.createdAt === "string" ? previous.createdAt : now,
5241
+ updatedAt: now
5242
+ }, null, 2)}
5243
+ `, { encoding: "utf8", mode: 384 });
5244
+ try {
5245
+ chmodSync2(namespace.metadataFile, 384);
5246
+ } catch {}
5247
+ }
5248
+
5249
+ // packages/server/src/server-helpers/github-api-session-index.ts
5250
+ function cleanString4(value) {
5251
+ return typeof value === "string" && value.trim().length > 0 ? value.trim() : null;
5252
+ }
5253
+ function resolveGitHubApiSessionIndexFile(projectRoot) {
5254
+ return resolve17(resolveRemoteUserNamespacesRoot(projectRoot), ".api-sessions.json");
5255
+ }
5256
+ function parseEntry(value) {
5257
+ if (!value || typeof value !== "object" || Array.isArray(value))
5258
+ return null;
5259
+ const record = value;
5260
+ const token = cleanString4(record.token);
5261
+ const namespaceKey = cleanString4(record.namespaceKey);
5262
+ const namespaceRoot = cleanString4(record.namespaceRoot);
5263
+ const authStateFile = cleanString4(record.authStateFile);
5264
+ const checkoutBaseDir = cleanString4(record.checkoutBaseDir);
5265
+ const snapshotBaseDir = cleanString4(record.snapshotBaseDir);
5266
+ const createdAt = cleanString4(record.createdAt);
5267
+ if (!token || !namespaceKey || !namespaceRoot || !authStateFile || !checkoutBaseDir || !snapshotBaseDir || !createdAt)
5268
+ return null;
5269
+ return {
5270
+ token,
5271
+ namespaceKey,
5272
+ namespaceRoot,
5273
+ authStateFile,
5274
+ checkoutBaseDir,
5275
+ snapshotBaseDir,
5276
+ createdAt,
5277
+ login: cleanString4(record.login),
5278
+ userId: cleanString4(record.userId),
5279
+ selectedRepo: cleanString4(record.selectedRepo)
5280
+ };
5281
+ }
5282
+ function readIndex(indexFile) {
5283
+ if (!existsSync10(indexFile))
5284
+ return [];
5285
+ try {
5286
+ const parsed = JSON.parse(readFileSync7(indexFile, "utf8"));
5287
+ return Array.isArray(parsed.sessions) ? parsed.sessions.flatMap((entry) => {
5288
+ const parsedEntry = parseEntry(entry);
5289
+ return parsedEntry ? [parsedEntry] : [];
5290
+ }) : [];
5291
+ } catch {
5292
+ return [];
5293
+ }
5294
+ }
5295
+ function writeIndex(indexFile, sessions) {
5296
+ mkdirSync10(dirname12(indexFile), { recursive: true });
5297
+ writeFileSync9(indexFile, `${JSON.stringify({ sessions }, null, 2)}
5298
+ `, { encoding: "utf8", mode: 384 });
5299
+ try {
5300
+ chmodSync3(indexFile, 384);
5301
+ } catch {}
5302
+ }
5303
+ function registerGitHubApiSession(input) {
5304
+ const cleanToken = cleanString4(input.token);
5305
+ if (!cleanToken)
5306
+ throw new Error("GitHub API session token is required");
5307
+ const indexFile = resolveGitHubApiSessionIndexFile(input.projectRoot);
5308
+ const createdAt = new Date().toISOString();
5309
+ const entry = {
5310
+ token: cleanToken,
5311
+ login: input.namespace.login,
5312
+ userId: input.namespace.userId,
5313
+ namespaceKey: input.namespace.key,
5314
+ namespaceRoot: input.namespace.root,
5315
+ authStateFile: input.namespace.authStateFile,
5316
+ checkoutBaseDir: input.namespace.checkoutBaseDir,
5317
+ snapshotBaseDir: input.namespace.snapshotBaseDir,
5318
+ selectedRepo: cleanString4(input.selectedRepo),
5319
+ createdAt
5320
+ };
5321
+ const previous = readIndex(indexFile).filter((session) => session.token !== cleanToken);
5322
+ writeIndex(indexFile, [...previous.slice(-199), entry]);
5323
+ return entry;
5324
+ }
5325
+ function readGitHubApiSession(input) {
5326
+ const cleanToken = cleanString4(input.token);
5327
+ if (!cleanToken)
5328
+ return null;
5329
+ return readIndex(resolveGitHubApiSessionIndexFile(input.projectRoot)).find((entry) => entry.token === cleanToken) ?? null;
5330
+ }
5331
+
5109
5332
  // packages/server/src/server-helpers/inspector-agent-lifecycle.ts
5110
5333
  function createInspectorAgentLifecycleController(options) {
5111
5334
  const initialDelayMs = Math.max(1, options.initialDelayMs ?? 5000);
@@ -5209,21 +5432,21 @@ function inspectorAgentLifecycleSnapshot(input) {
5209
5432
  // packages/server/src/server-helpers/project-registry.ts
5210
5433
  import { createHash as createHash2 } from "crypto";
5211
5434
  import { spawnSync as spawnSync2 } from "child_process";
5212
- import { existsSync as existsSync9, mkdirSync as mkdirSync9, readFileSync as readFileSync6, readdirSync as readdirSync3, writeFileSync as writeFileSync8 } from "fs";
5213
- import { dirname as dirname10, resolve as resolve16 } from "path";
5435
+ import { existsSync as existsSync11, mkdirSync as mkdirSync11, readFileSync as readFileSync8, readdirSync as readdirSync3, writeFileSync as writeFileSync10 } from "fs";
5436
+ import { dirname as dirname13, resolve as resolve18 } from "path";
5214
5437
  function normalizeRepoSlug(value) {
5215
5438
  const trimmed = value.trim();
5216
5439
  return /^[^/\s]+\/[^/\s]+$/.test(trimmed) ? trimmed : null;
5217
5440
  }
5218
5441
  function registryPath(projectRoot) {
5219
- return resolve16(projectRoot, ".rig", "state", "projects.json");
5442
+ return resolve18(projectRoot, ".rig", "state", "projects.json");
5220
5443
  }
5221
5444
  function readRegistry(projectRoot) {
5222
5445
  const path = registryPath(projectRoot);
5223
- if (!existsSync9(path))
5446
+ if (!existsSync11(path))
5224
5447
  return {};
5225
5448
  try {
5226
- const payload = JSON.parse(readFileSync6(path, "utf8"));
5449
+ const payload = JSON.parse(readFileSync8(path, "utf8"));
5227
5450
  if (!payload || typeof payload !== "object" || Array.isArray(payload))
5228
5451
  return {};
5229
5452
  const projects = payload.projects;
@@ -5234,14 +5457,14 @@ function readRegistry(projectRoot) {
5234
5457
  }
5235
5458
  function writeRegistry(projectRoot, projects) {
5236
5459
  const path = registryPath(projectRoot);
5237
- mkdirSync9(dirname10(path), { recursive: true });
5238
- writeFileSync8(path, `${JSON.stringify({ projects }, null, 2)}
5460
+ mkdirSync11(dirname13(path), { recursive: true });
5461
+ writeFileSync10(path, `${JSON.stringify({ projects }, null, 2)}
5239
5462
  `, "utf8");
5240
5463
  }
5241
5464
  function resolveConfigPath(projectRoot) {
5242
5465
  for (const name of ["rig.config.ts", "rig.config.mts", "rig.config.json"]) {
5243
- const path = resolve16(projectRoot, name);
5244
- if (existsSync9(path))
5466
+ const path = resolve18(projectRoot, name);
5467
+ if (existsSync11(path))
5245
5468
  return path;
5246
5469
  }
5247
5470
  return null;
@@ -5250,7 +5473,7 @@ function hashFile(path) {
5250
5473
  if (!path)
5251
5474
  return null;
5252
5475
  try {
5253
- return createHash2("sha256").update(readFileSync6(path)).digest("hex");
5476
+ return createHash2("sha256").update(readFileSync8(path)).digest("hex");
5254
5477
  } catch {
5255
5478
  return null;
5256
5479
  }
@@ -5266,11 +5489,11 @@ function readDefaultBranch(projectRoot) {
5266
5489
  return head.status === 0 && head.stdout.trim() && head.stdout.trim() !== "HEAD" ? head.stdout.trim() : null;
5267
5490
  }
5268
5491
  function buildRunSummary(projectRoot) {
5269
- const runsDir = resolve16(projectRoot, ".rig", "runs");
5492
+ const runsDir = resolve18(projectRoot, ".rig", "runs");
5270
5493
  try {
5271
5494
  const runs = readdirSync3(runsDir, { withFileTypes: true }).filter((entry) => entry.isDirectory()).flatMap((entry) => {
5272
5495
  try {
5273
- const run = JSON.parse(readFileSync6(resolve16(runsDir, entry.name, "run.json"), "utf8"));
5496
+ const run = JSON.parse(readFileSync8(resolve18(runsDir, entry.name, "run.json"), "utf8"));
5274
5497
  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 : "" }];
5275
5498
  } catch {
5276
5499
  return [];
@@ -5322,10 +5545,14 @@ function upsertProjectRecord(projectRoot, input) {
5322
5545
  function linkProjectCheckout(projectRoot, repoSlug, checkout) {
5323
5546
  return upsertProjectRecord(projectRoot, { repoSlug, checkout });
5324
5547
  }
5548
+ function projectRegistryContainsCheckout(projectRoot, checkoutPath) {
5549
+ const target = resolve18(checkoutPath);
5550
+ return Object.values(readRegistry(projectRoot)).some((project) => project.checkouts.some((checkout) => checkout.path ? resolve18(checkout.path) === target : false));
5551
+ }
5325
5552
 
5326
5553
  // packages/server/src/server-helpers/remote-checkout.ts
5327
- import { existsSync as existsSync10, mkdirSync as mkdirSync10, writeFileSync as writeFileSync9 } from "fs";
5328
- import { dirname as dirname11, isAbsolute as isAbsolute2, relative as relative3, resolve as resolve17 } from "path";
5554
+ import { existsSync as existsSync12, mkdirSync as mkdirSync12, writeFileSync as writeFileSync11 } from "fs";
5555
+ import { dirname as dirname14, isAbsolute as isAbsolute3, relative as relative4, resolve as resolve19 } from "path";
5329
5556
  function safeSlugSegments(repoSlug) {
5330
5557
  const segments = repoSlug.split("/").map((part) => part.trim()).filter(Boolean);
5331
5558
  if (segments.length !== 2 || segments.some((segment) => segment === "." || segment === ".." || segment.includes("\\"))) {
@@ -5342,7 +5569,7 @@ function safeCheckoutKey(value) {
5342
5569
  }
5343
5570
  function repoSlugPath(baseDir, repoSlug, checkoutKey) {
5344
5571
  const key = safeCheckoutKey(checkoutKey);
5345
- return resolve17(baseDir, ...key ? [key] : [], ...safeSlugSegments(repoSlug));
5572
+ return resolve19(baseDir, ...key ? [key] : [], ...safeSlugSegments(repoSlug));
5346
5573
  }
5347
5574
  function sanitizeSnapshotId(value, fallback) {
5348
5575
  const raw = (value ?? fallback).trim();
@@ -5350,7 +5577,7 @@ function sanitizeSnapshotId(value, fallback) {
5350
5577
  return safe || fallback;
5351
5578
  }
5352
5579
  function assertWithinRoot(root, relativePath) {
5353
- if (!relativePath || isAbsolute2(relativePath) || relativePath.includes("\x00")) {
5580
+ if (!relativePath || isAbsolute3(relativePath) || relativePath.includes("\x00")) {
5354
5581
  throw new Error(`Invalid snapshot file path: ${relativePath}`);
5355
5582
  }
5356
5583
  const normalizedRelative = relativePath.replace(/\\/g, "/");
@@ -5358,9 +5585,9 @@ function assertWithinRoot(root, relativePath) {
5358
5585
  if (segments.some((segment) => segment === "" || segment === "." || segment === "..")) {
5359
5586
  throw new Error(`Unsafe snapshot file path: ${relativePath}`);
5360
5587
  }
5361
- const target = resolve17(root, ...segments);
5362
- const rel = relative3(root, target);
5363
- if (rel === ".." || rel.split(/[\\/]/)[0] === ".." || isAbsolute2(rel)) {
5588
+ const target = resolve19(root, ...segments);
5589
+ const rel = relative4(root, target);
5590
+ if (rel === ".." || rel.split(/[\\/]/)[0] === ".." || isAbsolute3(rel)) {
5364
5591
  throw new Error(`Snapshot file path escapes checkout root: ${relativePath}`);
5365
5592
  }
5366
5593
  return target;
@@ -5378,17 +5605,17 @@ function parseSnapshotArchiveContentBase64(contentBase64) {
5378
5605
  function extractUploadedSnapshotArchive(input) {
5379
5606
  const archive = decodeSnapshotArchive(input.archive);
5380
5607
  const snapshotId = sanitizeSnapshotId(input.snapshotId, `snapshot-${(input.now?.() ?? new Date).toISOString().replace(/[:.]/g, "-")}`);
5381
- const checkoutPath = resolve17(repoSlugPath(input.baseDir, input.repoSlug, input.checkoutKey), snapshotId);
5382
- mkdirSync10(checkoutPath, { recursive: true });
5608
+ const checkoutPath = resolve19(repoSlugPath(input.baseDir, input.repoSlug, input.checkoutKey), snapshotId);
5609
+ mkdirSync12(checkoutPath, { recursive: true });
5383
5610
  for (const file of archive.files) {
5384
5611
  if (!file || typeof file.path !== "string" || typeof file.contentBase64 !== "string") {
5385
5612
  throw new Error("Invalid snapshot archive file entry");
5386
5613
  }
5387
5614
  const target = assertWithinRoot(checkoutPath, file.path);
5388
- mkdirSync10(dirname11(target), { recursive: true });
5389
- writeFileSync9(target, Buffer.from(file.contentBase64, "base64"));
5615
+ mkdirSync12(dirname14(target), { recursive: true });
5616
+ writeFileSync11(target, Buffer.from(file.contentBase64, "base64"));
5390
5617
  }
5391
- writeFileSync9(resolve17(checkoutPath, ".rig-uploaded-snapshot.json"), `${JSON.stringify({
5618
+ writeFileSync11(resolve19(checkoutPath, ".rig-uploaded-snapshot.json"), `${JSON.stringify({
5392
5619
  repoSlug: input.repoSlug,
5393
5620
  snapshotId,
5394
5621
  fileCount: archive.files.length,
@@ -5423,7 +5650,7 @@ function gitCredentialConfig(token) {
5423
5650
  };
5424
5651
  }
5425
5652
  async function prepareRemoteCheckout(input) {
5426
- const exists = input.exists ?? existsSync10;
5653
+ const exists = input.exists ?? existsSync12;
5427
5654
  const strategy = input.strategy;
5428
5655
  if (strategy.kind === "uploaded-snapshot") {
5429
5656
  return extractUploadedSnapshotArchive({
@@ -5435,7 +5662,7 @@ async function prepareRemoteCheckout(input) {
5435
5662
  });
5436
5663
  }
5437
5664
  if (strategy.kind === "existing-path") {
5438
- const checkoutPath2 = resolve17(strategy.path);
5665
+ const checkoutPath2 = resolve19(strategy.path);
5439
5666
  if (!exists(checkoutPath2)) {
5440
5667
  throw new Error(`Existing remote checkout path does not exist: ${checkoutPath2}`);
5441
5668
  }
@@ -5471,9 +5698,9 @@ function buildServerControlStatus() {
5471
5698
  };
5472
5699
  }
5473
5700
  function buildProjectConfigStatus(root) {
5474
- const hasConfigTs = existsSync11(resolve18(root, "rig.config.ts"));
5475
- const hasConfigJson = existsSync11(resolve18(root, "rig.config.json"));
5476
- const hasLegacyTaskConfig = existsSync11(resolve18(root, ".rig", "task-config.json"));
5701
+ const hasConfigTs = existsSync13(resolve20(root, "rig.config.ts"));
5702
+ const hasConfigJson = existsSync13(resolve20(root, "rig.config.json"));
5703
+ const hasLegacyTaskConfig = existsSync13(resolve20(root, ".rig", "task-config.json"));
5477
5704
  let kind = "missing";
5478
5705
  if (hasConfigTs)
5479
5706
  kind = "rig-config-ts";
@@ -5509,24 +5736,24 @@ function repoParts(repoSlug) {
5509
5736
  return { owner, repo, slug: `${owner}/${repo}` };
5510
5737
  }
5511
5738
  function repairDir(checkoutPath) {
5512
- const dir = resolve18(checkoutPath, ".rig", "state", "repairs", new Date().toISOString().replace(/[:.]/g, "-"));
5513
- mkdirSync11(dir, { recursive: true });
5739
+ const dir = resolve20(checkoutPath, ".rig", "state", "repairs", new Date().toISOString().replace(/[:.]/g, "-"));
5740
+ mkdirSync13(dir, { recursive: true });
5514
5741
  return dir;
5515
5742
  }
5516
5743
  function backupCheckoutFile(checkoutPath, relativePath) {
5517
- const source = resolve18(checkoutPath, relativePath);
5518
- const backupPath = resolve18(repairDir(checkoutPath), relativePath.replace(/[\\/]/g, "__"));
5519
- mkdirSync11(dirname12(backupPath), { recursive: true });
5520
- copyFileSync(source, backupPath);
5744
+ const source = resolve20(checkoutPath, relativePath);
5745
+ const backupPath = resolve20(repairDir(checkoutPath), relativePath.replace(/[\\/]/g, "__"));
5746
+ mkdirSync13(dirname15(backupPath), { recursive: true });
5747
+ copyFileSync2(source, backupPath);
5521
5748
  return backupPath;
5522
5749
  }
5523
5750
  function parsePackageJsonLosslessly(checkoutPath) {
5524
- const packagePath = resolve18(checkoutPath, "package.json");
5525
- if (!existsSync11(packagePath)) {
5751
+ const packagePath = resolve20(checkoutPath, "package.json");
5752
+ if (!existsSync13(packagePath)) {
5526
5753
  return { existed: false, packageJson: { name: basename(checkoutPath) || "rig-project", private: true } };
5527
5754
  }
5528
5755
  try {
5529
- const parsed = JSON.parse(readFileSync7(packagePath, "utf8"));
5756
+ const parsed = JSON.parse(readFileSync9(packagePath, "utf8"));
5530
5757
  return {
5531
5758
  existed: true,
5532
5759
  packageJson: parsed && typeof parsed === "object" && !Array.isArray(parsed) ? parsed : { name: basename(checkoutPath) || "rig-project", private: true }
@@ -5540,9 +5767,9 @@ function parsePackageJsonLosslessly(checkoutPath) {
5540
5767
  }
5541
5768
  }
5542
5769
  function ensureRemoteCheckoutRigPackageDeps(checkoutPath) {
5543
- const hasConfig = ["rig.config.ts", "rig.config.mts", "rig.config.json"].some((name) => existsSync11(resolve18(checkoutPath, name)));
5544
- const packagePath = resolve18(checkoutPath, "package.json");
5545
- if (!hasConfig && !existsSync11(packagePath)) {
5770
+ const hasConfig = ["rig.config.ts", "rig.config.mts", "rig.config.json"].some((name) => existsSync13(resolve20(checkoutPath, name)));
5771
+ const packagePath = resolve20(checkoutPath, "package.json");
5772
+ if (!hasConfig && !existsSync13(packagePath)) {
5546
5773
  return { skipped: true, reason: "package.json and rig.config missing" };
5547
5774
  }
5548
5775
  const parsed = parsePackageJsonLosslessly(checkoutPath);
@@ -5561,7 +5788,7 @@ function ensureRemoteCheckoutRigPackageDeps(checkoutPath) {
5561
5788
  }
5562
5789
  const changed = !parsed.existed || Boolean(parsed.backupPath) || added.length > 0 || updated.length > 0 || existingDevDependencies !== devDependencies && (!existingDevDependencies || typeof existingDevDependencies !== "object" || Array.isArray(existingDevDependencies));
5563
5790
  if (changed) {
5564
- writeFileSync10(packagePath, `${JSON.stringify({ ...parsed.packageJson, devDependencies }, null, 2)}
5791
+ writeFileSync12(packagePath, `${JSON.stringify({ ...parsed.packageJson, devDependencies }, null, 2)}
5565
5792
  `, "utf8");
5566
5793
  }
5567
5794
  return {
@@ -5587,11 +5814,11 @@ function configLooksStructurallyUsable(source) {
5587
5814
  return /taskSource\s*:/.test(source) && /workspace\s*:/.test(source) && /project\s*:/.test(source);
5588
5815
  }
5589
5816
  function ensureRemoteCheckoutRigConfig(checkoutPath, repoSlug, reason = "missing or incomplete rig config") {
5590
- const configPath = resolve18(checkoutPath, "rig.config.ts");
5591
- const existingConfigName = ["rig.config.ts", "rig.config.mts", "rig.config.json"].find((name) => existsSync11(resolve18(checkoutPath, name)));
5817
+ const configPath = resolve20(checkoutPath, "rig.config.ts");
5818
+ const existingConfigName = ["rig.config.ts", "rig.config.mts", "rig.config.json"].find((name) => existsSync13(resolve20(checkoutPath, name)));
5592
5819
  if (existingConfigName) {
5593
- const existingPath = resolve18(checkoutPath, existingConfigName);
5594
- const source = readFileSync7(existingPath, "utf8");
5820
+ const existingPath = resolve20(checkoutPath, existingConfigName);
5821
+ const source = readFileSync9(existingPath, "utf8");
5595
5822
  if (existingConfigName !== "rig.config.json" && configLooksStructurallyUsable(source)) {
5596
5823
  return { path: existingPath, changed: false, reason: "config structurally complete" };
5597
5824
  }
@@ -5605,7 +5832,7 @@ function ensureRemoteCheckoutRigConfig(checkoutPath, repoSlug, reason = "missing
5605
5832
  }
5606
5833
  }
5607
5834
  const backupPath = existingConfigName ? backupCheckoutFile(checkoutPath, existingConfigName) : undefined;
5608
- writeFileSync10(configPath, generatedRigConfigSource(repoSlug), "utf8");
5835
+ writeFileSync12(configPath, generatedRigConfigSource(repoSlug), "utf8");
5609
5836
  return {
5610
5837
  path: configPath,
5611
5838
  changed: true,
@@ -5614,7 +5841,7 @@ function ensureRemoteCheckoutRigConfig(checkoutPath, repoSlug, reason = "missing
5614
5841
  };
5615
5842
  }
5616
5843
  function validateRemoteCheckoutRigConfig(checkoutPath) {
5617
- const configFile = ["rig.config.ts", "rig.config.mts", "rig.config.json"].find((name) => existsSync11(resolve18(checkoutPath, name)));
5844
+ const configFile = ["rig.config.ts", "rig.config.mts", "rig.config.json"].find((name) => existsSync13(resolve20(checkoutPath, name)));
5618
5845
  if (!configFile)
5619
5846
  return { ok: false, error: "missing rig config" };
5620
5847
  if (process.env.RIG_TEST_SKIP_REMOTE_CHECKOUT_INSTALL === "1") {
@@ -5636,7 +5863,7 @@ function validateRemoteCheckoutRigConfig(checkoutPath) {
5636
5863
  return { ok: true, configFile };
5637
5864
  }
5638
5865
  function installRemoteCheckoutPackages(checkoutPath) {
5639
- if (!existsSync11(resolve18(checkoutPath, "package.json"))) {
5866
+ if (!existsSync13(resolve20(checkoutPath, "package.json"))) {
5640
5867
  return { skipped: true, reason: "package.json missing" };
5641
5868
  }
5642
5869
  if (process.env.RIG_TEST_SKIP_REMOTE_CHECKOUT_INSTALL === "1") {
@@ -5649,8 +5876,8 @@ function installRemoteCheckoutPackages(checkoutPath) {
5649
5876
  return { ok: true, command: "bun install", stdout: result.stdout?.trim() || undefined };
5650
5877
  }
5651
5878
  function repairRemoteCheckoutForRig(checkoutPath, repoSlug) {
5652
- const hasConfig = ["rig.config.ts", "rig.config.mts", "rig.config.json"].some((name) => existsSync11(resolve18(checkoutPath, name)));
5653
- const hasPackage = existsSync11(resolve18(checkoutPath, "package.json"));
5879
+ const hasConfig = ["rig.config.ts", "rig.config.mts", "rig.config.json"].some((name) => existsSync13(resolve20(checkoutPath, name)));
5880
+ const hasPackage = existsSync13(resolve20(checkoutPath, "package.json"));
5654
5881
  if (!hasConfig && !hasPackage) {
5655
5882
  return {
5656
5883
  packageJson: { skipped: true, reason: "package.json and rig.config missing" },
@@ -5748,26 +5975,26 @@ function buildRemoteRunLogEntry(body, identifiers) {
5748
5975
  }
5749
5976
  function readGitHeadCommit(projectRoot) {
5750
5977
  try {
5751
- let gitDir = resolve18(projectRoot, ".git");
5978
+ let gitDir = resolve20(projectRoot, ".git");
5752
5979
  try {
5753
- const dotGit = readFileSync7(gitDir, "utf8").trim();
5980
+ const dotGit = readFileSync9(gitDir, "utf8").trim();
5754
5981
  const gitDirPrefix = "gitdir:";
5755
5982
  if (dotGit.startsWith(gitDirPrefix)) {
5756
- gitDir = resolve18(projectRoot, dotGit.slice(gitDirPrefix.length).trim());
5983
+ gitDir = resolve20(projectRoot, dotGit.slice(gitDirPrefix.length).trim());
5757
5984
  }
5758
5985
  } catch {}
5759
- const head = readFileSync7(resolve18(gitDir, "HEAD"), "utf8").trim();
5986
+ const head = readFileSync9(resolve20(gitDir, "HEAD"), "utf8").trim();
5760
5987
  const refPrefix = "ref:";
5761
5988
  if (!head.startsWith(refPrefix)) {
5762
5989
  return normalizeCommit(head);
5763
5990
  }
5764
5991
  const ref = head.slice(refPrefix.length).trim();
5765
- const refPath = resolve18(gitDir, ref);
5766
- if (existsSync11(refPath)) {
5767
- return normalizeCommit(readFileSync7(refPath, "utf8").trim());
5992
+ const refPath = resolve20(gitDir, ref);
5993
+ if (existsSync13(refPath)) {
5994
+ return normalizeCommit(readFileSync9(refPath, "utf8").trim());
5768
5995
  }
5769
- const commonDir = normalizeString(readFileSync7(resolve18(gitDir, "commondir"), "utf8"));
5770
- return commonDir ? normalizeCommit(readFileSync7(resolve18(gitDir, commonDir, ref), "utf8").trim()) : null;
5996
+ const commonDir = normalizeString(readFileSync9(resolve20(gitDir, "commondir"), "utf8"));
5997
+ return commonDir ? normalizeCommit(readFileSync9(resolve20(gitDir, commonDir, ref), "utf8").trim()) : null;
5771
5998
  } catch {
5772
5999
  return null;
5773
6000
  }
@@ -5805,9 +6032,9 @@ function configuredRepoFromTaskSource(taskSource) {
5805
6032
  const repo = normalizeString(taskSource?.repo);
5806
6033
  return owner && repo ? `${owner}/${repo}` : null;
5807
6034
  }
5808
- async function buildTaskSourceStatus(state, config) {
6035
+ async function buildTaskSourceStatus(state, config, requestAuth) {
5809
6036
  const diagnostics = state.snapshotService.getTaskSourceErrors();
5810
- const selectedRepo = createGitHubAuthStore(state.projectRoot).status({
6037
+ const selectedRepo = requestScopedAuthStore(state.projectRoot, requestAuth).status({
5811
6038
  oauthConfigured: Boolean(process.env.RIG_GITHUB_OAUTH_CLIENT_ID?.trim())
5812
6039
  }).selectedRepo;
5813
6040
  try {
@@ -5905,31 +6132,83 @@ function normalizePrMode(value) {
5905
6132
  const mode = normalizeString(value);
5906
6133
  return mode === "auto" || mode === "ask" || mode === "off" ? mode : undefined;
5907
6134
  }
6135
+ function requestAuthResult(input) {
6136
+ return {
6137
+ authorized: input.authorized,
6138
+ actor: input.actor ?? null,
6139
+ reason: input.reason,
6140
+ login: input.login ?? null,
6141
+ userId: input.userId ?? null,
6142
+ userNamespace: input.userNamespace ?? null,
6143
+ authStateFile: input.authStateFile ?? null
6144
+ };
6145
+ }
6146
+ function namespaceFromSessionIndex(entry) {
6147
+ const stateDir2 = dirname15(entry.authStateFile);
6148
+ return {
6149
+ key: entry.namespaceKey,
6150
+ userId: entry.userId,
6151
+ login: entry.login,
6152
+ root: entry.namespaceRoot,
6153
+ stateDir: stateDir2,
6154
+ authStateFile: entry.authStateFile,
6155
+ metadataFile: resolve20(stateDir2, "user-namespace.json"),
6156
+ checkoutBaseDir: entry.checkoutBaseDir,
6157
+ snapshotBaseDir: entry.snapshotBaseDir
6158
+ };
6159
+ }
5908
6160
  function authorizeRigHttpRequest(input) {
5909
6161
  if (input.legacyAuthorized) {
5910
- return { authorized: true, actor: "rig-local-server", reason: "server-token" };
6162
+ return requestAuthResult({ authorized: true, actor: "rig-local-server", reason: "server-token" });
5911
6163
  }
5912
6164
  const bearer = bearerTokenFromRequest(input.req);
5913
6165
  const store = createGitHubAuthStore(input.projectRoot);
5914
6166
  const storedToken = store.readToken();
5915
6167
  const session = bearer ? store.readApiSession(bearer) : null;
5916
6168
  if (session) {
5917
- return { authorized: true, actor: session.login ?? "github-operator", reason: "github-session" };
6169
+ return requestAuthResult({
6170
+ authorized: true,
6171
+ actor: session.login ?? "github-operator",
6172
+ reason: "github-session",
6173
+ login: session.login,
6174
+ userId: session.userId,
6175
+ authStateFile: store.stateFile
6176
+ });
5918
6177
  }
5919
6178
  if (bearer && storedToken && bearer === storedToken) {
5920
6179
  const status = store.status({ oauthConfigured: Boolean(process.env.RIG_GITHUB_OAUTH_CLIENT_ID?.trim()) });
5921
- return { authorized: true, actor: status.login ?? "github-operator", reason: "github-token" };
6180
+ return requestAuthResult({
6181
+ authorized: true,
6182
+ actor: status.login ?? "github-operator",
6183
+ reason: "github-token",
6184
+ login: status.login,
6185
+ userId: status.userId,
6186
+ authStateFile: store.stateFile
6187
+ });
6188
+ }
6189
+ const indexedSession = readGitHubApiSession({ projectRoot: input.projectRoot, token: bearer });
6190
+ if (indexedSession) {
6191
+ const userNamespace = namespaceFromSessionIndex(indexedSession);
6192
+ return requestAuthResult({
6193
+ authorized: true,
6194
+ actor: indexedSession.login ?? "github-operator",
6195
+ reason: "github-user-session",
6196
+ login: indexedSession.login,
6197
+ userId: indexedSession.userId,
6198
+ userNamespace,
6199
+ authStateFile: indexedSession.authStateFile
6200
+ });
5922
6201
  }
5923
6202
  if (isPublicRigAuthBootstrapRoute(input.pathname)) {
5924
- return { authorized: true, actor: null, reason: "public-bootstrap" };
6203
+ return requestAuthResult({ authorized: true, actor: null, reason: "public-bootstrap" });
5925
6204
  }
5926
6205
  if (!input.serverAuthToken && !storedToken) {
5927
6206
  if (isLoopbackRequest(input.req)) {
5928
- return { authorized: true, actor: null, reason: "loopback-dev-no-auth" };
6207
+ return requestAuthResult({ authorized: true, actor: null, reason: "loopback-dev-no-auth" });
5929
6208
  }
5930
- return { authorized: false, actor: null, reason: "auth-required" };
6209
+ return requestAuthResult({ authorized: false, actor: null, reason: "auth-required" });
5931
6210
  }
5932
- return { authorized: false, actor: null, reason: storedToken ? "github-token-required" : "auth-required" };
6211
+ return requestAuthResult({ authorized: false, actor: null, reason: storedToken ? "github-token-required" : "auth-required" });
5933
6212
  }
5934
6213
  async function fetchGitHubUserInfo(token) {
5935
6214
  const response = await fetch("https://api.github.com/user", {
@@ -5949,6 +6228,67 @@ async function fetchGitHubUserInfo(token) {
5949
6228
  scopes: cleanHeaderScopes(response.headers.get("x-oauth-scopes"))
5950
6229
  };
5951
6230
  }
6231
+ function shouldWriteRootAuthCompat(projectRoot) {
6232
+ if (process.env.RIG_REMOTE_USER_NAMESPACE_ROOT?.trim())
6233
+ return false;
6234
+ const stateDir2 = normalizeString(process.env.RIG_STATE_DIR);
6235
+ if (!stateDir2)
6236
+ return true;
6237
+ return resolve20(stateDir2) === resolve20(projectRoot, ".rig", "state");
6238
+ }
6239
+ function requestScopedRegistryRoot(stateProjectRoot, requestAuth) {
6240
+ return requestAuth.userNamespace?.root ?? stateProjectRoot;
6241
+ }
6242
+ function requestScopedAuthStore(stateProjectRoot, requestAuth) {
6243
+ return requestAuth.authStateFile ? createGitHubAuthStoreFromStateFile(requestAuth.authStateFile) : createGitHubAuthStore(stateProjectRoot);
6244
+ }
6245
+ function userNamespaceResponse(namespace) {
6246
+ return namespace ? serializeRemoteUserNamespace(namespace) : undefined;
6247
+ }
6248
+ function resolveNamespacedBaseDir(input) {
6249
+ if (input.explicitBaseDir)
6250
+ return input.explicitBaseDir;
6251
+ const envBase = normalizeString(process.env[input.envName]);
6252
+ if (input.userNamespace) {
6253
+ return envBase ? resolve20(envBase, input.userNamespace.key) : input.userNamespace[input.legacySubdir === "remote-checkouts" ? "checkoutBaseDir" : "snapshotBaseDir"];
6254
+ }
6255
+ return envBase ?? (normalizeString(process.env.RIG_STATE_DIR) ? resolve20(normalizeString(process.env.RIG_STATE_DIR), input.legacySubdir) : resolve20(input.legacyProjectRoot, ".rig", input.legacySubdir));
6256
+ }
6257
+ function explicitCheckoutKey(body, checkoutInput, requestAuth) {
6258
+ return normalizeString(body.checkoutKey) ?? normalizeString(checkoutInput.checkoutKey) ?? normalizeString(checkoutInput.key) ?? (requestAuth.userNamespace ? undefined : "default");
6259
+ }
6260
+ function saveGitHubTokenForRemoteUser(input) {
6261
+ const namespace = resolveRemoteUserNamespace(input.projectRoot, { userId: input.user.userId, login: input.user.login });
6262
+ writeRemoteUserNamespaceMetadata(namespace);
6263
+ const store = createGitHubAuthStoreFromStateFile(namespace.authStateFile);
6264
+ store.saveToken({
6265
+ token: input.token,
6266
+ tokenSource: input.tokenSource,
6267
+ login: input.user.login,
6268
+ userId: input.user.userId,
6269
+ scopes: input.user.scopes,
6270
+ selectedRepo: input.selectedRepo
6271
+ });
6272
+ const apiSession = store.createApiSession();
6273
+ registerGitHubApiSession({ projectRoot: input.projectRoot, token: apiSession.token, namespace, selectedRepo: input.selectedRepo });
6274
+ const requestedRoot = normalizeString(input.requestedProjectRoot);
6275
+ if (requestedRoot && isAbsolute4(requestedRoot) && existsSync13(resolve20(requestedRoot))) {
6276
+ copyGitHubAuthStateToLocalProjectRoot(namespace.authStateFile, resolve20(requestedRoot));
6277
+ }
6278
+ if (shouldWriteRootAuthCompat(input.projectRoot)) {
6279
+ const rootStore = createGitHubAuthStore(input.projectRoot);
6280
+ rootStore.saveToken({
6281
+ token: input.token,
6282
+ tokenSource: input.tokenSource,
6283
+ login: input.user.login,
6284
+ userId: input.user.userId,
6285
+ scopes: input.user.scopes,
6286
+ selectedRepo: input.selectedRepo
6287
+ });
6288
+ rootStore.createApiSession();
6289
+ }
6290
+ return { store, namespace, apiSessionToken: apiSession.token };
6291
+ }
5952
6292
  async function postGitHubForm(endpoint, body) {
5953
6293
  const response = await fetch(endpoint, {
5954
6294
  method: "POST",
@@ -5966,11 +6306,11 @@ function resolveRequestedProjectRoot(currentRoot, rawRoot) {
5966
6306
  const requestedRoot = normalizeString(rawRoot);
5967
6307
  if (!requestedRoot)
5968
6308
  return currentRoot;
5969
- if (!isAbsolute3(requestedRoot)) {
6309
+ if (!isAbsolute4(requestedRoot)) {
5970
6310
  throw new Error("projectRoot must be an absolute path on the Rig server host");
5971
6311
  }
5972
- const normalizedRoot = resolve18(requestedRoot);
5973
- if (!existsSync11(normalizedRoot)) {
6312
+ const normalizedRoot = resolve20(requestedRoot);
6313
+ if (!existsSync13(normalizedRoot)) {
5974
6314
  throw new Error("projectRoot does not exist on the Rig server host");
5975
6315
  }
5976
6316
  return normalizedRoot;
@@ -6631,7 +6971,7 @@ data: ${JSON.stringify({ connectedAt: new Date().toISOString() })}
6631
6971
  }
6632
6972
  if (url.pathname === "/api/server/status") {
6633
6973
  const config = buildProjectConfigStatus(state.projectRoot);
6634
- const taskSource = await buildTaskSourceStatus(state, config);
6974
+ const taskSource = await buildTaskSourceStatus(state, config, requestAuth);
6635
6975
  return deps.jsonResponse({
6636
6976
  ok: true,
6637
6977
  projectRoot: state.projectRoot,
@@ -6655,8 +6995,9 @@ data: ${JSON.stringify({ connectedAt: new Date().toISOString() })}
6655
6995
  path: normalizeString(rawCheckout?.path) ?? state.projectRoot,
6656
6996
  ref: normalizeString(rawCheckout?.ref) ?? undefined
6657
6997
  } : undefined;
6658
- const record = upsertProjectRecord(state.projectRoot, { repoSlug, checkout });
6659
- return deps.jsonResponse({ ok: true, project: record });
6998
+ const registryRoot = requestScopedRegistryRoot(state.projectRoot, requestAuth);
6999
+ const record = upsertProjectRecord(registryRoot, { repoSlug, checkout });
7000
+ return deps.jsonResponse({ ok: true, project: record, userNamespace: userNamespaceResponse(requestAuth.userNamespace) });
6660
7001
  }
6661
7002
  const snapshotUploadMatch = url.pathname.match(/^\/api\/projects\/(.+?)\/upload-snapshot$/);
6662
7003
  if (snapshotUploadMatch && req.method === "POST") {
@@ -6669,8 +7010,15 @@ data: ${JSON.stringify({ connectedAt: new Date().toISOString() })}
6669
7010
  if (!archiveContentBase64) {
6670
7011
  return deps.badRequest("archiveContentBase64 is required");
6671
7012
  }
6672
- 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"));
6673
- const checkoutKey = normalizeString(body.checkoutKey) ?? "default";
7013
+ const registryRoot = requestScopedRegistryRoot(state.projectRoot, requestAuth);
7014
+ const baseDir = resolveNamespacedBaseDir({
7015
+ explicitBaseDir: normalizeString(body.baseDir),
7016
+ envName: "RIG_REMOTE_SNAPSHOT_BASE_DIR",
7017
+ userNamespace: requestAuth.userNamespace,
7018
+ legacyProjectRoot: state.projectRoot,
7019
+ legacySubdir: "remote-snapshots"
7020
+ });
7021
+ const checkoutKey = explicitCheckoutKey(body, body, requestAuth);
6674
7022
  try {
6675
7023
  const archive = parseSnapshotArchiveContentBase64(archiveContentBase64);
6676
7024
  const checkout = extractUploadedSnapshotArchive({
@@ -6683,14 +7031,14 @@ data: ${JSON.stringify({ connectedAt: new Date().toISOString() })}
6683
7031
  const checkoutRepair = repairRemoteCheckoutForRig(checkout.path, repoSlug);
6684
7032
  const packageInstall = installRemoteCheckoutPackages(checkout.path);
6685
7033
  const postInstallConfigValidation = validateRemoteCheckoutRigConfig(checkout.path);
6686
- const project = linkProjectCheckout(state.projectRoot, repoSlug, {
7034
+ const project = linkProjectCheckout(registryRoot, repoSlug, {
6687
7035
  kind: "uploaded-snapshot",
6688
7036
  path: checkout.path,
6689
7037
  ref: checkout.snapshotId
6690
7038
  });
6691
7039
  deps.snapshotService.invalidate("uploaded-snapshot-checkout");
6692
7040
  deps.broadcastSnapshotInvalidation(state, "uploaded-snapshot-checkout");
6693
- return deps.jsonResponse({ ok: true, checkout, project, checkoutRepair, packageInstall, postInstallConfigValidation });
7041
+ return deps.jsonResponse({ ok: true, checkout, project, checkoutRepair, packageInstall, postInstallConfigValidation, userNamespace: userNamespaceResponse(requestAuth.userNamespace) });
6694
7042
  } catch (error) {
6695
7043
  return deps.jsonResponse({
6696
7044
  ok: false,
@@ -6710,10 +7058,17 @@ data: ${JSON.stringify({ connectedAt: new Date().toISOString() })}
6710
7058
  if (kind !== "managed-clone" && kind !== "current-ref" && kind !== "existing-path") {
6711
7059
  return deps.jsonResponse({ ok: false, error: "checkout kind must be managed-clone, current-ref, or existing-path" }, 400);
6712
7060
  }
6713
- 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"));
6714
- const checkoutKey = normalizeString(body.checkoutKey) ?? normalizeString(checkoutInput.checkoutKey) ?? normalizeString(checkoutInput.key) ?? "default";
7061
+ const registryRoot = requestScopedRegistryRoot(state.projectRoot, requestAuth);
7062
+ const baseDir = resolveNamespacedBaseDir({
7063
+ explicitBaseDir: normalizeString(body.baseDir) ?? normalizeString(checkoutInput.baseDir),
7064
+ envName: "RIG_REMOTE_CHECKOUT_BASE_DIR",
7065
+ userNamespace: requestAuth.userNamespace,
7066
+ legacyProjectRoot: state.projectRoot,
7067
+ legacySubdir: "remote-checkouts"
7068
+ });
7069
+ const checkoutKey = explicitCheckoutKey(body, checkoutInput, requestAuth);
6715
7070
  const repoUrl = normalizeString(body.repoUrl) ?? normalizeString(checkoutInput.repoUrl) ?? `https://github.com/${repoSlug}.git`;
6716
- const credentialToken = createGitHubAuthStore(state.projectRoot).readToken();
7071
+ const credentialToken = requestScopedAuthStore(state.projectRoot, requestAuth).readToken();
6717
7072
  try {
6718
7073
  const checkout = await prepareRemoteCheckout({
6719
7074
  command: runRemoteCheckoutCommand,
@@ -6722,14 +7077,14 @@ data: ${JSON.stringify({ connectedAt: new Date().toISOString() })}
6722
7077
  const checkoutRepair = repairRemoteCheckoutForRig(checkout.path, repoSlug);
6723
7078
  const packageInstall = installRemoteCheckoutPackages(checkout.path);
6724
7079
  const postInstallConfigValidation = validateRemoteCheckoutRigConfig(checkout.path);
6725
- const project = linkProjectCheckout(state.projectRoot, repoSlug, {
7080
+ const project = linkProjectCheckout(registryRoot, repoSlug, {
6726
7081
  kind: checkout.kind,
6727
7082
  path: checkout.path,
6728
7083
  ref: checkout.ref ?? checkout.snapshotId ?? undefined
6729
7084
  });
6730
7085
  deps.snapshotService.invalidate("remote-checkout-prepared");
6731
7086
  deps.broadcastSnapshotInvalidation(state, "remote-checkout-prepared");
6732
- return deps.jsonResponse({ ok: true, checkout, project, checkoutRepair, packageInstall, postInstallConfigValidation });
7087
+ return deps.jsonResponse({ ok: true, checkout, project, checkoutRepair, packageInstall, postInstallConfigValidation, userNamespace: userNamespaceResponse(requestAuth.userNamespace) });
6733
7088
  } catch (error) {
6734
7089
  return deps.jsonResponse({ ok: false, error: error instanceof Error ? error.message : String(error) }, 400);
6735
7090
  }
@@ -6746,16 +7101,18 @@ data: ${JSON.stringify({ connectedAt: new Date().toISOString() })}
6746
7101
  if (kind !== "local" && kind !== "managed-clone" && kind !== "current-ref" && kind !== "uploaded-snapshot" && kind !== "existing-path") {
6747
7102
  return deps.jsonResponse({ ok: false, error: "checkout kind is required" }, 400);
6748
7103
  }
6749
- const project = linkProjectCheckout(state.projectRoot, repoSlug, {
7104
+ const registryRoot = requestScopedRegistryRoot(state.projectRoot, requestAuth);
7105
+ const project = linkProjectCheckout(registryRoot, repoSlug, {
6750
7106
  kind,
6751
7107
  path: normalizeString(body.path) ?? state.projectRoot,
6752
7108
  ref: normalizeString(body.ref) ?? undefined
6753
7109
  });
6754
- return deps.jsonResponse({ ok: true, project });
7110
+ return deps.jsonResponse({ ok: true, project, userNamespace: userNamespaceResponse(requestAuth.userNamespace) });
6755
7111
  }
6756
7112
  if (req.method === "GET") {
6757
- const project = getProjectRecord(state.projectRoot, repoSlug);
6758
- return project ? deps.jsonResponse({ ok: true, project }) : deps.notFound();
7113
+ const registryRoot = requestScopedRegistryRoot(state.projectRoot, requestAuth);
7114
+ const project = getProjectRecord(registryRoot, repoSlug);
7115
+ return project ? deps.jsonResponse({ ok: true, project, userNamespace: userNamespaceResponse(requestAuth.userNamespace) }) : deps.notFound();
6759
7116
  }
6760
7117
  }
6761
7118
  if (url.pathname === "/api/server/project-root" && req.method === "POST") {
@@ -6764,13 +7121,26 @@ data: ${JSON.stringify({ connectedAt: new Date().toISOString() })}
6764
7121
  if (!requestedRoot) {
6765
7122
  return deps.badRequest("projectRoot is required");
6766
7123
  }
6767
- if (!isAbsolute3(requestedRoot)) {
7124
+ if (!isAbsolute4(requestedRoot)) {
6768
7125
  return deps.badRequest("projectRoot must be an absolute path on the Rig server host");
6769
7126
  }
6770
- const normalizedRoot = resolve18(requestedRoot);
6771
- const exists = existsSync11(normalizedRoot);
6772
- if (exists) {
6773
- createGitHubAuthStore(state.projectRoot).copyToProjectRoot(normalizedRoot);
7127
+ const normalizedRoot = resolve20(requestedRoot);
7128
+ const exists = existsSync13(normalizedRoot);
7129
+ if (exists && requestAuth.userNamespace) {
7130
+ const allowedByNamespace = isPathInsideNamespace(requestAuth.userNamespace.root, normalizedRoot);
7131
+ const allowedByRegistry = projectRegistryContainsCheckout(requestAuth.userNamespace.root, normalizedRoot);
7132
+ if (!allowedByNamespace && !allowedByRegistry) {
7133
+ return deps.jsonResponse({
7134
+ ok: false,
7135
+ error: "Requested project root is outside the authenticated GitHub user namespace.",
7136
+ projectRoot: state.projectRoot,
7137
+ requestedProjectRoot: normalizedRoot,
7138
+ userNamespace: userNamespaceResponse(requestAuth.userNamespace)
7139
+ }, 403);
7140
+ }
7141
+ copyGitHubAuthStateToLocalProjectRoot(requestAuth.userNamespace.authStateFile, normalizedRoot);
7142
+ } else if (exists) {
7143
+ createGitHubAuthStore(state.projectRoot).copyToLocalProjectRoot(normalizedRoot);
6774
7144
  }
6775
7145
  const control = buildServerControlStatus();
6776
7146
  const switchCommand = process.env.RIG_PROJECT_ROOT_SWITCH_COMMAND?.trim();
@@ -6785,7 +7155,7 @@ data: ${JSON.stringify({ connectedAt: new Date().toISOString() })}
6785
7155
  message: "Requested project root does not exist on the Rig server host."
6786
7156
  }, 404);
6787
7157
  }
6788
- if (!existsSync11(resolve18(normalizedRoot, "rig.config.ts")) && !existsSync11(resolve18(normalizedRoot, "rig.config.json"))) {
7158
+ if (!existsSync13(resolve20(normalizedRoot, "rig.config.ts")) && !existsSync13(resolve20(normalizedRoot, "rig.config.json"))) {
6789
7159
  return deps.jsonResponse({
6790
7160
  ok: false,
6791
7161
  projectRoot: state.projectRoot,
@@ -6820,6 +7190,7 @@ data: ${JSON.stringify({ connectedAt: new Date().toISOString() })}
6820
7190
  exists,
6821
7191
  control,
6822
7192
  requiresRestart: false,
7193
+ userNamespace: userNamespaceResponse(requestAuth.userNamespace),
6823
7194
  message: "Project-root switch accepted. Rig server restart has been scheduled."
6824
7195
  }, 202);
6825
7196
  }
@@ -6834,11 +7205,11 @@ data: ${JSON.stringify({ connectedAt: new Date().toISOString() })}
6834
7205
  }, 409);
6835
7206
  }
6836
7207
  if (url.pathname === "/api/github/auth/status") {
6837
- const store = createGitHubAuthStore(state.projectRoot);
6838
- return deps.jsonResponse({ ok: true, ...store.status({ oauthConfigured: Boolean(process.env.RIG_GITHUB_OAUTH_CLIENT_ID?.trim()) }) });
7208
+ const store = requestScopedAuthStore(state.projectRoot, requestAuth);
7209
+ return deps.jsonResponse({ ok: true, ...store.status({ oauthConfigured: Boolean(process.env.RIG_GITHUB_OAUTH_CLIENT_ID?.trim()) }), userNamespace: userNamespaceResponse(requestAuth.userNamespace) });
6839
7210
  }
6840
7211
  if (url.pathname === "/api/github/repo/permissions") {
6841
- const store = createGitHubAuthStore(state.projectRoot);
7212
+ const store = requestScopedAuthStore(state.projectRoot, requestAuth);
6842
7213
  const auth = store.status({ oauthConfigured: Boolean(process.env.RIG_GITHUB_OAUTH_CLIENT_ID?.trim()) });
6843
7214
  if (!auth.signedIn) {
6844
7215
  return deps.jsonResponse({ ok: false, signedIn: false, canOpenPullRequest: false, reason: "not-authenticated" }, 401);
@@ -6866,24 +7237,20 @@ data: ${JSON.stringify({ connectedAt: new Date().toISOString() })}
6866
7237
  }
6867
7238
  try {
6868
7239
  const user = await fetchGitHubUserInfo(token);
6869
- const storeRoots = [
6870
- state.projectRoot,
6871
- ...requestedProjectRoot && isAbsolute3(requestedProjectRoot) && existsSync11(resolve18(requestedProjectRoot)) ? [resolve18(requestedProjectRoot)] : []
6872
- ].filter((root, index, roots) => roots.indexOf(root) === index);
6873
- const stores = storeRoots.map((root) => createGitHubAuthStore(root));
6874
- for (const store2 of stores) {
6875
- store2.saveToken({
6876
- token,
6877
- tokenSource: "manual-token",
6878
- login: user.login,
6879
- userId: user.userId,
6880
- scopes: user.scopes,
6881
- selectedRepo
6882
- });
6883
- }
6884
- const store = stores[stores.length - 1] ?? createGitHubAuthStore(state.projectRoot);
6885
- const apiSession = store.createApiSession();
6886
- return deps.jsonResponse({ ok: true, ...store.status({ oauthConfigured: Boolean(process.env.RIG_GITHUB_OAUTH_CLIENT_ID?.trim()) }), apiSessionToken: apiSession.token });
7240
+ const saved = saveGitHubTokenForRemoteUser({
7241
+ projectRoot: state.projectRoot,
7242
+ token,
7243
+ tokenSource: "manual-token",
7244
+ user,
7245
+ selectedRepo,
7246
+ requestedProjectRoot
7247
+ });
7248
+ return deps.jsonResponse({
7249
+ ok: true,
7250
+ ...saved.store.status({ oauthConfigured: Boolean(process.env.RIG_GITHUB_OAUTH_CLIENT_ID?.trim()) }),
7251
+ apiSessionToken: saved.apiSessionToken,
7252
+ userNamespace: userNamespaceResponse(saved.namespace)
7253
+ });
6887
7254
  } catch (error) {
6888
7255
  const message = error instanceof Error ? error.message : String(error);
6889
7256
  return deps.jsonResponse({ ok: false, error: message }, 400);
@@ -6950,9 +7317,21 @@ data: ${JSON.stringify({ connectedAt: new Date().toISOString() })}
6950
7317
  }
6951
7318
  const token = result.payload.access_token;
6952
7319
  const user = await fetchGitHubUserInfo(token);
6953
- store.saveToken({ token, tokenSource: "oauth-device", login: user.login, userId: user.userId, scopes: user.scopes });
6954
- const apiSession = store.createApiSession();
6955
- return deps.jsonResponse({ ok: true, status: "signed-in", ...store.status({ oauthConfigured: true }), apiSessionToken: apiSession.token });
7320
+ const saved = saveGitHubTokenForRemoteUser({
7321
+ projectRoot: state.projectRoot,
7322
+ token,
7323
+ tokenSource: "oauth-device",
7324
+ user,
7325
+ selectedRepo: null
7326
+ });
7327
+ store.clearPendingDevice(pollId);
7328
+ return deps.jsonResponse({
7329
+ ok: true,
7330
+ status: "signed-in",
7331
+ ...saved.store.status({ oauthConfigured: true }),
7332
+ apiSessionToken: saved.apiSessionToken,
7333
+ userNamespace: userNamespaceResponse(saved.namespace)
7334
+ });
6956
7335
  }
6957
7336
  if (url.pathname === "/api/github/repo/probe" && req.method === "POST") {
6958
7337
  const body = await deps.readJsonBody(req);
@@ -6961,7 +7340,7 @@ data: ${JSON.stringify({ connectedAt: new Date().toISOString() })}
6961
7340
  if (!owner || !repo) {
6962
7341
  return deps.badRequest("owner and repo are required");
6963
7342
  }
6964
- const store = createGitHubAuthStore(state.projectRoot);
7343
+ const store = requestScopedAuthStore(state.projectRoot, requestAuth);
6965
7344
  const authStatus = store.status({ oauthConfigured: Boolean(process.env.RIG_GITHUB_OAUTH_CLIENT_ID?.trim()) });
6966
7345
  const probe = await probeGitHubRepository({ owner, repo, token: store.readToken(), scopes: authStatus.scopes });
6967
7346
  return deps.jsonResponse({ ok: probe.ok, probe }, probe.ok ? 200 : 400);
@@ -6982,7 +7361,7 @@ data: ${JSON.stringify({ connectedAt: new Date().toISOString() })}
6982
7361
  return deps.badRequest(error instanceof Error ? error.message : String(error));
6983
7362
  }
6984
7363
  const authStatus = createGitHubAuthStore(state.projectRoot).status({ oauthConfigured: Boolean(process.env.RIG_GITHUB_OAUTH_CLIENT_ID?.trim()) });
6985
- const configPath = resolve18(targetRoot, "rig.config.ts");
7364
+ const configPath = resolve20(targetRoot, "rig.config.ts");
6986
7365
  const source = buildGitHubProjectConfigSource({
6987
7366
  projectName: rawProjectName,
6988
7367
  owner,
@@ -6994,8 +7373,8 @@ data: ${JSON.stringify({ connectedAt: new Date().toISOString() })}
6994
7373
  ok: true,
6995
7374
  projectRoot: targetRoot,
6996
7375
  configPath,
6997
- exists: existsSync11(configPath),
6998
- requiresOverwrite: existsSync11(configPath),
7376
+ exists: existsSync13(configPath),
7377
+ requiresOverwrite: existsSync13(configPath),
6999
7378
  source,
7000
7379
  owner,
7001
7380
  repo,
@@ -7031,8 +7410,8 @@ data: ${JSON.stringify({ connectedAt: new Date().toISOString() })}
7031
7410
  assignee,
7032
7411
  githubUserId: authStatus.userId ?? authStatus.login
7033
7412
  });
7034
- const configPath = resolve18(targetRoot, "rig.config.ts");
7035
- if (existsSync11(configPath) && !overwrite) {
7413
+ const configPath = resolve20(targetRoot, "rig.config.ts");
7414
+ if (existsSync13(configPath) && !overwrite) {
7036
7415
  return deps.jsonResponse({
7037
7416
  ok: false,
7038
7417
  error: "rig.config.ts already exists. Confirm overwrite to replace it; Rig will create a backup first.",
@@ -7048,11 +7427,11 @@ data: ${JSON.stringify({ connectedAt: new Date().toISOString() })}
7048
7427
  return deps.jsonResponse({ ok: false, error: repoProbe.message, repoProbe }, 400);
7049
7428
  }
7050
7429
  let backupPath = null;
7051
- if (existsSync11(configPath)) {
7430
+ if (existsSync13(configPath)) {
7052
7431
  backupPath = backupConfigPath(configPath);
7053
- copyFileSync(configPath, backupPath);
7432
+ copyFileSync2(configPath, backupPath);
7054
7433
  }
7055
- writeFileSync10(configPath, source, "utf8");
7434
+ writeFileSync12(configPath, source, "utf8");
7056
7435
  const selectedRepo = `${owner}/${repo}`;
7057
7436
  store.saveSelectedRepo(selectedRepo);
7058
7437
  const targetStore = createGitHubAuthStore(targetRoot);
@@ -7338,7 +7717,7 @@ data: ${JSON.stringify({ connectedAt: new Date().toISOString() })}
7338
7717
  }
7339
7718
  if (url.pathname === "/api/pi-rig/install" && req.method === "POST") {
7340
7719
  const configuredPackageSource = normalizeString(process.env.RIG_PI_RIG_PACKAGE_SOURCE);
7341
- 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";
7720
+ 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";
7342
7721
  if (process.env.RIG_TEST_FAKE_PI_INSTALL === "1") {
7343
7722
  return deps.jsonResponse({ ok: true, installed: true, piOk: true, piRigOk: true, extensionPath: "remote:~/.pi/agent/extensions/pi-rig", packageSource });
7344
7723
  }
@@ -7654,9 +8033,9 @@ data: ${JSON.stringify({ connectedAt: new Date().toISOString() })}
7654
8033
  } catch {
7655
8034
  return deps.badRequest("Invalid artifact path");
7656
8035
  }
7657
- mkdirSync11(dirname12(artifactPath), { recursive: true });
8036
+ mkdirSync13(dirname15(artifactPath), { recursive: true });
7658
8037
  const bytes = Buffer.from(contentBase64, "base64");
7659
- writeFileSync10(artifactPath, bytes);
8038
+ writeFileSync12(artifactPath, bytes);
7660
8039
  writeJsonFile4(`${artifactPath}.json`, {
7661
8040
  workspaceId: normalizeString(body.workspaceId) ?? RIG_WORKSPACE_ID,
7662
8041
  runId,
@@ -7699,7 +8078,6 @@ data: ${JSON.stringify({ connectedAt: new Date().toISOString() })}
7699
8078
  hostId,
7700
8079
  endpointId: leaseId
7701
8080
  });
7702
- await updateRemoteRunTaskSourceLifecycle(state.projectRoot, { ...run, status: "completed", completedAt, hostId, endpointId: leaseId }, "closed", "Remote Rig task run completed and closed this task.");
7703
8081
  await deps.enqueueRunLinearEvent(state.projectRoot, {
7704
8082
  type: "run.completed",
7705
8083
  runId,
@@ -7818,12 +8196,12 @@ data: ${JSON.stringify({ connectedAt: new Date().toISOString() })}
7818
8196
  try {
7819
8197
  const runsRoot = resolveAuthorityPaths(state.projectRoot).runsDir;
7820
8198
  const runRoot = deps.normalizeRelativePath(runsRoot, runId);
7821
- const artifactsRoot = resolve18(runRoot, "remote-artifacts");
8199
+ const artifactsRoot = resolve20(runRoot, "remote-artifacts");
7822
8200
  artifactPath = deps.normalizeRelativePath(artifactsRoot, fileName);
7823
8201
  } catch {
7824
8202
  return deps.badRequest("Invalid artifact path");
7825
8203
  }
7826
- if (!existsSync11(artifactPath)) {
8204
+ if (!existsSync13(artifactPath)) {
7827
8205
  return deps.notFound();
7828
8206
  }
7829
8207
  return new Response(Bun.file(artifactPath));
@@ -8716,8 +9094,8 @@ async function routeWebSocketRequest(state, deps, request) {
8716
9094
  }
8717
9095
 
8718
9096
  // packages/server/src/server-helpers/inspector-jobs.ts
8719
- import { existsSync as existsSync15, mkdirSync as mkdirSync14, readFileSync as readFileSync10, writeFileSync as writeFileSync13 } from "fs";
8720
- import { dirname as dirname15, resolve as resolve21 } from "path";
9097
+ import { existsSync as existsSync17, mkdirSync as mkdirSync16, readFileSync as readFileSync12, writeFileSync as writeFileSync15 } from "fs";
9098
+ import { dirname as dirname18, resolve as resolve23 } from "path";
8721
9099
  import { readJsonFile as readJsonFile3 } from "@rig/runtime/control-plane/authority-files";
8722
9100
  import { resolveMonorepoRoot as resolveMonorepoRoot5 } from "@rig/runtime/control-plane/native/utils";
8723
9101
  import { normalizeTaskLifecycleStatus as normalizeTaskLifecycleStatus2 } from "@rig/runtime/control-plane/state-sync/types";
@@ -8825,8 +9203,8 @@ import { randomUUID as randomUUID3 } from "crypto";
8825
9203
 
8826
9204
  // packages/server/src/inspector/mission.ts
8827
9205
  import { randomUUID as randomUUID2 } from "crypto";
8828
- import { appendFileSync, existsSync as existsSync12, mkdirSync as mkdirSync12, readFileSync as readFileSync8, readdirSync as readdirSync4, renameSync, writeFileSync as writeFileSync11 } from "fs";
8829
- import { dirname as dirname13, join, resolve as resolve19 } from "path";
9206
+ import { appendFileSync, existsSync as existsSync14, mkdirSync as mkdirSync14, readFileSync as readFileSync10, readdirSync as readdirSync4, renameSync, writeFileSync as writeFileSync13 } from "fs";
9207
+ import { dirname as dirname16, join, resolve as resolve21 } from "path";
8830
9208
  function isJsonValue(value) {
8831
9209
  if (value === null)
8832
9210
  return true;
@@ -8866,7 +9244,7 @@ function isRecord2(value) {
8866
9244
  }
8867
9245
  function readJsonRecord(path) {
8868
9246
  try {
8869
- const parsed = JSON.parse(readFileSync8(path, "utf8"));
9247
+ const parsed = JSON.parse(readFileSync10(path, "utf8"));
8870
9248
  if (!isRecord2(parsed)) {
8871
9249
  return { ok: false, error: `Mission file ${path} does not contain an object` };
8872
9250
  }
@@ -8946,14 +9324,14 @@ function missionActionDetails(mission) {
8946
9324
  };
8947
9325
  }
8948
9326
  function writeJsonFile5(path, value) {
8949
- mkdirSync12(dirname13(path), { recursive: true });
9327
+ mkdirSync14(dirname16(path), { recursive: true });
8950
9328
  const tempPath = `${path}.${process.pid}.${Date.now()}.tmp`;
8951
- writeFileSync11(tempPath, `${JSON.stringify(value, null, 2)}
9329
+ writeFileSync13(tempPath, `${JSON.stringify(value, null, 2)}
8952
9330
  `, "utf8");
8953
9331
  renameSync(tempPath, path);
8954
9332
  }
8955
9333
  function resolveInspectorMissionPaths(projectRoot) {
8956
- const inspectorDir = resolve19(resolveRigServerPaths(projectRoot).stateDir, "inspector");
9334
+ const inspectorDir = resolve21(resolveRigServerPaths(projectRoot).stateDir, "inspector");
8957
9335
  return {
8958
9336
  inspectorDir,
8959
9337
  missionsDir: join(inspectorDir, "missions"),
@@ -8962,8 +9340,8 @@ function resolveInspectorMissionPaths(projectRoot) {
8962
9340
  }
8963
9341
  function createInspectorMissionController(options) {
8964
9342
  const paths = resolveInspectorMissionPaths(options.projectRoot);
8965
- mkdirSync12(paths.missionsDir, { recursive: true });
8966
- mkdirSync12(paths.journalsDir, { recursive: true });
9343
+ mkdirSync14(paths.missionsDir, { recursive: true });
9344
+ mkdirSync14(paths.journalsDir, { recursive: true });
8967
9345
  const now = options.now ?? (() => new Date().toISOString());
8968
9346
  const nextId = options.idGenerator ?? (() => `mission:${randomUUID2()}`);
8969
9347
  function missionPath(missionId) {
@@ -8973,15 +9351,15 @@ function createInspectorMissionController(options) {
8973
9351
  return join(paths.journalsDir, `${missionId}.jsonl`);
8974
9352
  }
8975
9353
  function appendMissionJournal(entry) {
8976
- mkdirSync12(paths.journalsDir, { recursive: true });
9354
+ mkdirSync14(paths.journalsDir, { recursive: true });
8977
9355
  appendFileSync(journalPath(entry.missionId), `${JSON.stringify(entry)}
8978
9356
  `, "utf8");
8979
9357
  }
8980
9358
  function listMissionJournal(missionId) {
8981
9359
  const path = journalPath(missionId);
8982
- if (!existsSync12(path))
9360
+ if (!existsSync14(path))
8983
9361
  return [];
8984
- return readFileSync8(path, "utf8").split(`
9362
+ return readFileSync10(path, "utf8").split(`
8985
9363
  `).filter((line) => line.trim().length > 0).map((line) => JSON.parse(line)).filter(isRecord2).map((entry) => ({
8986
9364
  id: typeof entry.id === "string" ? entry.id : `journal:${randomUUID2()}`,
8987
9365
  missionId,
@@ -8997,7 +9375,7 @@ function createInspectorMissionController(options) {
8997
9375
  }
8998
9376
  function readMissionOnly(missionId) {
8999
9377
  const path = missionPath(missionId);
9000
- if (!existsSync12(path)) {
9378
+ if (!existsSync14(path)) {
9001
9379
  return { ok: false, error: `Mission ${missionId} was not found` };
9002
9380
  }
9003
9381
  const read = readJsonRecord(path);
@@ -9048,7 +9426,7 @@ function createInspectorMissionController(options) {
9048
9426
  const source = cloneJsonRecord(input.sourceTask);
9049
9427
  const missionId = nextId();
9050
9428
  const path = missionPath(missionId);
9051
- if (existsSync12(path)) {
9429
+ if (existsSync14(path)) {
9052
9430
  const existing = readMissionOnly(missionId);
9053
9431
  if (!existing.ok)
9054
9432
  return existing;
@@ -10648,8 +11026,8 @@ function createCodexInspectorTransport(options) {
10648
11026
  const sendRequest = async (method, params) => {
10649
11027
  const id = nextRequestId;
10650
11028
  nextRequestId += 1;
10651
- const response = new Promise((resolve20, reject) => {
10652
- pendingResponses.set(id, { resolve: resolve20, reject });
11029
+ const response = new Promise((resolve22, reject) => {
11030
+ pendingResponses.set(id, { resolve: resolve22, reject });
10653
11031
  });
10654
11032
  response.catch(() => {});
10655
11033
  try {
@@ -10959,9 +11337,9 @@ function createCodexInspectorTransport(options) {
10959
11337
  }
10960
11338
  lastAssistantMessage = null;
10961
11339
  lastError = null;
10962
- const turnResult = new Promise((resolve20, reject) => {
11340
+ const turnResult = new Promise((resolve22, reject) => {
10963
11341
  currentTurn = {
10964
- resolve: resolve20,
11342
+ resolve: resolve22,
10965
11343
  reject,
10966
11344
  events: []
10967
11345
  };
@@ -11021,13 +11399,13 @@ function createCodexInspectorTransport(options) {
11021
11399
  };
11022
11400
  }
11023
11401
  function writeChildLine(child, line) {
11024
- return new Promise((resolve20, reject) => {
11402
+ return new Promise((resolve22, reject) => {
11025
11403
  child.stdin.write(line, (error) => {
11026
11404
  if (error) {
11027
11405
  reject(error);
11028
11406
  return;
11029
11407
  }
11030
- resolve20();
11408
+ resolve22();
11031
11409
  });
11032
11410
  });
11033
11411
  }
@@ -11040,10 +11418,10 @@ function terminateChild(child) {
11040
11418
  } catch {}
11041
11419
  }
11042
11420
  async function waitForChildSpawn(child) {
11043
- await new Promise((resolve20, reject) => {
11421
+ await new Promise((resolve22, reject) => {
11044
11422
  const onSpawn = () => {
11045
11423
  cleanup();
11046
- resolve20();
11424
+ resolve22();
11047
11425
  };
11048
11426
  const onError = (error) => {
11049
11427
  cleanup();
@@ -11555,8 +11933,8 @@ function createGlobalInspectorService(options) {
11555
11933
 
11556
11934
  // packages/server/src/inspector/upstream-sync.ts
11557
11935
  import { spawnSync as spawnSync4 } from "child_process";
11558
- import { existsSync as existsSync13, mkdirSync as mkdirSync13, readFileSync as readFileSync9, writeFileSync as writeFileSync12 } from "fs";
11559
- import { dirname as dirname14, resolve as resolve20 } from "path";
11936
+ import { existsSync as existsSync15, mkdirSync as mkdirSync15, readFileSync as readFileSync11, writeFileSync as writeFileSync14 } from "fs";
11937
+ import { dirname as dirname17, resolve as resolve22 } from "path";
11560
11938
  import { resolveMonorepoRoot as resolveMonorepoRoot4 } from "@rig/runtime/control-plane/native/utils";
11561
11939
  var UPSTREAM_VALIDATION_DESCRIPTIONS = {
11562
11940
  "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.",
@@ -11694,34 +12072,34 @@ function defaultGitRunner(repoRoot, args) {
11694
12072
  }
11695
12073
  function upstreamStatePath(projectRoot, override) {
11696
12074
  if (override) {
11697
- return resolve20(override);
12075
+ return resolve22(override);
11698
12076
  }
11699
- return resolve20(resolveRigServerPaths(projectRoot).stateDir, "inspector", "upstream-sync.json");
12077
+ return resolve22(resolveRigServerPaths(projectRoot).stateDir, "inspector", "upstream-sync.json");
11700
12078
  }
11701
12079
  function readUpstreamState(projectRoot, statePath) {
11702
12080
  const path = upstreamStatePath(projectRoot, statePath);
11703
- if (!existsSync13(path)) {
12081
+ if (!existsSync15(path)) {
11704
12082
  return null;
11705
12083
  }
11706
12084
  try {
11707
- return JSON.parse(readFileSync9(path, "utf-8"));
12085
+ return JSON.parse(readFileSync11(path, "utf-8"));
11708
12086
  } catch {
11709
12087
  return null;
11710
12088
  }
11711
12089
  }
11712
12090
  function writeUpstreamState(projectRoot, state, statePath) {
11713
12091
  const path = upstreamStatePath(projectRoot, statePath);
11714
- mkdirSync13(dirname14(path), { recursive: true });
11715
- writeFileSync12(path, `${JSON.stringify(state, null, 2)}
12092
+ mkdirSync15(dirname17(path), { recursive: true });
12093
+ writeFileSync14(path, `${JSON.stringify(state, null, 2)}
11716
12094
  `, "utf8");
11717
12095
  }
11718
12096
  function readImportedRevision(projectRoot, upstreamsDocPath) {
11719
12097
  const monorepoRoot = resolveMonorepoRoot4(projectRoot);
11720
- const docPath = upstreamsDocPath ? resolve20(upstreamsDocPath) : resolve20(monorepoRoot, "docs", "UPSTREAMS.md");
11721
- if (!existsSync13(docPath)) {
12098
+ const docPath = upstreamsDocPath ? resolve22(upstreamsDocPath) : resolve22(monorepoRoot, "docs", "UPSTREAMS.md");
12099
+ if (!existsSync15(docPath)) {
11722
12100
  throw new Error(`UPSTREAMS.md not found at ${docPath}`);
11723
12101
  }
11724
- const docContent = readFileSync9(docPath, "utf-8");
12102
+ const docContent = readFileSync11(docPath, "utf-8");
11725
12103
  const revision = parseImportedUpstreamRevision(docContent, "upstream") ?? parseImportedUpstreamRevision(docContent, "humoongate");
11726
12104
  if (!revision) {
11727
12105
  throw new Error(`Failed to parse upstream imported revision from ${docPath}`);
@@ -11743,7 +12121,7 @@ function resolveRemoteBranch(repoRoot, remote, gitRunner) {
11743
12121
  return null;
11744
12122
  }
11745
12123
  function isGitCheckout(path, gitRunner) {
11746
- if (!existsSync13(resolve20(path, ".git"))) {
12124
+ if (!existsSync15(resolve22(path, ".git"))) {
11747
12125
  return false;
11748
12126
  }
11749
12127
  const result = gitRunner(path, ["rev-parse", "--is-inside-work-tree"]);
@@ -11752,12 +12130,12 @@ function isGitCheckout(path, gitRunner) {
11752
12130
  function resolveUpstreamCheckout(projectRoot, explicitCheckout, gitRunner) {
11753
12131
  const monorepoRoot = resolveMonorepoRoot4(projectRoot);
11754
12132
  const candidates = [
11755
- explicitCheckout ? resolve20(explicitCheckout) : "",
11756
- process.env.UPSTREAM_CHECKOUT?.trim() ? resolve20(process.env.UPSTREAM_CHECKOUT.trim()) : "",
11757
- process.env.HUMOONGATE_UPSTREAM_CHECKOUT?.trim() ? resolve20(process.env.HUMOONGATE_UPSTREAM_CHECKOUT.trim()) : "",
11758
- resolve20(projectRoot, "..", "humoongate"),
11759
- resolve20(monorepoRoot, "..", "humoongate"),
11760
- resolve20(monorepoRoot, "humoongate")
12133
+ explicitCheckout ? resolve22(explicitCheckout) : "",
12134
+ process.env.UPSTREAM_CHECKOUT?.trim() ? resolve22(process.env.UPSTREAM_CHECKOUT.trim()) : "",
12135
+ process.env.HUMOONGATE_UPSTREAM_CHECKOUT?.trim() ? resolve22(process.env.HUMOONGATE_UPSTREAM_CHECKOUT.trim()) : "",
12136
+ resolve22(projectRoot, "..", "humoongate"),
12137
+ resolve22(monorepoRoot, "..", "humoongate"),
12138
+ resolve22(monorepoRoot, "humoongate")
11761
12139
  ].filter(Boolean);
11762
12140
  for (const candidate of candidates) {
11763
12141
  if (isGitCheckout(candidate, gitRunner)) {
@@ -11993,10 +12371,10 @@ async function runUpstreamSyncScan(options) {
11993
12371
  }
11994
12372
 
11995
12373
  // packages/server/src/server-helpers/task-config.ts
11996
- import { existsSync as existsSync14 } from "fs";
12374
+ import { existsSync as existsSync16 } from "fs";
11997
12375
  async function readTaskConfig(projectRoot) {
11998
12376
  const taskConfigPath = resolveRigServerPaths(projectRoot).taskConfigPath;
11999
- if (!existsSync14(taskConfigPath)) {
12377
+ if (!existsSync16(taskConfigPath)) {
12000
12378
  return {};
12001
12379
  }
12002
12380
  try {
@@ -12032,11 +12410,11 @@ function resolveFollowupSourceCommit(input) {
12032
12410
  }
12033
12411
  async function createInspectorFollowupTask(projectRoot, input) {
12034
12412
  const monorepoRoot = resolveMonorepoRoot5(projectRoot);
12035
- const issuesPath = resolve21(monorepoRoot, ".beads", "issues.jsonl");
12036
- const taskStatePath = resolve21(monorepoRoot, ".beads", "task-state.json");
12037
- const taskConfigPath = resolve21(monorepoRoot, ".rig", "task-config.json");
12038
- mkdirSync14(dirname15(issuesPath), { recursive: true });
12039
- mkdirSync14(dirname15(taskConfigPath), { recursive: true });
12413
+ const issuesPath = resolve23(monorepoRoot, ".beads", "issues.jsonl");
12414
+ const taskStatePath = resolve23(monorepoRoot, ".beads", "task-state.json");
12415
+ const taskConfigPath = resolve23(monorepoRoot, ".rig", "task-config.json");
12416
+ mkdirSync16(dirname18(issuesPath), { recursive: true });
12417
+ mkdirSync16(dirname18(taskConfigPath), { recursive: true });
12040
12418
  const summary = normalizeString(input.summary) ?? "Inspector follow-up";
12041
12419
  const description = normalizeString(input.description) ?? normalizeString(input.details?.description) ?? `Created by the global inspector: ${summary}`;
12042
12420
  const acceptanceCriteria = normalizeString(input.acceptanceCriteria) ?? "Investigate the detected drift and port the relevant changes into Rig.";
@@ -12055,7 +12433,7 @@ async function createInspectorFollowupTask(projectRoot, input) {
12055
12433
  const sourceKey = normalizeString(input.sourceKey) ?? normalizeString(input.details?.sourceKey);
12056
12434
  const createdAt = normalizeString(input.createdAt) ?? new Date().toISOString();
12057
12435
  const status = normalizeTaskLifecycleStatus2(normalizeString(input.status) ?? "open") ?? "open";
12058
- const existingIssueLines = existsSync15(issuesPath) ? readFileSync10(issuesPath, "utf8").split(/\r?\n/).map((line) => line.trim()).filter(Boolean) : [];
12436
+ const existingIssueLines = existsSync17(issuesPath) ? readFileSync12(issuesPath, "utf8").split(/\r?\n/).map((line) => line.trim()).filter(Boolean) : [];
12059
12437
  const existingIssues = existingIssueLines.map((line) => {
12060
12438
  try {
12061
12439
  return JSON.parse(line);
@@ -12064,7 +12442,7 @@ async function createInspectorFollowupTask(projectRoot, input) {
12064
12442
  }
12065
12443
  }).filter((value) => value !== null);
12066
12444
  const existingIds = new Set(existingIssues.map((issue) => typeof issue.id === "string" ? issue.id : null).filter((value) => value !== null));
12067
- const rawTaskState = existsSync15(taskStatePath) ? readJsonFile3(taskStatePath, {}) : {};
12445
+ const rawTaskState = existsSync17(taskStatePath) ? readJsonFile3(taskStatePath, {}) : {};
12068
12446
  const tasks = rawTaskState.tasks && typeof rawTaskState.tasks === "object" && !Array.isArray(rawTaskState.tasks) ? rawTaskState.tasks : {};
12069
12447
  const existingTaskIdFromSourceKey = sourceKey == null ? null : Object.entries(tasks).find(([, metadata]) => {
12070
12448
  if (!metadata || typeof metadata !== "object" || Array.isArray(metadata)) {
@@ -12090,7 +12468,7 @@ async function createInspectorFollowupTask(projectRoot, input) {
12090
12468
  updated_at: createdAt,
12091
12469
  labels: mergedLabels
12092
12470
  };
12093
- writeFileSync13(issuesPath, existingIssueLines.length > 0 ? `${existingIssueLines.join(`
12471
+ writeFileSync15(issuesPath, existingIssueLines.length > 0 ? `${existingIssueLines.join(`
12094
12472
  `)}
12095
12473
  ${JSON.stringify(issueRecord)}
12096
12474
  ` : `${JSON.stringify(issueRecord)}
@@ -12108,7 +12486,7 @@ ${JSON.stringify(issueRecord)}
12108
12486
  labels: mergedLabels
12109
12487
  };
12110
12488
  });
12111
- writeFileSync13(issuesPath, `${updatedIssues.map((issue) => JSON.stringify(issue)).join(`
12489
+ writeFileSync15(issuesPath, `${updatedIssues.map((issue) => JSON.stringify(issue)).join(`
12112
12490
  `)}
12113
12491
  `, "utf8");
12114
12492
  }
@@ -12131,14 +12509,14 @@ ${JSON.stringify(issueRecord)}
12131
12509
  }
12132
12510
  };
12133
12511
  }
12134
- writeFileSync13(taskConfigPath, `${JSON.stringify(taskConfig, null, 2)}
12512
+ writeFileSync15(taskConfigPath, `${JSON.stringify(taskConfig, null, 2)}
12135
12513
  `, "utf8");
12136
12514
  tasks[taskId] = {
12137
12515
  status,
12138
12516
  sourceCommit: resolveFollowupSourceCommit(input),
12139
12517
  ...sourceKey ? { sourceKey } : {}
12140
12518
  };
12141
- writeFileSync13(taskStatePath, `${JSON.stringify({
12519
+ writeFileSync15(taskStatePath, `${JSON.stringify({
12142
12520
  schemaVersion: 1,
12143
12521
  baseTrackerCommit: typeof rawTaskState.baseTrackerCommit === "string" ? rawTaskState.baseTrackerCommit : null,
12144
12522
  tasks
@@ -12446,12 +12824,12 @@ function isAuthorizedLinearWebhookRequest(req) {
12446
12824
  }
12447
12825
 
12448
12826
  // packages/server/src/server-helpers/notifications.ts
12449
- import { existsSync as existsSync16, mkdirSync as mkdirSync15, readFileSync as readFileSync11 } from "fs";
12450
- import { dirname as dirname16 } from "path";
12827
+ import { existsSync as existsSync18, mkdirSync as mkdirSync17, readFileSync as readFileSync13 } from "fs";
12828
+ import { dirname as dirname19 } from "path";
12451
12829
  async function loadNotificationConfig(path) {
12452
- if (!existsSync16(path)) {
12830
+ if (!existsSync18(path)) {
12453
12831
  const defaultConfig = { targets: [] };
12454
- mkdirSync15(dirname16(path), { recursive: true });
12832
+ mkdirSync17(dirname19(path), { recursive: true });
12455
12833
  await Bun.write(path, `${JSON.stringify(defaultConfig, null, 2)}
12456
12834
  `);
12457
12835
  return defaultConfig;
@@ -12466,10 +12844,10 @@ async function loadNotificationConfig(path) {
12466
12844
  }
12467
12845
  }
12468
12846
  function readRecentEvents(file, limit) {
12469
- if (!existsSync16(file)) {
12847
+ if (!existsSync18(file)) {
12470
12848
  return [];
12471
12849
  }
12472
- const lines = readFileSync11(file, "utf-8").split(/\r?\n/).map((line) => line.trim()).filter(Boolean).slice(-limit);
12850
+ const lines = readFileSync13(file, "utf-8").split(/\r?\n/).map((line) => line.trim()).filter(Boolean).slice(-limit);
12473
12851
  const events = [];
12474
12852
  for (const line of lines) {
12475
12853
  try {
@@ -12564,11 +12942,11 @@ function extractObjectLiteralBlock(source, property) {
12564
12942
  }
12565
12943
  function readFallbackIssueAnalysisConfig(projectRoot) {
12566
12944
  for (const fileName of ["rig.config.ts", "rig.config.json"]) {
12567
- const path = resolve22(projectRoot, fileName);
12568
- if (!existsSync17(path))
12945
+ const path = resolve24(projectRoot, fileName);
12946
+ if (!existsSync19(path))
12569
12947
  continue;
12570
12948
  try {
12571
- const source = readFileSync12(path, "utf8");
12949
+ const source = readFileSync14(path, "utf8");
12572
12950
  if (fileName.endsWith(".json"))
12573
12951
  return JSON.parse(source);
12574
12952
  const issueBlock = extractObjectLiteralBlock(source, "issueAnalysis");
@@ -12701,8 +13079,8 @@ async function createIssueAnalysisRunnerForServerState(state, input) {
12701
13079
  async function withServerPathEnv(projectRoot, fn) {
12702
13080
  const waitForTurn = serverPathEnvQueue;
12703
13081
  let releaseTurn;
12704
- serverPathEnvQueue = new Promise((resolve23) => {
12705
- releaseTurn = resolve23;
13082
+ serverPathEnvQueue = new Promise((resolve25) => {
13083
+ releaseTurn = resolve25;
12706
13084
  });
12707
13085
  await waitForTurn;
12708
13086
  const paths = resolveServerAuthorityPaths(projectRoot);
@@ -12738,9 +13116,9 @@ async function withServerAuthorityEnvIfNeeded(projectRoot, fn) {
12738
13116
  return withServerPathEnv(projectRoot, fn);
12739
13117
  }
12740
13118
  async function readWorkspaceTasks(projectRoot) {
12741
- const issuesPath = resolve22(resolveMonorepoRoot6(projectRoot), ".beads", "issues.jsonl");
13119
+ const issuesPath = resolve24(resolveMonorepoRoot6(projectRoot), ".beads", "issues.jsonl");
12742
13120
  const taskConfig = await readTaskConfig(projectRoot);
12743
- if (!existsSync17(issuesPath)) {
13121
+ if (!existsSync19(issuesPath)) {
12744
13122
  return [];
12745
13123
  }
12746
13124
  const latestById = new Map;
@@ -12814,11 +13192,11 @@ function resolveTaskArtifactDirsFromRuns(projectRoot, taskId, knownRuns) {
12814
13192
  continue;
12815
13193
  add(run.artifactRoot);
12816
13194
  if (run.worktreePath) {
12817
- add(resolve22(run.worktreePath, "artifacts", taskId));
13195
+ add(resolve24(run.worktreePath, "artifacts", taskId));
12818
13196
  }
12819
13197
  }
12820
13198
  for (const artifactsRoot of listAuthorityArtifactRoots(projectRoot)) {
12821
- add(resolve22(artifactsRoot, taskId));
13199
+ add(resolve24(artifactsRoot, taskId));
12822
13200
  }
12823
13201
  return candidates;
12824
13202
  }
@@ -12832,7 +13210,7 @@ async function listArtifactSummaries(projectRoot, taskId, knownTaskIds, knownRun
12832
13210
  }
12833
13211
  }
12834
13212
  return taskIds.flatMap((currentTaskId) => {
12835
- const currentRoot = resolveTaskArtifactDirsFromRuns(projectRoot, currentTaskId, runs).find((path) => existsSync17(path));
13213
+ const currentRoot = resolveTaskArtifactDirsFromRuns(projectRoot, currentTaskId, runs).find((path) => existsSync19(path));
12836
13214
  if (!currentRoot) {
12837
13215
  return [];
12838
13216
  }
@@ -12844,7 +13222,7 @@ async function listArtifactSummaries(projectRoot, taskId, knownTaskIds, knownRun
12844
13222
  taskId: currentTaskId,
12845
13223
  kind: "file",
12846
13224
  label: fileName,
12847
- path: resolve22(currentRoot, fileName),
13225
+ path: resolve24(currentRoot, fileName),
12848
13226
  url: null,
12849
13227
  metadata: {
12850
13228
  fileName
@@ -12887,11 +13265,11 @@ function buildInspectorStreamPayload(state, sequence) {
12887
13265
  }
12888
13266
  function listRemoteRunArtifacts(projectRoot, runId) {
12889
13267
  const root = remoteArtifactsRoot(projectRoot, runId);
12890
- if (!existsSync17(root)) {
13268
+ if (!existsSync19(root)) {
12891
13269
  return [];
12892
13270
  }
12893
13271
  return readdirSync5(root, { withFileTypes: true }).filter((entry) => entry.isFile()).filter((entry) => !entry.name.endsWith(".json")).map((entry) => {
12894
- const artifactPath = resolve22(root, entry.name);
13272
+ const artifactPath = resolve24(root, entry.name);
12895
13273
  const stat = statSync6(artifactPath);
12896
13274
  const meta = readJsonFile4(`${artifactPath}.json`, null);
12897
13275
  return {
@@ -13133,8 +13511,8 @@ function fileStats(path) {
13133
13511
  }
13134
13512
  }
13135
13513
  function runFileCursor(projectRoot, run) {
13136
- const runDir = dirname17(runLogsPath(projectRoot, run.runId));
13137
- const runJson = fileStats(resolve22(runDir, "run.json"));
13514
+ const runDir = dirname20(runLogsPath(projectRoot, run.runId));
13515
+ const runJson = fileStats(resolve24(runDir, "run.json"));
13138
13516
  const timeline = fileStats(runTimelinePath(projectRoot, run.runId));
13139
13517
  const logs = fileStats(runLogsPath(projectRoot, run.runId));
13140
13518
  return {
@@ -13184,10 +13562,10 @@ function startRunFileWatcher(state, pollMs) {
13184
13562
  }, Math.max(250, Math.min(pollMs, 1000)));
13185
13563
  }
13186
13564
  function startPoller(state, pollMs) {
13187
- let offset = existsSync17(state.eventsFile) ? statSync6(state.eventsFile).size : 0;
13565
+ let offset = existsSync19(state.eventsFile) ? statSync6(state.eventsFile).size : 0;
13188
13566
  return setInterval(async () => {
13189
13567
  try {
13190
- if (!existsSync17(state.eventsFile)) {
13568
+ if (!existsSync19(state.eventsFile)) {
13191
13569
  return;
13192
13570
  }
13193
13571
  const file = await open(state.eventsFile, "r");
@@ -13315,7 +13693,7 @@ function resolveProjectRoot() {
13315
13693
  return resolveRigProjectRoot({
13316
13694
  envProjectRoot: process.env.PROJECT_RIG_ROOT ?? null,
13317
13695
  cwd: process.cwd(),
13318
- fallbackRoot: resolve22(import.meta.dir, "../..")
13696
+ fallbackRoot: resolve24(import.meta.dir, "../..")
13319
13697
  });
13320
13698
  }
13321
13699
  var __testOnly = {