@h-rig/cli 0.0.6-alpha.3 → 0.0.6-alpha.30
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 +3606 -1172
- package/dist/src/commands/_authority-runs.js +1 -0
- package/dist/src/commands/_cli-format.js +369 -0
- package/dist/src/commands/_connection-state.js +1 -3
- package/dist/src/commands/_doctor-checks.js +13 -27
- package/dist/src/commands/_help-catalog.js +388 -0
- package/dist/src/commands/_operator-surface.js +204 -0
- package/dist/src/commands/_operator-view.js +861 -56
- package/dist/src/commands/_parsers.js +0 -2
- package/dist/src/commands/_pi-frontend.js +841 -0
- package/dist/src/commands/_pi-install.js +4 -3
- package/dist/src/commands/_pi-worker-bridge-extension.js +759 -0
- package/dist/src/commands/_policy.js +0 -2
- package/dist/src/commands/_preflight.js +32 -109
- package/dist/src/commands/_run-driver-helpers.js +0 -2
- package/dist/src/commands/_server-client.js +161 -31
- package/dist/src/commands/_snapshot-upload.js +8 -23
- package/dist/src/commands/_task-picker.js +44 -16
- package/dist/src/commands/agent.js +9 -9
- package/dist/src/commands/browser.js +4 -6
- package/dist/src/commands/connect.js +132 -25
- package/dist/src/commands/dist.js +4 -6
- package/dist/src/commands/doctor.js +13 -27
- package/dist/src/commands/github.js +10 -25
- package/dist/src/commands/inbox.js +351 -31
- package/dist/src/commands/init.js +298 -71
- package/dist/src/commands/inspect.js +10 -12
- package/dist/src/commands/inspector.js +2 -4
- package/dist/src/commands/plugin.js +76 -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 +1157 -122
- package/dist/src/commands/server.js +217 -33
- package/dist/src/commands/setup.js +17 -37
- package/dist/src/commands/task-report-bug.js +5 -7
- package/dist/src/commands/task-run-driver.js +660 -73
- package/dist/src/commands/task.js +1542 -252
- package/dist/src/commands/test.js +3 -5
- package/dist/src/commands/workspace.js +4 -6
- package/dist/src/commands.js +3599 -1159
- package/dist/src/index.js +3646 -1215
- 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 +6 -4
|
@@ -7,8 +7,6 @@ import { resolve } from "path";
|
|
|
7
7
|
import { EventBus } from "@rig/runtime/control-plane/runtime/events";
|
|
8
8
|
import { CliError } from "@rig/runtime/control-plane/errors";
|
|
9
9
|
import { evaluate, loadPolicy, resolveAction } from "@rig/runtime/control-plane/runtime/guard";
|
|
10
|
-
import { PluginManager } from "@rig/runtime/control-plane/runtime/plugins";
|
|
11
|
-
import { loadRuntimeContextFromEnv } from "@rig/runtime/control-plane/runtime/context";
|
|
12
10
|
import { buildBinary } from "@rig/runtime/control-plane/runtime/isolation";
|
|
13
11
|
import { CliError as CliError2 } from "@rig/runtime/control-plane/errors";
|
|
14
12
|
function formatCommand(parts) {
|
|
@@ -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
|
|
|
@@ -88,17 +86,16 @@ function resolveSelectedConnection(projectRoot, options = {}) {
|
|
|
88
86
|
const global = readGlobalConnections(options);
|
|
89
87
|
const connection = global.connections[repo.selected];
|
|
90
88
|
if (!connection) {
|
|
91
|
-
throw new CliError2(`Selected Rig
|
|
89
|
+
throw new CliError2(`Selected Rig server "${repo.selected}" was not found. Run \`rig server list\` or \`rig server use local\`.`, 1);
|
|
92
90
|
}
|
|
93
91
|
return { alias: repo.selected, connection };
|
|
94
92
|
}
|
|
95
93
|
|
|
96
94
|
// packages/cli/src/commands/_server-client.ts
|
|
97
|
-
import { spawnSync } from "child_process";
|
|
98
95
|
import { existsSync as existsSync2, readFileSync as readFileSync2 } from "fs";
|
|
99
96
|
import { resolve as resolve2 } from "path";
|
|
100
97
|
import { ensureLocalRigServerConnection } from "@rig/runtime/local-server";
|
|
101
|
-
var
|
|
98
|
+
var scopedGitHubBearerTokens = new Map;
|
|
102
99
|
function cleanToken(value) {
|
|
103
100
|
const trimmed = value?.trim();
|
|
104
101
|
return trimmed ? trimmed : null;
|
|
@@ -115,25 +112,13 @@ function readPrivateRemoteSessionToken(projectRoot) {
|
|
|
115
112
|
}
|
|
116
113
|
}
|
|
117
114
|
function readGitHubBearerTokenForRemote(projectRoot) {
|
|
118
|
-
|
|
119
|
-
|
|
115
|
+
const scopedKey = resolve2(projectRoot);
|
|
116
|
+
if (scopedGitHubBearerTokens.has(scopedKey))
|
|
117
|
+
return scopedGitHubBearerTokens.get(scopedKey) ?? null;
|
|
120
118
|
const privateSession = readPrivateRemoteSessionToken(projectRoot);
|
|
121
|
-
if (privateSession)
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
}
|
|
125
|
-
const envToken = cleanToken(process.env.RIG_GITHUB_TOKEN) ?? cleanToken(process.env.GITHUB_TOKEN) ?? cleanToken(process.env.GH_TOKEN);
|
|
126
|
-
if (envToken) {
|
|
127
|
-
cachedGitHubBearerToken = envToken;
|
|
128
|
-
return cachedGitHubBearerToken;
|
|
129
|
-
}
|
|
130
|
-
const result = spawnSync("gh", ["auth", "token"], {
|
|
131
|
-
encoding: "utf8",
|
|
132
|
-
timeout: 5000,
|
|
133
|
-
stdio: ["ignore", "pipe", "ignore"]
|
|
134
|
-
});
|
|
135
|
-
cachedGitHubBearerToken = result.status === 0 ? cleanToken(result.stdout) : null;
|
|
136
|
-
return cachedGitHubBearerToken;
|
|
119
|
+
if (privateSession)
|
|
120
|
+
return privateSession;
|
|
121
|
+
return cleanToken(process.env.RIG_SERVER_AUTH_TOKEN) ?? cleanToken(process.env.RIG_REMOTE_AUTH_TOKEN);
|
|
137
122
|
}
|
|
138
123
|
async function ensureServerForCli(projectRoot) {
|
|
139
124
|
try {
|
|
@@ -202,78 +187,6 @@ async function requestServerJson(context, pathname, init = {}) {
|
|
|
202
187
|
return payload;
|
|
203
188
|
}
|
|
204
189
|
|
|
205
|
-
// packages/cli/src/commands/_pi-install.ts
|
|
206
|
-
import { existsSync as existsSync3, readFileSync as readFileSync3, rmSync } from "fs";
|
|
207
|
-
import { homedir as homedir2 } from "os";
|
|
208
|
-
import { resolve as resolve3 } from "path";
|
|
209
|
-
var PI_RIG_PACKAGE_NAME = "@rig/pi-rig";
|
|
210
|
-
async function defaultCommandRunner(command, options = {}) {
|
|
211
|
-
const proc = Bun.spawn(command, { cwd: options.cwd, stdout: "pipe", stderr: "pipe" });
|
|
212
|
-
const [stdout, stderr, exitCode] = await Promise.all([
|
|
213
|
-
new Response(proc.stdout).text(),
|
|
214
|
-
new Response(proc.stderr).text(),
|
|
215
|
-
proc.exited
|
|
216
|
-
]);
|
|
217
|
-
return { exitCode, stdout, stderr };
|
|
218
|
-
}
|
|
219
|
-
function resolvePiRigExtensionPath(homeDir) {
|
|
220
|
-
return resolve3(homeDir, ".pi", "agent", "extensions", "pi-rig");
|
|
221
|
-
}
|
|
222
|
-
function resolvePiHomeDir(inputHomeDir) {
|
|
223
|
-
return inputHomeDir ?? process.env.RIG_PI_HOME_DIR?.trim() ?? homedir2();
|
|
224
|
-
}
|
|
225
|
-
function piListContainsPiRig(output) {
|
|
226
|
-
return output.split(/\r?\n/).some((line) => {
|
|
227
|
-
const normalized = line.trim();
|
|
228
|
-
return normalized.includes(PI_RIG_PACKAGE_NAME) || /(?:^|[\\/])packages[\\/]pi-rig(?:$|\s)/.test(normalized);
|
|
229
|
-
});
|
|
230
|
-
}
|
|
231
|
-
async function safeRun(runner, command, options) {
|
|
232
|
-
try {
|
|
233
|
-
return await runner(command, options);
|
|
234
|
-
} catch (error) {
|
|
235
|
-
return { exitCode: 1, stdout: "", stderr: error instanceof Error ? error.message : String(error) };
|
|
236
|
-
}
|
|
237
|
-
}
|
|
238
|
-
async function checkPiRigInstall(input = {}) {
|
|
239
|
-
const home = resolvePiHomeDir(input.homeDir);
|
|
240
|
-
const extensionPath = resolvePiRigExtensionPath(home);
|
|
241
|
-
if (process.env.RIG_TEST_FAKE_PI_INSTALL === "1") {
|
|
242
|
-
return {
|
|
243
|
-
extensionPath,
|
|
244
|
-
pi: { ok: true, label: "pi", detail: "fake-pi" },
|
|
245
|
-
piRig: { ok: true, label: "pi-rig global extension", detail: extensionPath }
|
|
246
|
-
};
|
|
247
|
-
}
|
|
248
|
-
const exists = input.exists ?? existsSync3;
|
|
249
|
-
const runner = input.commandRunner ?? defaultCommandRunner;
|
|
250
|
-
const piResult = await safeRun(runner, ["pi", "--version"]);
|
|
251
|
-
const piListResult = piResult.exitCode === 0 ? await safeRun(runner, ["pi", "list"]) : { exitCode: 1, stdout: "", stderr: "" };
|
|
252
|
-
const listedPiRig = piListResult.exitCode === 0 && piListContainsPiRig(`${piListResult.stdout}
|
|
253
|
-
${piListResult.stderr}`);
|
|
254
|
-
const legacyBridge = exists(resolve3(extensionPath, "index.ts"));
|
|
255
|
-
const hasPiRig = listedPiRig;
|
|
256
|
-
return {
|
|
257
|
-
extensionPath,
|
|
258
|
-
pi: {
|
|
259
|
-
ok: piResult.exitCode === 0,
|
|
260
|
-
label: "pi",
|
|
261
|
-
detail: (piResult.stdout || piResult.stderr).trim() || undefined,
|
|
262
|
-
hint: piResult.exitCode === 0 ? undefined : "Install Pi or run `rig init --yes` to install/update the Pi runtime."
|
|
263
|
-
},
|
|
264
|
-
piRig: {
|
|
265
|
-
ok: hasPiRig,
|
|
266
|
-
label: "pi-rig global extension",
|
|
267
|
-
detail: hasPiRig ? piListResult.stdout.trim() || PI_RIG_PACKAGE_NAME : legacyBridge ? `${extensionPath} (legacy bridge; reinstall required)` : undefined,
|
|
268
|
-
hint: hasPiRig ? undefined : "Run `rig init --yes` to install/enable the global pi-rig package with `pi install`."
|
|
269
|
-
}
|
|
270
|
-
};
|
|
271
|
-
}
|
|
272
|
-
async function buildPiSetupChecks(input = {}) {
|
|
273
|
-
const status = await checkPiRigInstall(input);
|
|
274
|
-
return [status.pi, status.piRig];
|
|
275
|
-
}
|
|
276
|
-
|
|
277
190
|
// packages/cli/src/commands/_preflight.ts
|
|
278
191
|
function preflightCheck(id, label, status, detail, remediation) {
|
|
279
192
|
return {
|
|
@@ -329,6 +242,9 @@ function permissionAllowsPr(payload) {
|
|
|
329
242
|
}
|
|
330
243
|
return null;
|
|
331
244
|
}
|
|
245
|
+
function isNotFoundError(error) {
|
|
246
|
+
return /\b(404|not found)\b/i.test(message(error));
|
|
247
|
+
}
|
|
332
248
|
function projectCheckoutReady(payload) {
|
|
333
249
|
if (!payload || typeof payload !== "object" || Array.isArray(payload))
|
|
334
250
|
return null;
|
|
@@ -361,19 +277,33 @@ async function runFastTaskRunPreflight(context, options = {}) {
|
|
|
361
277
|
const checks = [];
|
|
362
278
|
const request = options.requestJson ?? ((pathname, init) => requestServerJson(context, pathname, init));
|
|
363
279
|
const taskId = options.taskId?.trim() || null;
|
|
280
|
+
const requiresCurrentRunApi = Boolean(taskId);
|
|
281
|
+
const selectedServer = options.requestJson ? null : await ensureServerForCli(context.projectRoot).catch(() => null);
|
|
282
|
+
const allowLocalLegacyTaskRunCompatibility = selectedServer?.connectionKind === "local";
|
|
283
|
+
let legacyServerCompatibility = false;
|
|
364
284
|
try {
|
|
365
285
|
await request("/api/server/status");
|
|
366
286
|
checks.push(preflightCheck("server", "Rig server reachable", "pass"));
|
|
367
287
|
} catch (error) {
|
|
368
|
-
|
|
288
|
+
if (isNotFoundError(error)) {
|
|
289
|
+
try {
|
|
290
|
+
await request("/health");
|
|
291
|
+
legacyServerCompatibility = !requiresCurrentRunApi || allowLocalLegacyTaskRunCompatibility;
|
|
292
|
+
checks.push(requiresCurrentRunApi && !allowLocalLegacyTaskRunCompatibility ? preflightCheck("server", "Rig server reachable", "fail", "legacy /health endpoint only; current task-run APIs are required", "Upgrade/select the Rig server before launching a task run.") : preflightCheck("server", "Rig server reachable", "pass", allowLocalLegacyTaskRunCompatibility ? "local legacy /health endpoint; submit endpoint will be authoritative" : "legacy /health endpoint"));
|
|
293
|
+
} catch (healthError) {
|
|
294
|
+
checks.push(preflightCheck("server", "Rig server reachable", "fail", message(healthError), "Start or select a reachable Rig server."));
|
|
295
|
+
}
|
|
296
|
+
} else {
|
|
297
|
+
checks.push(preflightCheck("server", "Rig server reachable", "fail", message(error), "Start or select a reachable Rig server."));
|
|
298
|
+
}
|
|
369
299
|
}
|
|
370
300
|
const repo = readRepoConnection(context.projectRoot);
|
|
371
|
-
checks.push(repo ? preflightCheck("project-link", "project linked to Rig
|
|
301
|
+
checks.push(repo ? preflightCheck("project-link", "project linked to Rig server", repo.project ? "pass" : "warn", `${repo.selected}${repo.project ? ` -> ${repo.project}` : ""}`, "Run `rig init --yes --repo owner/repo` to record the GitHub repo slug.") : preflightCheck("project-link", "project linked to Rig server", legacyServerCompatibility ? "warn" : "fail", "missing .rig/state/connection.json", "Run `rig init` or `rig server use <alias|local>`."));
|
|
372
302
|
try {
|
|
373
303
|
const auth = await request("/api/github/auth/status");
|
|
374
|
-
checks.push(isAuthenticated(auth) ? preflightCheck("github-auth", "GitHub auth valid", "pass") : preflightCheck("github-auth", "GitHub auth valid", "fail", "not authenticated", "Run `rig github auth import-gh` or `rig github auth token --token <token>`."));
|
|
304
|
+
checks.push(isAuthenticated(auth) ? preflightCheck("github-auth", "GitHub auth valid", "pass") : preflightCheck("github-auth", "GitHub auth valid", legacyServerCompatibility ? "warn" : "fail", "not authenticated", "Run `rig github auth import-gh` or `rig github auth token --token <token>`."));
|
|
375
305
|
} catch (error) {
|
|
376
|
-
checks.push(preflightCheck("github-auth", "GitHub auth valid", "fail", message(error), "Fix GitHub auth on the selected Rig server."));
|
|
306
|
+
checks.push(preflightCheck("github-auth", "GitHub auth valid", legacyServerCompatibility ? "warn" : "fail", message(error), "Fix GitHub auth on the selected Rig server."));
|
|
377
307
|
}
|
|
378
308
|
try {
|
|
379
309
|
const projection = await request("/api/workspace/task-projection");
|
|
@@ -401,9 +331,9 @@ async function runFastTaskRunPreflight(context, options = {}) {
|
|
|
401
331
|
try {
|
|
402
332
|
const tasks = await request(`/api/workspace/tasks?limit=200&refresh=1`);
|
|
403
333
|
const found = Array.isArray(tasks) && tasks.some((task) => taskMatchesId(task, taskId));
|
|
404
|
-
checks.push(found ? preflightCheck("issue", "task/issue accessible", "pass", taskId) : preflightCheck("issue", "task/issue accessible", "fail", taskId, "Confirm the issue exists and matches the configured task filters."));
|
|
334
|
+
checks.push(found ? preflightCheck("issue", "task/issue accessible", "pass", taskId) : preflightCheck("issue", "task/issue accessible", legacyServerCompatibility ? "warn" : "fail", taskId, "Confirm the issue exists and matches the configured task filters."));
|
|
405
335
|
} catch (error) {
|
|
406
|
-
checks.push(preflightCheck("issue", "task/issue accessible", "fail", message(error), "Fix the task source before launching a run."));
|
|
336
|
+
checks.push(preflightCheck("issue", "task/issue accessible", legacyServerCompatibility ? "warn" : "fail", message(error), "Fix the task source before launching a run."));
|
|
407
337
|
}
|
|
408
338
|
try {
|
|
409
339
|
const runs = await request("/api/runs?limit=200");
|
|
@@ -414,14 +344,7 @@ async function runFastTaskRunPreflight(context, options = {}) {
|
|
|
414
344
|
}
|
|
415
345
|
}
|
|
416
346
|
if ((options.runtimeAdapter ?? "pi") === "pi") {
|
|
417
|
-
|
|
418
|
-
ok: false,
|
|
419
|
-
label: "pi/pi-rig checks",
|
|
420
|
-
hint: message(error)
|
|
421
|
-
}]);
|
|
422
|
-
for (const pi of piChecks) {
|
|
423
|
-
checks.push(preflightCheck(pi.label === "pi" ? "pi" : "pi-rig", pi.label, pi.ok ? "pass" : "fail", pi.detail, pi.hint ?? (pi.ok ? undefined : "Run `rig init --yes` to install/update Pi and enable pi-rig.")));
|
|
424
|
-
}
|
|
347
|
+
checks.push(preflightCheck("runtime", "worker Pi SDK session daemon", "pass", selectedServer?.connectionKind === "remote" ? "remote worker-owned runtime" : "bundled server-owned runtime"));
|
|
425
348
|
} else {
|
|
426
349
|
checks.push(preflightCheck("runtime", "runtime adapter", "pass", options.runtimeAdapter));
|
|
427
350
|
}
|
|
@@ -7,8 +7,6 @@ import { resolve as resolve3 } from "path";
|
|
|
7
7
|
import { EventBus } from "@rig/runtime/control-plane/runtime/events";
|
|
8
8
|
import { CliError } from "@rig/runtime/control-plane/errors";
|
|
9
9
|
import { evaluate, loadPolicy, resolveAction } from "@rig/runtime/control-plane/runtime/guard";
|
|
10
|
-
import { PluginManager } from "@rig/runtime/control-plane/runtime/plugins";
|
|
11
|
-
import { loadRuntimeContextFromEnv } from "@rig/runtime/control-plane/runtime/context";
|
|
12
10
|
import { buildBinary } from "@rig/runtime/control-plane/runtime/isolation";
|
|
13
11
|
import { CliError as CliError2 } from "@rig/runtime/control-plane/errors";
|
|
14
12
|
|
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
// @bun
|
|
2
2
|
// packages/cli/src/commands/_server-client.ts
|
|
3
|
-
import { spawnSync } from "child_process";
|
|
4
3
|
import { existsSync as existsSync2, readFileSync as readFileSync2 } from "fs";
|
|
5
4
|
import { resolve as resolve2 } from "path";
|
|
6
5
|
|
|
@@ -8,8 +7,6 @@ import { resolve as resolve2 } from "path";
|
|
|
8
7
|
import { EventBus } from "@rig/runtime/control-plane/runtime/events";
|
|
9
8
|
import { CliError } from "@rig/runtime/control-plane/errors";
|
|
10
9
|
import { evaluate, loadPolicy, resolveAction } from "@rig/runtime/control-plane/runtime/guard";
|
|
11
|
-
import { PluginManager } from "@rig/runtime/control-plane/runtime/plugins";
|
|
12
|
-
import { loadRuntimeContextFromEnv } from "@rig/runtime/control-plane/runtime/context";
|
|
13
10
|
import { buildBinary } from "@rig/runtime/control-plane/runtime/isolation";
|
|
14
11
|
import { CliError as CliError2 } from "@rig/runtime/control-plane/errors";
|
|
15
12
|
|
|
@@ -93,19 +90,20 @@ function resolveSelectedConnection(projectRoot, options = {}) {
|
|
|
93
90
|
const global = readGlobalConnections(options);
|
|
94
91
|
const connection = global.connections[repo.selected];
|
|
95
92
|
if (!connection) {
|
|
96
|
-
throw new CliError2(`Selected Rig
|
|
93
|
+
throw new CliError2(`Selected Rig server "${repo.selected}" was not found. Run \`rig server list\` or \`rig server use local\`.`, 1);
|
|
97
94
|
}
|
|
98
95
|
return { alias: repo.selected, connection };
|
|
99
96
|
}
|
|
100
97
|
|
|
101
98
|
// packages/cli/src/commands/_server-client.ts
|
|
102
|
-
var
|
|
99
|
+
var scopedGitHubBearerTokens = new Map;
|
|
103
100
|
function cleanToken(value) {
|
|
104
101
|
const trimmed = value?.trim();
|
|
105
102
|
return trimmed ? trimmed : null;
|
|
106
103
|
}
|
|
107
|
-
function setGitHubBearerTokenForCurrentProcess(token) {
|
|
108
|
-
|
|
104
|
+
function setGitHubBearerTokenForCurrentProcess(token, projectRoot) {
|
|
105
|
+
const scopedKey = resolve2(projectRoot ?? process.cwd());
|
|
106
|
+
scopedGitHubBearerTokens.set(scopedKey, cleanToken(token ?? undefined));
|
|
109
107
|
}
|
|
110
108
|
function readPrivateRemoteSessionToken(projectRoot) {
|
|
111
109
|
const path = resolve2(projectRoot, ".rig", "state", "github-auth.json");
|
|
@@ -119,25 +117,13 @@ function readPrivateRemoteSessionToken(projectRoot) {
|
|
|
119
117
|
}
|
|
120
118
|
}
|
|
121
119
|
function readGitHubBearerTokenForRemote(projectRoot) {
|
|
122
|
-
|
|
123
|
-
|
|
120
|
+
const scopedKey = resolve2(projectRoot);
|
|
121
|
+
if (scopedGitHubBearerTokens.has(scopedKey))
|
|
122
|
+
return scopedGitHubBearerTokens.get(scopedKey) ?? null;
|
|
124
123
|
const privateSession = readPrivateRemoteSessionToken(projectRoot);
|
|
125
|
-
if (privateSession)
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
}
|
|
129
|
-
const envToken = cleanToken(process.env.RIG_GITHUB_TOKEN) ?? cleanToken(process.env.GITHUB_TOKEN) ?? cleanToken(process.env.GH_TOKEN);
|
|
130
|
-
if (envToken) {
|
|
131
|
-
cachedGitHubBearerToken = envToken;
|
|
132
|
-
return cachedGitHubBearerToken;
|
|
133
|
-
}
|
|
134
|
-
const result = spawnSync("gh", ["auth", "token"], {
|
|
135
|
-
encoding: "utf8",
|
|
136
|
-
timeout: 5000,
|
|
137
|
-
stdio: ["ignore", "pipe", "ignore"]
|
|
138
|
-
});
|
|
139
|
-
cachedGitHubBearerToken = result.status === 0 ? cleanToken(result.stdout) : null;
|
|
140
|
-
return cachedGitHubBearerToken;
|
|
124
|
+
if (privateSession)
|
|
125
|
+
return privateSession;
|
|
126
|
+
return cleanToken(process.env.RIG_SERVER_AUTH_TOKEN) ?? cleanToken(process.env.RIG_REMOTE_AUTH_TOKEN);
|
|
141
127
|
}
|
|
142
128
|
async function ensureServerForCli(projectRoot) {
|
|
143
129
|
try {
|
|
@@ -275,16 +261,36 @@ async function registerProjectViaServer(context, input) {
|
|
|
275
261
|
function sleep(ms) {
|
|
276
262
|
return new Promise((resolve3) => setTimeout(resolve3, ms));
|
|
277
263
|
}
|
|
264
|
+
function isRetryableProjectRootSwitchError(error) {
|
|
265
|
+
if (!(error instanceof Error))
|
|
266
|
+
return false;
|
|
267
|
+
const message = error.message.toLowerCase();
|
|
268
|
+
return message.includes("rig server request failed (401): auth-required") || message.includes("rig server request failed (401): github-token-required") || message.includes("rig server request failed (502)") || message.includes("rig server request failed (503)") || message.includes("bad gateway") || message.includes("fetch failed") || message.includes("econnrefused") || message.includes("connection refused");
|
|
269
|
+
}
|
|
278
270
|
async function switchServerProjectRootViaServer(context, projectRoot, options = {}) {
|
|
279
|
-
const switched = await requestServerJson(context, "/api/server/project-root", {
|
|
280
|
-
method: "POST",
|
|
281
|
-
headers: { "content-type": "application/json" },
|
|
282
|
-
body: JSON.stringify({ projectRoot })
|
|
283
|
-
});
|
|
284
271
|
const timeoutMs = options.timeoutMs ?? 30000;
|
|
285
272
|
const pollMs = options.pollMs ?? 1000;
|
|
286
273
|
const deadline = Date.now() + timeoutMs;
|
|
287
274
|
let lastError;
|
|
275
|
+
let switched = null;
|
|
276
|
+
while (Date.now() < deadline) {
|
|
277
|
+
try {
|
|
278
|
+
switched = await requestServerJson(context, "/api/server/project-root", {
|
|
279
|
+
method: "POST",
|
|
280
|
+
headers: { "content-type": "application/json" },
|
|
281
|
+
body: JSON.stringify({ projectRoot })
|
|
282
|
+
});
|
|
283
|
+
break;
|
|
284
|
+
} catch (error) {
|
|
285
|
+
lastError = error;
|
|
286
|
+
if (!isRetryableProjectRootSwitchError(error))
|
|
287
|
+
throw error;
|
|
288
|
+
await sleep(pollMs);
|
|
289
|
+
}
|
|
290
|
+
}
|
|
291
|
+
if (!switched) {
|
|
292
|
+
throw new CliError2(`Rig server did not accept project-root switch to ${projectRoot} before timeout (${lastError instanceof Error ? lastError.message : String(lastError ?? "no response")}).`, 1);
|
|
293
|
+
}
|
|
288
294
|
while (Date.now() < deadline) {
|
|
289
295
|
try {
|
|
290
296
|
const status = await requestServerJson(context, "/api/server/status");
|
|
@@ -302,6 +308,14 @@ async function switchServerProjectRootViaServer(context, projectRoot, options =
|
|
|
302
308
|
}
|
|
303
309
|
throw new CliError2(`Rig server did not switch to ${projectRoot} before timeout (${lastError instanceof Error ? lastError.message : String(lastError ?? "no status")}).`, 1);
|
|
304
310
|
}
|
|
311
|
+
async function listRunsViaServer(context, options = {}) {
|
|
312
|
+
const url = new URL("http://rig.local/api/runs");
|
|
313
|
+
if (options.limit !== undefined)
|
|
314
|
+
url.searchParams.set("limit", String(options.limit));
|
|
315
|
+
const payload = await requestServerJson(context, `${url.pathname}${url.search}`);
|
|
316
|
+
const runs = Array.isArray(payload) ? payload : payload && typeof payload === "object" && !Array.isArray(payload) && Array.isArray(payload.runs) ? payload.runs : [];
|
|
317
|
+
return runs.filter((entry) => Boolean(entry && typeof entry === "object" && !Array.isArray(entry)));
|
|
318
|
+
}
|
|
305
319
|
async function getRunDetailsViaServer(context, runId) {
|
|
306
320
|
const payload = await requestServerJson(context, `/api/runs/${encodeURIComponent(runId)}`);
|
|
307
321
|
return payload && typeof payload === "object" && !Array.isArray(payload) ? payload : {};
|
|
@@ -315,6 +329,37 @@ async function getRunLogsViaServer(context, runId, options = {}) {
|
|
|
315
329
|
const payload = await requestServerJson(context, `${url.pathname}${url.search}`);
|
|
316
330
|
return payload && typeof payload === "object" && !Array.isArray(payload) ? payload : { entries: [] };
|
|
317
331
|
}
|
|
332
|
+
async function getRunTimelineViaServer(context, runId, options = {}) {
|
|
333
|
+
const url = new URL(`http://rig.local/api/runs/${encodeURIComponent(runId)}/timeline`);
|
|
334
|
+
if (options.limit !== undefined)
|
|
335
|
+
url.searchParams.set("limit", String(options.limit));
|
|
336
|
+
if (options.cursor)
|
|
337
|
+
url.searchParams.set("cursor", options.cursor);
|
|
338
|
+
const payload = await requestServerJson(context, `${url.pathname}${url.search}`);
|
|
339
|
+
return payload && typeof payload === "object" && !Array.isArray(payload) ? payload : { entries: [] };
|
|
340
|
+
}
|
|
341
|
+
async function ensureTaskLabelsViaServer(context) {
|
|
342
|
+
const payload = await requestServerJson(context, "/api/workspace/task-labels", { method: "POST" });
|
|
343
|
+
return payload && typeof payload === "object" && !Array.isArray(payload) ? payload : {};
|
|
344
|
+
}
|
|
345
|
+
async function listGitHubProjectsViaServer(context, owner) {
|
|
346
|
+
const url = new URL("http://rig.local/api/github/projects");
|
|
347
|
+
url.searchParams.set("owner", owner);
|
|
348
|
+
const payload = await requestServerJson(context, `${url.pathname}${url.search}`);
|
|
349
|
+
return payload && typeof payload === "object" && !Array.isArray(payload) ? payload : { projects: [] };
|
|
350
|
+
}
|
|
351
|
+
async function getGitHubProjectStatusFieldViaServer(context, projectId) {
|
|
352
|
+
const payload = await requestServerJson(context, `/api/github/projects/${encodeURIComponent(projectId)}/status-field`);
|
|
353
|
+
return payload && typeof payload === "object" && !Array.isArray(payload) ? payload : {};
|
|
354
|
+
}
|
|
355
|
+
async function updateWorkspaceTaskViaServer(context, input) {
|
|
356
|
+
const payload = await requestServerJson(context, "/api/tasks/update", {
|
|
357
|
+
method: "POST",
|
|
358
|
+
headers: { "content-type": "application/json" },
|
|
359
|
+
body: JSON.stringify(input)
|
|
360
|
+
});
|
|
361
|
+
return payload && typeof payload === "object" && !Array.isArray(payload) ? payload : { ok: true };
|
|
362
|
+
}
|
|
318
363
|
async function stopRunViaServer(context, runId) {
|
|
319
364
|
const payload = await requestServerJson(context, "/api/runs/stop", {
|
|
320
365
|
method: "POST",
|
|
@@ -331,6 +376,74 @@ async function steerRunViaServer(context, runId, message) {
|
|
|
331
376
|
});
|
|
332
377
|
return payload && typeof payload === "object" && !Array.isArray(payload) ? payload : { ok: true };
|
|
333
378
|
}
|
|
379
|
+
async function getRunPiSessionViaServer(context, runId) {
|
|
380
|
+
const payload = await requestServerJson(context, `/api/runs/${encodeURIComponent(runId)}/pi`);
|
|
381
|
+
return payload && typeof payload === "object" && !Array.isArray(payload) ? payload : {};
|
|
382
|
+
}
|
|
383
|
+
async function getRunPiMessagesViaServer(context, runId) {
|
|
384
|
+
const payload = await requestServerJson(context, `/api/runs/${encodeURIComponent(runId)}/pi/messages`);
|
|
385
|
+
return payload && typeof payload === "object" && !Array.isArray(payload) ? payload : { messages: [] };
|
|
386
|
+
}
|
|
387
|
+
async function getRunPiStatusViaServer(context, runId) {
|
|
388
|
+
const payload = await requestServerJson(context, `/api/runs/${encodeURIComponent(runId)}/pi/status`);
|
|
389
|
+
return payload && typeof payload === "object" && !Array.isArray(payload) ? payload : {};
|
|
390
|
+
}
|
|
391
|
+
async function getRunPiCommandsViaServer(context, runId) {
|
|
392
|
+
const payload = await requestServerJson(context, `/api/runs/${encodeURIComponent(runId)}/pi/commands`);
|
|
393
|
+
return payload && typeof payload === "object" && !Array.isArray(payload) ? payload : { commands: [] };
|
|
394
|
+
}
|
|
395
|
+
async function sendRunPiPromptViaServer(context, runId, text, streamingBehavior) {
|
|
396
|
+
const payload = await requestServerJson(context, `/api/runs/${encodeURIComponent(runId)}/pi/prompt`, {
|
|
397
|
+
method: "POST",
|
|
398
|
+
headers: { "content-type": "application/json" },
|
|
399
|
+
body: JSON.stringify({ text, streamingBehavior })
|
|
400
|
+
});
|
|
401
|
+
return payload && typeof payload === "object" && !Array.isArray(payload) ? payload : { accepted: true };
|
|
402
|
+
}
|
|
403
|
+
async function sendRunPiShellViaServer(context, runId, text) {
|
|
404
|
+
const payload = await requestServerJson(context, `/api/runs/${encodeURIComponent(runId)}/pi/shell`, {
|
|
405
|
+
method: "POST",
|
|
406
|
+
headers: { "content-type": "application/json" },
|
|
407
|
+
body: JSON.stringify({ text })
|
|
408
|
+
});
|
|
409
|
+
return payload && typeof payload === "object" && !Array.isArray(payload) ? payload : { accepted: true };
|
|
410
|
+
}
|
|
411
|
+
async function runRunPiCommandViaServer(context, runId, text) {
|
|
412
|
+
const payload = await requestServerJson(context, `/api/runs/${encodeURIComponent(runId)}/pi/commands/run`, {
|
|
413
|
+
method: "POST",
|
|
414
|
+
headers: { "content-type": "application/json" },
|
|
415
|
+
body: JSON.stringify({ text })
|
|
416
|
+
});
|
|
417
|
+
return payload && typeof payload === "object" && !Array.isArray(payload) ? payload : { type: "done" };
|
|
418
|
+
}
|
|
419
|
+
async function respondRunPiCommandViaServer(context, runId, requestId, value) {
|
|
420
|
+
const payload = await requestServerJson(context, `/api/runs/${encodeURIComponent(runId)}/pi/commands/respond`, {
|
|
421
|
+
method: "POST",
|
|
422
|
+
headers: { "content-type": "application/json" },
|
|
423
|
+
body: JSON.stringify({ requestId, value })
|
|
424
|
+
});
|
|
425
|
+
return payload && typeof payload === "object" && !Array.isArray(payload) ? payload : { type: "done" };
|
|
426
|
+
}
|
|
427
|
+
async function respondRunPiExtensionUiViaServer(context, runId, requestId, valueOrCancel) {
|
|
428
|
+
const payload = await requestServerJson(context, `/api/runs/${encodeURIComponent(runId)}/pi/extension-ui/respond`, {
|
|
429
|
+
method: "POST",
|
|
430
|
+
headers: { "content-type": "application/json" },
|
|
431
|
+
body: JSON.stringify({ requestId, ...valueOrCancel })
|
|
432
|
+
});
|
|
433
|
+
return payload && typeof payload === "object" && !Array.isArray(payload) ? payload : { accepted: true };
|
|
434
|
+
}
|
|
435
|
+
async function abortRunPiViaServer(context, runId) {
|
|
436
|
+
const payload = await requestServerJson(context, `/api/runs/${encodeURIComponent(runId)}/pi/abort`, { method: "POST" });
|
|
437
|
+
return payload && typeof payload === "object" && !Array.isArray(payload) ? payload : { aborted: true };
|
|
438
|
+
}
|
|
439
|
+
async function buildRunPiEventsWebSocketUrl(context, runId) {
|
|
440
|
+
const server = await ensureServerForCli(context.projectRoot);
|
|
441
|
+
const url = new URL(`${server.baseUrl.replace(/\/+$/, "")}/api/runs/${encodeURIComponent(runId)}/pi/events`);
|
|
442
|
+
url.protocol = url.protocol === "https:" ? "wss:" : "ws:";
|
|
443
|
+
if (server.authToken)
|
|
444
|
+
url.searchParams.set("token", server.authToken);
|
|
445
|
+
return url.toString();
|
|
446
|
+
}
|
|
334
447
|
async function submitTaskRunViaServer(context, input) {
|
|
335
448
|
const isTaskRun = Boolean(input.taskId);
|
|
336
449
|
const endpoint = isTaskRun ? "/api/runs/task" : "/api/runs/adhoc";
|
|
@@ -363,20 +476,37 @@ async function submitTaskRunViaServer(context, input) {
|
|
|
363
476
|
return { runId };
|
|
364
477
|
}
|
|
365
478
|
export {
|
|
479
|
+
updateWorkspaceTaskViaServer,
|
|
366
480
|
switchServerProjectRootViaServer,
|
|
367
481
|
submitTaskRunViaServer,
|
|
368
482
|
stopRunViaServer,
|
|
369
483
|
steerRunViaServer,
|
|
370
484
|
setGitHubBearerTokenForCurrentProcess,
|
|
485
|
+
sendRunPiShellViaServer,
|
|
486
|
+
sendRunPiPromptViaServer,
|
|
371
487
|
selectNextWorkspaceTaskViaServer,
|
|
488
|
+
runRunPiCommandViaServer,
|
|
489
|
+
respondRunPiExtensionUiViaServer,
|
|
490
|
+
respondRunPiCommandViaServer,
|
|
372
491
|
requestServerJson,
|
|
373
492
|
registerProjectViaServer,
|
|
374
493
|
prepareRemoteCheckoutViaServer,
|
|
375
494
|
postGitHubTokenViaServer,
|
|
376
495
|
listWorkspaceTasksViaServer,
|
|
496
|
+
listRunsViaServer,
|
|
497
|
+
listGitHubProjectsViaServer,
|
|
377
498
|
getWorkspaceTaskViaServer,
|
|
499
|
+
getRunTimelineViaServer,
|
|
500
|
+
getRunPiStatusViaServer,
|
|
501
|
+
getRunPiSessionViaServer,
|
|
502
|
+
getRunPiMessagesViaServer,
|
|
503
|
+
getRunPiCommandsViaServer,
|
|
378
504
|
getRunLogsViaServer,
|
|
379
505
|
getRunDetailsViaServer,
|
|
506
|
+
getGitHubProjectStatusFieldViaServer,
|
|
380
507
|
getGitHubAuthStatusViaServer,
|
|
381
|
-
|
|
508
|
+
ensureTaskLabelsViaServer,
|
|
509
|
+
ensureServerForCli,
|
|
510
|
+
buildRunPiEventsWebSocketUrl,
|
|
511
|
+
abortRunPiViaServer
|
|
382
512
|
};
|
|
@@ -4,7 +4,6 @@ import { mkdir, readdir, readFile, writeFile } from "fs/promises";
|
|
|
4
4
|
import { dirname as dirname2, resolve as resolve3, relative, sep } from "path";
|
|
5
5
|
|
|
6
6
|
// packages/cli/src/commands/_server-client.ts
|
|
7
|
-
import { spawnSync } from "child_process";
|
|
8
7
|
import { existsSync as existsSync2, readFileSync as readFileSync2 } from "fs";
|
|
9
8
|
import { resolve as resolve2 } from "path";
|
|
10
9
|
|
|
@@ -12,8 +11,6 @@ import { resolve as resolve2 } from "path";
|
|
|
12
11
|
import { EventBus } from "@rig/runtime/control-plane/runtime/events";
|
|
13
12
|
import { CliError } from "@rig/runtime/control-plane/errors";
|
|
14
13
|
import { evaluate, loadPolicy, resolveAction } from "@rig/runtime/control-plane/runtime/guard";
|
|
15
|
-
import { PluginManager } from "@rig/runtime/control-plane/runtime/plugins";
|
|
16
|
-
import { loadRuntimeContextFromEnv } from "@rig/runtime/control-plane/runtime/context";
|
|
17
14
|
import { buildBinary } from "@rig/runtime/control-plane/runtime/isolation";
|
|
18
15
|
import { CliError as CliError2 } from "@rig/runtime/control-plane/errors";
|
|
19
16
|
|
|
@@ -97,13 +94,13 @@ function resolveSelectedConnection(projectRoot, options = {}) {
|
|
|
97
94
|
const global = readGlobalConnections(options);
|
|
98
95
|
const connection = global.connections[repo.selected];
|
|
99
96
|
if (!connection) {
|
|
100
|
-
throw new CliError2(`Selected Rig
|
|
97
|
+
throw new CliError2(`Selected Rig server "${repo.selected}" was not found. Run \`rig server list\` or \`rig server use local\`.`, 1);
|
|
101
98
|
}
|
|
102
99
|
return { alias: repo.selected, connection };
|
|
103
100
|
}
|
|
104
101
|
|
|
105
102
|
// packages/cli/src/commands/_server-client.ts
|
|
106
|
-
var
|
|
103
|
+
var scopedGitHubBearerTokens = new Map;
|
|
107
104
|
function cleanToken(value) {
|
|
108
105
|
const trimmed = value?.trim();
|
|
109
106
|
return trimmed ? trimmed : null;
|
|
@@ -120,25 +117,13 @@ function readPrivateRemoteSessionToken(projectRoot) {
|
|
|
120
117
|
}
|
|
121
118
|
}
|
|
122
119
|
function readGitHubBearerTokenForRemote(projectRoot) {
|
|
123
|
-
|
|
124
|
-
|
|
120
|
+
const scopedKey = resolve2(projectRoot);
|
|
121
|
+
if (scopedGitHubBearerTokens.has(scopedKey))
|
|
122
|
+
return scopedGitHubBearerTokens.get(scopedKey) ?? null;
|
|
125
123
|
const privateSession = readPrivateRemoteSessionToken(projectRoot);
|
|
126
|
-
if (privateSession)
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
}
|
|
130
|
-
const envToken = cleanToken(process.env.RIG_GITHUB_TOKEN) ?? cleanToken(process.env.GITHUB_TOKEN) ?? cleanToken(process.env.GH_TOKEN);
|
|
131
|
-
if (envToken) {
|
|
132
|
-
cachedGitHubBearerToken = envToken;
|
|
133
|
-
return cachedGitHubBearerToken;
|
|
134
|
-
}
|
|
135
|
-
const result = spawnSync("gh", ["auth", "token"], {
|
|
136
|
-
encoding: "utf8",
|
|
137
|
-
timeout: 5000,
|
|
138
|
-
stdio: ["ignore", "pipe", "ignore"]
|
|
139
|
-
});
|
|
140
|
-
cachedGitHubBearerToken = result.status === 0 ? cleanToken(result.stdout) : null;
|
|
141
|
-
return cachedGitHubBearerToken;
|
|
124
|
+
if (privateSession)
|
|
125
|
+
return privateSession;
|
|
126
|
+
return cleanToken(process.env.RIG_SERVER_AUTH_TOKEN) ?? cleanToken(process.env.RIG_REMOTE_AUTH_TOKEN);
|
|
142
127
|
}
|
|
143
128
|
async function ensureServerForCli(projectRoot) {
|
|
144
129
|
try {
|