@h-rig/cli 0.0.6-alpha.5 → 0.0.6-alpha.50
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/bin/rig.js +4072 -1204
- 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 +65 -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 +1109 -63
- package/dist/src/commands/_parsers.js +0 -2
- package/dist/src/commands/_pi-frontend.js +1066 -0
- package/dist/src/commands/_pi-install.js +4 -3
- package/dist/src/commands/_pi-remote-session.js +757 -0
- package/dist/src/commands/_pi-worker-bridge-extension.js +820 -0
- package/dist/src/commands/_policy.js +0 -2
- package/dist/src/commands/_preflight.js +84 -116
- package/dist/src/commands/_run-driver-helpers.js +2 -2
- package/dist/src/commands/_server-client.js +211 -48
- package/dist/src/commands/_snapshot-upload.js +60 -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 +65 -34
- package/dist/src/commands/github.js +62 -32
- package/dist/src/commands/inbox.js +396 -31
- package/dist/src/commands/init.js +342 -88
- package/dist/src/commands/inspect.js +282 -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 +1407 -130
- package/dist/src/commands/server.js +266 -40
- package/dist/src/commands/setup.js +69 -44
- package/dist/src/commands/task-report-bug.js +5 -7
- package/dist/src/commands/task-run-driver.js +662 -70
- package/dist/src/commands/task.js +1846 -259
- package/dist/src/commands/test.js +3 -5
- package/dist/src/commands/workspace.js +4 -6
- package/dist/src/commands.js +4054 -1180
- package/dist/src/index.js +4065 -1200
- 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 -4
|
@@ -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,33 @@ 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
|
-
const envToken = cleanToken(process.env.RIG_GITHUB_TOKEN) ?? cleanToken(process.env.GITHUB_TOKEN) ?? cleanToken(process.env.GH_TOKEN);
|
|
136
|
-
if (envToken) {
|
|
137
|
-
cachedGitHubBearerToken = envToken;
|
|
138
|
-
return cachedGitHubBearerToken;
|
|
139
|
-
}
|
|
140
|
-
const result = spawnSync("gh", ["auth", "token"], {
|
|
141
|
-
encoding: "utf8",
|
|
142
|
-
timeout: 5000,
|
|
143
|
-
stdio: ["ignore", "pipe", "ignore"]
|
|
144
|
-
});
|
|
145
|
-
cachedGitHubBearerToken = result.status === 0 ? cleanToken(result.stdout) : null;
|
|
146
|
-
return cachedGitHubBearerToken;
|
|
144
|
+
if (privateSession)
|
|
145
|
+
return privateSession;
|
|
146
|
+
return cleanToken(process.env.RIG_SERVER_AUTH_TOKEN) ?? cleanToken(process.env.RIG_REMOTE_AUTH_TOKEN);
|
|
147
147
|
}
|
|
148
148
|
async function ensureServerForCli(projectRoot) {
|
|
149
149
|
try {
|
|
150
150
|
const selected = resolveSelectedConnection(projectRoot);
|
|
151
151
|
if (selected?.connection.kind === "remote") {
|
|
152
|
+
const authToken = readGitHubBearerTokenForRemote(projectRoot);
|
|
153
|
+
const serverProjectRoot = selected.serverProjectRoot ?? await backfillRemoteServerProjectRoot(projectRoot, selected.connection.baseUrl, authToken);
|
|
152
154
|
return {
|
|
153
155
|
baseUrl: selected.connection.baseUrl,
|
|
154
|
-
authToken
|
|
155
|
-
connectionKind: "remote"
|
|
156
|
+
authToken,
|
|
157
|
+
connectionKind: "remote",
|
|
158
|
+
serverProjectRoot
|
|
156
159
|
};
|
|
157
160
|
}
|
|
158
161
|
const connection = await ensureLocalRigServerConnection(projectRoot);
|
|
159
162
|
return {
|
|
160
163
|
baseUrl: connection.baseUrl,
|
|
161
164
|
authToken: connection.authToken,
|
|
162
|
-
connectionKind: "local"
|
|
165
|
+
connectionKind: "local",
|
|
166
|
+
serverProjectRoot: resolve2(projectRoot)
|
|
163
167
|
};
|
|
164
168
|
} catch (error) {
|
|
165
169
|
if (error instanceof Error) {
|
|
@@ -168,6 +172,29 @@ async function ensureServerForCli(projectRoot) {
|
|
|
168
172
|
throw error;
|
|
169
173
|
}
|
|
170
174
|
}
|
|
175
|
+
async function backfillRemoteServerProjectRoot(projectRoot, baseUrl, authToken) {
|
|
176
|
+
const repo = readRepoConnection(projectRoot);
|
|
177
|
+
const slug = repo?.project?.trim();
|
|
178
|
+
if (!slug)
|
|
179
|
+
return null;
|
|
180
|
+
try {
|
|
181
|
+
const response = await fetch(`${baseUrl}/api/projects/${encodeURIComponent(slug)}`, {
|
|
182
|
+
headers: mergeHeaders(undefined, authToken)
|
|
183
|
+
});
|
|
184
|
+
if (!response.ok)
|
|
185
|
+
return null;
|
|
186
|
+
const payload = await response.json();
|
|
187
|
+
const project = payload.project && typeof payload.project === "object" && !Array.isArray(payload.project) ? payload.project : null;
|
|
188
|
+
const checkouts = Array.isArray(project?.checkouts) ? project.checkouts : [];
|
|
189
|
+
const latestCheckout = [...checkouts].reverse().find((entry) => Boolean(entry && typeof entry === "object" && !Array.isArray(entry) && typeof entry.path === "string"));
|
|
190
|
+
const path = typeof latestCheckout?.path === "string" && latestCheckout.path.trim() ? latestCheckout.path.trim() : null;
|
|
191
|
+
if (path)
|
|
192
|
+
writeRepoServerProjectRoot(projectRoot, path);
|
|
193
|
+
return path;
|
|
194
|
+
} catch {
|
|
195
|
+
return null;
|
|
196
|
+
}
|
|
197
|
+
}
|
|
171
198
|
function mergeHeaders(headers, authToken) {
|
|
172
199
|
const merged = new Headers(headers);
|
|
173
200
|
if (authToken) {
|
|
@@ -192,9 +219,12 @@ function diagnosticMessage(payload) {
|
|
|
192
219
|
}
|
|
193
220
|
async function requestServerJson(context, pathname, init = {}) {
|
|
194
221
|
const server = await ensureServerForCli(context.projectRoot);
|
|
222
|
+
const headers = mergeHeaders(init.headers, server.authToken);
|
|
223
|
+
if (server.serverProjectRoot)
|
|
224
|
+
headers.set("x-rig-project-root", server.serverProjectRoot);
|
|
195
225
|
const response = await fetch(`${server.baseUrl}${pathname}`, {
|
|
196
226
|
...init,
|
|
197
|
-
headers
|
|
227
|
+
headers
|
|
198
228
|
});
|
|
199
229
|
const text = await response.text();
|
|
200
230
|
const payload = text.trim().length > 0 ? (() => {
|
|
@@ -226,7 +256,8 @@ async function loadRigConfigOrNull(projectRoot) {
|
|
|
226
256
|
import { existsSync as existsSync3, readFileSync as readFileSync3, rmSync } from "fs";
|
|
227
257
|
import { homedir as homedir2 } from "os";
|
|
228
258
|
import { resolve as resolve3 } from "path";
|
|
229
|
-
var PI_RIG_PACKAGE_NAME = "@rig/pi-rig";
|
|
259
|
+
var PI_RIG_PACKAGE_NAME = "@h-rig/pi-rig";
|
|
260
|
+
var LEGACY_PI_RIG_PACKAGE_NAME = "@rig/pi-rig";
|
|
230
261
|
async function defaultCommandRunner(command, options = {}) {
|
|
231
262
|
const proc = Bun.spawn(command, { cwd: options.cwd, stdout: "pipe", stderr: "pipe" });
|
|
232
263
|
const [stdout, stderr, exitCode] = await Promise.all([
|
|
@@ -245,7 +276,7 @@ function resolvePiHomeDir(inputHomeDir) {
|
|
|
245
276
|
function piListContainsPiRig(output) {
|
|
246
277
|
return output.split(/\r?\n/).some((line) => {
|
|
247
278
|
const normalized = line.trim();
|
|
248
|
-
return normalized.includes(PI_RIG_PACKAGE_NAME) || /(?:^|[\\/])packages[\\/]pi-rig(?:$|\s)/.test(normalized);
|
|
279
|
+
return normalized.includes(PI_RIG_PACKAGE_NAME) || normalized.includes(LEGACY_PI_RIG_PACKAGE_NAME) || /(?:^|[\\/])packages[\\/]pi-rig(?:$|\s)/.test(normalized);
|
|
249
280
|
});
|
|
250
281
|
}
|
|
251
282
|
async function safeRun(runner, command, options) {
|
|
@@ -421,7 +452,7 @@ async function runRigDoctorChecks(options) {
|
|
|
421
452
|
const taskSourceKind = config?.taskSource?.kind;
|
|
422
453
|
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
454
|
const repo = readRepoConnection(projectRoot);
|
|
424
|
-
checks.push(repo ? check("project-link", "repo selected Rig
|
|
455
|
+
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
456
|
const selected = (() => {
|
|
426
457
|
try {
|
|
427
458
|
return resolveSelectedConnection(projectRoot);
|
|
@@ -429,7 +460,7 @@ async function runRigDoctorChecks(options) {
|
|
|
429
460
|
return null;
|
|
430
461
|
}
|
|
431
462
|
})();
|
|
432
|
-
checks.push(selected ? check("connection", "selected server connection", "pass", selected.connection.kind === "remote" ? selected.connection.baseUrl : "local auto") : check("connection", "selected server
|
|
463
|
+
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
464
|
let server = null;
|
|
434
465
|
try {
|
|
435
466
|
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,33 @@ 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
|
-
const envToken = cleanToken(process.env.RIG_GITHUB_TOKEN) ?? cleanToken(process.env.GITHUB_TOKEN) ?? cleanToken(process.env.GH_TOKEN);
|
|
148
|
-
if (envToken) {
|
|
149
|
-
cachedGitHubBearerToken = envToken;
|
|
150
|
-
return cachedGitHubBearerToken;
|
|
151
|
-
}
|
|
152
|
-
const result = spawnSync("gh", ["auth", "token"], {
|
|
153
|
-
encoding: "utf8",
|
|
154
|
-
timeout: 5000,
|
|
155
|
-
stdio: ["ignore", "pipe", "ignore"]
|
|
156
|
-
});
|
|
157
|
-
cachedGitHubBearerToken = result.status === 0 ? cleanToken(result.stdout) : null;
|
|
158
|
-
return cachedGitHubBearerToken;
|
|
156
|
+
if (privateSession)
|
|
157
|
+
return privateSession;
|
|
158
|
+
return cleanToken(process.env.RIG_SERVER_AUTH_TOKEN) ?? cleanToken(process.env.RIG_REMOTE_AUTH_TOKEN);
|
|
159
159
|
}
|
|
160
160
|
async function ensureServerForCli(projectRoot) {
|
|
161
161
|
try {
|
|
162
162
|
const selected = resolveSelectedConnection(projectRoot);
|
|
163
163
|
if (selected?.connection.kind === "remote") {
|
|
164
|
+
const authToken = readGitHubBearerTokenForRemote(projectRoot);
|
|
165
|
+
const serverProjectRoot = selected.serverProjectRoot ?? await backfillRemoteServerProjectRoot(projectRoot, selected.connection.baseUrl, authToken);
|
|
164
166
|
return {
|
|
165
167
|
baseUrl: selected.connection.baseUrl,
|
|
166
|
-
authToken
|
|
167
|
-
connectionKind: "remote"
|
|
168
|
+
authToken,
|
|
169
|
+
connectionKind: "remote",
|
|
170
|
+
serverProjectRoot
|
|
168
171
|
};
|
|
169
172
|
}
|
|
170
173
|
const connection = await ensureLocalRigServerConnection(projectRoot);
|
|
171
174
|
return {
|
|
172
175
|
baseUrl: connection.baseUrl,
|
|
173
176
|
authToken: connection.authToken,
|
|
174
|
-
connectionKind: "local"
|
|
177
|
+
connectionKind: "local",
|
|
178
|
+
serverProjectRoot: resolve2(projectRoot)
|
|
175
179
|
};
|
|
176
180
|
} catch (error) {
|
|
177
181
|
if (error instanceof Error) {
|
|
@@ -180,6 +184,29 @@ async function ensureServerForCli(projectRoot) {
|
|
|
180
184
|
throw error;
|
|
181
185
|
}
|
|
182
186
|
}
|
|
187
|
+
async function backfillRemoteServerProjectRoot(projectRoot, baseUrl, authToken) {
|
|
188
|
+
const repo = readRepoConnection(projectRoot);
|
|
189
|
+
const slug = repo?.project?.trim();
|
|
190
|
+
if (!slug)
|
|
191
|
+
return null;
|
|
192
|
+
try {
|
|
193
|
+
const response = await fetch(`${baseUrl}/api/projects/${encodeURIComponent(slug)}`, {
|
|
194
|
+
headers: mergeHeaders(undefined, authToken)
|
|
195
|
+
});
|
|
196
|
+
if (!response.ok)
|
|
197
|
+
return null;
|
|
198
|
+
const payload = await response.json();
|
|
199
|
+
const project = payload.project && typeof payload.project === "object" && !Array.isArray(payload.project) ? payload.project : null;
|
|
200
|
+
const checkouts = Array.isArray(project?.checkouts) ? project.checkouts : [];
|
|
201
|
+
const latestCheckout = [...checkouts].reverse().find((entry) => Boolean(entry && typeof entry === "object" && !Array.isArray(entry) && typeof entry.path === "string"));
|
|
202
|
+
const path = typeof latestCheckout?.path === "string" && latestCheckout.path.trim() ? latestCheckout.path.trim() : null;
|
|
203
|
+
if (path)
|
|
204
|
+
writeRepoServerProjectRoot(projectRoot, path);
|
|
205
|
+
return path;
|
|
206
|
+
} catch {
|
|
207
|
+
return null;
|
|
208
|
+
}
|
|
209
|
+
}
|
|
183
210
|
function mergeHeaders(headers, authToken) {
|
|
184
211
|
const merged = new Headers(headers);
|
|
185
212
|
if (authToken) {
|
|
@@ -204,9 +231,12 @@ function diagnosticMessage(payload) {
|
|
|
204
231
|
}
|
|
205
232
|
async function requestServerJson(context, pathname, init = {}) {
|
|
206
233
|
const server = await ensureServerForCli(context.projectRoot);
|
|
234
|
+
const headers = mergeHeaders(init.headers, server.authToken);
|
|
235
|
+
if (server.serverProjectRoot)
|
|
236
|
+
headers.set("x-rig-project-root", server.serverProjectRoot);
|
|
207
237
|
const response = await fetch(`${server.baseUrl}${pathname}`, {
|
|
208
238
|
...init,
|
|
209
|
-
headers
|
|
239
|
+
headers
|
|
210
240
|
});
|
|
211
241
|
const text = await response.text();
|
|
212
242
|
const payload = text.trim().length > 0 ? (() => {
|
|
@@ -244,7 +274,7 @@ function printPayload(context, payload, fallback) {
|
|
|
244
274
|
console.log(fallback);
|
|
245
275
|
}
|
|
246
276
|
function readGhToken() {
|
|
247
|
-
const result =
|
|
277
|
+
const result = spawnSync("gh", ["auth", "token"], { encoding: "utf8" });
|
|
248
278
|
if (result.status !== 0) {
|
|
249
279
|
const detail = result.stderr?.trim() || result.stdout?.trim() || "gh auth token failed";
|
|
250
280
|
throw new CliError2(`Could not import GitHub token from gh: ${detail}`, 1);
|