@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
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Interactive CLI is app-shell first.
|
|
3
|
+
*
|
|
4
|
+
* Native scenes handle the common surfaces (project menu, runs, tasks, inbox,
|
|
5
|
+
* server, init, doctor, Pi attach). Every other interactive command still
|
|
6
|
+
* enters OpenTUI and is executed inside an embedded plain-CLI PTY, preserving
|
|
7
|
+
* the exact existing options/actions/wizards without pretending unsupported
|
|
8
|
+
* commands were handled by a read-only scene. Machine/internal paths stay plain.
|
|
9
|
+
*/
|
|
10
|
+
export declare function isOpenTuiAppCommand(argv: readonly string[], env?: NodeJS.ProcessEnv): boolean;
|
|
11
|
+
export declare function shouldLaunchTtyApp(input: {
|
|
12
|
+
readonly argv: readonly string[];
|
|
13
|
+
readonly stdoutIsTty?: boolean;
|
|
14
|
+
readonly stdinIsTty?: boolean;
|
|
15
|
+
readonly env?: NodeJS.ProcessEnv;
|
|
16
|
+
}): boolean;
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
// @bun
|
|
2
|
+
// packages/cli/src/app-opentui/launch-routing.ts
|
|
3
|
+
var GLOBAL_OPTIONS_WITH_VALUE = new Set(["--project", "--policy-mode", "--run-id"]);
|
|
4
|
+
var GLOBAL_FLAGS = new Set(["--dry-run"]);
|
|
5
|
+
function envTruthy(value) {
|
|
6
|
+
if (!value)
|
|
7
|
+
return false;
|
|
8
|
+
const normalized = value.trim().toLowerCase();
|
|
9
|
+
return normalized !== "0" && normalized !== "false" && normalized !== "no";
|
|
10
|
+
}
|
|
11
|
+
function stripGlobalAppOptions(argv) {
|
|
12
|
+
const rest = [];
|
|
13
|
+
for (let index = 0;index < argv.length; index += 1) {
|
|
14
|
+
const arg = argv[index];
|
|
15
|
+
if (GLOBAL_FLAGS.has(arg))
|
|
16
|
+
continue;
|
|
17
|
+
if (GLOBAL_OPTIONS_WITH_VALUE.has(arg)) {
|
|
18
|
+
index += 1;
|
|
19
|
+
continue;
|
|
20
|
+
}
|
|
21
|
+
rest.push(arg);
|
|
22
|
+
}
|
|
23
|
+
return rest;
|
|
24
|
+
}
|
|
25
|
+
function isOpenTuiAppCommand(argv, env = process.env) {
|
|
26
|
+
const commandArgv = stripGlobalAppOptions(argv);
|
|
27
|
+
const [group = "", command = ""] = commandArgv;
|
|
28
|
+
const normalizedGroup = group.toLowerCase();
|
|
29
|
+
const normalizedCommand = command.toLowerCase();
|
|
30
|
+
if (!normalizedGroup)
|
|
31
|
+
return true;
|
|
32
|
+
if (normalizedGroup === "__opentui-pi-host" || normalizedGroup === "__rig-server")
|
|
33
|
+
return false;
|
|
34
|
+
if (normalizedGroup === "server" && normalizedCommand === "task-run")
|
|
35
|
+
return false;
|
|
36
|
+
if (normalizedGroup === "server" && normalizedCommand === "start" && env.RIG_SERVER_INTERNAL_EXEC === "1")
|
|
37
|
+
return false;
|
|
38
|
+
return true;
|
|
39
|
+
}
|
|
40
|
+
function shouldLaunchTtyApp(input) {
|
|
41
|
+
const env = input.env ?? process.env;
|
|
42
|
+
if (input.argv.includes("--json"))
|
|
43
|
+
return false;
|
|
44
|
+
if (env.RIG_CLI_PLAIN_HELP === "1" || env.RIG_PLAIN === "1")
|
|
45
|
+
return false;
|
|
46
|
+
if (envTruthy(env.CI))
|
|
47
|
+
return false;
|
|
48
|
+
if (!isOpenTuiAppCommand(input.argv, env))
|
|
49
|
+
return false;
|
|
50
|
+
return Boolean(input.stdoutIsTty && input.stdinIsTty);
|
|
51
|
+
}
|
|
52
|
+
export {
|
|
53
|
+
shouldLaunchTtyApp,
|
|
54
|
+
isOpenTuiAppCommand
|
|
55
|
+
};
|
|
@@ -7,10 +7,10 @@ function computeStageLayout(width, height) {
|
|
|
7
7
|
const horizontalGutter = compact ? 1 : 2;
|
|
8
8
|
const centerWidth = Math.max(36, safeWidth - horizontalGutter * 2);
|
|
9
9
|
const centerLeft = horizontalGutter;
|
|
10
|
-
const typeBarTop = Math.max(0, safeHeight - 3);
|
|
11
10
|
const footerTop = Math.max(0, safeHeight - 1);
|
|
11
|
+
const typeBarTop = footerTop;
|
|
12
12
|
const centerTop = compact ? 1 : Math.max(1, Math.floor(safeHeight * 0.055));
|
|
13
|
-
const centerHeight = Math.max(8,
|
|
13
|
+
const centerHeight = Math.max(8, footerTop - centerTop);
|
|
14
14
|
return { width: safeWidth, height: safeHeight, centerWidth, centerLeft, centerTop, centerHeight, typeBarTop, footerTop, compact };
|
|
15
15
|
}
|
|
16
16
|
function visibleWidth(text) {
|
|
@@ -143,17 +143,21 @@ function cleanToken(value) {
|
|
|
143
143
|
const trimmed = value?.trim();
|
|
144
144
|
return trimmed ? trimmed : null;
|
|
145
145
|
}
|
|
146
|
-
function
|
|
146
|
+
function readRemoteAuthState(projectRoot) {
|
|
147
147
|
const path = resolve2(projectRoot, ".rig", "state", "github-auth.json");
|
|
148
148
|
if (!existsSync2(path))
|
|
149
149
|
return null;
|
|
150
150
|
try {
|
|
151
151
|
const parsed = JSON.parse(readFileSync2(path, "utf8"));
|
|
152
|
-
return
|
|
152
|
+
return parsed && typeof parsed === "object" && !Array.isArray(parsed) ? parsed : null;
|
|
153
153
|
} catch {
|
|
154
154
|
return null;
|
|
155
155
|
}
|
|
156
156
|
}
|
|
157
|
+
function readPrivateRemoteSessionToken(projectRoot) {
|
|
158
|
+
const parsed = readRemoteAuthState(projectRoot);
|
|
159
|
+
return parsed ? cleanToken(typeof parsed.apiSessionToken === "string" ? parsed.apiSessionToken : typeof parsed.sessionToken === "string" ? parsed.sessionToken : undefined) : null;
|
|
160
|
+
}
|
|
157
161
|
function readGitHubBearerTokenForRemote(projectRoot) {
|
|
158
162
|
const scopedKey = resolve2(projectRoot);
|
|
159
163
|
if (scopedGitHubBearerTokens.has(scopedKey))
|
|
@@ -164,15 +168,25 @@ function readGitHubBearerTokenForRemote(projectRoot) {
|
|
|
164
168
|
return cleanToken(process.env.RIG_SERVER_AUTH_TOKEN) ?? cleanToken(process.env.RIG_REMOTE_AUTH_TOKEN);
|
|
165
169
|
}
|
|
166
170
|
function readStoredGitHubAuthToken(projectRoot) {
|
|
167
|
-
const
|
|
168
|
-
|
|
171
|
+
const parsed = readRemoteAuthState(projectRoot);
|
|
172
|
+
return parsed ? cleanToken(typeof parsed.token === "string" ? parsed.token : undefined) : null;
|
|
173
|
+
}
|
|
174
|
+
function inferRemoteServerProjectRootFromAuthState(projectRoot) {
|
|
175
|
+
const repo = readRepoConnection(projectRoot);
|
|
176
|
+
const slug = repo?.project?.trim();
|
|
177
|
+
if (!slug || !/^[^/]+\/[^/]+$/.test(slug))
|
|
169
178
|
return null;
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
179
|
+
const auth = readRemoteAuthState(projectRoot);
|
|
180
|
+
const authUpdatedAt = typeof auth?.updatedAt === "string" ? Date.parse(auth.updatedAt) : Number.NaN;
|
|
181
|
+
const repoLinkedAt = typeof repo?.linkedAt === "string" ? Date.parse(repo.linkedAt) : Number.NaN;
|
|
182
|
+
if (Number.isFinite(authUpdatedAt) && Number.isFinite(repoLinkedAt) && authUpdatedAt + 1000 < repoLinkedAt)
|
|
174
183
|
return null;
|
|
175
|
-
|
|
184
|
+
const checkoutBaseDir = typeof auth?.checkoutBaseDir === "string" && auth.checkoutBaseDir.trim() ? auth.checkoutBaseDir.trim() : null;
|
|
185
|
+
if (!checkoutBaseDir)
|
|
186
|
+
return null;
|
|
187
|
+
const inferred = `${checkoutBaseDir.replace(/\/+$/, "")}/${slug}`;
|
|
188
|
+
writeRepoServerProjectRoot(projectRoot, inferred);
|
|
189
|
+
return inferred;
|
|
176
190
|
}
|
|
177
191
|
function readLocalConnectionFallbackToken(projectRoot) {
|
|
178
192
|
return readGitHubBearerTokenForRemote(projectRoot) ?? cleanToken(process.env.RIG_GITHUB_TOKEN) ?? readStoredGitHubAuthToken(projectRoot);
|
|
@@ -183,7 +197,7 @@ async function ensureServerForCli(projectRoot) {
|
|
|
183
197
|
if (selected?.connection.kind === "remote") {
|
|
184
198
|
reportServerPhase(`Connecting to ${selected.alias}\u2026`);
|
|
185
199
|
const authToken = readGitHubBearerTokenForRemote(projectRoot);
|
|
186
|
-
const serverProjectRoot = selected.serverProjectRoot ?? await backfillRemoteServerProjectRoot(projectRoot, selected.connection.baseUrl, authToken);
|
|
200
|
+
const serverProjectRoot = selected.serverProjectRoot ?? inferRemoteServerProjectRootFromAuthState(projectRoot) ?? await backfillRemoteServerProjectRoot(projectRoot, selected.connection.baseUrl, authToken);
|
|
187
201
|
return {
|
|
188
202
|
baseUrl: selected.connection.baseUrl,
|
|
189
203
|
authToken,
|
|
@@ -212,7 +226,10 @@ async function backfillRemoteServerProjectRoot(projectRoot, baseUrl, authToken)
|
|
|
212
226
|
if (!slug)
|
|
213
227
|
return null;
|
|
214
228
|
try {
|
|
215
|
-
const
|
|
229
|
+
const url = new URL(`${baseUrl}/api/projects/${encodeURIComponent(slug)}`);
|
|
230
|
+
if (authToken && queryAuthFallbackEnabled())
|
|
231
|
+
url.searchParams.set("rt", authToken);
|
|
232
|
+
const response = await fetch(url, {
|
|
216
233
|
headers: mergeHeaders(undefined, authToken)
|
|
217
234
|
});
|
|
218
235
|
if (!response.ok)
|
|
@@ -229,10 +246,23 @@ async function backfillRemoteServerProjectRoot(projectRoot, baseUrl, authToken)
|
|
|
229
246
|
return null;
|
|
230
247
|
}
|
|
231
248
|
}
|
|
249
|
+
function mergeCookie(existing, name, value) {
|
|
250
|
+
const encoded = `${name}=${encodeURIComponent(value)}`;
|
|
251
|
+
if (!existing?.trim())
|
|
252
|
+
return encoded;
|
|
253
|
+
const parts = existing.split(";").map((part) => part.trim()).filter((part) => part && !part.startsWith(`${name}=`));
|
|
254
|
+
return [...parts, encoded].join("; ");
|
|
255
|
+
}
|
|
256
|
+
function queryAuthFallbackEnabled(env = process.env) {
|
|
257
|
+
return env.RIG_ENABLE_QUERY_AUTH_FALLBACK === "1" || env.RIG_QUERY_AUTH_FALLBACK === "1";
|
|
258
|
+
}
|
|
232
259
|
function mergeHeaders(headers, authToken) {
|
|
233
260
|
const merged = new Headers(headers);
|
|
234
261
|
if (authToken) {
|
|
235
|
-
|
|
262
|
+
const bearer = `Bearer ${authToken}`;
|
|
263
|
+
merged.set("authorization", bearer);
|
|
264
|
+
merged.set("x-auth", bearer);
|
|
265
|
+
merged.set("cookie", mergeCookie(merged.get("cookie"), "rig_auth", authToken));
|
|
236
266
|
}
|
|
237
267
|
return merged;
|
|
238
268
|
}
|
|
@@ -293,15 +323,34 @@ async function buildServerFailureContext(projectRoot, server) {
|
|
|
293
323
|
hint: "Check the selected server with `rig server status`, or switch with `rig server use <alias|local>`."
|
|
294
324
|
};
|
|
295
325
|
}
|
|
326
|
+
function isLoopbackRemoteBaseUrl(baseUrl) {
|
|
327
|
+
try {
|
|
328
|
+
const host = new URL(baseUrl).hostname.toLowerCase();
|
|
329
|
+
return host === "localhost" || host === "127.0.0.1" || host === "::1" || host === "[::1]";
|
|
330
|
+
} catch {
|
|
331
|
+
return false;
|
|
332
|
+
}
|
|
333
|
+
}
|
|
334
|
+
function canUseRemoteWithoutProjectRoot(pathname) {
|
|
335
|
+
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/");
|
|
336
|
+
}
|
|
296
337
|
async function requestServerJson(context, pathname, init = {}) {
|
|
297
338
|
const server = await ensureServerForCli(context.projectRoot);
|
|
339
|
+
const requestUrl = new URL(`${server.baseUrl}${pathname}`);
|
|
340
|
+
if (server.connectionKind === "remote" && !server.serverProjectRoot && !canUseRemoteWithoutProjectRoot(requestUrl.pathname) && (server.authToken || !isLoopbackRemoteBaseUrl(server.baseUrl))) {
|
|
341
|
+
const repo = readRepoConnection(context.projectRoot);
|
|
342
|
+
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." });
|
|
343
|
+
}
|
|
298
344
|
const headers = mergeHeaders(init.headers, server.authToken);
|
|
299
345
|
if (server.serverProjectRoot)
|
|
300
346
|
headers.set("x-rig-project-root", server.serverProjectRoot);
|
|
347
|
+
if (server.connectionKind === "remote" && server.authToken && queryAuthFallbackEnabled()) {
|
|
348
|
+
requestUrl.searchParams.set("rt", server.authToken);
|
|
349
|
+
}
|
|
301
350
|
reportServerPhase(`${(init.method ?? "GET").toUpperCase()} ${pathname.split("?")[0]}\u2026`);
|
|
302
351
|
let response;
|
|
303
352
|
try {
|
|
304
|
-
response = await fetch(
|
|
353
|
+
response = await fetch(requestUrl, {
|
|
305
354
|
...init,
|
|
306
355
|
headers
|
|
307
356
|
});
|
|
@@ -711,6 +760,7 @@ async function attachRunBundledPiFrontend(context, input) {
|
|
|
711
760
|
let detached = false;
|
|
712
761
|
try {
|
|
713
762
|
await runPiMain([
|
|
763
|
+
"--offline",
|
|
714
764
|
"--no-extensions",
|
|
715
765
|
"--no-skills",
|
|
716
766
|
"--no-prompt-templates",
|
|
@@ -751,9 +801,9 @@ function parseArgs(argv) {
|
|
|
751
801
|
const runId = takeOption(argv, "--run-id")?.trim();
|
|
752
802
|
const projectRoot = takeOption(argv, "--project-root")?.trim();
|
|
753
803
|
if (!runId)
|
|
754
|
-
throw new Error("Missing --run-id for
|
|
804
|
+
throw new Error("Missing --run-id for Pi host child.");
|
|
755
805
|
if (!projectRoot)
|
|
756
|
-
throw new Error("Missing --project-root for
|
|
806
|
+
throw new Error("Missing --project-root for Pi host child.");
|
|
757
807
|
return { runId, projectRoot };
|
|
758
808
|
}
|
|
759
809
|
async function runOpenTuiPiHostChild(argv = process.argv.slice(2)) {
|
|
@@ -767,7 +817,7 @@ if (import.meta.main) {
|
|
|
767
817
|
} catch (error) {
|
|
768
818
|
const message = error instanceof Error ? error.stack || error.message : String(error);
|
|
769
819
|
process.stderr.write(`
|
|
770
|
-
|
|
820
|
+
Pi host failed:
|
|
771
821
|
${message}
|
|
772
822
|
`);
|
|
773
823
|
process.exit(1);
|
|
@@ -1,5 +1,14 @@
|
|
|
1
1
|
import { StyledText } from "@opentui/core";
|
|
2
2
|
export type PiPtyHostStatus = "starting" | "running" | "exited" | "failed";
|
|
3
|
+
/**
|
|
4
|
+
* Temporary embedded-Pi presentation contract.
|
|
5
|
+
*
|
|
6
|
+
* The worker/remote Pi daemon still owns tools, skills, cwd, model, session and
|
|
7
|
+
* context. This local host only mirrors a PTY/xterm viewport into selectable
|
|
8
|
+
* OpenTUI text. Keep this snapshot path explicit until Rig grows a dedicated
|
|
9
|
+
* PTY/xterm viewport renderable or deliberately suspends OpenTUI for direct Pi
|
|
10
|
+
* terminal ownership.
|
|
11
|
+
*/
|
|
3
12
|
export type PiPtyHostSnapshot = {
|
|
4
13
|
readonly runId: string;
|
|
5
14
|
readonly status: PiPtyHostStatus;
|
|
@@ -7,7 +7,9 @@ import { Terminal as XtermTerminal } from "@xterm/headless";
|
|
|
7
7
|
var MIN_COLS = 40;
|
|
8
8
|
var MIN_ROWS = 12;
|
|
9
9
|
var MAX_ROWS = 300;
|
|
10
|
-
var MAX_SNAPSHOT_LINES =
|
|
10
|
+
var MAX_SNAPSHOT_LINES = 360;
|
|
11
|
+
var STYLED_SNAPSHOT_MARGIN = 6;
|
|
12
|
+
var SNAPSHOT_DELAY_MS = 120;
|
|
11
13
|
var fallbackChildScriptPath = fileURLToPath(new URL("./pi-host-child.ts", import.meta.url));
|
|
12
14
|
var activeHost = null;
|
|
13
15
|
function clampCols(cols) {
|
|
@@ -105,17 +107,15 @@ function lineToStyledText(line, cols) {
|
|
|
105
107
|
function childCommandPrefix() {
|
|
106
108
|
const execName = basename(process.execPath).toLowerCase();
|
|
107
109
|
const currentEntry = process.argv[1];
|
|
108
|
-
if (
|
|
109
|
-
return [process.execPath, currentEntry, "__opentui-pi-host"];
|
|
110
|
+
if (execName === "bun" || execName === "bun.exe") {
|
|
111
|
+
return currentEntry ? [process.execPath, currentEntry, "__opentui-pi-host"] : [process.execPath, fallbackChildScriptPath];
|
|
110
112
|
}
|
|
111
|
-
|
|
112
|
-
return [process.execPath, "__opentui-pi-host"];
|
|
113
|
-
return [process.execPath, fallbackChildScriptPath];
|
|
113
|
+
return [process.execPath, "__opentui-pi-host"];
|
|
114
114
|
}
|
|
115
115
|
function withEnv(base) {
|
|
116
116
|
const env = {};
|
|
117
117
|
for (const [key, value] of Object.entries(base ?? process.env)) {
|
|
118
|
-
if (key === "RIG_PLAIN" || key === "RIG_CLI_PLAIN_HELP")
|
|
118
|
+
if (key === "RIG_PLAIN" || key === "RIG_CLI_PLAIN_HELP" || key === "NO_COLOR")
|
|
119
119
|
continue;
|
|
120
120
|
if (typeof value === "string")
|
|
121
121
|
env[key] = value;
|
|
@@ -174,7 +174,7 @@ class PiPtyHost {
|
|
|
174
174
|
allowProposedApi: true,
|
|
175
175
|
cols: this.cols,
|
|
176
176
|
rows: this.rows,
|
|
177
|
-
scrollback:
|
|
177
|
+
scrollback: 1000
|
|
178
178
|
});
|
|
179
179
|
this.registerTerminalResponders();
|
|
180
180
|
}
|
|
@@ -188,7 +188,7 @@ class PiPtyHost {
|
|
|
188
188
|
if (this._disposed)
|
|
189
189
|
throw new Error("Pi PTY host is disposed.");
|
|
190
190
|
if (typeof Bun.Terminal !== "function") {
|
|
191
|
-
throw new Error("Bun native PTY is unavailable in this runtime.
|
|
191
|
+
throw new Error("Bun native PTY is unavailable in this runtime. Pi host requires Bun.Terminal.");
|
|
192
192
|
}
|
|
193
193
|
const spawnOptions = {
|
|
194
194
|
cwd: this.projectRoot,
|
|
@@ -212,7 +212,7 @@ class PiPtyHost {
|
|
|
212
212
|
this.proc = proc;
|
|
213
213
|
this.pty = proc.terminal;
|
|
214
214
|
this.status = "running";
|
|
215
|
-
this.message = "bundled Pi running inside
|
|
215
|
+
this.message = "bundled Pi running inside this app";
|
|
216
216
|
this.emitSnapshotSoon(0);
|
|
217
217
|
proc.exited.then((exitCode) => {
|
|
218
218
|
if (this._disposed)
|
|
@@ -307,7 +307,7 @@ class PiPtyHost {
|
|
|
307
307
|
this.respondToRawTerminalQueries(text);
|
|
308
308
|
this.terminal.write(data, () => this.emitSnapshotSoon());
|
|
309
309
|
}
|
|
310
|
-
emitSnapshotSoon(delayMs =
|
|
310
|
+
emitSnapshotSoon(delayMs = SNAPSHOT_DELAY_MS) {
|
|
311
311
|
if (this._disposed || this.notifyTimer)
|
|
312
312
|
return;
|
|
313
313
|
this.notifyTimer = setTimeout(() => {
|
|
@@ -321,16 +321,18 @@ class PiPtyHost {
|
|
|
321
321
|
const buffer = this.terminal.buffer.active;
|
|
322
322
|
const end = buffer.length;
|
|
323
323
|
const start = Math.max(0, end - MAX_SNAPSHOT_LINES);
|
|
324
|
+
const styledStart = Math.max(start, end - this.rows - STYLED_SNAPSHOT_MARGIN);
|
|
324
325
|
const lines = [];
|
|
325
326
|
const styledLines = [];
|
|
326
327
|
for (let row = start;row < end; row += 1) {
|
|
327
328
|
const line = buffer.getLine(row);
|
|
328
329
|
lines.push((line?.translateToString(false) ?? "").slice(0, this.cols));
|
|
329
|
-
|
|
330
|
+
if (row >= styledStart)
|
|
331
|
+
styledLines[lines.length - 1] = lineToStyledText(line, this.cols);
|
|
330
332
|
}
|
|
331
333
|
while (lines.length < this.rows) {
|
|
332
334
|
lines.push("");
|
|
333
|
-
styledLines.
|
|
335
|
+
styledLines[lines.length - 1] = new StyledText([{ __isChunk: true, text: "" }]);
|
|
334
336
|
}
|
|
335
337
|
return {
|
|
336
338
|
runId: this.runId,
|