@h-rig/cli 0.0.6-alpha.2 → 0.0.6-alpha.21
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 +1288 -315
- package/dist/src/commands/_authority-runs.js +1 -0
- package/dist/src/commands/_cli-format.js +106 -0
- package/dist/src/commands/_doctor-checks.js +10 -22
- package/dist/src/commands/_operator-surface.js +204 -0
- package/dist/src/commands/_operator-view.js +207 -51
- package/dist/src/commands/_pi-install.js +4 -3
- package/dist/src/commands/_pi-session.js +253 -0
- package/dist/src/commands/_preflight.js +33 -28
- package/dist/src/commands/_server-client.js +80 -27
- package/dist/src/commands/_snapshot-upload.js +7 -20
- package/dist/src/commands/_task-picker.js +44 -16
- package/dist/src/commands/agent.js +2 -0
- package/dist/src/commands/doctor.js +10 -22
- package/dist/src/commands/github.js +9 -22
- package/dist/src/commands/init.js +295 -66
- package/dist/src/commands/queue.js +1 -0
- package/dist/src/commands/run.js +456 -95
- package/dist/src/commands/server.js +9 -22
- package/dist/src/commands/setup.js +10 -22
- package/dist/src/commands/task-run-driver.js +539 -64
- package/dist/src/commands/task.js +502 -130
- package/dist/src/commands.js +1276 -306
- package/dist/src/index.js +1288 -315
- package/dist/src/launcher.js +5 -3
- package/dist/src/runner.js +3 -2
- package/package.json +5 -4
|
@@ -94,11 +94,10 @@ function resolveSelectedConnection(projectRoot, options = {}) {
|
|
|
94
94
|
}
|
|
95
95
|
|
|
96
96
|
// packages/cli/src/commands/_server-client.ts
|
|
97
|
-
import { spawnSync } from "child_process";
|
|
98
97
|
import { existsSync as existsSync2, readFileSync as readFileSync2 } from "fs";
|
|
99
98
|
import { resolve as resolve2 } from "path";
|
|
100
99
|
import { ensureLocalRigServerConnection } from "@rig/runtime/local-server";
|
|
101
|
-
var
|
|
100
|
+
var scopedGitHubBearerTokens = new Map;
|
|
102
101
|
function cleanToken(value) {
|
|
103
102
|
const trimmed = value?.trim();
|
|
104
103
|
return trimmed ? trimmed : null;
|
|
@@ -115,25 +114,13 @@ function readPrivateRemoteSessionToken(projectRoot) {
|
|
|
115
114
|
}
|
|
116
115
|
}
|
|
117
116
|
function readGitHubBearerTokenForRemote(projectRoot) {
|
|
118
|
-
|
|
119
|
-
|
|
117
|
+
const scopedKey = resolve2(projectRoot);
|
|
118
|
+
if (scopedGitHubBearerTokens.has(scopedKey))
|
|
119
|
+
return scopedGitHubBearerTokens.get(scopedKey) ?? null;
|
|
120
120
|
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;
|
|
121
|
+
if (privateSession)
|
|
122
|
+
return privateSession;
|
|
123
|
+
return cleanToken(process.env.RIG_SERVER_AUTH_TOKEN) ?? cleanToken(process.env.RIG_REMOTE_AUTH_TOKEN);
|
|
137
124
|
}
|
|
138
125
|
async function ensureServerForCli(projectRoot) {
|
|
139
126
|
try {
|
|
@@ -206,7 +193,8 @@ async function requestServerJson(context, pathname, init = {}) {
|
|
|
206
193
|
import { existsSync as existsSync3, readFileSync as readFileSync3, rmSync } from "fs";
|
|
207
194
|
import { homedir as homedir2 } from "os";
|
|
208
195
|
import { resolve as resolve3 } from "path";
|
|
209
|
-
var PI_RIG_PACKAGE_NAME = "@rig/pi-rig";
|
|
196
|
+
var PI_RIG_PACKAGE_NAME = "@h-rig/pi-rig";
|
|
197
|
+
var LEGACY_PI_RIG_PACKAGE_NAME = "@rig/pi-rig";
|
|
210
198
|
async function defaultCommandRunner(command, options = {}) {
|
|
211
199
|
const proc = Bun.spawn(command, { cwd: options.cwd, stdout: "pipe", stderr: "pipe" });
|
|
212
200
|
const [stdout, stderr, exitCode] = await Promise.all([
|
|
@@ -225,7 +213,7 @@ function resolvePiHomeDir(inputHomeDir) {
|
|
|
225
213
|
function piListContainsPiRig(output) {
|
|
226
214
|
return output.split(/\r?\n/).some((line) => {
|
|
227
215
|
const normalized = line.trim();
|
|
228
|
-
return normalized.includes(PI_RIG_PACKAGE_NAME) || /(?:^|[\\/])packages[\\/]pi-rig(?:$|\s)/.test(normalized);
|
|
216
|
+
return normalized.includes(PI_RIG_PACKAGE_NAME) || normalized.includes(LEGACY_PI_RIG_PACKAGE_NAME) || /(?:^|[\\/])packages[\\/]pi-rig(?:$|\s)/.test(normalized);
|
|
229
217
|
});
|
|
230
218
|
}
|
|
231
219
|
async function safeRun(runner, command, options) {
|
|
@@ -329,6 +317,9 @@ function permissionAllowsPr(payload) {
|
|
|
329
317
|
}
|
|
330
318
|
return null;
|
|
331
319
|
}
|
|
320
|
+
function isNotFoundError(error) {
|
|
321
|
+
return /\b(404|not found)\b/i.test(message(error));
|
|
322
|
+
}
|
|
332
323
|
function projectCheckoutReady(payload) {
|
|
333
324
|
if (!payload || typeof payload !== "object" || Array.isArray(payload))
|
|
334
325
|
return null;
|
|
@@ -361,19 +352,33 @@ async function runFastTaskRunPreflight(context, options = {}) {
|
|
|
361
352
|
const checks = [];
|
|
362
353
|
const request = options.requestJson ?? ((pathname, init) => requestServerJson(context, pathname, init));
|
|
363
354
|
const taskId = options.taskId?.trim() || null;
|
|
355
|
+
const requiresCurrentRunApi = Boolean(taskId);
|
|
356
|
+
const selectedServer = options.requestJson ? null : await ensureServerForCli(context.projectRoot).catch(() => null);
|
|
357
|
+
const allowLocalLegacyTaskRunCompatibility = selectedServer?.connectionKind === "local";
|
|
358
|
+
let legacyServerCompatibility = false;
|
|
364
359
|
try {
|
|
365
360
|
await request("/api/server/status");
|
|
366
361
|
checks.push(preflightCheck("server", "Rig server reachable", "pass"));
|
|
367
362
|
} catch (error) {
|
|
368
|
-
|
|
363
|
+
if (isNotFoundError(error)) {
|
|
364
|
+
try {
|
|
365
|
+
await request("/health");
|
|
366
|
+
legacyServerCompatibility = !requiresCurrentRunApi || allowLocalLegacyTaskRunCompatibility;
|
|
367
|
+
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"));
|
|
368
|
+
} catch (healthError) {
|
|
369
|
+
checks.push(preflightCheck("server", "Rig server reachable", "fail", message(healthError), "Start or select a reachable Rig server."));
|
|
370
|
+
}
|
|
371
|
+
} else {
|
|
372
|
+
checks.push(preflightCheck("server", "Rig server reachable", "fail", message(error), "Start or select a reachable Rig server."));
|
|
373
|
+
}
|
|
369
374
|
}
|
|
370
375
|
const repo = readRepoConnection(context.projectRoot);
|
|
371
|
-
checks.push(repo ? preflightCheck("project-link", "project linked to Rig connection", 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 connection", "fail", "missing .rig/state/connection.json", "Run `rig init` or `rig connect use <alias|local>`."));
|
|
376
|
+
checks.push(repo ? preflightCheck("project-link", "project linked to Rig connection", 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 connection", legacyServerCompatibility ? "warn" : "fail", "missing .rig/state/connection.json", "Run `rig init` or `rig connect use <alias|local>`."));
|
|
372
377
|
try {
|
|
373
378
|
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>`."));
|
|
379
|
+
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
380
|
} catch (error) {
|
|
376
|
-
checks.push(preflightCheck("github-auth", "GitHub auth valid", "fail", message(error), "Fix GitHub auth on the selected Rig server."));
|
|
381
|
+
checks.push(preflightCheck("github-auth", "GitHub auth valid", legacyServerCompatibility ? "warn" : "fail", message(error), "Fix GitHub auth on the selected Rig server."));
|
|
377
382
|
}
|
|
378
383
|
try {
|
|
379
384
|
const projection = await request("/api/workspace/task-projection");
|
|
@@ -401,9 +406,9 @@ async function runFastTaskRunPreflight(context, options = {}) {
|
|
|
401
406
|
try {
|
|
402
407
|
const tasks = await request(`/api/workspace/tasks?limit=200&refresh=1`);
|
|
403
408
|
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."));
|
|
409
|
+
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
410
|
} catch (error) {
|
|
406
|
-
checks.push(preflightCheck("issue", "task/issue accessible", "fail", message(error), "Fix the task source before launching a run."));
|
|
411
|
+
checks.push(preflightCheck("issue", "task/issue accessible", legacyServerCompatibility ? "warn" : "fail", message(error), "Fix the task source before launching a run."));
|
|
407
412
|
}
|
|
408
413
|
try {
|
|
409
414
|
const runs = await request("/api/runs?limit=200");
|
|
@@ -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
|
|
|
@@ -99,13 +98,14 @@ function resolveSelectedConnection(projectRoot, options = {}) {
|
|
|
99
98
|
}
|
|
100
99
|
|
|
101
100
|
// packages/cli/src/commands/_server-client.ts
|
|
102
|
-
var
|
|
101
|
+
var scopedGitHubBearerTokens = new Map;
|
|
103
102
|
function cleanToken(value) {
|
|
104
103
|
const trimmed = value?.trim();
|
|
105
104
|
return trimmed ? trimmed : null;
|
|
106
105
|
}
|
|
107
|
-
function setGitHubBearerTokenForCurrentProcess(token) {
|
|
108
|
-
|
|
106
|
+
function setGitHubBearerTokenForCurrentProcess(token, projectRoot) {
|
|
107
|
+
const scopedKey = resolve2(projectRoot ?? process.cwd());
|
|
108
|
+
scopedGitHubBearerTokens.set(scopedKey, cleanToken(token ?? undefined));
|
|
109
109
|
}
|
|
110
110
|
function readPrivateRemoteSessionToken(projectRoot) {
|
|
111
111
|
const path = resolve2(projectRoot, ".rig", "state", "github-auth.json");
|
|
@@ -119,25 +119,13 @@ function readPrivateRemoteSessionToken(projectRoot) {
|
|
|
119
119
|
}
|
|
120
120
|
}
|
|
121
121
|
function readGitHubBearerTokenForRemote(projectRoot) {
|
|
122
|
-
|
|
123
|
-
|
|
122
|
+
const scopedKey = resolve2(projectRoot);
|
|
123
|
+
if (scopedGitHubBearerTokens.has(scopedKey))
|
|
124
|
+
return scopedGitHubBearerTokens.get(scopedKey) ?? null;
|
|
124
125
|
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;
|
|
126
|
+
if (privateSession)
|
|
127
|
+
return privateSession;
|
|
128
|
+
return cleanToken(process.env.RIG_SERVER_AUTH_TOKEN) ?? cleanToken(process.env.RIG_REMOTE_AUTH_TOKEN);
|
|
141
129
|
}
|
|
142
130
|
async function ensureServerForCli(projectRoot) {
|
|
143
131
|
try {
|
|
@@ -275,16 +263,36 @@ async function registerProjectViaServer(context, input) {
|
|
|
275
263
|
function sleep(ms) {
|
|
276
264
|
return new Promise((resolve3) => setTimeout(resolve3, ms));
|
|
277
265
|
}
|
|
266
|
+
function isRetryableProjectRootSwitchError(error) {
|
|
267
|
+
if (!(error instanceof Error))
|
|
268
|
+
return false;
|
|
269
|
+
const message = error.message.toLowerCase();
|
|
270
|
+
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");
|
|
271
|
+
}
|
|
278
272
|
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
273
|
const timeoutMs = options.timeoutMs ?? 30000;
|
|
285
274
|
const pollMs = options.pollMs ?? 1000;
|
|
286
275
|
const deadline = Date.now() + timeoutMs;
|
|
287
276
|
let lastError;
|
|
277
|
+
let switched = null;
|
|
278
|
+
while (Date.now() < deadline) {
|
|
279
|
+
try {
|
|
280
|
+
switched = await requestServerJson(context, "/api/server/project-root", {
|
|
281
|
+
method: "POST",
|
|
282
|
+
headers: { "content-type": "application/json" },
|
|
283
|
+
body: JSON.stringify({ projectRoot })
|
|
284
|
+
});
|
|
285
|
+
break;
|
|
286
|
+
} catch (error) {
|
|
287
|
+
lastError = error;
|
|
288
|
+
if (!isRetryableProjectRootSwitchError(error))
|
|
289
|
+
throw error;
|
|
290
|
+
await sleep(pollMs);
|
|
291
|
+
}
|
|
292
|
+
}
|
|
293
|
+
if (!switched) {
|
|
294
|
+
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);
|
|
295
|
+
}
|
|
288
296
|
while (Date.now() < deadline) {
|
|
289
297
|
try {
|
|
290
298
|
const status = await requestServerJson(context, "/api/server/status");
|
|
@@ -302,6 +310,14 @@ async function switchServerProjectRootViaServer(context, projectRoot, options =
|
|
|
302
310
|
}
|
|
303
311
|
throw new CliError2(`Rig server did not switch to ${projectRoot} before timeout (${lastError instanceof Error ? lastError.message : String(lastError ?? "no status")}).`, 1);
|
|
304
312
|
}
|
|
313
|
+
async function listRunsViaServer(context, options = {}) {
|
|
314
|
+
const url = new URL("http://rig.local/api/runs");
|
|
315
|
+
if (options.limit !== undefined)
|
|
316
|
+
url.searchParams.set("limit", String(options.limit));
|
|
317
|
+
const payload = await requestServerJson(context, `${url.pathname}${url.search}`);
|
|
318
|
+
const runs = Array.isArray(payload) ? payload : payload && typeof payload === "object" && !Array.isArray(payload) && Array.isArray(payload.runs) ? payload.runs : [];
|
|
319
|
+
return runs.filter((entry) => Boolean(entry && typeof entry === "object" && !Array.isArray(entry)));
|
|
320
|
+
}
|
|
305
321
|
async function getRunDetailsViaServer(context, runId) {
|
|
306
322
|
const payload = await requestServerJson(context, `/api/runs/${encodeURIComponent(runId)}`);
|
|
307
323
|
return payload && typeof payload === "object" && !Array.isArray(payload) ? payload : {};
|
|
@@ -315,6 +331,37 @@ async function getRunLogsViaServer(context, runId, options = {}) {
|
|
|
315
331
|
const payload = await requestServerJson(context, `${url.pathname}${url.search}`);
|
|
316
332
|
return payload && typeof payload === "object" && !Array.isArray(payload) ? payload : { entries: [] };
|
|
317
333
|
}
|
|
334
|
+
async function getRunTimelineViaServer(context, runId, options = {}) {
|
|
335
|
+
const url = new URL(`http://rig.local/api/runs/${encodeURIComponent(runId)}/timeline`);
|
|
336
|
+
if (options.limit !== undefined)
|
|
337
|
+
url.searchParams.set("limit", String(options.limit));
|
|
338
|
+
if (options.cursor)
|
|
339
|
+
url.searchParams.set("cursor", options.cursor);
|
|
340
|
+
const payload = await requestServerJson(context, `${url.pathname}${url.search}`);
|
|
341
|
+
return payload && typeof payload === "object" && !Array.isArray(payload) ? payload : { entries: [] };
|
|
342
|
+
}
|
|
343
|
+
async function ensureTaskLabelsViaServer(context) {
|
|
344
|
+
const payload = await requestServerJson(context, "/api/workspace/task-labels", { method: "POST" });
|
|
345
|
+
return payload && typeof payload === "object" && !Array.isArray(payload) ? payload : {};
|
|
346
|
+
}
|
|
347
|
+
async function listGitHubProjectsViaServer(context, owner) {
|
|
348
|
+
const url = new URL("http://rig.local/api/github/projects");
|
|
349
|
+
url.searchParams.set("owner", owner);
|
|
350
|
+
const payload = await requestServerJson(context, `${url.pathname}${url.search}`);
|
|
351
|
+
return payload && typeof payload === "object" && !Array.isArray(payload) ? payload : { projects: [] };
|
|
352
|
+
}
|
|
353
|
+
async function getGitHubProjectStatusFieldViaServer(context, projectId) {
|
|
354
|
+
const payload = await requestServerJson(context, `/api/github/projects/${encodeURIComponent(projectId)}/status-field`);
|
|
355
|
+
return payload && typeof payload === "object" && !Array.isArray(payload) ? payload : {};
|
|
356
|
+
}
|
|
357
|
+
async function updateWorkspaceTaskViaServer(context, input) {
|
|
358
|
+
const payload = await requestServerJson(context, "/api/tasks/update", {
|
|
359
|
+
method: "POST",
|
|
360
|
+
headers: { "content-type": "application/json" },
|
|
361
|
+
body: JSON.stringify(input)
|
|
362
|
+
});
|
|
363
|
+
return payload && typeof payload === "object" && !Array.isArray(payload) ? payload : { ok: true };
|
|
364
|
+
}
|
|
318
365
|
async function stopRunViaServer(context, runId) {
|
|
319
366
|
const payload = await requestServerJson(context, "/api/runs/stop", {
|
|
320
367
|
method: "POST",
|
|
@@ -363,6 +410,7 @@ async function submitTaskRunViaServer(context, input) {
|
|
|
363
410
|
return { runId };
|
|
364
411
|
}
|
|
365
412
|
export {
|
|
413
|
+
updateWorkspaceTaskViaServer,
|
|
366
414
|
switchServerProjectRootViaServer,
|
|
367
415
|
submitTaskRunViaServer,
|
|
368
416
|
stopRunViaServer,
|
|
@@ -374,9 +422,14 @@ export {
|
|
|
374
422
|
prepareRemoteCheckoutViaServer,
|
|
375
423
|
postGitHubTokenViaServer,
|
|
376
424
|
listWorkspaceTasksViaServer,
|
|
425
|
+
listRunsViaServer,
|
|
426
|
+
listGitHubProjectsViaServer,
|
|
377
427
|
getWorkspaceTaskViaServer,
|
|
428
|
+
getRunTimelineViaServer,
|
|
378
429
|
getRunLogsViaServer,
|
|
379
430
|
getRunDetailsViaServer,
|
|
431
|
+
getGitHubProjectStatusFieldViaServer,
|
|
380
432
|
getGitHubAuthStatusViaServer,
|
|
433
|
+
ensureTaskLabelsViaServer,
|
|
381
434
|
ensureServerForCli
|
|
382
435
|
};
|
|
@@ -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
|
|
|
@@ -103,7 +102,7 @@ function resolveSelectedConnection(projectRoot, options = {}) {
|
|
|
103
102
|
}
|
|
104
103
|
|
|
105
104
|
// packages/cli/src/commands/_server-client.ts
|
|
106
|
-
var
|
|
105
|
+
var scopedGitHubBearerTokens = new Map;
|
|
107
106
|
function cleanToken(value) {
|
|
108
107
|
const trimmed = value?.trim();
|
|
109
108
|
return trimmed ? trimmed : null;
|
|
@@ -120,25 +119,13 @@ function readPrivateRemoteSessionToken(projectRoot) {
|
|
|
120
119
|
}
|
|
121
120
|
}
|
|
122
121
|
function readGitHubBearerTokenForRemote(projectRoot) {
|
|
123
|
-
|
|
124
|
-
|
|
122
|
+
const scopedKey = resolve2(projectRoot);
|
|
123
|
+
if (scopedGitHubBearerTokens.has(scopedKey))
|
|
124
|
+
return scopedGitHubBearerTokens.get(scopedKey) ?? null;
|
|
125
125
|
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;
|
|
126
|
+
if (privateSession)
|
|
127
|
+
return privateSession;
|
|
128
|
+
return cleanToken(process.env.RIG_SERVER_AUTH_TOKEN) ?? cleanToken(process.env.RIG_REMOTE_AUTH_TOKEN);
|
|
142
129
|
}
|
|
143
130
|
async function ensureServerForCli(projectRoot) {
|
|
144
131
|
try {
|
|
@@ -1,6 +1,9 @@
|
|
|
1
1
|
// @bun
|
|
2
2
|
// packages/cli/src/commands/_task-picker.ts
|
|
3
|
-
import {
|
|
3
|
+
import { cancel, isCancel, select } from "@clack/prompts";
|
|
4
|
+
|
|
5
|
+
// packages/cli/src/commands/_operator-surface.ts
|
|
6
|
+
import { createInterface as createPromptInterface } from "readline/promises";
|
|
4
7
|
function taskId(task) {
|
|
5
8
|
return typeof task.id === "string" && task.id.trim() ? task.id : "<unknown>";
|
|
6
9
|
}
|
|
@@ -13,6 +16,19 @@ function taskStatus(task) {
|
|
|
13
16
|
function renderTaskPickerRows(tasks) {
|
|
14
17
|
return tasks.map((task, index) => `${index + 1}. ${taskId(task)} \xB7 ${taskStatus(task)} \xB7 ${taskTitle(task)}`);
|
|
15
18
|
}
|
|
19
|
+
async function promptForTaskSelection(question) {
|
|
20
|
+
const rl = createPromptInterface({ input: process.stdin, output: process.stdout });
|
|
21
|
+
try {
|
|
22
|
+
return await rl.question(question);
|
|
23
|
+
} finally {
|
|
24
|
+
rl.close();
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
// packages/cli/src/commands/_task-picker.ts
|
|
29
|
+
function taskId2(task) {
|
|
30
|
+
return typeof task.id === "string" && task.id.trim() ? task.id : "<unknown>";
|
|
31
|
+
}
|
|
16
32
|
async function selectTaskWithTextPicker(tasks, io = {}) {
|
|
17
33
|
if (tasks.length === 0)
|
|
18
34
|
return null;
|
|
@@ -22,25 +38,37 @@ async function selectTaskWithTextPicker(tasks, io = {}) {
|
|
|
22
38
|
if (!isTty) {
|
|
23
39
|
throw new Error("task run requires an interactive terminal to pick a task; pass --task <id>, --next, or --detach with a task id.");
|
|
24
40
|
}
|
|
25
|
-
|
|
26
|
-
const
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
41
|
+
if (io.prompt || io.renderer) {
|
|
42
|
+
const prompt = io.prompt ?? promptForTaskSelection;
|
|
43
|
+
const renderer = io.renderer ?? { writeLine: (line) => process.stdout.write(`${line}
|
|
44
|
+
`) };
|
|
45
|
+
renderer.writeLine("Select Rig task:");
|
|
46
|
+
for (const row of renderTaskPickerRows(tasks))
|
|
47
|
+
renderer.writeLine(` ${row}`);
|
|
48
|
+
const answer2 = (await prompt(`Task [1-${tasks.length}] or id: `)).trim();
|
|
49
|
+
if (!answer2)
|
|
50
|
+
return null;
|
|
51
|
+
if (/^\d+$/.test(answer2)) {
|
|
52
|
+
const index2 = Number.parseInt(answer2, 10) - 1;
|
|
53
|
+
return tasks[index2] ?? null;
|
|
31
54
|
}
|
|
55
|
+
return tasks.find((task) => taskId2(task) === answer2) ?? null;
|
|
56
|
+
}
|
|
57
|
+
const options = tasks.map((task, index2) => ({
|
|
58
|
+
value: `${index2}`,
|
|
59
|
+
label: `${taskId2(task)} \xB7 ${typeof task.title === "string" && task.title.trim() ? task.title.trim() : "Untitled task"}`,
|
|
60
|
+
hint: typeof task.status === "string" && task.status.trim() ? task.status.trim() : undefined
|
|
61
|
+
}));
|
|
62
|
+
const answer = await select({
|
|
63
|
+
message: "Select Rig task",
|
|
64
|
+
options
|
|
32
65
|
});
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
console.log(` ${row}`);
|
|
36
|
-
const answer = (await prompt(`Task [1-${tasks.length}] or id: `)).trim();
|
|
37
|
-
if (!answer)
|
|
66
|
+
if (isCancel(answer)) {
|
|
67
|
+
cancel("No task selected.");
|
|
38
68
|
return null;
|
|
39
|
-
if (/^\d+$/.test(answer)) {
|
|
40
|
-
const index = Number.parseInt(answer, 10) - 1;
|
|
41
|
-
return tasks[index] ?? null;
|
|
42
69
|
}
|
|
43
|
-
|
|
70
|
+
const index = Number.parseInt(String(answer), 10);
|
|
71
|
+
return Number.isFinite(index) ? tasks[index] ?? null : null;
|
|
44
72
|
}
|
|
45
73
|
export {
|
|
46
74
|
selectTaskWithTextPicker,
|
|
@@ -126,6 +126,7 @@ function upsertAgentAuthorityRun(projectRoot, input) {
|
|
|
126
126
|
const runtimeAdapter = normalizeRuntimeAdapter(input.runtimeAdapter ?? existing?.runtimeAdapter ?? "claude-code");
|
|
127
127
|
const title = resolveTaskTitleForAuthorityRun(projectRoot, input.taskId) ?? input.taskId;
|
|
128
128
|
const next = {
|
|
129
|
+
...existing ?? {},
|
|
129
130
|
runId: input.runId,
|
|
130
131
|
projectRoot,
|
|
131
132
|
workspaceId: existing?.workspaceId ?? RIG_WORKSPACE_ID,
|
|
@@ -220,6 +221,7 @@ import { ensureProjectMainFreshBeforeRun } from "@rig/runtime/control-plane/proj
|
|
|
220
221
|
|
|
221
222
|
// packages/cli/src/commands/_server-client.ts
|
|
222
223
|
import { ensureLocalRigServerConnection } from "@rig/runtime/local-server";
|
|
224
|
+
var scopedGitHubBearerTokens = new Map;
|
|
223
225
|
|
|
224
226
|
// packages/cli/src/commands/_preflight.ts
|
|
225
227
|
async function runProjectMainSyncPreflight(context, options) {
|
|
@@ -104,11 +104,10 @@ function resolveSelectedConnection(projectRoot, options = {}) {
|
|
|
104
104
|
}
|
|
105
105
|
|
|
106
106
|
// packages/cli/src/commands/_server-client.ts
|
|
107
|
-
import { spawnSync } from "child_process";
|
|
108
107
|
import { existsSync as existsSync2, readFileSync as readFileSync2 } from "fs";
|
|
109
108
|
import { resolve as resolve2 } from "path";
|
|
110
109
|
import { ensureLocalRigServerConnection } from "@rig/runtime/local-server";
|
|
111
|
-
var
|
|
110
|
+
var scopedGitHubBearerTokens = new Map;
|
|
112
111
|
function cleanToken(value) {
|
|
113
112
|
const trimmed = value?.trim();
|
|
114
113
|
return trimmed ? trimmed : null;
|
|
@@ -125,25 +124,13 @@ function readPrivateRemoteSessionToken(projectRoot) {
|
|
|
125
124
|
}
|
|
126
125
|
}
|
|
127
126
|
function readGitHubBearerTokenForRemote(projectRoot) {
|
|
128
|
-
|
|
129
|
-
|
|
127
|
+
const scopedKey = resolve2(projectRoot);
|
|
128
|
+
if (scopedGitHubBearerTokens.has(scopedKey))
|
|
129
|
+
return scopedGitHubBearerTokens.get(scopedKey) ?? null;
|
|
130
130
|
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;
|
|
131
|
+
if (privateSession)
|
|
132
|
+
return privateSession;
|
|
133
|
+
return cleanToken(process.env.RIG_SERVER_AUTH_TOKEN) ?? cleanToken(process.env.RIG_REMOTE_AUTH_TOKEN);
|
|
147
134
|
}
|
|
148
135
|
async function ensureServerForCli(projectRoot) {
|
|
149
136
|
try {
|
|
@@ -226,7 +213,8 @@ async function loadRigConfigOrNull(projectRoot) {
|
|
|
226
213
|
import { existsSync as existsSync3, readFileSync as readFileSync3, rmSync } from "fs";
|
|
227
214
|
import { homedir as homedir2 } from "os";
|
|
228
215
|
import { resolve as resolve3 } from "path";
|
|
229
|
-
var PI_RIG_PACKAGE_NAME = "@rig/pi-rig";
|
|
216
|
+
var PI_RIG_PACKAGE_NAME = "@h-rig/pi-rig";
|
|
217
|
+
var LEGACY_PI_RIG_PACKAGE_NAME = "@rig/pi-rig";
|
|
230
218
|
async function defaultCommandRunner(command, options = {}) {
|
|
231
219
|
const proc = Bun.spawn(command, { cwd: options.cwd, stdout: "pipe", stderr: "pipe" });
|
|
232
220
|
const [stdout, stderr, exitCode] = await Promise.all([
|
|
@@ -245,7 +233,7 @@ function resolvePiHomeDir(inputHomeDir) {
|
|
|
245
233
|
function piListContainsPiRig(output) {
|
|
246
234
|
return output.split(/\r?\n/).some((line) => {
|
|
247
235
|
const normalized = line.trim();
|
|
248
|
-
return normalized.includes(PI_RIG_PACKAGE_NAME) || /(?:^|[\\/])packages[\\/]pi-rig(?:$|\s)/.test(normalized);
|
|
236
|
+
return normalized.includes(PI_RIG_PACKAGE_NAME) || normalized.includes(LEGACY_PI_RIG_PACKAGE_NAME) || /(?:^|[\\/])packages[\\/]pi-rig(?:$|\s)/.test(normalized);
|
|
249
237
|
});
|
|
250
238
|
}
|
|
251
239
|
async function safeRun(runner, command, options) {
|
|
@@ -1,6 +1,6 @@
|
|
|
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";
|
|
@@ -32,7 +32,6 @@ function takeOption(args, option) {
|
|
|
32
32
|
}
|
|
33
33
|
|
|
34
34
|
// packages/cli/src/commands/_server-client.ts
|
|
35
|
-
import { spawnSync } from "child_process";
|
|
36
35
|
import { existsSync as existsSync2, readFileSync as readFileSync2 } from "fs";
|
|
37
36
|
import { resolve as resolve2 } from "path";
|
|
38
37
|
import { ensureLocalRigServerConnection } from "@rig/runtime/local-server";
|
|
@@ -120,7 +119,7 @@ function resolveSelectedConnection(projectRoot, options = {}) {
|
|
|
120
119
|
}
|
|
121
120
|
|
|
122
121
|
// packages/cli/src/commands/_server-client.ts
|
|
123
|
-
var
|
|
122
|
+
var scopedGitHubBearerTokens = new Map;
|
|
124
123
|
function cleanToken(value) {
|
|
125
124
|
const trimmed = value?.trim();
|
|
126
125
|
return trimmed ? trimmed : null;
|
|
@@ -137,25 +136,13 @@ function readPrivateRemoteSessionToken(projectRoot) {
|
|
|
137
136
|
}
|
|
138
137
|
}
|
|
139
138
|
function readGitHubBearerTokenForRemote(projectRoot) {
|
|
140
|
-
|
|
141
|
-
|
|
139
|
+
const scopedKey = resolve2(projectRoot);
|
|
140
|
+
if (scopedGitHubBearerTokens.has(scopedKey))
|
|
141
|
+
return scopedGitHubBearerTokens.get(scopedKey) ?? null;
|
|
142
142
|
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;
|
|
143
|
+
if (privateSession)
|
|
144
|
+
return privateSession;
|
|
145
|
+
return cleanToken(process.env.RIG_SERVER_AUTH_TOKEN) ?? cleanToken(process.env.RIG_REMOTE_AUTH_TOKEN);
|
|
159
146
|
}
|
|
160
147
|
async function ensureServerForCli(projectRoot) {
|
|
161
148
|
try {
|
|
@@ -244,7 +231,7 @@ function printPayload(context, payload, fallback) {
|
|
|
244
231
|
console.log(fallback);
|
|
245
232
|
}
|
|
246
233
|
function readGhToken() {
|
|
247
|
-
const result =
|
|
234
|
+
const result = spawnSync("gh", ["auth", "token"], { encoding: "utf8" });
|
|
248
235
|
if (result.status !== 0) {
|
|
249
236
|
const detail = result.stderr?.trim() || result.stdout?.trim() || "gh auth token failed";
|
|
250
237
|
throw new CliError2(`Could not import GitHub token from gh: ${detail}`, 1);
|