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