@h-rig/cli 0.0.6-alpha.6 → 0.0.6-alpha.61
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 +5033 -1895
- package/dist/src/commands/_authority-runs.js +2 -3
- 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 +446 -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 +46 -19
- package/dist/src/commands/_run-replay.js +142 -0
- 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 +10 -12
- 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 +1600 -131
- package/dist/src/commands/server.js +281 -42
- 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 +736 -93
- package/dist/src/commands/task.js +1863 -262
- package/dist/src/commands/test.js +3 -5
- package/dist/src/commands/workspace.js +4 -6
- package/dist/src/commands.js +5675 -2531
- package/dist/src/index.js +5654 -2519
- 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
|
@@ -3,8 +3,6 @@
|
|
|
3
3
|
import { EventBus } from "@rig/runtime/control-plane/runtime/events";
|
|
4
4
|
import { CliError } from "@rig/runtime/control-plane/errors";
|
|
5
5
|
import { evaluate, loadPolicy, resolveAction } from "@rig/runtime/control-plane/runtime/guard";
|
|
6
|
-
import { PluginManager } from "@rig/runtime/control-plane/runtime/plugins";
|
|
7
|
-
import { loadRuntimeContextFromEnv } from "@rig/runtime/control-plane/runtime/context";
|
|
8
6
|
import { buildBinary } from "@rig/runtime/control-plane/runtime/isolation";
|
|
9
7
|
import { CliError as CliError2 } from "@rig/runtime/control-plane/errors";
|
|
10
8
|
function takeOption(args, option) {
|
|
@@ -34,12 +32,14 @@ Usage: ${usage}`);
|
|
|
34
32
|
}
|
|
35
33
|
}
|
|
36
34
|
|
|
35
|
+
// packages/cli/src/commands/server.ts
|
|
36
|
+
import { resolveRigServerCommand } from "@rig/runtime/local-server";
|
|
37
|
+
|
|
37
38
|
// packages/cli/src/commands/_authority-runs.ts
|
|
38
39
|
import {
|
|
39
40
|
readAuthorityRun,
|
|
40
41
|
readJsonlFile,
|
|
41
|
-
|
|
42
|
-
writeJsonFile
|
|
42
|
+
writeAuthorityRunRecord
|
|
43
43
|
} from "@rig/runtime/control-plane/authority-files";
|
|
44
44
|
|
|
45
45
|
// packages/cli/src/commands/_paths.ts
|
|
@@ -60,11 +60,8 @@ function normalizeRuntimeAdapter(value) {
|
|
|
60
60
|
return "claude-code";
|
|
61
61
|
}
|
|
62
62
|
|
|
63
|
-
// packages/cli/src/commands/
|
|
64
|
-
import {
|
|
65
|
-
import { existsSync as existsSync2, readFileSync as readFileSync2 } from "fs";
|
|
66
|
-
import { resolve as resolve2 } from "path";
|
|
67
|
-
import { ensureLocalRigServerConnection } from "@rig/runtime/local-server";
|
|
63
|
+
// packages/cli/src/commands/connect.ts
|
|
64
|
+
import { cancel, isCancel, select } from "@clack/prompts";
|
|
68
65
|
|
|
69
66
|
// packages/cli/src/commands/_connection-state.ts
|
|
70
67
|
import { existsSync, mkdirSync, readFileSync, writeFileSync } from "fs";
|
|
@@ -91,6 +88,11 @@ function readJsonFile(path) {
|
|
|
91
88
|
throw new CliError2(`Invalid Rig connection state at ${path}: ${error instanceof Error ? error.message : String(error)}`, 1);
|
|
92
89
|
}
|
|
93
90
|
}
|
|
91
|
+
function writeJsonFile(path, value) {
|
|
92
|
+
mkdirSync(dirname(path), { recursive: true });
|
|
93
|
+
writeFileSync(path, `${JSON.stringify(value, null, 2)}
|
|
94
|
+
`, "utf8");
|
|
95
|
+
}
|
|
94
96
|
function normalizeConnection(value) {
|
|
95
97
|
if (!value || typeof value !== "object" || Array.isArray(value))
|
|
96
98
|
return null;
|
|
@@ -120,6 +122,18 @@ function readGlobalConnections(options = {}) {
|
|
|
120
122
|
}
|
|
121
123
|
return { connections };
|
|
122
124
|
}
|
|
125
|
+
function writeGlobalConnections(state, options = {}) {
|
|
126
|
+
writeJsonFile(resolveGlobalConnectionsPath(options.env ?? process.env), state);
|
|
127
|
+
}
|
|
128
|
+
function upsertGlobalConnection(alias, connection, options = {}) {
|
|
129
|
+
const cleanAlias = alias.trim();
|
|
130
|
+
if (!cleanAlias)
|
|
131
|
+
throw new CliError2("Connection alias is required.", 1);
|
|
132
|
+
const state = readGlobalConnections(options);
|
|
133
|
+
state.connections[cleanAlias] = connection;
|
|
134
|
+
writeGlobalConnections(state, options);
|
|
135
|
+
return state;
|
|
136
|
+
}
|
|
123
137
|
function readRepoConnection(projectRoot) {
|
|
124
138
|
const payload = readJsonFile(resolveRepoConnectionPath(projectRoot));
|
|
125
139
|
if (!payload || typeof payload !== "object" || Array.isArray(payload))
|
|
@@ -131,25 +145,213 @@ function readRepoConnection(projectRoot) {
|
|
|
131
145
|
return {
|
|
132
146
|
selected,
|
|
133
147
|
project: typeof record.project === "string" ? record.project : undefined,
|
|
134
|
-
linkedAt: typeof record.linkedAt === "string" ? record.linkedAt : undefined
|
|
148
|
+
linkedAt: typeof record.linkedAt === "string" ? record.linkedAt : undefined,
|
|
149
|
+
serverProjectRoot: typeof record.serverProjectRoot === "string" && record.serverProjectRoot.trim() ? record.serverProjectRoot.trim() : undefined
|
|
135
150
|
};
|
|
136
151
|
}
|
|
152
|
+
function writeRepoConnection(projectRoot, state) {
|
|
153
|
+
writeJsonFile(resolveRepoConnectionPath(projectRoot), state);
|
|
154
|
+
}
|
|
137
155
|
function resolveSelectedConnection(projectRoot, options = {}) {
|
|
138
156
|
const repo = readRepoConnection(projectRoot);
|
|
139
157
|
if (!repo)
|
|
140
158
|
return null;
|
|
141
159
|
if (repo.selected === "local")
|
|
142
|
-
return { alias: "local", connection: { kind: "local", mode: "auto" } };
|
|
160
|
+
return { alias: "local", connection: { kind: "local", mode: "auto" }, serverProjectRoot: repo.serverProjectRoot };
|
|
143
161
|
const global = readGlobalConnections(options);
|
|
144
162
|
const connection = global.connections[repo.selected];
|
|
145
163
|
if (!connection) {
|
|
146
|
-
throw new CliError2(`Selected Rig
|
|
164
|
+
throw new CliError2(`Selected Rig server "${repo.selected}" was not found. Run \`rig server list\` or \`rig server use local\`.`, 1);
|
|
165
|
+
}
|
|
166
|
+
return { alias: repo.selected, connection, serverProjectRoot: repo.serverProjectRoot };
|
|
167
|
+
}
|
|
168
|
+
function writeRepoServerProjectRoot(projectRoot, serverProjectRoot) {
|
|
169
|
+
const repo = readRepoConnection(projectRoot);
|
|
170
|
+
if (!repo)
|
|
171
|
+
return;
|
|
172
|
+
writeRepoConnection(projectRoot, { ...repo, serverProjectRoot });
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
// packages/cli/src/commands/_cli-format.ts
|
|
176
|
+
import { log, note } from "@clack/prompts";
|
|
177
|
+
import pc from "picocolors";
|
|
178
|
+
function truncate(value, width) {
|
|
179
|
+
if (value.length <= width)
|
|
180
|
+
return value;
|
|
181
|
+
if (width <= 1)
|
|
182
|
+
return "\u2026";
|
|
183
|
+
return `${value.slice(0, width - 1)}\u2026`;
|
|
184
|
+
}
|
|
185
|
+
function pad(value, width) {
|
|
186
|
+
return value.length >= width ? value : `${value}${" ".repeat(width - value.length)}`;
|
|
187
|
+
}
|
|
188
|
+
function statusColor(status) {
|
|
189
|
+
const normalized = status.toLowerCase();
|
|
190
|
+
if (["completed", "merged", "closed", "done", "accepted", "pass", "selected", "approved"].includes(normalized))
|
|
191
|
+
return pc.green;
|
|
192
|
+
if (["failed", "needs_attention", "needs-attention", "blocked", "error", "rejected"].includes(normalized))
|
|
193
|
+
return pc.red;
|
|
194
|
+
if (["running", "reviewing", "validating", "in_progress", "in-progress", "remote"].includes(normalized))
|
|
195
|
+
return pc.cyan;
|
|
196
|
+
if (["ready", "open", "queued", "created", "preparing", "local", "pending"].includes(normalized))
|
|
197
|
+
return pc.yellow;
|
|
198
|
+
return pc.dim;
|
|
199
|
+
}
|
|
200
|
+
function formatStatusPill(status) {
|
|
201
|
+
const label = status || "unknown";
|
|
202
|
+
return statusColor(label)(`\u25CF ${label}`);
|
|
203
|
+
}
|
|
204
|
+
function formatSection(title, subtitle) {
|
|
205
|
+
return `${pc.bold(pc.cyan("\u25C6"))} ${pc.bold(title)}${subtitle ? pc.dim(` \u2014 ${subtitle}`) : ""}`;
|
|
206
|
+
}
|
|
207
|
+
function formatSuccessCard(title, rows = []) {
|
|
208
|
+
const body = rows.filter(([, value]) => value !== undefined && value !== null && String(value).length > 0).map(([key, value]) => `${pc.dim("\u2502")} ${pc.dim(key.padEnd(12))} ${value}`);
|
|
209
|
+
return [formatSection(title), ...body].join(`
|
|
210
|
+
`);
|
|
211
|
+
}
|
|
212
|
+
function formatNextSteps(steps) {
|
|
213
|
+
if (steps.length === 0)
|
|
214
|
+
return [];
|
|
215
|
+
return [pc.bold("Next"), ...steps.map((step) => `${pc.dim("\u203A")} ${step}`)];
|
|
216
|
+
}
|
|
217
|
+
function formatConnectionList(connections) {
|
|
218
|
+
const rows = [["local", { kind: "local", mode: "auto" }], ...Object.entries(connections)];
|
|
219
|
+
const aliasWidth = Math.min(24, Math.max(5, ...rows.map(([alias]) => alias.length)));
|
|
220
|
+
const lines = rows.map(([alias, connection]) => [
|
|
221
|
+
pc.bold(pad(truncate(alias, aliasWidth), aliasWidth)),
|
|
222
|
+
formatStatusPill(connection.kind),
|
|
223
|
+
connection.kind === "remote" ? connection.baseUrl ?? "" : connection.mode ?? "local"
|
|
224
|
+
].join(" "));
|
|
225
|
+
return [formatSection("Rig servers", `${rows.length} available`), `${pc.bold(pad("ALIAS", aliasWidth))} ${pc.bold("KIND")} ${pc.bold("TARGET")}`, ...lines, "", ...formatNextSteps(["Select one: `rig server use <alias|local>`"])].join(`
|
|
226
|
+
`);
|
|
227
|
+
}
|
|
228
|
+
function formatConnectionStatus(selected, connections) {
|
|
229
|
+
const connection = selected === "local" ? { kind: "local", mode: "auto" } : connections[selected];
|
|
230
|
+
const target = !connection ? "not configured" : connection.kind === "remote" ? connection.baseUrl : "local";
|
|
231
|
+
return [
|
|
232
|
+
formatSection("Rig server", "selected for this repo"),
|
|
233
|
+
`${pc.dim("\u2502")} ${pc.dim("selected ")} ${pc.bold(selected)}`,
|
|
234
|
+
`${pc.dim("\u2502")} ${pc.dim("kind ")} ${formatStatusPill(connection?.kind ?? "unknown")}`,
|
|
235
|
+
`${pc.dim("\u2502")} ${pc.dim("target ")} ${target ?? "not configured"}`,
|
|
236
|
+
"",
|
|
237
|
+
...formatNextSteps(["Change: `rig server use <alias|local>`", "List saved servers: `rig server list`"])
|
|
238
|
+
].join(`
|
|
239
|
+
`);
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
// packages/cli/src/commands/connect.ts
|
|
243
|
+
function usageName(options) {
|
|
244
|
+
return `rig ${options.group}`;
|
|
245
|
+
}
|
|
246
|
+
function parseConnection(alias, value, options) {
|
|
247
|
+
if (alias === "local" && !value)
|
|
248
|
+
return { kind: "local", mode: "auto" };
|
|
249
|
+
if (!value)
|
|
250
|
+
throw new CliError2(`Missing remote server URL. Usage: ${usageName(options)} add <alias> <url>`, 1);
|
|
251
|
+
let parsed;
|
|
252
|
+
try {
|
|
253
|
+
parsed = new URL(value);
|
|
254
|
+
} catch {
|
|
255
|
+
throw new CliError2(`Invalid Rig server URL: ${value}`, 1);
|
|
256
|
+
}
|
|
257
|
+
if (parsed.protocol !== "https:" && parsed.protocol !== "http:") {
|
|
258
|
+
throw new CliError2("Rig remote server URL must be http(s).", 1);
|
|
259
|
+
}
|
|
260
|
+
return { kind: "remote", baseUrl: parsed.toString().replace(/\/+$/, "") };
|
|
261
|
+
}
|
|
262
|
+
function printJsonOrText(context, payload, text) {
|
|
263
|
+
if (context.outputMode === "json") {
|
|
264
|
+
console.log(JSON.stringify(payload, null, 2));
|
|
265
|
+
} else {
|
|
266
|
+
console.log(text);
|
|
267
|
+
}
|
|
268
|
+
}
|
|
269
|
+
async function promptForConnectionAlias(context) {
|
|
270
|
+
const state = readGlobalConnections();
|
|
271
|
+
const repo = readRepoConnection(context.projectRoot);
|
|
272
|
+
const options = [
|
|
273
|
+
{ value: "local", label: "local", hint: "Use/start a local Rig server" },
|
|
274
|
+
...Object.entries(state.connections).map(([alias, connection]) => ({
|
|
275
|
+
value: alias,
|
|
276
|
+
label: alias,
|
|
277
|
+
hint: connection.kind === "remote" ? connection.baseUrl : "local"
|
|
278
|
+
}))
|
|
279
|
+
].filter((option, index, all) => all.findIndex((candidate) => candidate.value === option.value) === index);
|
|
280
|
+
const answer = await select({
|
|
281
|
+
message: "Select Rig server for this repo",
|
|
282
|
+
initialValue: repo?.selected ?? "local",
|
|
283
|
+
options
|
|
284
|
+
});
|
|
285
|
+
if (isCancel(answer)) {
|
|
286
|
+
cancel("No server selected.");
|
|
287
|
+
throw new CliError2("No server selected.", 3);
|
|
288
|
+
}
|
|
289
|
+
return String(answer);
|
|
290
|
+
}
|
|
291
|
+
async function executeConnectionCommand(context, args, options) {
|
|
292
|
+
const [command, ...rest] = args;
|
|
293
|
+
switch (command ?? "status") {
|
|
294
|
+
case "list": {
|
|
295
|
+
requireNoExtraArgs(rest, `${usageName(options)} list`);
|
|
296
|
+
const state = readGlobalConnections();
|
|
297
|
+
printJsonOrText(context, state, formatConnectionList(state.connections));
|
|
298
|
+
return { ok: true, group: options.group, command: "list", details: state };
|
|
299
|
+
}
|
|
300
|
+
case "add": {
|
|
301
|
+
const [alias, url, ...extra] = rest;
|
|
302
|
+
if (!alias)
|
|
303
|
+
throw new CliError2(`Missing alias. Usage: ${usageName(options)} add <alias> <url>`, 1);
|
|
304
|
+
requireNoExtraArgs(extra, `${usageName(options)} add <alias> <url>`);
|
|
305
|
+
const connection = parseConnection(alias, url, options);
|
|
306
|
+
const state = upsertGlobalConnection(alias, connection);
|
|
307
|
+
printJsonOrText(context, { alias, connection }, formatSuccessCard("Rig server saved", [
|
|
308
|
+
["alias", alias],
|
|
309
|
+
["target", connection.kind === "remote" ? connection.baseUrl : "local"],
|
|
310
|
+
["next", `${usageName(options)} use ${alias}`]
|
|
311
|
+
]));
|
|
312
|
+
return { ok: true, group: options.group, command: "add", details: { alias, connection, count: Object.keys(state.connections).length } };
|
|
313
|
+
}
|
|
314
|
+
case "use": {
|
|
315
|
+
let [alias, ...extra] = rest;
|
|
316
|
+
requireNoExtraArgs(extra, `${usageName(options)} use <alias|local>`);
|
|
317
|
+
if (!alias && options.interactiveUse && context.outputMode === "text" && process.stdin.isTTY) {
|
|
318
|
+
alias = await promptForConnectionAlias(context);
|
|
319
|
+
}
|
|
320
|
+
if (!alias)
|
|
321
|
+
throw new CliError2(`Missing alias. Usage: ${usageName(options)} use <alias|local>`, 1);
|
|
322
|
+
if (alias !== "local") {
|
|
323
|
+
const state = readGlobalConnections();
|
|
324
|
+
if (!state.connections[alias])
|
|
325
|
+
throw new CliError2(`Unknown Rig server: ${alias}`, 1);
|
|
326
|
+
}
|
|
327
|
+
const repoState = { selected: alias, linkedAt: new Date().toISOString() };
|
|
328
|
+
writeRepoConnection(context.projectRoot, repoState);
|
|
329
|
+
printJsonOrText(context, repoState, formatSuccessCard("Rig server selected", [
|
|
330
|
+
["selected", alias],
|
|
331
|
+
["scope", "this repo"],
|
|
332
|
+
["next", "rig task list"]
|
|
333
|
+
]));
|
|
334
|
+
return { ok: true, group: options.group, command: "use", details: repoState };
|
|
335
|
+
}
|
|
336
|
+
case "status": {
|
|
337
|
+
requireNoExtraArgs(rest, `${usageName(options)} status`);
|
|
338
|
+
const repo = readRepoConnection(context.projectRoot);
|
|
339
|
+
const global = readGlobalConnections();
|
|
340
|
+
const details = { selected: repo?.selected ?? "local", repo, connections: global.connections };
|
|
341
|
+
printJsonOrText(context, details, formatConnectionStatus(details.selected, global.connections));
|
|
342
|
+
return { ok: true, group: options.group, command: "status", details };
|
|
343
|
+
}
|
|
344
|
+
default:
|
|
345
|
+
throw new CliError2(`Unknown ${options.group} command: ${String(command)}
|
|
346
|
+
Usage: ${usageName(options)} <list|add|use|status>`, 1);
|
|
147
347
|
}
|
|
148
|
-
return { alias: repo.selected, connection };
|
|
149
348
|
}
|
|
150
349
|
|
|
151
350
|
// packages/cli/src/commands/_server-client.ts
|
|
152
|
-
|
|
351
|
+
import { existsSync as existsSync2, readFileSync as readFileSync2 } from "fs";
|
|
352
|
+
import { resolve as resolve2 } from "path";
|
|
353
|
+
import { ensureLocalRigServerConnection } from "@rig/runtime/local-server";
|
|
354
|
+
var scopedGitHubBearerTokens = new Map;
|
|
153
355
|
function cleanToken(value) {
|
|
154
356
|
const trimmed = value?.trim();
|
|
155
357
|
return trimmed ? trimmed : null;
|
|
@@ -166,41 +368,47 @@ function readPrivateRemoteSessionToken(projectRoot) {
|
|
|
166
368
|
}
|
|
167
369
|
}
|
|
168
370
|
function readGitHubBearerTokenForRemote(projectRoot) {
|
|
169
|
-
|
|
170
|
-
|
|
371
|
+
const scopedKey = resolve2(projectRoot);
|
|
372
|
+
if (scopedGitHubBearerTokens.has(scopedKey))
|
|
373
|
+
return scopedGitHubBearerTokens.get(scopedKey) ?? null;
|
|
171
374
|
const privateSession = readPrivateRemoteSessionToken(projectRoot);
|
|
172
|
-
if (privateSession)
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
return
|
|
375
|
+
if (privateSession)
|
|
376
|
+
return privateSession;
|
|
377
|
+
return cleanToken(process.env.RIG_SERVER_AUTH_TOKEN) ?? cleanToken(process.env.RIG_REMOTE_AUTH_TOKEN);
|
|
378
|
+
}
|
|
379
|
+
function readStoredGitHubAuthToken(projectRoot) {
|
|
380
|
+
const path = resolve2(projectRoot, ".rig", "state", "github-auth.json");
|
|
381
|
+
if (!existsSync2(path))
|
|
382
|
+
return null;
|
|
383
|
+
try {
|
|
384
|
+
const parsed = JSON.parse(readFileSync2(path, "utf8"));
|
|
385
|
+
return cleanToken(typeof parsed.token === "string" ? parsed.token : undefined);
|
|
386
|
+
} catch {
|
|
387
|
+
return null;
|
|
180
388
|
}
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
stdio: ["ignore", "pipe", "ignore"]
|
|
185
|
-
});
|
|
186
|
-
cachedGitHubBearerToken = result.status === 0 ? cleanToken(result.stdout) : null;
|
|
187
|
-
return cachedGitHubBearerToken;
|
|
389
|
+
}
|
|
390
|
+
function readLocalConnectionFallbackToken(projectRoot) {
|
|
391
|
+
return readGitHubBearerTokenForRemote(projectRoot) ?? cleanToken(process.env.RIG_GITHUB_TOKEN) ?? readStoredGitHubAuthToken(projectRoot);
|
|
188
392
|
}
|
|
189
393
|
async function ensureServerForCli(projectRoot) {
|
|
190
394
|
try {
|
|
191
395
|
const selected = resolveSelectedConnection(projectRoot);
|
|
192
396
|
if (selected?.connection.kind === "remote") {
|
|
397
|
+
const authToken = readGitHubBearerTokenForRemote(projectRoot);
|
|
398
|
+
const serverProjectRoot = selected.serverProjectRoot ?? await backfillRemoteServerProjectRoot(projectRoot, selected.connection.baseUrl, authToken);
|
|
193
399
|
return {
|
|
194
400
|
baseUrl: selected.connection.baseUrl,
|
|
195
|
-
authToken
|
|
196
|
-
connectionKind: "remote"
|
|
401
|
+
authToken,
|
|
402
|
+
connectionKind: "remote",
|
|
403
|
+
serverProjectRoot
|
|
197
404
|
};
|
|
198
405
|
}
|
|
199
406
|
const connection = await ensureLocalRigServerConnection(projectRoot);
|
|
200
407
|
return {
|
|
201
408
|
baseUrl: connection.baseUrl,
|
|
202
|
-
authToken: connection.authToken,
|
|
203
|
-
connectionKind: "local"
|
|
409
|
+
authToken: connection.authToken ?? readLocalConnectionFallbackToken(projectRoot),
|
|
410
|
+
connectionKind: "local",
|
|
411
|
+
serverProjectRoot: resolve2(projectRoot)
|
|
204
412
|
};
|
|
205
413
|
} catch (error) {
|
|
206
414
|
if (error instanceof Error) {
|
|
@@ -209,6 +417,29 @@ async function ensureServerForCli(projectRoot) {
|
|
|
209
417
|
throw error;
|
|
210
418
|
}
|
|
211
419
|
}
|
|
420
|
+
async function backfillRemoteServerProjectRoot(projectRoot, baseUrl, authToken) {
|
|
421
|
+
const repo = readRepoConnection(projectRoot);
|
|
422
|
+
const slug = repo?.project?.trim();
|
|
423
|
+
if (!slug)
|
|
424
|
+
return null;
|
|
425
|
+
try {
|
|
426
|
+
const response = await fetch(`${baseUrl}/api/projects/${encodeURIComponent(slug)}`, {
|
|
427
|
+
headers: mergeHeaders(undefined, authToken)
|
|
428
|
+
});
|
|
429
|
+
if (!response.ok)
|
|
430
|
+
return null;
|
|
431
|
+
const payload = await response.json();
|
|
432
|
+
const project = payload.project && typeof payload.project === "object" && !Array.isArray(payload.project) ? payload.project : null;
|
|
433
|
+
const checkouts = Array.isArray(project?.checkouts) ? project.checkouts : [];
|
|
434
|
+
const latestCheckout = [...checkouts].reverse().find((entry) => Boolean(entry && typeof entry === "object" && !Array.isArray(entry) && typeof entry.path === "string"));
|
|
435
|
+
const path = typeof latestCheckout?.path === "string" && latestCheckout.path.trim() ? latestCheckout.path.trim() : null;
|
|
436
|
+
if (path)
|
|
437
|
+
writeRepoServerProjectRoot(projectRoot, path);
|
|
438
|
+
return path;
|
|
439
|
+
} catch {
|
|
440
|
+
return null;
|
|
441
|
+
}
|
|
442
|
+
}
|
|
212
443
|
function mergeHeaders(headers, authToken) {
|
|
213
444
|
const merged = new Headers(headers);
|
|
214
445
|
if (authToken) {
|
|
@@ -233,9 +464,12 @@ function diagnosticMessage(payload) {
|
|
|
233
464
|
}
|
|
234
465
|
async function requestServerJson(context, pathname, init = {}) {
|
|
235
466
|
const server = await ensureServerForCli(context.projectRoot);
|
|
467
|
+
const headers = mergeHeaders(init.headers, server.authToken);
|
|
468
|
+
if (server.serverProjectRoot)
|
|
469
|
+
headers.set("x-rig-project-root", server.serverProjectRoot);
|
|
236
470
|
const response = await fetch(`${server.baseUrl}${pathname}`, {
|
|
237
471
|
...init,
|
|
238
|
-
headers
|
|
472
|
+
headers
|
|
239
473
|
});
|
|
240
474
|
const text = await response.text();
|
|
241
475
|
const payload = text.trim().length > 0 ? (() => {
|
|
@@ -286,7 +520,10 @@ async function submitTaskRunViaServer(context, input) {
|
|
|
286
520
|
|
|
287
521
|
// packages/cli/src/commands/server.ts
|
|
288
522
|
async function executeServer(context, args, options) {
|
|
289
|
-
const [command = "
|
|
523
|
+
const [command = "status", ...rest] = args;
|
|
524
|
+
if (["status", "list", "add", "use"].includes(command)) {
|
|
525
|
+
return executeConnectionCommand(context, [command, ...rest], { group: "server", interactiveUse: true });
|
|
526
|
+
}
|
|
290
527
|
switch (command) {
|
|
291
528
|
case "start": {
|
|
292
529
|
let pending = rest;
|
|
@@ -298,8 +535,9 @@ async function executeServer(context, args, options) {
|
|
|
298
535
|
pending = pollResult.rest;
|
|
299
536
|
const authTokenResult = takeOption(pending, "--auth-token");
|
|
300
537
|
pending = authTokenResult.rest;
|
|
301
|
-
requireNoExtraArgs(pending, "
|
|
302
|
-
const
|
|
538
|
+
requireNoExtraArgs(pending, "rig server start [--host <host>] [--port <n>] [--poll-ms <n>] [--auth-token <token>]");
|
|
539
|
+
const serverCommand = resolveRigServerCommand(context.projectRoot);
|
|
540
|
+
const commandParts = [serverCommand.command, ...serverCommand.commandArgs, "start"];
|
|
303
541
|
if (hostResult.value) {
|
|
304
542
|
commandParts.push("--host", hostResult.value);
|
|
305
543
|
}
|
|
@@ -321,8 +559,9 @@ async function executeServer(context, args, options) {
|
|
|
321
559
|
let pending = rest;
|
|
322
560
|
const eventResult = takeOption(pending, "--event");
|
|
323
561
|
pending = eventResult.rest;
|
|
324
|
-
requireNoExtraArgs(pending, "
|
|
325
|
-
const
|
|
562
|
+
requireNoExtraArgs(pending, "rig server notify-test [--event <type>]");
|
|
563
|
+
const serverCommand = resolveRigServerCommand(context.projectRoot);
|
|
564
|
+
const commandParts = [serverCommand.command, ...serverCommand.commandArgs, "notify-test"];
|
|
326
565
|
if (eventResult.value) {
|
|
327
566
|
commandParts.push("--event", eventResult.value);
|
|
328
567
|
}
|
|
@@ -349,7 +588,7 @@ async function executeServer(context, args, options) {
|
|
|
349
588
|
pending = dirtyBaselineResult.rest;
|
|
350
589
|
const prResult = takeOption(pending, "--pr");
|
|
351
590
|
pending = prResult.rest;
|
|
352
|
-
requireNoExtraArgs(pending, "
|
|
591
|
+
requireNoExtraArgs(pending, "rig --run-id <run-id> server task-run [--task <id>] [--title <text>] [--runtime-adapter claude-code|codex|pi] [--model <model>] [--runtime-mode <mode>] [--interaction-mode <mode>] [--initial-prompt <text>] [--dirty-baseline head|dirty-snapshot] [--pr auto|ask|off]");
|
|
353
592
|
if (!taskResult.value && !initialPromptResult.value && !titleResult.value) {
|
|
354
593
|
throw new CliError2("server task-run requires either --task <id> or --initial-prompt/--title for an ad hoc run.", 2);
|
|
355
594
|
}
|