@h-rig/cli 0.0.6-alpha.82 → 0.0.6-alpha.83
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/build-rig-binaries.js +40 -8
- package/dist/bin/rig.js +2798 -1440
- package/dist/src/app/board.js +62 -13
- package/dist/src/app-opentui/adapters/command.d.ts +2 -0
- package/dist/src/app-opentui/adapters/command.js +329 -0
- package/dist/src/app-opentui/adapters/common.js +2 -2
- package/dist/src/app-opentui/adapters/doctor.js +63 -14
- package/dist/src/app-opentui/adapters/fleet.js +84 -20
- package/dist/src/app-opentui/adapters/inbox.js +83 -19
- package/dist/src/app-opentui/adapters/init.js +87 -23
- package/dist/src/app-opentui/adapters/pi-attach.js +96 -34
- package/dist/src/app-opentui/adapters/run-detail.js +83 -19
- package/dist/src/app-opentui/adapters/server.js +100 -23
- package/dist/src/app-opentui/adapters/tasks.d.ts +11 -0
- package/dist/src/app-opentui/adapters/tasks.js +742 -94
- package/dist/src/app-opentui/bootstrap.d.ts +1 -6
- package/dist/src/app-opentui/bootstrap.js +13769 -12368
- package/dist/src/app-opentui/command-pty-host.d.ts +62 -0
- package/dist/src/app-opentui/command-pty-host.js +248 -0
- package/dist/src/app-opentui/events.js +1 -1
- package/dist/src/app-opentui/focus-manager.d.ts +14 -0
- package/dist/src/app-opentui/focus-manager.js +24 -0
- package/dist/src/app-opentui/index.js +3802 -2882
- package/dist/src/app-opentui/intent.js +154 -48
- package/dist/src/app-opentui/keymap.d.ts +20 -0
- package/dist/src/app-opentui/keymap.js +707 -0
- package/dist/src/app-opentui/launch-routing.d.ts +16 -0
- package/dist/src/app-opentui/launch-routing.js +55 -0
- package/dist/src/app-opentui/layout.js +2 -2
- package/dist/src/app-opentui/pi-host-child.js +66 -16
- package/dist/src/app-opentui/pi-pty-host.d.ts +9 -0
- package/dist/src/app-opentui/pi-pty-host.js +9 -11
- package/dist/src/app-opentui/registry.js +3231 -2403
- package/dist/src/app-opentui/render/ascii-fleet.d.ts +15 -0
- package/dist/src/app-opentui/render/ascii-fleet.js +82 -0
- package/dist/src/app-opentui/render/graphics.d.ts +1 -1
- package/dist/src/app-opentui/render/graphics.js +131 -16
- package/dist/src/app-opentui/render/image-visual-layer-worker.js +408 -957
- package/dist/src/app-opentui/render/image-visual-layer.d.ts +18 -10
- package/dist/src/app-opentui/render/image-visual-layer.js +386 -356
- package/dist/src/app-opentui/render/panel-layout.d.ts +38 -0
- package/dist/src/app-opentui/render/panel-layout.js +48 -0
- package/dist/src/app-opentui/render/panels.d.ts +2 -0
- package/dist/src/app-opentui/render/panels.js +62 -29
- package/dist/src/app-opentui/render/preloader.d.ts +10 -0
- package/dist/src/app-opentui/render/preloader.js +163 -0
- package/dist/src/app-opentui/render/scene.d.ts +3 -0
- package/dist/src/app-opentui/render/scene.js +12 -0
- package/dist/src/app-opentui/render/text.js +5 -1
- package/dist/src/app-opentui/render/type-bar.d.ts +2 -1
- package/dist/src/app-opentui/render/type-bar.js +51 -16
- package/dist/src/app-opentui/runtime-resources.d.ts +16 -0
- package/dist/src/app-opentui/runtime-resources.js +62 -0
- package/dist/src/app-opentui/runtime.js +2322 -1513
- package/dist/src/app-opentui/scenes/command.d.ts +3 -0
- package/dist/src/app-opentui/scenes/command.js +103 -0
- package/dist/src/app-opentui/scenes/doctor.d.ts +2 -1
- package/dist/src/app-opentui/scenes/doctor.js +53 -14
- package/dist/src/app-opentui/scenes/error.js +44 -14
- package/dist/src/app-opentui/scenes/fleet.js +40 -21
- package/dist/src/app-opentui/scenes/handoff.js +142 -27
- package/dist/src/app-opentui/scenes/help.js +63 -48
- package/dist/src/app-opentui/scenes/inbox.d.ts +2 -1
- package/dist/src/app-opentui/scenes/inbox.js +64 -17
- package/dist/src/app-opentui/scenes/init.d.ts +2 -1
- package/dist/src/app-opentui/scenes/init.js +62 -21
- package/dist/src/app-opentui/scenes/main.d.ts +2 -1
- package/dist/src/app-opentui/scenes/main.js +80 -24
- package/dist/src/app-opentui/scenes/run-detail.d.ts +2 -1
- package/dist/src/app-opentui/scenes/run-detail.js +39 -25
- package/dist/src/app-opentui/scenes/server.d.ts +2 -1
- package/dist/src/app-opentui/scenes/server.js +71 -20
- package/dist/src/app-opentui/scenes/tasks.js +44 -22
- package/dist/src/app-opentui/state.js +70 -30
- package/dist/src/app-opentui/terminal-capabilities.d.ts +7 -0
- package/dist/src/app-opentui/terminal-capabilities.js +15 -0
- package/dist/src/app-opentui/types.d.ts +10 -2
- package/dist/src/commands/_doctor-checks.js +62 -13
- package/dist/src/commands/_operator-view.js +63 -13
- package/dist/src/commands/_pi-frontend.js +63 -13
- package/dist/src/commands/_preflight.js +64 -14
- package/dist/src/commands/_server-client.js +82 -18
- package/dist/src/commands/_snapshot-upload.js +62 -13
- package/dist/src/commands/connect.js +7 -1
- package/dist/src/commands/doctor.js +62 -13
- package/dist/src/commands/github.js +144 -23
- package/dist/src/commands/inbox.js +62 -13
- package/dist/src/commands/init.js +82 -18
- package/dist/src/commands/inspect.js +62 -13
- package/dist/src/commands/run.js +66 -13
- package/dist/src/commands/server.js +69 -14
- package/dist/src/commands/setup.js +62 -13
- package/dist/src/commands/stats.js +62 -13
- package/dist/src/commands/task-run-driver.js +62 -13
- package/dist/src/commands/task.js +65 -14
- package/dist/src/commands.js +227 -92
- package/dist/src/index.js +234 -99
- package/package.json +8 -9
- package/dist/src/app-opentui/render/image-visual-layer-native-canvas.d.ts +0 -2
- package/dist/src/app-opentui/render/image-visual-layer-native-canvas.js +0 -1484
|
@@ -192,17 +192,21 @@ function setGitHubBearerTokenForCurrentProcess(token, projectRoot) {
|
|
|
192
192
|
const scopedKey = resolve2(projectRoot ?? process.cwd());
|
|
193
193
|
scopedGitHubBearerTokens.set(scopedKey, cleanToken(token ?? undefined));
|
|
194
194
|
}
|
|
195
|
-
function
|
|
195
|
+
function readRemoteAuthState(projectRoot) {
|
|
196
196
|
const path = resolve2(projectRoot, ".rig", "state", "github-auth.json");
|
|
197
197
|
if (!existsSync2(path))
|
|
198
198
|
return null;
|
|
199
199
|
try {
|
|
200
200
|
const parsed = JSON.parse(readFileSync2(path, "utf8"));
|
|
201
|
-
return
|
|
201
|
+
return parsed && typeof parsed === "object" && !Array.isArray(parsed) ? parsed : null;
|
|
202
202
|
} catch {
|
|
203
203
|
return null;
|
|
204
204
|
}
|
|
205
205
|
}
|
|
206
|
+
function readPrivateRemoteSessionToken(projectRoot) {
|
|
207
|
+
const parsed = readRemoteAuthState(projectRoot);
|
|
208
|
+
return parsed ? cleanToken(typeof parsed.apiSessionToken === "string" ? parsed.apiSessionToken : typeof parsed.sessionToken === "string" ? parsed.sessionToken : undefined) : null;
|
|
209
|
+
}
|
|
206
210
|
function readGitHubBearerTokenForRemote(projectRoot) {
|
|
207
211
|
const scopedKey = resolve2(projectRoot);
|
|
208
212
|
if (scopedGitHubBearerTokens.has(scopedKey))
|
|
@@ -213,15 +217,25 @@ function readGitHubBearerTokenForRemote(projectRoot) {
|
|
|
213
217
|
return cleanToken(process.env.RIG_SERVER_AUTH_TOKEN) ?? cleanToken(process.env.RIG_REMOTE_AUTH_TOKEN);
|
|
214
218
|
}
|
|
215
219
|
function readStoredGitHubAuthToken(projectRoot) {
|
|
216
|
-
const
|
|
217
|
-
|
|
220
|
+
const parsed = readRemoteAuthState(projectRoot);
|
|
221
|
+
return parsed ? cleanToken(typeof parsed.token === "string" ? parsed.token : undefined) : null;
|
|
222
|
+
}
|
|
223
|
+
function inferRemoteServerProjectRootFromAuthState(projectRoot) {
|
|
224
|
+
const repo = readRepoConnection(projectRoot);
|
|
225
|
+
const slug = repo?.project?.trim();
|
|
226
|
+
if (!slug || !/^[^/]+\/[^/]+$/.test(slug))
|
|
218
227
|
return null;
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
228
|
+
const auth = readRemoteAuthState(projectRoot);
|
|
229
|
+
const authUpdatedAt = typeof auth?.updatedAt === "string" ? Date.parse(auth.updatedAt) : Number.NaN;
|
|
230
|
+
const repoLinkedAt = typeof repo?.linkedAt === "string" ? Date.parse(repo.linkedAt) : Number.NaN;
|
|
231
|
+
if (Number.isFinite(authUpdatedAt) && Number.isFinite(repoLinkedAt) && authUpdatedAt + 1000 < repoLinkedAt)
|
|
223
232
|
return null;
|
|
224
|
-
|
|
233
|
+
const checkoutBaseDir = typeof auth?.checkoutBaseDir === "string" && auth.checkoutBaseDir.trim() ? auth.checkoutBaseDir.trim() : null;
|
|
234
|
+
if (!checkoutBaseDir)
|
|
235
|
+
return null;
|
|
236
|
+
const inferred = `${checkoutBaseDir.replace(/\/+$/, "")}/${slug}`;
|
|
237
|
+
writeRepoServerProjectRoot(projectRoot, inferred);
|
|
238
|
+
return inferred;
|
|
225
239
|
}
|
|
226
240
|
function readLocalConnectionFallbackToken(projectRoot) {
|
|
227
241
|
return readGitHubBearerTokenForRemote(projectRoot) ?? cleanToken(process.env.RIG_GITHUB_TOKEN) ?? readStoredGitHubAuthToken(projectRoot);
|
|
@@ -232,7 +246,7 @@ async function ensureServerForCli(projectRoot) {
|
|
|
232
246
|
if (selected?.connection.kind === "remote") {
|
|
233
247
|
reportServerPhase(`Connecting to ${selected.alias}\u2026`);
|
|
234
248
|
const authToken = readGitHubBearerTokenForRemote(projectRoot);
|
|
235
|
-
const serverProjectRoot = selected.serverProjectRoot ?? await backfillRemoteServerProjectRoot(projectRoot, selected.connection.baseUrl, authToken);
|
|
249
|
+
const serverProjectRoot = selected.serverProjectRoot ?? inferRemoteServerProjectRootFromAuthState(projectRoot) ?? await backfillRemoteServerProjectRoot(projectRoot, selected.connection.baseUrl, authToken);
|
|
236
250
|
return {
|
|
237
251
|
baseUrl: selected.connection.baseUrl,
|
|
238
252
|
authToken,
|
|
@@ -261,7 +275,10 @@ async function backfillRemoteServerProjectRoot(projectRoot, baseUrl, authToken)
|
|
|
261
275
|
if (!slug)
|
|
262
276
|
return null;
|
|
263
277
|
try {
|
|
264
|
-
const
|
|
278
|
+
const url = new URL(`${baseUrl}/api/projects/${encodeURIComponent(slug)}`);
|
|
279
|
+
if (authToken && queryAuthFallbackEnabled())
|
|
280
|
+
url.searchParams.set("rt", authToken);
|
|
281
|
+
const response = await fetch(url, {
|
|
265
282
|
headers: mergeHeaders(undefined, authToken)
|
|
266
283
|
});
|
|
267
284
|
if (!response.ok)
|
|
@@ -288,10 +305,23 @@ function appendTaskFilterParams(url, filters) {
|
|
|
288
305
|
if (filters.limit !== undefined)
|
|
289
306
|
url.searchParams.set("limit", String(filters.limit));
|
|
290
307
|
}
|
|
308
|
+
function mergeCookie(existing, name, value) {
|
|
309
|
+
const encoded = `${name}=${encodeURIComponent(value)}`;
|
|
310
|
+
if (!existing?.trim())
|
|
311
|
+
return encoded;
|
|
312
|
+
const parts = existing.split(";").map((part) => part.trim()).filter((part) => part && !part.startsWith(`${name}=`));
|
|
313
|
+
return [...parts, encoded].join("; ");
|
|
314
|
+
}
|
|
315
|
+
function queryAuthFallbackEnabled(env = process.env) {
|
|
316
|
+
return env.RIG_ENABLE_QUERY_AUTH_FALLBACK === "1" || env.RIG_QUERY_AUTH_FALLBACK === "1";
|
|
317
|
+
}
|
|
291
318
|
function mergeHeaders(headers, authToken) {
|
|
292
319
|
const merged = new Headers(headers);
|
|
293
320
|
if (authToken) {
|
|
294
|
-
|
|
321
|
+
const bearer = `Bearer ${authToken}`;
|
|
322
|
+
merged.set("authorization", bearer);
|
|
323
|
+
merged.set("x-auth", bearer);
|
|
324
|
+
merged.set("cookie", mergeCookie(merged.get("cookie"), "rig_auth", authToken));
|
|
295
325
|
}
|
|
296
326
|
return merged;
|
|
297
327
|
}
|
|
@@ -351,15 +381,34 @@ async function buildServerFailureContext(projectRoot, server) {
|
|
|
351
381
|
hint: "Check the selected server with `rig server status`, or switch with `rig server use <alias|local>`."
|
|
352
382
|
};
|
|
353
383
|
}
|
|
384
|
+
function isLoopbackRemoteBaseUrl(baseUrl) {
|
|
385
|
+
try {
|
|
386
|
+
const host = new URL(baseUrl).hostname.toLowerCase();
|
|
387
|
+
return host === "localhost" || host === "127.0.0.1" || host === "::1" || host === "[::1]";
|
|
388
|
+
} catch {
|
|
389
|
+
return false;
|
|
390
|
+
}
|
|
391
|
+
}
|
|
392
|
+
function canUseRemoteWithoutProjectRoot(pathname) {
|
|
393
|
+
return pathname === "/health" || pathname === "/api/health" || pathname === "/api/server/status" || pathname === "/api/server/project-root" || pathname.startsWith("/api/github/auth/") || pathname === "/api/projects" || pathname.startsWith("/api/projects/");
|
|
394
|
+
}
|
|
354
395
|
async function requestServerJson(context, pathname, init = {}) {
|
|
355
396
|
const server = await ensureServerForCli(context.projectRoot);
|
|
397
|
+
const requestUrl = new URL(`${server.baseUrl}${pathname}`);
|
|
398
|
+
if (server.connectionKind === "remote" && !server.serverProjectRoot && !canUseRemoteWithoutProjectRoot(requestUrl.pathname) && (server.authToken || !isLoopbackRemoteBaseUrl(server.baseUrl))) {
|
|
399
|
+
const repo = readRepoConnection(context.projectRoot);
|
|
400
|
+
throw new CliError(`Remote server is selected for ${repo?.project ?? "this repo"}, but this checkout has no server-host project root link.`, 1, { hint: "Run `rig init --repair` or `rig init --yes --repo owner/repo --server remote` to repair the remote project link before task/run commands." });
|
|
401
|
+
}
|
|
356
402
|
const headers = mergeHeaders(init.headers, server.authToken);
|
|
357
403
|
if (server.serverProjectRoot)
|
|
358
404
|
headers.set("x-rig-project-root", server.serverProjectRoot);
|
|
405
|
+
if (server.connectionKind === "remote" && server.authToken && queryAuthFallbackEnabled()) {
|
|
406
|
+
requestUrl.searchParams.set("rt", server.authToken);
|
|
407
|
+
}
|
|
359
408
|
reportServerPhase(`${(init.method ?? "GET").toUpperCase()} ${pathname.split("?")[0]}\u2026`);
|
|
360
409
|
let response;
|
|
361
410
|
try {
|
|
362
|
-
response = await fetch(
|
|
411
|
+
response = await fetch(requestUrl, {
|
|
363
412
|
...init,
|
|
364
413
|
headers
|
|
365
414
|
});
|
|
@@ -421,11 +470,26 @@ async function getGitHubAuthStatusViaServer(context) {
|
|
|
421
470
|
return payload && typeof payload === "object" && !Array.isArray(payload) ? payload : {};
|
|
422
471
|
}
|
|
423
472
|
async function postGitHubTokenViaServer(context, token, options = {}) {
|
|
424
|
-
const
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
473
|
+
const server = await ensureServerForCli(context.projectRoot);
|
|
474
|
+
const requestUrl = new URL(`${server.baseUrl}/api/github/auth/token`);
|
|
475
|
+
reportServerPhase("POST /api/github/auth/token\u2026");
|
|
476
|
+
let response;
|
|
477
|
+
try {
|
|
478
|
+
response = await fetch(requestUrl, {
|
|
479
|
+
method: "POST",
|
|
480
|
+
headers: { "content-type": "application/json" },
|
|
481
|
+
body: JSON.stringify({ token, selectedRepo: options.selectedRepo, projectRoot: options.projectRoot ?? server.serverProjectRoot ?? undefined })
|
|
482
|
+
});
|
|
483
|
+
} catch (error) {
|
|
484
|
+
const failure = await buildServerFailureContext(context.projectRoot, server);
|
|
485
|
+
throw new CliError(`Rig server auth bootstrap failed: ${error instanceof Error ? error.message : String(error)}
|
|
486
|
+
${failure.contextLine}`, 1, { hint: failure.hint });
|
|
487
|
+
}
|
|
488
|
+
const payload = await response.json().catch(() => null);
|
|
489
|
+
if (!response.ok) {
|
|
490
|
+
const detail = payload && typeof payload === "object" && !Array.isArray(payload) ? String(payload.error ?? JSON.stringify(payload)) : `HTTP ${response.status}`;
|
|
491
|
+
throw new CliError(`Rig server auth bootstrap failed (${response.status}): ${detail}`, 1, { hint: "Re-run `rig github auth import-gh`, or check the selected server with `rig server status`." });
|
|
492
|
+
}
|
|
429
493
|
return payload && typeof payload === "object" && !Array.isArray(payload) ? payload : {};
|
|
430
494
|
}
|
|
431
495
|
async function prepareRemoteCheckoutViaServer(context, input) {
|
|
@@ -716,7 +780,7 @@ function projectRootOf(ctx) {
|
|
|
716
780
|
const root = ctx.rig?.projectRoot ?? ctx.projectRoot ?? ctx.getState().projectRoot;
|
|
717
781
|
if (typeof root === "string" && root.trim())
|
|
718
782
|
return root;
|
|
719
|
-
throw new Error("
|
|
783
|
+
throw new Error("App adapter requires a projectRoot.");
|
|
720
784
|
}
|
|
721
785
|
function normalizeAppError(error) {
|
|
722
786
|
const message = error instanceof Error ? error.message : String(error);
|
|
@@ -777,8 +841,8 @@ var MIN_COLS = 40;
|
|
|
777
841
|
var MIN_ROWS = 12;
|
|
778
842
|
var MAX_ROWS = 300;
|
|
779
843
|
var MAX_SNAPSHOT_LINES = 360;
|
|
780
|
-
var STYLED_SNAPSHOT_MARGIN =
|
|
781
|
-
var SNAPSHOT_DELAY_MS =
|
|
844
|
+
var STYLED_SNAPSHOT_MARGIN = 6;
|
|
845
|
+
var SNAPSHOT_DELAY_MS = 120;
|
|
782
846
|
var fallbackChildScriptPath = fileURLToPath(new URL("./pi-host-child.ts", import.meta.url));
|
|
783
847
|
var activeHost = null;
|
|
784
848
|
function clampCols(cols) {
|
|
@@ -876,17 +940,15 @@ function lineToStyledText(line, cols) {
|
|
|
876
940
|
function childCommandPrefix() {
|
|
877
941
|
const execName = basename(process.execPath).toLowerCase();
|
|
878
942
|
const currentEntry = process.argv[1];
|
|
879
|
-
if (
|
|
880
|
-
return [process.execPath, currentEntry, "__opentui-pi-host"];
|
|
943
|
+
if (execName === "bun" || execName === "bun.exe") {
|
|
944
|
+
return currentEntry ? [process.execPath, currentEntry, "__opentui-pi-host"] : [process.execPath, fallbackChildScriptPath];
|
|
881
945
|
}
|
|
882
|
-
|
|
883
|
-
return [process.execPath, "__opentui-pi-host"];
|
|
884
|
-
return [process.execPath, fallbackChildScriptPath];
|
|
946
|
+
return [process.execPath, "__opentui-pi-host"];
|
|
885
947
|
}
|
|
886
948
|
function withEnv(base) {
|
|
887
949
|
const env = {};
|
|
888
950
|
for (const [key, value] of Object.entries(base ?? process.env)) {
|
|
889
|
-
if (key === "RIG_PLAIN" || key === "RIG_CLI_PLAIN_HELP")
|
|
951
|
+
if (key === "RIG_PLAIN" || key === "RIG_CLI_PLAIN_HELP" || key === "NO_COLOR")
|
|
890
952
|
continue;
|
|
891
953
|
if (typeof value === "string")
|
|
892
954
|
env[key] = value;
|
|
@@ -937,7 +999,7 @@ class PiPtyHost {
|
|
|
937
999
|
allowProposedApi: true,
|
|
938
1000
|
cols: this.cols,
|
|
939
1001
|
rows: this.rows,
|
|
940
|
-
scrollback:
|
|
1002
|
+
scrollback: 1000
|
|
941
1003
|
});
|
|
942
1004
|
this.registerTerminalResponders();
|
|
943
1005
|
}
|
|
@@ -951,7 +1013,7 @@ class PiPtyHost {
|
|
|
951
1013
|
if (this._disposed)
|
|
952
1014
|
throw new Error("Pi PTY host is disposed.");
|
|
953
1015
|
if (typeof Bun.Terminal !== "function") {
|
|
954
|
-
throw new Error("Bun native PTY is unavailable in this runtime.
|
|
1016
|
+
throw new Error("Bun native PTY is unavailable in this runtime. Pi host requires Bun.Terminal.");
|
|
955
1017
|
}
|
|
956
1018
|
const spawnOptions = {
|
|
957
1019
|
cwd: this.projectRoot,
|
|
@@ -975,7 +1037,7 @@ class PiPtyHost {
|
|
|
975
1037
|
this.proc = proc;
|
|
976
1038
|
this.pty = proc.terminal;
|
|
977
1039
|
this.status = "running";
|
|
978
|
-
this.message = "bundled Pi running inside
|
|
1040
|
+
this.message = "bundled Pi running inside this app";
|
|
979
1041
|
this.emitSnapshotSoon(0);
|
|
980
1042
|
proc.exited.then((exitCode) => {
|
|
981
1043
|
if (this._disposed)
|
|
@@ -1165,7 +1227,7 @@ async function refreshFleet(ctx, options = {}) {
|
|
|
1165
1227
|
try {
|
|
1166
1228
|
const { listRunsViaServer: listRunsViaServer2, resolveServerConnectionLabel: resolveServerConnectionLabel2 } = await Promise.resolve().then(() => (init__server_client(), exports__server_client));
|
|
1167
1229
|
const projectRoot = projectRootOf(ctx);
|
|
1168
|
-
emitProgress(ctx, label, "contacting selected
|
|
1230
|
+
emitProgress(ctx, label, "contacting selected server");
|
|
1169
1231
|
const [runs, serverLabel] = await Promise.all([
|
|
1170
1232
|
listRunsViaServer2({ projectRoot }, { limit: options.limit ?? 50 }),
|
|
1171
1233
|
resolveServerConnectionLabel2(projectRoot)
|
|
@@ -1244,7 +1306,7 @@ async function attachRunWithBundledPi(ctx, runId) {
|
|
|
1244
1306
|
throw error;
|
|
1245
1307
|
}
|
|
1246
1308
|
emitStarted(ctx, label, { piAttach: { runId: cleanRunId, status: "entering-pi" } });
|
|
1247
|
-
patchData(ctx, { piAttach: { runId: cleanRunId, status: "entering-pi", message: "starting bundled Pi
|
|
1309
|
+
patchData(ctx, { piAttach: { runId: cleanRunId, status: "entering-pi", message: "starting bundled Pi" } });
|
|
1248
1310
|
try {
|
|
1249
1311
|
await preparePiAttachHandoff(ctx, cleanRunId).catch(() => null);
|
|
1250
1312
|
emitProgress(ctx, label, "starting Bun PTY host for bundled Pi");
|
|
@@ -1257,7 +1319,7 @@ async function attachRunWithBundledPi(ctx, runId) {
|
|
|
1257
1319
|
piAttach: {
|
|
1258
1320
|
runId: cleanRunId,
|
|
1259
1321
|
status: snapshot.status === "failed" ? "failed" : snapshot.status === "exited" ? "returned" : "entering-pi",
|
|
1260
|
-
message: snapshot.message ?? "bundled Pi running
|
|
1322
|
+
message: snapshot.message ?? "bundled Pi running",
|
|
1261
1323
|
result: { runId: cleanRunId, status: snapshot.status, exitCode: snapshot.exitCode }
|
|
1262
1324
|
}
|
|
1263
1325
|
});
|
|
@@ -1284,7 +1346,7 @@ async function attachRunWithBundledPi(ctx, runId) {
|
|
|
1284
1346
|
status: "running",
|
|
1285
1347
|
rendered: "bundled Pi embedded via Bun.Terminal + @xterm/headless"
|
|
1286
1348
|
};
|
|
1287
|
-
emitProgress(ctx, label, "bundled Pi is running
|
|
1349
|
+
emitProgress(ctx, label, "bundled Pi is running", { piAttach: { runId: cleanRunId, status: "entering-pi", message: "bundled Pi running", result: record } });
|
|
1288
1350
|
return record;
|
|
1289
1351
|
} catch (error) {
|
|
1290
1352
|
patchData(ctx, { piAttach: { runId: cleanRunId, status: "failed", message: error instanceof Error ? error.message : String(error) } });
|
|
@@ -192,17 +192,21 @@ function setGitHubBearerTokenForCurrentProcess(token, projectRoot) {
|
|
|
192
192
|
const scopedKey = resolve2(projectRoot ?? process.cwd());
|
|
193
193
|
scopedGitHubBearerTokens.set(scopedKey, cleanToken(token ?? undefined));
|
|
194
194
|
}
|
|
195
|
-
function
|
|
195
|
+
function readRemoteAuthState(projectRoot) {
|
|
196
196
|
const path = resolve2(projectRoot, ".rig", "state", "github-auth.json");
|
|
197
197
|
if (!existsSync2(path))
|
|
198
198
|
return null;
|
|
199
199
|
try {
|
|
200
200
|
const parsed = JSON.parse(readFileSync2(path, "utf8"));
|
|
201
|
-
return
|
|
201
|
+
return parsed && typeof parsed === "object" && !Array.isArray(parsed) ? parsed : null;
|
|
202
202
|
} catch {
|
|
203
203
|
return null;
|
|
204
204
|
}
|
|
205
205
|
}
|
|
206
|
+
function readPrivateRemoteSessionToken(projectRoot) {
|
|
207
|
+
const parsed = readRemoteAuthState(projectRoot);
|
|
208
|
+
return parsed ? cleanToken(typeof parsed.apiSessionToken === "string" ? parsed.apiSessionToken : typeof parsed.sessionToken === "string" ? parsed.sessionToken : undefined) : null;
|
|
209
|
+
}
|
|
206
210
|
function readGitHubBearerTokenForRemote(projectRoot) {
|
|
207
211
|
const scopedKey = resolve2(projectRoot);
|
|
208
212
|
if (scopedGitHubBearerTokens.has(scopedKey))
|
|
@@ -213,15 +217,25 @@ function readGitHubBearerTokenForRemote(projectRoot) {
|
|
|
213
217
|
return cleanToken(process.env.RIG_SERVER_AUTH_TOKEN) ?? cleanToken(process.env.RIG_REMOTE_AUTH_TOKEN);
|
|
214
218
|
}
|
|
215
219
|
function readStoredGitHubAuthToken(projectRoot) {
|
|
216
|
-
const
|
|
217
|
-
|
|
220
|
+
const parsed = readRemoteAuthState(projectRoot);
|
|
221
|
+
return parsed ? cleanToken(typeof parsed.token === "string" ? parsed.token : undefined) : null;
|
|
222
|
+
}
|
|
223
|
+
function inferRemoteServerProjectRootFromAuthState(projectRoot) {
|
|
224
|
+
const repo = readRepoConnection(projectRoot);
|
|
225
|
+
const slug = repo?.project?.trim();
|
|
226
|
+
if (!slug || !/^[^/]+\/[^/]+$/.test(slug))
|
|
218
227
|
return null;
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
228
|
+
const auth = readRemoteAuthState(projectRoot);
|
|
229
|
+
const authUpdatedAt = typeof auth?.updatedAt === "string" ? Date.parse(auth.updatedAt) : Number.NaN;
|
|
230
|
+
const repoLinkedAt = typeof repo?.linkedAt === "string" ? Date.parse(repo.linkedAt) : Number.NaN;
|
|
231
|
+
if (Number.isFinite(authUpdatedAt) && Number.isFinite(repoLinkedAt) && authUpdatedAt + 1000 < repoLinkedAt)
|
|
223
232
|
return null;
|
|
224
|
-
|
|
233
|
+
const checkoutBaseDir = typeof auth?.checkoutBaseDir === "string" && auth.checkoutBaseDir.trim() ? auth.checkoutBaseDir.trim() : null;
|
|
234
|
+
if (!checkoutBaseDir)
|
|
235
|
+
return null;
|
|
236
|
+
const inferred = `${checkoutBaseDir.replace(/\/+$/, "")}/${slug}`;
|
|
237
|
+
writeRepoServerProjectRoot(projectRoot, inferred);
|
|
238
|
+
return inferred;
|
|
225
239
|
}
|
|
226
240
|
function readLocalConnectionFallbackToken(projectRoot) {
|
|
227
241
|
return readGitHubBearerTokenForRemote(projectRoot) ?? cleanToken(process.env.RIG_GITHUB_TOKEN) ?? readStoredGitHubAuthToken(projectRoot);
|
|
@@ -232,7 +246,7 @@ async function ensureServerForCli(projectRoot) {
|
|
|
232
246
|
if (selected?.connection.kind === "remote") {
|
|
233
247
|
reportServerPhase(`Connecting to ${selected.alias}\u2026`);
|
|
234
248
|
const authToken = readGitHubBearerTokenForRemote(projectRoot);
|
|
235
|
-
const serverProjectRoot = selected.serverProjectRoot ?? await backfillRemoteServerProjectRoot(projectRoot, selected.connection.baseUrl, authToken);
|
|
249
|
+
const serverProjectRoot = selected.serverProjectRoot ?? inferRemoteServerProjectRootFromAuthState(projectRoot) ?? await backfillRemoteServerProjectRoot(projectRoot, selected.connection.baseUrl, authToken);
|
|
236
250
|
return {
|
|
237
251
|
baseUrl: selected.connection.baseUrl,
|
|
238
252
|
authToken,
|
|
@@ -261,7 +275,10 @@ async function backfillRemoteServerProjectRoot(projectRoot, baseUrl, authToken)
|
|
|
261
275
|
if (!slug)
|
|
262
276
|
return null;
|
|
263
277
|
try {
|
|
264
|
-
const
|
|
278
|
+
const url = new URL(`${baseUrl}/api/projects/${encodeURIComponent(slug)}`);
|
|
279
|
+
if (authToken && queryAuthFallbackEnabled())
|
|
280
|
+
url.searchParams.set("rt", authToken);
|
|
281
|
+
const response = await fetch(url, {
|
|
265
282
|
headers: mergeHeaders(undefined, authToken)
|
|
266
283
|
});
|
|
267
284
|
if (!response.ok)
|
|
@@ -288,10 +305,23 @@ function appendTaskFilterParams(url, filters) {
|
|
|
288
305
|
if (filters.limit !== undefined)
|
|
289
306
|
url.searchParams.set("limit", String(filters.limit));
|
|
290
307
|
}
|
|
308
|
+
function mergeCookie(existing, name, value) {
|
|
309
|
+
const encoded = `${name}=${encodeURIComponent(value)}`;
|
|
310
|
+
if (!existing?.trim())
|
|
311
|
+
return encoded;
|
|
312
|
+
const parts = existing.split(";").map((part) => part.trim()).filter((part) => part && !part.startsWith(`${name}=`));
|
|
313
|
+
return [...parts, encoded].join("; ");
|
|
314
|
+
}
|
|
315
|
+
function queryAuthFallbackEnabled(env = process.env) {
|
|
316
|
+
return env.RIG_ENABLE_QUERY_AUTH_FALLBACK === "1" || env.RIG_QUERY_AUTH_FALLBACK === "1";
|
|
317
|
+
}
|
|
291
318
|
function mergeHeaders(headers, authToken) {
|
|
292
319
|
const merged = new Headers(headers);
|
|
293
320
|
if (authToken) {
|
|
294
|
-
|
|
321
|
+
const bearer = `Bearer ${authToken}`;
|
|
322
|
+
merged.set("authorization", bearer);
|
|
323
|
+
merged.set("x-auth", bearer);
|
|
324
|
+
merged.set("cookie", mergeCookie(merged.get("cookie"), "rig_auth", authToken));
|
|
295
325
|
}
|
|
296
326
|
return merged;
|
|
297
327
|
}
|
|
@@ -351,15 +381,34 @@ async function buildServerFailureContext(projectRoot, server) {
|
|
|
351
381
|
hint: "Check the selected server with `rig server status`, or switch with `rig server use <alias|local>`."
|
|
352
382
|
};
|
|
353
383
|
}
|
|
384
|
+
function isLoopbackRemoteBaseUrl(baseUrl) {
|
|
385
|
+
try {
|
|
386
|
+
const host = new URL(baseUrl).hostname.toLowerCase();
|
|
387
|
+
return host === "localhost" || host === "127.0.0.1" || host === "::1" || host === "[::1]";
|
|
388
|
+
} catch {
|
|
389
|
+
return false;
|
|
390
|
+
}
|
|
391
|
+
}
|
|
392
|
+
function canUseRemoteWithoutProjectRoot(pathname) {
|
|
393
|
+
return pathname === "/health" || pathname === "/api/health" || pathname === "/api/server/status" || pathname === "/api/server/project-root" || pathname.startsWith("/api/github/auth/") || pathname === "/api/projects" || pathname.startsWith("/api/projects/");
|
|
394
|
+
}
|
|
354
395
|
async function requestServerJson(context, pathname, init = {}) {
|
|
355
396
|
const server = await ensureServerForCli(context.projectRoot);
|
|
397
|
+
const requestUrl = new URL(`${server.baseUrl}${pathname}`);
|
|
398
|
+
if (server.connectionKind === "remote" && !server.serverProjectRoot && !canUseRemoteWithoutProjectRoot(requestUrl.pathname) && (server.authToken || !isLoopbackRemoteBaseUrl(server.baseUrl))) {
|
|
399
|
+
const repo = readRepoConnection(context.projectRoot);
|
|
400
|
+
throw new CliError(`Remote server is selected for ${repo?.project ?? "this repo"}, but this checkout has no server-host project root link.`, 1, { hint: "Run `rig init --repair` or `rig init --yes --repo owner/repo --server remote` to repair the remote project link before task/run commands." });
|
|
401
|
+
}
|
|
356
402
|
const headers = mergeHeaders(init.headers, server.authToken);
|
|
357
403
|
if (server.serverProjectRoot)
|
|
358
404
|
headers.set("x-rig-project-root", server.serverProjectRoot);
|
|
405
|
+
if (server.connectionKind === "remote" && server.authToken && queryAuthFallbackEnabled()) {
|
|
406
|
+
requestUrl.searchParams.set("rt", server.authToken);
|
|
407
|
+
}
|
|
359
408
|
reportServerPhase(`${(init.method ?? "GET").toUpperCase()} ${pathname.split("?")[0]}\u2026`);
|
|
360
409
|
let response;
|
|
361
410
|
try {
|
|
362
|
-
response = await fetch(
|
|
411
|
+
response = await fetch(requestUrl, {
|
|
363
412
|
...init,
|
|
364
413
|
headers
|
|
365
414
|
});
|
|
@@ -421,11 +470,26 @@ async function getGitHubAuthStatusViaServer(context) {
|
|
|
421
470
|
return payload && typeof payload === "object" && !Array.isArray(payload) ? payload : {};
|
|
422
471
|
}
|
|
423
472
|
async function postGitHubTokenViaServer(context, token, options = {}) {
|
|
424
|
-
const
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
473
|
+
const server = await ensureServerForCli(context.projectRoot);
|
|
474
|
+
const requestUrl = new URL(`${server.baseUrl}/api/github/auth/token`);
|
|
475
|
+
reportServerPhase("POST /api/github/auth/token\u2026");
|
|
476
|
+
let response;
|
|
477
|
+
try {
|
|
478
|
+
response = await fetch(requestUrl, {
|
|
479
|
+
method: "POST",
|
|
480
|
+
headers: { "content-type": "application/json" },
|
|
481
|
+
body: JSON.stringify({ token, selectedRepo: options.selectedRepo, projectRoot: options.projectRoot ?? server.serverProjectRoot ?? undefined })
|
|
482
|
+
});
|
|
483
|
+
} catch (error) {
|
|
484
|
+
const failure = await buildServerFailureContext(context.projectRoot, server);
|
|
485
|
+
throw new CliError(`Rig server auth bootstrap failed: ${error instanceof Error ? error.message : String(error)}
|
|
486
|
+
${failure.contextLine}`, 1, { hint: failure.hint });
|
|
487
|
+
}
|
|
488
|
+
const payload = await response.json().catch(() => null);
|
|
489
|
+
if (!response.ok) {
|
|
490
|
+
const detail = payload && typeof payload === "object" && !Array.isArray(payload) ? String(payload.error ?? JSON.stringify(payload)) : `HTTP ${response.status}`;
|
|
491
|
+
throw new CliError(`Rig server auth bootstrap failed (${response.status}): ${detail}`, 1, { hint: "Re-run `rig github auth import-gh`, or check the selected server with `rig server status`." });
|
|
492
|
+
}
|
|
429
493
|
return payload && typeof payload === "object" && !Array.isArray(payload) ? payload : {};
|
|
430
494
|
}
|
|
431
495
|
async function prepareRemoteCheckoutViaServer(context, input) {
|
|
@@ -716,7 +780,7 @@ function projectRootOf(ctx) {
|
|
|
716
780
|
const root = ctx.rig?.projectRoot ?? ctx.projectRoot ?? ctx.getState().projectRoot;
|
|
717
781
|
if (typeof root === "string" && root.trim())
|
|
718
782
|
return root;
|
|
719
|
-
throw new Error("
|
|
783
|
+
throw new Error("App adapter requires a projectRoot.");
|
|
720
784
|
}
|
|
721
785
|
function normalizeAppError(error) {
|
|
722
786
|
const message = error instanceof Error ? error.message : String(error);
|