@h-rig/cli 0.0.6-alpha.81 → 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 +2807 -1443
- 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 +102 -36
- 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 +13776 -12369
- 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 +3806 -2878
- 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 +15 -13
- package/dist/src/app-opentui/registry.js +3228 -2396
- 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 +413 -958
- package/dist/src/app-opentui/render/image-visual-layer.d.ts +19 -10
- package/dist/src/app-opentui/render/image-visual-layer.js +391 -357
- 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 +2329 -1512
- 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 -1480
|
@@ -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);
|
|
@@ -776,7 +840,9 @@ import { Terminal as XtermTerminal } from "@xterm/headless";
|
|
|
776
840
|
var MIN_COLS = 40;
|
|
777
841
|
var MIN_ROWS = 12;
|
|
778
842
|
var MAX_ROWS = 300;
|
|
779
|
-
var MAX_SNAPSHOT_LINES =
|
|
843
|
+
var MAX_SNAPSHOT_LINES = 360;
|
|
844
|
+
var STYLED_SNAPSHOT_MARGIN = 6;
|
|
845
|
+
var SNAPSHOT_DELAY_MS = 120;
|
|
780
846
|
var fallbackChildScriptPath = fileURLToPath(new URL("./pi-host-child.ts", import.meta.url));
|
|
781
847
|
var activeHost = null;
|
|
782
848
|
function clampCols(cols) {
|
|
@@ -874,17 +940,15 @@ function lineToStyledText(line, cols) {
|
|
|
874
940
|
function childCommandPrefix() {
|
|
875
941
|
const execName = basename(process.execPath).toLowerCase();
|
|
876
942
|
const currentEntry = process.argv[1];
|
|
877
|
-
if (
|
|
878
|
-
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];
|
|
879
945
|
}
|
|
880
|
-
|
|
881
|
-
return [process.execPath, "__opentui-pi-host"];
|
|
882
|
-
return [process.execPath, fallbackChildScriptPath];
|
|
946
|
+
return [process.execPath, "__opentui-pi-host"];
|
|
883
947
|
}
|
|
884
948
|
function withEnv(base) {
|
|
885
949
|
const env = {};
|
|
886
950
|
for (const [key, value] of Object.entries(base ?? process.env)) {
|
|
887
|
-
if (key === "RIG_PLAIN" || key === "RIG_CLI_PLAIN_HELP")
|
|
951
|
+
if (key === "RIG_PLAIN" || key === "RIG_CLI_PLAIN_HELP" || key === "NO_COLOR")
|
|
888
952
|
continue;
|
|
889
953
|
if (typeof value === "string")
|
|
890
954
|
env[key] = value;
|
|
@@ -935,7 +999,7 @@ class PiPtyHost {
|
|
|
935
999
|
allowProposedApi: true,
|
|
936
1000
|
cols: this.cols,
|
|
937
1001
|
rows: this.rows,
|
|
938
|
-
scrollback:
|
|
1002
|
+
scrollback: 1000
|
|
939
1003
|
});
|
|
940
1004
|
this.registerTerminalResponders();
|
|
941
1005
|
}
|
|
@@ -949,7 +1013,7 @@ class PiPtyHost {
|
|
|
949
1013
|
if (this._disposed)
|
|
950
1014
|
throw new Error("Pi PTY host is disposed.");
|
|
951
1015
|
if (typeof Bun.Terminal !== "function") {
|
|
952
|
-
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.");
|
|
953
1017
|
}
|
|
954
1018
|
const spawnOptions = {
|
|
955
1019
|
cwd: this.projectRoot,
|
|
@@ -973,7 +1037,7 @@ class PiPtyHost {
|
|
|
973
1037
|
this.proc = proc;
|
|
974
1038
|
this.pty = proc.terminal;
|
|
975
1039
|
this.status = "running";
|
|
976
|
-
this.message = "bundled Pi running inside
|
|
1040
|
+
this.message = "bundled Pi running inside this app";
|
|
977
1041
|
this.emitSnapshotSoon(0);
|
|
978
1042
|
proc.exited.then((exitCode) => {
|
|
979
1043
|
if (this._disposed)
|
|
@@ -1068,7 +1132,7 @@ class PiPtyHost {
|
|
|
1068
1132
|
this.respondToRawTerminalQueries(text);
|
|
1069
1133
|
this.terminal.write(data, () => this.emitSnapshotSoon());
|
|
1070
1134
|
}
|
|
1071
|
-
emitSnapshotSoon(delayMs =
|
|
1135
|
+
emitSnapshotSoon(delayMs = SNAPSHOT_DELAY_MS) {
|
|
1072
1136
|
if (this._disposed || this.notifyTimer)
|
|
1073
1137
|
return;
|
|
1074
1138
|
this.notifyTimer = setTimeout(() => {
|
|
@@ -1082,16 +1146,18 @@ class PiPtyHost {
|
|
|
1082
1146
|
const buffer = this.terminal.buffer.active;
|
|
1083
1147
|
const end = buffer.length;
|
|
1084
1148
|
const start = Math.max(0, end - MAX_SNAPSHOT_LINES);
|
|
1149
|
+
const styledStart = Math.max(start, end - this.rows - STYLED_SNAPSHOT_MARGIN);
|
|
1085
1150
|
const lines = [];
|
|
1086
1151
|
const styledLines = [];
|
|
1087
1152
|
for (let row = start;row < end; row += 1) {
|
|
1088
1153
|
const line = buffer.getLine(row);
|
|
1089
1154
|
lines.push((line?.translateToString(false) ?? "").slice(0, this.cols));
|
|
1090
|
-
|
|
1155
|
+
if (row >= styledStart)
|
|
1156
|
+
styledLines[lines.length - 1] = lineToStyledText(line, this.cols);
|
|
1091
1157
|
}
|
|
1092
1158
|
while (lines.length < this.rows) {
|
|
1093
1159
|
lines.push("");
|
|
1094
|
-
styledLines.
|
|
1160
|
+
styledLines[lines.length - 1] = new StyledText([{ __isChunk: true, text: "" }]);
|
|
1095
1161
|
}
|
|
1096
1162
|
return {
|
|
1097
1163
|
runId: this.runId,
|
|
@@ -1161,7 +1227,7 @@ async function refreshFleet(ctx, options = {}) {
|
|
|
1161
1227
|
try {
|
|
1162
1228
|
const { listRunsViaServer: listRunsViaServer2, resolveServerConnectionLabel: resolveServerConnectionLabel2 } = await Promise.resolve().then(() => (init__server_client(), exports__server_client));
|
|
1163
1229
|
const projectRoot = projectRootOf(ctx);
|
|
1164
|
-
emitProgress(ctx, label, "contacting selected
|
|
1230
|
+
emitProgress(ctx, label, "contacting selected server");
|
|
1165
1231
|
const [runs, serverLabel] = await Promise.all([
|
|
1166
1232
|
listRunsViaServer2({ projectRoot }, { limit: options.limit ?? 50 }),
|
|
1167
1233
|
resolveServerConnectionLabel2(projectRoot)
|
|
@@ -1240,7 +1306,7 @@ async function attachRunWithBundledPi(ctx, runId) {
|
|
|
1240
1306
|
throw error;
|
|
1241
1307
|
}
|
|
1242
1308
|
emitStarted(ctx, label, { piAttach: { runId: cleanRunId, status: "entering-pi" } });
|
|
1243
|
-
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" } });
|
|
1244
1310
|
try {
|
|
1245
1311
|
await preparePiAttachHandoff(ctx, cleanRunId).catch(() => null);
|
|
1246
1312
|
emitProgress(ctx, label, "starting Bun PTY host for bundled Pi");
|
|
@@ -1253,7 +1319,7 @@ async function attachRunWithBundledPi(ctx, runId) {
|
|
|
1253
1319
|
piAttach: {
|
|
1254
1320
|
runId: cleanRunId,
|
|
1255
1321
|
status: snapshot.status === "failed" ? "failed" : snapshot.status === "exited" ? "returned" : "entering-pi",
|
|
1256
|
-
message: snapshot.message ?? "bundled Pi running
|
|
1322
|
+
message: snapshot.message ?? "bundled Pi running",
|
|
1257
1323
|
result: { runId: cleanRunId, status: snapshot.status, exitCode: snapshot.exitCode }
|
|
1258
1324
|
}
|
|
1259
1325
|
});
|
|
@@ -1280,7 +1346,7 @@ async function attachRunWithBundledPi(ctx, runId) {
|
|
|
1280
1346
|
status: "running",
|
|
1281
1347
|
rendered: "bundled Pi embedded via Bun.Terminal + @xterm/headless"
|
|
1282
1348
|
};
|
|
1283
|
-
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 } });
|
|
1284
1350
|
return record;
|
|
1285
1351
|
} catch (error) {
|
|
1286
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);
|