@h-rig/cli 0.0.6-alpha.6 → 0.0.6-alpha.60
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/README.md +1 -1
- package/dist/bin/rig.js +4184 -1228
- package/dist/src/commands/_cli-format.js +369 -0
- package/dist/src/commands/_connection-state.js +12 -6
- package/dist/src/commands/_doctor-checks.js +79 -34
- package/dist/src/commands/_help-catalog.js +445 -0
- package/dist/src/commands/_operator-surface.js +220 -0
- package/dist/src/commands/_operator-view.js +1124 -64
- package/dist/src/commands/_parsers.js +0 -2
- package/dist/src/commands/_pi-frontend.js +1080 -0
- package/dist/src/commands/_pi-install.js +4 -3
- package/dist/src/commands/_pi-remote-session.js +771 -0
- package/dist/src/commands/_pi-worker-bridge-extension.js +834 -0
- package/dist/src/commands/_policy.js +0 -2
- package/dist/src/commands/_preflight.js +98 -116
- package/dist/src/commands/_run-driver-helpers.js +34 -2
- package/dist/src/commands/_server-client.js +225 -48
- package/dist/src/commands/_snapshot-upload.js +74 -30
- package/dist/src/commands/_spinner.js +63 -0
- package/dist/src/commands/_task-picker.js +44 -16
- package/dist/src/commands/agent.js +8 -9
- package/dist/src/commands/browser.js +4 -6
- package/dist/src/commands/connect.js +134 -26
- package/dist/src/commands/dist.js +4 -6
- package/dist/src/commands/doctor.js +79 -34
- package/dist/src/commands/github.js +76 -32
- package/dist/src/commands/inbox.js +410 -31
- package/dist/src/commands/init.js +398 -90
- package/dist/src/commands/inspect.js +296 -23
- package/dist/src/commands/inspector.js +2 -4
- package/dist/src/commands/pi.js +168 -0
- package/dist/src/commands/plugin.js +81 -22
- package/dist/src/commands/profile-and-review.js +8 -10
- package/dist/src/commands/queue.js +2 -3
- package/dist/src/commands/remote.js +18 -20
- package/dist/src/commands/repo-git-harness.js +6 -8
- package/dist/src/commands/run.js +1422 -131
- package/dist/src/commands/server.js +280 -40
- package/dist/src/commands/setup.js +84 -45
- package/dist/src/commands/task-report-bug.js +5 -7
- package/dist/src/commands/task-run-driver.js +710 -70
- package/dist/src/commands/task.js +1861 -260
- package/dist/src/commands/test.js +3 -5
- package/dist/src/commands/workspace.js +4 -6
- package/dist/src/commands.js +4143 -1181
- package/dist/src/index.js +4156 -1203
- package/dist/src/launcher.js +5 -3
- package/dist/src/report-bug.js +3 -3
- package/dist/src/runner.js +5 -19
- package/package.json +7 -5
|
@@ -5,8 +5,6 @@ var __require = import.meta.require;
|
|
|
5
5
|
import { EventBus } from "@rig/runtime/control-plane/runtime/events";
|
|
6
6
|
import { CliError } from "@rig/runtime/control-plane/errors";
|
|
7
7
|
import { evaluate, loadPolicy, resolveAction } from "@rig/runtime/control-plane/runtime/guard";
|
|
8
|
-
import { PluginManager } from "@rig/runtime/control-plane/runtime/plugins";
|
|
9
|
-
import { loadRuntimeContextFromEnv } from "@rig/runtime/control-plane/runtime/context";
|
|
10
8
|
import { buildBinary } from "@rig/runtime/control-plane/runtime/isolation";
|
|
11
9
|
import { CliError as CliError2 } from "@rig/runtime/control-plane/errors";
|
|
12
10
|
function requireNoExtraArgs(args, usage) {
|
|
@@ -46,6 +44,11 @@ function readJsonFile(path) {
|
|
|
46
44
|
throw new CliError2(`Invalid Rig connection state at ${path}: ${error instanceof Error ? error.message : String(error)}`, 1);
|
|
47
45
|
}
|
|
48
46
|
}
|
|
47
|
+
function writeJsonFile(path, value) {
|
|
48
|
+
mkdirSync(dirname(path), { recursive: true });
|
|
49
|
+
writeFileSync(path, `${JSON.stringify(value, null, 2)}
|
|
50
|
+
`, "utf8");
|
|
51
|
+
}
|
|
49
52
|
function normalizeConnection(value) {
|
|
50
53
|
if (!value || typeof value !== "object" || Array.isArray(value))
|
|
51
54
|
return null;
|
|
@@ -86,29 +89,38 @@ function readRepoConnection(projectRoot) {
|
|
|
86
89
|
return {
|
|
87
90
|
selected,
|
|
88
91
|
project: typeof record.project === "string" ? record.project : undefined,
|
|
89
|
-
linkedAt: typeof record.linkedAt === "string" ? record.linkedAt : undefined
|
|
92
|
+
linkedAt: typeof record.linkedAt === "string" ? record.linkedAt : undefined,
|
|
93
|
+
serverProjectRoot: typeof record.serverProjectRoot === "string" && record.serverProjectRoot.trim() ? record.serverProjectRoot.trim() : undefined
|
|
90
94
|
};
|
|
91
95
|
}
|
|
96
|
+
function writeRepoConnection(projectRoot, state) {
|
|
97
|
+
writeJsonFile(resolveRepoConnectionPath(projectRoot), state);
|
|
98
|
+
}
|
|
92
99
|
function resolveSelectedConnection(projectRoot, options = {}) {
|
|
93
100
|
const repo = readRepoConnection(projectRoot);
|
|
94
101
|
if (!repo)
|
|
95
102
|
return null;
|
|
96
103
|
if (repo.selected === "local")
|
|
97
|
-
return { alias: "local", connection: { kind: "local", mode: "auto" } };
|
|
104
|
+
return { alias: "local", connection: { kind: "local", mode: "auto" }, serverProjectRoot: repo.serverProjectRoot };
|
|
98
105
|
const global = readGlobalConnections(options);
|
|
99
106
|
const connection = global.connections[repo.selected];
|
|
100
107
|
if (!connection) {
|
|
101
|
-
throw new CliError2(`Selected Rig
|
|
108
|
+
throw new CliError2(`Selected Rig server "${repo.selected}" was not found. Run \`rig server list\` or \`rig server use local\`.`, 1);
|
|
102
109
|
}
|
|
103
|
-
return { alias: repo.selected, connection };
|
|
110
|
+
return { alias: repo.selected, connection, serverProjectRoot: repo.serverProjectRoot };
|
|
111
|
+
}
|
|
112
|
+
function writeRepoServerProjectRoot(projectRoot, serverProjectRoot) {
|
|
113
|
+
const repo = readRepoConnection(projectRoot);
|
|
114
|
+
if (!repo)
|
|
115
|
+
return;
|
|
116
|
+
writeRepoConnection(projectRoot, { ...repo, serverProjectRoot });
|
|
104
117
|
}
|
|
105
118
|
|
|
106
119
|
// packages/cli/src/commands/_server-client.ts
|
|
107
|
-
import { spawnSync } from "child_process";
|
|
108
120
|
import { existsSync as existsSync2, readFileSync as readFileSync2 } from "fs";
|
|
109
121
|
import { resolve as resolve2 } from "path";
|
|
110
122
|
import { ensureLocalRigServerConnection } from "@rig/runtime/local-server";
|
|
111
|
-
var
|
|
123
|
+
var scopedGitHubBearerTokens = new Map;
|
|
112
124
|
function cleanToken(value) {
|
|
113
125
|
const trimmed = value?.trim();
|
|
114
126
|
return trimmed ? trimmed : null;
|
|
@@ -125,41 +137,47 @@ function readPrivateRemoteSessionToken(projectRoot) {
|
|
|
125
137
|
}
|
|
126
138
|
}
|
|
127
139
|
function readGitHubBearerTokenForRemote(projectRoot) {
|
|
128
|
-
|
|
129
|
-
|
|
140
|
+
const scopedKey = resolve2(projectRoot);
|
|
141
|
+
if (scopedGitHubBearerTokens.has(scopedKey))
|
|
142
|
+
return scopedGitHubBearerTokens.get(scopedKey) ?? null;
|
|
130
143
|
const privateSession = readPrivateRemoteSessionToken(projectRoot);
|
|
131
|
-
if (privateSession)
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
return
|
|
144
|
+
if (privateSession)
|
|
145
|
+
return privateSession;
|
|
146
|
+
return cleanToken(process.env.RIG_SERVER_AUTH_TOKEN) ?? cleanToken(process.env.RIG_REMOTE_AUTH_TOKEN);
|
|
147
|
+
}
|
|
148
|
+
function readStoredGitHubAuthToken(projectRoot) {
|
|
149
|
+
const path = resolve2(projectRoot, ".rig", "state", "github-auth.json");
|
|
150
|
+
if (!existsSync2(path))
|
|
151
|
+
return null;
|
|
152
|
+
try {
|
|
153
|
+
const parsed = JSON.parse(readFileSync2(path, "utf8"));
|
|
154
|
+
return cleanToken(typeof parsed.token === "string" ? parsed.token : undefined);
|
|
155
|
+
} catch {
|
|
156
|
+
return null;
|
|
139
157
|
}
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
stdio: ["ignore", "pipe", "ignore"]
|
|
144
|
-
});
|
|
145
|
-
cachedGitHubBearerToken = result.status === 0 ? cleanToken(result.stdout) : null;
|
|
146
|
-
return cachedGitHubBearerToken;
|
|
158
|
+
}
|
|
159
|
+
function readLocalConnectionFallbackToken(projectRoot) {
|
|
160
|
+
return readGitHubBearerTokenForRemote(projectRoot) ?? cleanToken(process.env.RIG_GITHUB_TOKEN) ?? readStoredGitHubAuthToken(projectRoot);
|
|
147
161
|
}
|
|
148
162
|
async function ensureServerForCli(projectRoot) {
|
|
149
163
|
try {
|
|
150
164
|
const selected = resolveSelectedConnection(projectRoot);
|
|
151
165
|
if (selected?.connection.kind === "remote") {
|
|
166
|
+
const authToken = readGitHubBearerTokenForRemote(projectRoot);
|
|
167
|
+
const serverProjectRoot = selected.serverProjectRoot ?? await backfillRemoteServerProjectRoot(projectRoot, selected.connection.baseUrl, authToken);
|
|
152
168
|
return {
|
|
153
169
|
baseUrl: selected.connection.baseUrl,
|
|
154
|
-
authToken
|
|
155
|
-
connectionKind: "remote"
|
|
170
|
+
authToken,
|
|
171
|
+
connectionKind: "remote",
|
|
172
|
+
serverProjectRoot
|
|
156
173
|
};
|
|
157
174
|
}
|
|
158
175
|
const connection = await ensureLocalRigServerConnection(projectRoot);
|
|
159
176
|
return {
|
|
160
177
|
baseUrl: connection.baseUrl,
|
|
161
|
-
authToken: connection.authToken,
|
|
162
|
-
connectionKind: "local"
|
|
178
|
+
authToken: connection.authToken ?? readLocalConnectionFallbackToken(projectRoot),
|
|
179
|
+
connectionKind: "local",
|
|
180
|
+
serverProjectRoot: resolve2(projectRoot)
|
|
163
181
|
};
|
|
164
182
|
} catch (error) {
|
|
165
183
|
if (error instanceof Error) {
|
|
@@ -168,6 +186,29 @@ async function ensureServerForCli(projectRoot) {
|
|
|
168
186
|
throw error;
|
|
169
187
|
}
|
|
170
188
|
}
|
|
189
|
+
async function backfillRemoteServerProjectRoot(projectRoot, baseUrl, authToken) {
|
|
190
|
+
const repo = readRepoConnection(projectRoot);
|
|
191
|
+
const slug = repo?.project?.trim();
|
|
192
|
+
if (!slug)
|
|
193
|
+
return null;
|
|
194
|
+
try {
|
|
195
|
+
const response = await fetch(`${baseUrl}/api/projects/${encodeURIComponent(slug)}`, {
|
|
196
|
+
headers: mergeHeaders(undefined, authToken)
|
|
197
|
+
});
|
|
198
|
+
if (!response.ok)
|
|
199
|
+
return null;
|
|
200
|
+
const payload = await response.json();
|
|
201
|
+
const project = payload.project && typeof payload.project === "object" && !Array.isArray(payload.project) ? payload.project : null;
|
|
202
|
+
const checkouts = Array.isArray(project?.checkouts) ? project.checkouts : [];
|
|
203
|
+
const latestCheckout = [...checkouts].reverse().find((entry) => Boolean(entry && typeof entry === "object" && !Array.isArray(entry) && typeof entry.path === "string"));
|
|
204
|
+
const path = typeof latestCheckout?.path === "string" && latestCheckout.path.trim() ? latestCheckout.path.trim() : null;
|
|
205
|
+
if (path)
|
|
206
|
+
writeRepoServerProjectRoot(projectRoot, path);
|
|
207
|
+
return path;
|
|
208
|
+
} catch {
|
|
209
|
+
return null;
|
|
210
|
+
}
|
|
211
|
+
}
|
|
171
212
|
function mergeHeaders(headers, authToken) {
|
|
172
213
|
const merged = new Headers(headers);
|
|
173
214
|
if (authToken) {
|
|
@@ -192,9 +233,12 @@ function diagnosticMessage(payload) {
|
|
|
192
233
|
}
|
|
193
234
|
async function requestServerJson(context, pathname, init = {}) {
|
|
194
235
|
const server = await ensureServerForCli(context.projectRoot);
|
|
236
|
+
const headers = mergeHeaders(init.headers, server.authToken);
|
|
237
|
+
if (server.serverProjectRoot)
|
|
238
|
+
headers.set("x-rig-project-root", server.serverProjectRoot);
|
|
195
239
|
const response = await fetch(`${server.baseUrl}${pathname}`, {
|
|
196
240
|
...init,
|
|
197
|
-
headers
|
|
241
|
+
headers
|
|
198
242
|
});
|
|
199
243
|
const text = await response.text();
|
|
200
244
|
const payload = text.trim().length > 0 ? (() => {
|
|
@@ -226,7 +270,8 @@ async function loadRigConfigOrNull(projectRoot) {
|
|
|
226
270
|
import { existsSync as existsSync3, readFileSync as readFileSync3, rmSync } from "fs";
|
|
227
271
|
import { homedir as homedir2 } from "os";
|
|
228
272
|
import { resolve as resolve3 } from "path";
|
|
229
|
-
var PI_RIG_PACKAGE_NAME = "@rig/pi-rig";
|
|
273
|
+
var PI_RIG_PACKAGE_NAME = "@h-rig/pi-rig";
|
|
274
|
+
var LEGACY_PI_RIG_PACKAGE_NAME = "@rig/pi-rig";
|
|
230
275
|
async function defaultCommandRunner(command, options = {}) {
|
|
231
276
|
const proc = Bun.spawn(command, { cwd: options.cwd, stdout: "pipe", stderr: "pipe" });
|
|
232
277
|
const [stdout, stderr, exitCode] = await Promise.all([
|
|
@@ -245,7 +290,7 @@ function resolvePiHomeDir(inputHomeDir) {
|
|
|
245
290
|
function piListContainsPiRig(output) {
|
|
246
291
|
return output.split(/\r?\n/).some((line) => {
|
|
247
292
|
const normalized = line.trim();
|
|
248
|
-
return normalized.includes(PI_RIG_PACKAGE_NAME) || /(?:^|[\\/])packages[\\/]pi-rig(?:$|\s)/.test(normalized);
|
|
293
|
+
return normalized.includes(PI_RIG_PACKAGE_NAME) || normalized.includes(LEGACY_PI_RIG_PACKAGE_NAME) || /(?:^|[\\/])packages[\\/]pi-rig(?:$|\s)/.test(normalized);
|
|
249
294
|
});
|
|
250
295
|
}
|
|
251
296
|
async function safeRun(runner, command, options) {
|
|
@@ -421,7 +466,7 @@ async function runRigDoctorChecks(options) {
|
|
|
421
466
|
const taskSourceKind = config?.taskSource?.kind;
|
|
422
467
|
checks.push(taskSourceKind ? check("task-source", "task source configured", "pass", taskSourceKind) : check("task-source", "task source configured", "fail", "missing taskSource", "Configure taskSource in rig.config.ts."));
|
|
423
468
|
const repo = readRepoConnection(projectRoot);
|
|
424
|
-
checks.push(repo ? check("project-link", "repo selected Rig
|
|
469
|
+
checks.push(repo ? check("project-link", "repo selected Rig server", repo.project ? "pass" : "warn", `${repo.selected}${repo.project ? ` -> ${repo.project}` : ""}`, "Run `rig init --yes --repo owner/repo` to link this checkout to a GitHub repo slug.") : check("project-link", "repo selected Rig server", "fail", "missing .rig/state/connection.json", "Run `rig init` or `rig server use <alias|local>`."));
|
|
425
470
|
const selected = (() => {
|
|
426
471
|
try {
|
|
427
472
|
return resolveSelectedConnection(projectRoot);
|
|
@@ -429,7 +474,7 @@ async function runRigDoctorChecks(options) {
|
|
|
429
474
|
return null;
|
|
430
475
|
}
|
|
431
476
|
})();
|
|
432
|
-
checks.push(selected ? check("connection", "selected server connection", "pass", selected.connection.kind === "remote" ? selected.connection.baseUrl : "local auto") : check("connection", "selected server
|
|
477
|
+
checks.push(selected ? check("connection", "selected server connection", "pass", selected.connection.kind === "remote" ? selected.connection.baseUrl : "local auto") : check("connection", "selected server", repo ? "fail" : "warn", repo ? "selected alias is missing" : "will auto-start local server", repo ? "Run `rig server list` and `rig server use <alias|local>`." : undefined));
|
|
433
478
|
let server = null;
|
|
434
479
|
try {
|
|
435
480
|
server = await (options.resolveServer ?? ensureServerForCli)(projectRoot);
|
|
@@ -1,13 +1,11 @@
|
|
|
1
1
|
// @bun
|
|
2
2
|
// packages/cli/src/commands/github.ts
|
|
3
|
-
import { spawnSync
|
|
3
|
+
import { spawnSync } from "child_process";
|
|
4
4
|
|
|
5
5
|
// packages/cli/src/runner.ts
|
|
6
6
|
import { EventBus } from "@rig/runtime/control-plane/runtime/events";
|
|
7
7
|
import { CliError } from "@rig/runtime/control-plane/errors";
|
|
8
8
|
import { evaluate, loadPolicy, resolveAction } from "@rig/runtime/control-plane/runtime/guard";
|
|
9
|
-
import { PluginManager } from "@rig/runtime/control-plane/runtime/plugins";
|
|
10
|
-
import { loadRuntimeContextFromEnv } from "@rig/runtime/control-plane/runtime/context";
|
|
11
9
|
import { buildBinary } from "@rig/runtime/control-plane/runtime/isolation";
|
|
12
10
|
import { CliError as CliError2 } from "@rig/runtime/control-plane/errors";
|
|
13
11
|
function takeOption(args, option) {
|
|
@@ -32,7 +30,6 @@ function takeOption(args, option) {
|
|
|
32
30
|
}
|
|
33
31
|
|
|
34
32
|
// packages/cli/src/commands/_server-client.ts
|
|
35
|
-
import { spawnSync } from "child_process";
|
|
36
33
|
import { existsSync as existsSync2, readFileSync as readFileSync2 } from "fs";
|
|
37
34
|
import { resolve as resolve2 } from "path";
|
|
38
35
|
import { ensureLocalRigServerConnection } from "@rig/runtime/local-server";
|
|
@@ -62,6 +59,11 @@ function readJsonFile(path) {
|
|
|
62
59
|
throw new CliError2(`Invalid Rig connection state at ${path}: ${error instanceof Error ? error.message : String(error)}`, 1);
|
|
63
60
|
}
|
|
64
61
|
}
|
|
62
|
+
function writeJsonFile(path, value) {
|
|
63
|
+
mkdirSync(dirname(path), { recursive: true });
|
|
64
|
+
writeFileSync(path, `${JSON.stringify(value, null, 2)}
|
|
65
|
+
`, "utf8");
|
|
66
|
+
}
|
|
65
67
|
function normalizeConnection(value) {
|
|
66
68
|
if (!value || typeof value !== "object" || Array.isArray(value))
|
|
67
69
|
return null;
|
|
@@ -102,25 +104,35 @@ function readRepoConnection(projectRoot) {
|
|
|
102
104
|
return {
|
|
103
105
|
selected,
|
|
104
106
|
project: typeof record.project === "string" ? record.project : undefined,
|
|
105
|
-
linkedAt: typeof record.linkedAt === "string" ? record.linkedAt : undefined
|
|
107
|
+
linkedAt: typeof record.linkedAt === "string" ? record.linkedAt : undefined,
|
|
108
|
+
serverProjectRoot: typeof record.serverProjectRoot === "string" && record.serverProjectRoot.trim() ? record.serverProjectRoot.trim() : undefined
|
|
106
109
|
};
|
|
107
110
|
}
|
|
111
|
+
function writeRepoConnection(projectRoot, state) {
|
|
112
|
+
writeJsonFile(resolveRepoConnectionPath(projectRoot), state);
|
|
113
|
+
}
|
|
108
114
|
function resolveSelectedConnection(projectRoot, options = {}) {
|
|
109
115
|
const repo = readRepoConnection(projectRoot);
|
|
110
116
|
if (!repo)
|
|
111
117
|
return null;
|
|
112
118
|
if (repo.selected === "local")
|
|
113
|
-
return { alias: "local", connection: { kind: "local", mode: "auto" } };
|
|
119
|
+
return { alias: "local", connection: { kind: "local", mode: "auto" }, serverProjectRoot: repo.serverProjectRoot };
|
|
114
120
|
const global = readGlobalConnections(options);
|
|
115
121
|
const connection = global.connections[repo.selected];
|
|
116
122
|
if (!connection) {
|
|
117
|
-
throw new CliError2(`Selected Rig
|
|
123
|
+
throw new CliError2(`Selected Rig server "${repo.selected}" was not found. Run \`rig server list\` or \`rig server use local\`.`, 1);
|
|
118
124
|
}
|
|
119
|
-
return { alias: repo.selected, connection };
|
|
125
|
+
return { alias: repo.selected, connection, serverProjectRoot: repo.serverProjectRoot };
|
|
126
|
+
}
|
|
127
|
+
function writeRepoServerProjectRoot(projectRoot, serverProjectRoot) {
|
|
128
|
+
const repo = readRepoConnection(projectRoot);
|
|
129
|
+
if (!repo)
|
|
130
|
+
return;
|
|
131
|
+
writeRepoConnection(projectRoot, { ...repo, serverProjectRoot });
|
|
120
132
|
}
|
|
121
133
|
|
|
122
134
|
// packages/cli/src/commands/_server-client.ts
|
|
123
|
-
var
|
|
135
|
+
var scopedGitHubBearerTokens = new Map;
|
|
124
136
|
function cleanToken(value) {
|
|
125
137
|
const trimmed = value?.trim();
|
|
126
138
|
return trimmed ? trimmed : null;
|
|
@@ -137,41 +149,47 @@ function readPrivateRemoteSessionToken(projectRoot) {
|
|
|
137
149
|
}
|
|
138
150
|
}
|
|
139
151
|
function readGitHubBearerTokenForRemote(projectRoot) {
|
|
140
|
-
|
|
141
|
-
|
|
152
|
+
const scopedKey = resolve2(projectRoot);
|
|
153
|
+
if (scopedGitHubBearerTokens.has(scopedKey))
|
|
154
|
+
return scopedGitHubBearerTokens.get(scopedKey) ?? null;
|
|
142
155
|
const privateSession = readPrivateRemoteSessionToken(projectRoot);
|
|
143
|
-
if (privateSession)
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
return
|
|
156
|
+
if (privateSession)
|
|
157
|
+
return privateSession;
|
|
158
|
+
return cleanToken(process.env.RIG_SERVER_AUTH_TOKEN) ?? cleanToken(process.env.RIG_REMOTE_AUTH_TOKEN);
|
|
159
|
+
}
|
|
160
|
+
function readStoredGitHubAuthToken(projectRoot) {
|
|
161
|
+
const path = resolve2(projectRoot, ".rig", "state", "github-auth.json");
|
|
162
|
+
if (!existsSync2(path))
|
|
163
|
+
return null;
|
|
164
|
+
try {
|
|
165
|
+
const parsed = JSON.parse(readFileSync2(path, "utf8"));
|
|
166
|
+
return cleanToken(typeof parsed.token === "string" ? parsed.token : undefined);
|
|
167
|
+
} catch {
|
|
168
|
+
return null;
|
|
151
169
|
}
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
stdio: ["ignore", "pipe", "ignore"]
|
|
156
|
-
});
|
|
157
|
-
cachedGitHubBearerToken = result.status === 0 ? cleanToken(result.stdout) : null;
|
|
158
|
-
return cachedGitHubBearerToken;
|
|
170
|
+
}
|
|
171
|
+
function readLocalConnectionFallbackToken(projectRoot) {
|
|
172
|
+
return readGitHubBearerTokenForRemote(projectRoot) ?? cleanToken(process.env.RIG_GITHUB_TOKEN) ?? readStoredGitHubAuthToken(projectRoot);
|
|
159
173
|
}
|
|
160
174
|
async function ensureServerForCli(projectRoot) {
|
|
161
175
|
try {
|
|
162
176
|
const selected = resolveSelectedConnection(projectRoot);
|
|
163
177
|
if (selected?.connection.kind === "remote") {
|
|
178
|
+
const authToken = readGitHubBearerTokenForRemote(projectRoot);
|
|
179
|
+
const serverProjectRoot = selected.serverProjectRoot ?? await backfillRemoteServerProjectRoot(projectRoot, selected.connection.baseUrl, authToken);
|
|
164
180
|
return {
|
|
165
181
|
baseUrl: selected.connection.baseUrl,
|
|
166
|
-
authToken
|
|
167
|
-
connectionKind: "remote"
|
|
182
|
+
authToken,
|
|
183
|
+
connectionKind: "remote",
|
|
184
|
+
serverProjectRoot
|
|
168
185
|
};
|
|
169
186
|
}
|
|
170
187
|
const connection = await ensureLocalRigServerConnection(projectRoot);
|
|
171
188
|
return {
|
|
172
189
|
baseUrl: connection.baseUrl,
|
|
173
|
-
authToken: connection.authToken,
|
|
174
|
-
connectionKind: "local"
|
|
190
|
+
authToken: connection.authToken ?? readLocalConnectionFallbackToken(projectRoot),
|
|
191
|
+
connectionKind: "local",
|
|
192
|
+
serverProjectRoot: resolve2(projectRoot)
|
|
175
193
|
};
|
|
176
194
|
} catch (error) {
|
|
177
195
|
if (error instanceof Error) {
|
|
@@ -180,6 +198,29 @@ async function ensureServerForCli(projectRoot) {
|
|
|
180
198
|
throw error;
|
|
181
199
|
}
|
|
182
200
|
}
|
|
201
|
+
async function backfillRemoteServerProjectRoot(projectRoot, baseUrl, authToken) {
|
|
202
|
+
const repo = readRepoConnection(projectRoot);
|
|
203
|
+
const slug = repo?.project?.trim();
|
|
204
|
+
if (!slug)
|
|
205
|
+
return null;
|
|
206
|
+
try {
|
|
207
|
+
const response = await fetch(`${baseUrl}/api/projects/${encodeURIComponent(slug)}`, {
|
|
208
|
+
headers: mergeHeaders(undefined, authToken)
|
|
209
|
+
});
|
|
210
|
+
if (!response.ok)
|
|
211
|
+
return null;
|
|
212
|
+
const payload = await response.json();
|
|
213
|
+
const project = payload.project && typeof payload.project === "object" && !Array.isArray(payload.project) ? payload.project : null;
|
|
214
|
+
const checkouts = Array.isArray(project?.checkouts) ? project.checkouts : [];
|
|
215
|
+
const latestCheckout = [...checkouts].reverse().find((entry) => Boolean(entry && typeof entry === "object" && !Array.isArray(entry) && typeof entry.path === "string"));
|
|
216
|
+
const path = typeof latestCheckout?.path === "string" && latestCheckout.path.trim() ? latestCheckout.path.trim() : null;
|
|
217
|
+
if (path)
|
|
218
|
+
writeRepoServerProjectRoot(projectRoot, path);
|
|
219
|
+
return path;
|
|
220
|
+
} catch {
|
|
221
|
+
return null;
|
|
222
|
+
}
|
|
223
|
+
}
|
|
183
224
|
function mergeHeaders(headers, authToken) {
|
|
184
225
|
const merged = new Headers(headers);
|
|
185
226
|
if (authToken) {
|
|
@@ -204,9 +245,12 @@ function diagnosticMessage(payload) {
|
|
|
204
245
|
}
|
|
205
246
|
async function requestServerJson(context, pathname, init = {}) {
|
|
206
247
|
const server = await ensureServerForCli(context.projectRoot);
|
|
248
|
+
const headers = mergeHeaders(init.headers, server.authToken);
|
|
249
|
+
if (server.serverProjectRoot)
|
|
250
|
+
headers.set("x-rig-project-root", server.serverProjectRoot);
|
|
207
251
|
const response = await fetch(`${server.baseUrl}${pathname}`, {
|
|
208
252
|
...init,
|
|
209
|
-
headers
|
|
253
|
+
headers
|
|
210
254
|
});
|
|
211
255
|
const text = await response.text();
|
|
212
256
|
const payload = text.trim().length > 0 ? (() => {
|
|
@@ -244,7 +288,7 @@ function printPayload(context, payload, fallback) {
|
|
|
244
288
|
console.log(fallback);
|
|
245
289
|
}
|
|
246
290
|
function readGhToken() {
|
|
247
|
-
const result =
|
|
291
|
+
const result = spawnSync("gh", ["auth", "token"], { encoding: "utf8" });
|
|
248
292
|
if (result.status !== 0) {
|
|
249
293
|
const detail = result.stderr?.trim() || result.stdout?.trim() || "gh auth token failed";
|
|
250
294
|
throw new CliError2(`Could not import GitHub token from gh: ${detail}`, 1);
|