@h-rig/cli 0.0.6-alpha.82 → 0.0.6-alpha.84
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 +23402 -14577
- package/dist/src/app/board.js +217 -41
- 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.d.ts +0 -2
- package/dist/src/app-opentui/adapters/doctor.js +64 -39
- package/dist/src/app-opentui/adapters/family.d.ts +62 -0
- package/dist/src/app-opentui/adapters/family.js +14305 -0
- package/dist/src/app-opentui/adapters/fleet.d.ts +0 -2
- package/dist/src/app-opentui/adapters/fleet.js +90 -60
- package/dist/src/app-opentui/adapters/inbox.d.ts +12 -2
- package/dist/src/app-opentui/adapters/inbox.js +137 -78
- package/dist/src/app-opentui/adapters/init.d.ts +0 -2
- package/dist/src/app-opentui/adapters/init.js +85 -47
- package/dist/src/app-opentui/adapters/inspect.d.ts +52 -0
- package/dist/src/app-opentui/adapters/inspect.js +1024 -0
- package/dist/src/app-opentui/adapters/pi-attach.d.ts +15 -6
- package/dist/src/app-opentui/adapters/pi-attach.js +442 -125
- package/dist/src/app-opentui/adapters/pi.d.ts +23 -0
- package/dist/src/app-opentui/adapters/pi.js +363 -0
- package/dist/src/app-opentui/adapters/plugin.d.ts +84 -0
- package/dist/src/app-opentui/adapters/plugin.js +544 -0
- package/dist/src/app-opentui/adapters/repo.d.ts +37 -0
- package/dist/src/app-opentui/adapters/repo.js +186 -0
- package/dist/src/app-opentui/adapters/run-detail.d.ts +9 -2
- package/dist/src/app-opentui/adapters/run-detail.js +180 -63
- package/dist/src/app-opentui/adapters/server.d.ts +10 -2
- package/dist/src/app-opentui/adapters/server.js +191 -45
- package/dist/src/app-opentui/adapters/tasks.d.ts +11 -2
- package/dist/src/app-opentui/adapters/tasks.js +1123 -143
- package/dist/src/app-opentui/adapters/workspace.d.ts +49 -0
- package/dist/src/app-opentui/adapters/workspace.js +333 -0
- package/dist/src/app-opentui/autocomplete.d.ts +20 -0
- package/dist/src/app-opentui/autocomplete.js +576 -0
- package/dist/src/app-opentui/bootstrap.d.ts +1 -6
- package/dist/src/app-opentui/bootstrap.js +25252 -16474
- package/dist/src/app-opentui/command-palette.d.ts +3 -0
- package/dist/src/app-opentui/command-palette.js +1010 -0
- 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/drone.js +8 -6
- package/dist/src/app-opentui/events.js +1 -1
- package/dist/src/app-opentui/fleet-stats.d.ts +32 -0
- package/dist/src/app-opentui/fleet-stats.js +114 -0
- 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 +5431 -2797
- package/dist/src/app-opentui/intent.js +179 -50
- package/dist/src/app-opentui/keymap.d.ts +21 -0
- package/dist/src/app-opentui/keymap.js +1748 -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.d.ts +7 -0
- package/dist/src/app-opentui/layout.js +13 -6
- package/dist/src/app-opentui/list-search.d.ts +24 -0
- package/dist/src/app-opentui/list-search.js +88 -1
- package/dist/src/app-opentui/pi-host-child.js +99 -17
- package/dist/src/app-opentui/pi-pty-host.d.ts +14 -0
- package/dist/src/app-opentui/pi-pty-host.js +30 -14
- package/dist/src/app-opentui/react/App.d.ts +9 -0
- package/dist/src/app-opentui/react/App.js +5144 -0
- package/dist/src/app-opentui/react/Backdrop.d.ts +5 -0
- package/dist/src/app-opentui/react/Backdrop.js +1834 -0
- package/dist/src/app-opentui/react/ChromeHost.d.ts +5 -0
- package/dist/src/app-opentui/react/ChromeHost.js +2942 -0
- package/dist/src/app-opentui/react/SceneFrameView.d.ts +7 -0
- package/dist/src/app-opentui/react/SceneFrameView.js +529 -0
- package/dist/src/app-opentui/react/context.d.ts +17 -0
- package/dist/src/app-opentui/react/context.js +37 -0
- package/dist/src/app-opentui/react/launch.d.ts +2 -0
- package/dist/src/app-opentui/react/launch.js +5743 -0
- package/dist/src/app-opentui/react/nav.d.ts +18 -0
- package/dist/src/app-opentui/react/nav.js +54 -0
- package/dist/src/app-opentui/react/scroll.d.ts +12 -0
- package/dist/src/app-opentui/react/scroll.js +21 -0
- package/dist/src/app-opentui/react/syntax.d.ts +2 -0
- package/dist/src/app-opentui/react/syntax.js +64 -0
- package/dist/src/app-opentui/registry.js +20428 -4828
- 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/constants.d.ts +31 -0
- package/dist/src/app-opentui/render/constants.js +66 -0
- package/dist/src/app-opentui/render/graphics.d.ts +2 -1
- package/dist/src/app-opentui/render/graphics.js +228 -46
- package/dist/src/app-opentui/render/image-visual-layer-worker.js +469 -930
- package/dist/src/app-opentui/render/image-visual-layer.d.ts +25 -10
- package/dist/src/app-opentui/render/image-visual-layer.js +448 -329
- package/dist/src/app-opentui/render/native-host.d.ts +37 -0
- package/dist/src/app-opentui/render/native-host.js +179 -0
- 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 +78 -38
- package/dist/src/app-opentui/render/preloader.d.ts +10 -0
- package/dist/src/app-opentui/render/preloader.js +165 -0
- package/dist/src/app-opentui/render/scene.d.ts +53 -1
- package/dist/src/app-opentui/render/scene.js +195 -6
- package/dist/src/app-opentui/render/text.d.ts +7 -1
- package/dist/src/app-opentui/render/text.js +15 -8
- package/dist/src/app-opentui/render/type-bar.d.ts +2 -1
- package/dist/src/app-opentui/render/type-bar.js +113 -39
- 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.d.ts +44 -1
- package/dist/src/app-opentui/runtime.js +5415 -2738
- package/dist/src/app-opentui/scenes/command.d.ts +3 -0
- package/dist/src/app-opentui/scenes/command.js +117 -0
- package/dist/src/app-opentui/scenes/doctor.d.ts +2 -1
- package/dist/src/app-opentui/scenes/doctor.js +221 -17
- package/dist/src/app-opentui/scenes/error.js +60 -20
- package/dist/src/app-opentui/scenes/family-domains/agent.d.ts +2 -0
- package/dist/src/app-opentui/scenes/family-domains/agent.js +348 -0
- package/dist/src/app-opentui/scenes/family-domains/browser.d.ts +2 -0
- package/dist/src/app-opentui/scenes/family-domains/browser.js +195 -0
- package/dist/src/app-opentui/scenes/family-domains/dist.d.ts +2 -0
- package/dist/src/app-opentui/scenes/family-domains/dist.js +243 -0
- package/dist/src/app-opentui/scenes/family-domains/git.d.ts +2 -0
- package/dist/src/app-opentui/scenes/family-domains/git.js +195 -0
- package/dist/src/app-opentui/scenes/family-domains/github.d.ts +2 -0
- package/dist/src/app-opentui/scenes/family-domains/github.js +274 -0
- package/dist/src/app-opentui/scenes/family-domains/harness.d.ts +2 -0
- package/dist/src/app-opentui/scenes/family-domains/harness.js +152 -0
- package/dist/src/app-opentui/scenes/family-domains/index.d.ts +4 -0
- package/dist/src/app-opentui/scenes/family-domains/index.js +1679 -0
- package/dist/src/app-opentui/scenes/family-domains/kit.d.ts +76 -0
- package/dist/src/app-opentui/scenes/family-domains/kit.js +305 -0
- package/dist/src/app-opentui/scenes/family-domains/profile.d.ts +2 -0
- package/dist/src/app-opentui/scenes/family-domains/profile.js +212 -0
- package/dist/src/app-opentui/scenes/family-domains/queue.d.ts +2 -0
- package/dist/src/app-opentui/scenes/family-domains/queue.js +146 -0
- package/dist/src/app-opentui/scenes/family-domains/remote.d.ts +2 -0
- package/dist/src/app-opentui/scenes/family-domains/remote.js +518 -0
- package/dist/src/app-opentui/scenes/family-domains/review.d.ts +2 -0
- package/dist/src/app-opentui/scenes/family-domains/review.js +280 -0
- package/dist/src/app-opentui/scenes/family-domains/setup.d.ts +2 -0
- package/dist/src/app-opentui/scenes/family-domains/setup.js +267 -0
- package/dist/src/app-opentui/scenes/family-domains/stats.d.ts +2 -0
- package/dist/src/app-opentui/scenes/family-domains/stats.js +370 -0
- package/dist/src/app-opentui/scenes/family.d.ts +3 -0
- package/dist/src/app-opentui/scenes/family.js +2144 -0
- package/dist/src/app-opentui/scenes/fleet.js +552 -43
- package/dist/src/app-opentui/scenes/handoff.js +342 -35
- package/dist/src/app-opentui/scenes/help.js +640 -56
- package/dist/src/app-opentui/scenes/inbox.d.ts +2 -1
- package/dist/src/app-opentui/scenes/inbox.js +329 -21
- package/dist/src/app-opentui/scenes/init.d.ts +2 -1
- package/dist/src/app-opentui/scenes/init.js +120 -34
- package/dist/src/app-opentui/scenes/inspect.d.ts +3 -0
- package/dist/src/app-opentui/scenes/inspect.js +793 -0
- package/dist/src/app-opentui/scenes/main.d.ts +2 -1
- package/dist/src/app-opentui/scenes/main.js +264 -29
- package/dist/src/app-opentui/scenes/pi.d.ts +3 -0
- package/dist/src/app-opentui/scenes/pi.js +508 -0
- package/dist/src/app-opentui/scenes/plugin.d.ts +3 -0
- package/dist/src/app-opentui/scenes/plugin.js +486 -0
- package/dist/src/app-opentui/scenes/repo.d.ts +3 -0
- package/dist/src/app-opentui/scenes/repo.js +424 -0
- package/dist/src/app-opentui/scenes/run-detail.d.ts +2 -1
- package/dist/src/app-opentui/scenes/run-detail.js +362 -35
- package/dist/src/app-opentui/scenes/server.d.ts +2 -1
- package/dist/src/app-opentui/scenes/server.js +243 -26
- package/dist/src/app-opentui/scenes/tasks.js +518 -41
- package/dist/src/app-opentui/scenes/workspace.d.ts +3 -0
- package/dist/src/app-opentui/scenes/workspace.js +426 -0
- package/dist/src/app-opentui/selectable.d.ts +19 -0
- package/dist/src/app-opentui/selectable.js +79 -0
- package/dist/src/app-opentui/state.js +129 -36
- package/dist/src/app-opentui/surface-catalog.d.ts +20 -0
- package/dist/src/app-opentui/surface-catalog.js +540 -0
- 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/theme.d.ts +28 -6
- package/dist/src/app-opentui/theme.js +61 -8
- package/dist/src/app-opentui/types.d.ts +130 -5
- package/dist/src/commands/_authority-runs.d.ts +1 -1
- package/dist/src/commands/_authority-runs.js +2 -12
- package/dist/src/commands/_doctor-checks.js +62 -13
- package/dist/src/commands/_help-catalog.js +95 -15
- package/dist/src/commands/_operator-view.js +97 -15
- package/dist/src/commands/_pi-frontend.d.ts +2 -0
- package/dist/src/commands/_pi-frontend.js +97 -13
- package/dist/src/commands/_preflight.js +64 -14
- package/dist/src/commands/_server-client.js +82 -18
- package/dist/src/commands/_server-events.d.ts +26 -0
- package/dist/src/commands/_server-events.js +310 -0
- package/dist/src/commands/_snapshot-upload.js +62 -13
- package/dist/src/commands/agent.js +2 -12
- 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 +100 -15
- package/dist/src/commands/server.js +71 -26
- package/dist/src/commands/setup.js +62 -13
- package/dist/src/commands/stats.js +157 -28
- package/dist/src/commands/task-run-driver.js +64 -25
- package/dist/src/commands/task.js +196 -43
- package/dist/src/commands.js +419 -123
- package/dist/src/index.js +426 -130
- package/package.json +11 -10
- 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
|
@@ -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
|
+
};
|
|
@@ -8,7 +8,14 @@ export type StageLayout = {
|
|
|
8
8
|
readonly typeBarTop: number;
|
|
9
9
|
readonly footerTop: number;
|
|
10
10
|
readonly compact: boolean;
|
|
11
|
+
/** Raw terminal is below the usable threshold; render a guidance message
|
|
12
|
+
* instead of a scene that would clip into nothing (#18). */
|
|
13
|
+
readonly tooSmall: boolean;
|
|
11
14
|
};
|
|
15
|
+
/** Below these raw dimensions, ad-terminal panels collapse below their 4×4
|
|
16
|
+
* floor and the UI would blank silently. We show a "resize me" card instead. */
|
|
17
|
+
export declare const MIN_TERMINAL_WIDTH = 48;
|
|
18
|
+
export declare const MIN_TERMINAL_HEIGHT = 16;
|
|
12
19
|
export declare function computeStageLayout(width: number, height: number): StageLayout;
|
|
13
20
|
export declare function visibleWidth(text: string): number;
|
|
14
21
|
export declare function alignText(text: string, width: number, align?: "left" | "center" | "right"): string;
|
|
@@ -1,17 +1,22 @@
|
|
|
1
1
|
// @bun
|
|
2
2
|
// packages/cli/src/app-opentui/layout.ts
|
|
3
|
+
var MIN_TERMINAL_WIDTH = 48;
|
|
4
|
+
var MIN_TERMINAL_HEIGHT = 16;
|
|
3
5
|
function computeStageLayout(width, height) {
|
|
4
|
-
const
|
|
5
|
-
const
|
|
6
|
+
const rawWidth = Math.trunc(width || 80);
|
|
7
|
+
const rawHeight = Math.trunc(height || 24);
|
|
8
|
+
const tooSmall = rawWidth < MIN_TERMINAL_WIDTH || rawHeight < MIN_TERMINAL_HEIGHT;
|
|
9
|
+
const safeWidth = Math.max(40, rawWidth);
|
|
10
|
+
const safeHeight = Math.max(18, rawHeight);
|
|
6
11
|
const compact = safeWidth < 92 || safeHeight < 30;
|
|
7
12
|
const horizontalGutter = compact ? 1 : 2;
|
|
8
13
|
const centerWidth = Math.max(36, safeWidth - horizontalGutter * 2);
|
|
9
14
|
const centerLeft = horizontalGutter;
|
|
10
|
-
const typeBarTop = Math.max(0, safeHeight - 3);
|
|
11
15
|
const footerTop = Math.max(0, safeHeight - 1);
|
|
16
|
+
const typeBarTop = Math.max(0, footerTop - 1);
|
|
12
17
|
const centerTop = compact ? 1 : Math.max(1, Math.floor(safeHeight * 0.055));
|
|
13
|
-
const centerHeight = Math.max(
|
|
14
|
-
return { width: safeWidth, height: safeHeight, centerWidth, centerLeft, centerTop, centerHeight, typeBarTop, footerTop, compact };
|
|
18
|
+
const centerHeight = Math.max(6, typeBarTop - centerTop);
|
|
19
|
+
return { width: safeWidth, height: safeHeight, centerWidth, centerLeft, centerTop, centerHeight, typeBarTop, footerTop, compact, tooSmall };
|
|
15
20
|
}
|
|
16
21
|
function visibleWidth(text) {
|
|
17
22
|
return [...text].length;
|
|
@@ -40,5 +45,7 @@ export {
|
|
|
40
45
|
visibleWidth,
|
|
41
46
|
truncateText,
|
|
42
47
|
computeStageLayout,
|
|
43
|
-
alignText
|
|
48
|
+
alignText,
|
|
49
|
+
MIN_TERMINAL_WIDTH,
|
|
50
|
+
MIN_TERMINAL_HEIGHT
|
|
44
51
|
};
|
|
@@ -6,3 +6,27 @@ export declare function listSearchQuery(state: AppState): string;
|
|
|
6
6
|
export declare function filterRunsForSearch(runs: readonly AppRunRecord[], query: string): AppRunRecord[];
|
|
7
7
|
export declare function filterTasksForSearch(tasks: readonly AppTaskRecord[], query: string): AppTaskRecord[];
|
|
8
8
|
export declare function searchSummary(kind: "runs" | "tasks", query: string, visible: number, total: number): string;
|
|
9
|
+
export type SortDir = "asc" | "desc";
|
|
10
|
+
export type RunSortKey = "status" | "runId" | "title" | "age";
|
|
11
|
+
export type RunSortSpec = {
|
|
12
|
+
readonly key: RunSortKey;
|
|
13
|
+
readonly dir: SortDir;
|
|
14
|
+
};
|
|
15
|
+
export type TaskSortKey = "status" | "id" | "priority" | "assignee" | "title";
|
|
16
|
+
export type TaskSortSpec = {
|
|
17
|
+
readonly key: TaskSortKey;
|
|
18
|
+
readonly dir: SortDir;
|
|
19
|
+
};
|
|
20
|
+
/** Read a typed run sort spec off the data bag, narrowing the `unknown` slice. */
|
|
21
|
+
export declare function runSortSpec(value: unknown): RunSortSpec | null;
|
|
22
|
+
/** Read a typed task sort spec off the data bag, narrowing the `unknown` slice. */
|
|
23
|
+
export declare function taskSortSpec(value: unknown): TaskSortSpec | null;
|
|
24
|
+
/** Cycle a column's sort: clicking the active column flips asc→desc→default;
|
|
25
|
+
* clicking a new column starts it ascending. `null` means "back to default". */
|
|
26
|
+
export declare function cycleRunSort(current: RunSortSpec | null, key: RunSortKey): RunSortSpec | null;
|
|
27
|
+
export declare function cycleTaskSort(current: TaskSortSpec | null, key: TaskSortKey): TaskSortSpec | null;
|
|
28
|
+
/** Sort runs by a column. `spec === null` returns the input order unchanged. */
|
|
29
|
+
export declare function sortRuns(runs: readonly AppRunRecord[], spec: RunSortSpec | null): AppRunRecord[];
|
|
30
|
+
/** Sort tasks by a column, client-side over already-fetched records. `spec ===
|
|
31
|
+
* null` returns the input order unchanged (server/adapter order is preserved). */
|
|
32
|
+
export declare function sortTasks(tasks: readonly AppTaskRecord[], spec: TaskSortSpec | null): AppTaskRecord[];
|
|
@@ -34,10 +34,97 @@ function filterTasksForSearch(tasks, query) {
|
|
|
34
34
|
function searchSummary(kind, query, visible, total) {
|
|
35
35
|
return query.trim() ? `search ${JSON.stringify(query)} \xB7 ${visible}/${total} ${kind}` : `${total} ${kind}`;
|
|
36
36
|
}
|
|
37
|
+
function runSortSpec(value) {
|
|
38
|
+
if (!value || typeof value !== "object" || Array.isArray(value))
|
|
39
|
+
return null;
|
|
40
|
+
const record = value;
|
|
41
|
+
const key = record.key;
|
|
42
|
+
const dir = record.dir;
|
|
43
|
+
if ((key === "status" || key === "runId" || key === "title" || key === "age") && (dir === "asc" || dir === "desc")) {
|
|
44
|
+
return { key, dir };
|
|
45
|
+
}
|
|
46
|
+
return null;
|
|
47
|
+
}
|
|
48
|
+
function taskSortSpec(value) {
|
|
49
|
+
if (!value || typeof value !== "object" || Array.isArray(value))
|
|
50
|
+
return null;
|
|
51
|
+
const record = value;
|
|
52
|
+
const key = record.key;
|
|
53
|
+
const dir = record.dir;
|
|
54
|
+
if ((key === "status" || key === "id" || key === "priority" || key === "assignee" || key === "title") && (dir === "asc" || dir === "desc")) {
|
|
55
|
+
return { key, dir };
|
|
56
|
+
}
|
|
57
|
+
return null;
|
|
58
|
+
}
|
|
59
|
+
function cycleRunSort(current, key) {
|
|
60
|
+
if (!current || current.key !== key)
|
|
61
|
+
return { key, dir: "asc" };
|
|
62
|
+
if (current.dir === "asc")
|
|
63
|
+
return { key, dir: "desc" };
|
|
64
|
+
return null;
|
|
65
|
+
}
|
|
66
|
+
function cycleTaskSort(current, key) {
|
|
67
|
+
if (!current || current.key !== key)
|
|
68
|
+
return { key, dir: "asc" };
|
|
69
|
+
if (current.dir === "asc")
|
|
70
|
+
return { key, dir: "desc" };
|
|
71
|
+
return null;
|
|
72
|
+
}
|
|
73
|
+
function ageValue(updatedAt) {
|
|
74
|
+
const parsed = Date.parse(updatedAt ?? "");
|
|
75
|
+
return Number.isFinite(parsed) ? parsed : 0;
|
|
76
|
+
}
|
|
77
|
+
function applyDir(rows, compare, dir) {
|
|
78
|
+
const sorted = [...rows].sort(compare);
|
|
79
|
+
return dir === "desc" ? sorted.reverse() : sorted;
|
|
80
|
+
}
|
|
81
|
+
function sortRuns(runs, spec) {
|
|
82
|
+
if (!spec)
|
|
83
|
+
return [...runs];
|
|
84
|
+
const { key, dir } = spec;
|
|
85
|
+
if (key === "age") {
|
|
86
|
+
const byNewest = applyDir(runs, (a, b) => ageValue(b.updatedAt) - ageValue(a.updatedAt) || a.runId.localeCompare(b.runId), "asc");
|
|
87
|
+
return dir === "asc" ? byNewest : [...byNewest].reverse();
|
|
88
|
+
}
|
|
89
|
+
const compare = (a, b) => {
|
|
90
|
+
if (key === "status")
|
|
91
|
+
return a.status.localeCompare(b.status) || a.runId.localeCompare(b.runId);
|
|
92
|
+
if (key === "title")
|
|
93
|
+
return a.title.localeCompare(b.title, undefined, { sensitivity: "base" }) || a.runId.localeCompare(b.runId);
|
|
94
|
+
return a.runId.localeCompare(b.runId, undefined, { numeric: true });
|
|
95
|
+
};
|
|
96
|
+
return applyDir(runs, compare, dir);
|
|
97
|
+
}
|
|
98
|
+
function sortTasks(tasks, spec) {
|
|
99
|
+
if (!spec)
|
|
100
|
+
return [...tasks];
|
|
101
|
+
const { key, dir } = spec;
|
|
102
|
+
const compare = (a, b) => {
|
|
103
|
+
if (key === "status")
|
|
104
|
+
return a.status.localeCompare(b.status) || a.id.localeCompare(b.id, undefined, { numeric: true });
|
|
105
|
+
if (key === "priority") {
|
|
106
|
+
const left = a.priority ?? Number.NEGATIVE_INFINITY;
|
|
107
|
+
const right = b.priority ?? Number.NEGATIVE_INFINITY;
|
|
108
|
+
return right - left || a.id.localeCompare(b.id, undefined, { numeric: true });
|
|
109
|
+
}
|
|
110
|
+
if (key === "assignee")
|
|
111
|
+
return (a.assignees[0] ?? "").localeCompare(b.assignees[0] ?? "") || a.id.localeCompare(b.id, undefined, { numeric: true });
|
|
112
|
+
if (key === "title")
|
|
113
|
+
return a.title.localeCompare(b.title, undefined, { sensitivity: "base" }) || a.id.localeCompare(b.id, undefined, { numeric: true });
|
|
114
|
+
return a.id.localeCompare(b.id, undefined, { numeric: true });
|
|
115
|
+
};
|
|
116
|
+
return applyDir(tasks, compare, dir);
|
|
117
|
+
}
|
|
37
118
|
export {
|
|
119
|
+
taskSortSpec,
|
|
120
|
+
sortTasks,
|
|
121
|
+
sortRuns,
|
|
38
122
|
searchSummary,
|
|
123
|
+
runSortSpec,
|
|
39
124
|
listSearchQuery,
|
|
40
125
|
isListSearchScene,
|
|
41
126
|
filterTasksForSearch,
|
|
42
|
-
filterRunsForSearch
|
|
127
|
+
filterRunsForSearch,
|
|
128
|
+
cycleTaskSort,
|
|
129
|
+
cycleRunSort
|
|
43
130
|
};
|
|
@@ -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
|
});
|
|
@@ -538,6 +587,14 @@ async function withSpinner(label, work, options = {}) {
|
|
|
538
587
|
}
|
|
539
588
|
|
|
540
589
|
// packages/cli/src/commands/_pi-frontend.ts
|
|
590
|
+
var TERMINAL_RUN_STATUSES = new Set(["completed", "failed", "stopped", "cancelled", "canceled", "closed", "merged", "needs_attention", "needs-attention"]);
|
|
591
|
+
|
|
592
|
+
class OperatorTranscriptUnavailableError extends Error {
|
|
593
|
+
constructor(message) {
|
|
594
|
+
super(message);
|
|
595
|
+
this.name = "OperatorTranscriptUnavailableError";
|
|
596
|
+
}
|
|
597
|
+
}
|
|
541
598
|
function setTemporaryEnv(updates) {
|
|
542
599
|
const previous = new Map;
|
|
543
600
|
for (const [key, value] of Object.entries(updates)) {
|
|
@@ -565,6 +622,23 @@ function buildOperatorPiEnv(input) {
|
|
|
565
622
|
...input.serverProjectRoot ? { RIG_PROJECT_ROOT: input.serverProjectRoot } : {}
|
|
566
623
|
};
|
|
567
624
|
}
|
|
625
|
+
function shouldRequireOperatorTranscript(status) {
|
|
626
|
+
return typeof status === "string" && TERMINAL_RUN_STATUSES.has(status.trim().toLowerCase());
|
|
627
|
+
}
|
|
628
|
+
function missingOperatorTranscriptMessage(runId, status) {
|
|
629
|
+
const shortRun = runId.trim().slice(0, 8) || "unknown";
|
|
630
|
+
const statusText = typeof status === "string" && status.trim() ? status.trim() : "terminal";
|
|
631
|
+
return `Run ${shortRun} is ${statusText}, but no worker Pi session transcript is recorded for this run. It likely failed before Pi started or predates full-session capture. Use \`rig run show ${runId}\` and \`rig run logs ${runId}\` for the lifecycle details.`;
|
|
632
|
+
}
|
|
633
|
+
function statusFromRunDetails(run) {
|
|
634
|
+
if (!run || typeof run !== "object" || Array.isArray(run))
|
|
635
|
+
return;
|
|
636
|
+
const record = run;
|
|
637
|
+
if (typeof record.status === "string")
|
|
638
|
+
return record.status;
|
|
639
|
+
const nested = record.run;
|
|
640
|
+
return nested && typeof nested === "object" && !Array.isArray(nested) ? nested.status : undefined;
|
|
641
|
+
}
|
|
568
642
|
async function prepareOperatorConsole(context, runId, tempSessionDir) {
|
|
569
643
|
const server = await ensureServerForCli(context.projectRoot);
|
|
570
644
|
const localCwd = join(homedir2(), ".rig", "drones", runId.slice(0, 8));
|
|
@@ -594,6 +668,13 @@ async function prepareOperatorConsole(context, runId, tempSessionDir) {
|
|
|
594
668
|
sessionFileArg = ["--session", localSessionPath];
|
|
595
669
|
}
|
|
596
670
|
} catch {}
|
|
671
|
+
if (sessionFileArg.length === 0) {
|
|
672
|
+
const run = await getRunDetailsViaServer(context, runId).catch(() => null);
|
|
673
|
+
const status = statusFromRunDetails(run);
|
|
674
|
+
if (shouldRequireOperatorTranscript(status)) {
|
|
675
|
+
throw new OperatorTranscriptUnavailableError(missingOperatorTranscriptMessage(runId, status));
|
|
676
|
+
}
|
|
677
|
+
}
|
|
597
678
|
return { server, sessionFileArg };
|
|
598
679
|
}
|
|
599
680
|
var RIG_PI_THEME = {
|
|
@@ -711,6 +792,7 @@ async function attachRunBundledPiFrontend(context, input) {
|
|
|
711
792
|
let detached = false;
|
|
712
793
|
try {
|
|
713
794
|
await runPiMain([
|
|
795
|
+
"--offline",
|
|
714
796
|
"--no-extensions",
|
|
715
797
|
"--no-skills",
|
|
716
798
|
"--no-prompt-templates",
|
|
@@ -751,9 +833,9 @@ function parseArgs(argv) {
|
|
|
751
833
|
const runId = takeOption(argv, "--run-id")?.trim();
|
|
752
834
|
const projectRoot = takeOption(argv, "--project-root")?.trim();
|
|
753
835
|
if (!runId)
|
|
754
|
-
throw new Error("Missing --run-id for
|
|
836
|
+
throw new Error("Missing --run-id for Pi host child.");
|
|
755
837
|
if (!projectRoot)
|
|
756
|
-
throw new Error("Missing --project-root for
|
|
838
|
+
throw new Error("Missing --project-root for Pi host child.");
|
|
757
839
|
return { runId, projectRoot };
|
|
758
840
|
}
|
|
759
841
|
async function runOpenTuiPiHostChild(argv = process.argv.slice(2)) {
|
|
@@ -765,9 +847,9 @@ if (import.meta.main) {
|
|
|
765
847
|
try {
|
|
766
848
|
await runOpenTuiPiHostChild();
|
|
767
849
|
} catch (error) {
|
|
768
|
-
const message = error instanceof Error ? error.stack || error.message : String(error);
|
|
850
|
+
const message = error instanceof Error ? error.name === "OperatorTranscriptUnavailableError" ? error.message : error.stack || error.message : String(error);
|
|
769
851
|
process.stderr.write(`
|
|
770
|
-
|
|
852
|
+
Pi host failed:
|
|
771
853
|
${message}
|
|
772
854
|
`);
|
|
773
855
|
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;
|
|
@@ -8,6 +17,7 @@ export type PiPtyHostSnapshot = {
|
|
|
8
17
|
readonly lines: readonly string[];
|
|
9
18
|
readonly styledLines?: readonly StyledText[];
|
|
10
19
|
readonly message?: string;
|
|
20
|
+
readonly resizeError?: string;
|
|
11
21
|
readonly exitCode?: number;
|
|
12
22
|
readonly signal?: string | null;
|
|
13
23
|
};
|
|
@@ -40,9 +50,13 @@ export declare class PiPtyHost {
|
|
|
40
50
|
private cols;
|
|
41
51
|
private rows;
|
|
42
52
|
private message;
|
|
53
|
+
private lastResizeError;
|
|
43
54
|
private exitCode;
|
|
44
55
|
private signal;
|
|
45
56
|
private notifyTimer;
|
|
57
|
+
/** Last streamed buffer content, so the onWriteParsed handler only emits a
|
|
58
|
+
* snapshot when the rendered content actually changed. */
|
|
59
|
+
private lastStreamKey;
|
|
46
60
|
private _disposed;
|
|
47
61
|
constructor(options: PiPtyHostOptions);
|
|
48
62
|
get disposed(): boolean;
|
|
@@ -8,8 +8,8 @@ var MIN_COLS = 40;
|
|
|
8
8
|
var MIN_ROWS = 12;
|
|
9
9
|
var MAX_ROWS = 300;
|
|
10
10
|
var MAX_SNAPSHOT_LINES = 360;
|
|
11
|
-
var STYLED_SNAPSHOT_MARGIN =
|
|
12
|
-
var SNAPSHOT_DELAY_MS =
|
|
11
|
+
var STYLED_SNAPSHOT_MARGIN = 6;
|
|
12
|
+
var SNAPSHOT_DELAY_MS = 120;
|
|
13
13
|
var fallbackChildScriptPath = fileURLToPath(new URL("./pi-host-child.ts", import.meta.url));
|
|
14
14
|
var activeHost = null;
|
|
15
15
|
function clampCols(cols) {
|
|
@@ -107,17 +107,15 @@ function lineToStyledText(line, cols) {
|
|
|
107
107
|
function childCommandPrefix() {
|
|
108
108
|
const execName = basename(process.execPath).toLowerCase();
|
|
109
109
|
const currentEntry = process.argv[1];
|
|
110
|
-
if (
|
|
111
|
-
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];
|
|
112
112
|
}
|
|
113
|
-
|
|
114
|
-
return [process.execPath, "__opentui-pi-host"];
|
|
115
|
-
return [process.execPath, fallbackChildScriptPath];
|
|
113
|
+
return [process.execPath, "__opentui-pi-host"];
|
|
116
114
|
}
|
|
117
115
|
function withEnv(base) {
|
|
118
116
|
const env = {};
|
|
119
117
|
for (const [key, value] of Object.entries(base ?? process.env)) {
|
|
120
|
-
if (key === "RIG_PLAIN" || key === "RIG_CLI_PLAIN_HELP")
|
|
118
|
+
if (key === "RIG_PLAIN" || key === "RIG_CLI_PLAIN_HELP" || key === "NO_COLOR")
|
|
121
119
|
continue;
|
|
122
120
|
if (typeof value === "string")
|
|
123
121
|
env[key] = value;
|
|
@@ -160,9 +158,11 @@ class PiPtyHost {
|
|
|
160
158
|
cols;
|
|
161
159
|
rows;
|
|
162
160
|
message = "starting bundled Pi";
|
|
161
|
+
lastResizeError = null;
|
|
163
162
|
exitCode;
|
|
164
163
|
signal;
|
|
165
164
|
notifyTimer = null;
|
|
165
|
+
lastStreamKey = "";
|
|
166
166
|
_disposed = false;
|
|
167
167
|
constructor(options) {
|
|
168
168
|
this.runId = options.runId;
|
|
@@ -176,9 +176,20 @@ class PiPtyHost {
|
|
|
176
176
|
allowProposedApi: true,
|
|
177
177
|
cols: this.cols,
|
|
178
178
|
rows: this.rows,
|
|
179
|
-
scrollback:
|
|
179
|
+
scrollback: 1000
|
|
180
180
|
});
|
|
181
181
|
this.registerTerminalResponders();
|
|
182
|
+
this.disposables.push(this.terminal.onWriteParsed(() => {
|
|
183
|
+
if (this._disposed)
|
|
184
|
+
return;
|
|
185
|
+
const snapshot = this.createSnapshot();
|
|
186
|
+
const key = snapshot.lines.join(`
|
|
187
|
+
`);
|
|
188
|
+
if (key === this.lastStreamKey)
|
|
189
|
+
return;
|
|
190
|
+
this.lastStreamKey = key;
|
|
191
|
+
this.onSnapshot?.(snapshot);
|
|
192
|
+
}));
|
|
182
193
|
}
|
|
183
194
|
get disposed() {
|
|
184
195
|
return this._disposed;
|
|
@@ -190,7 +201,7 @@ class PiPtyHost {
|
|
|
190
201
|
if (this._disposed)
|
|
191
202
|
throw new Error("Pi PTY host is disposed.");
|
|
192
203
|
if (typeof Bun.Terminal !== "function") {
|
|
193
|
-
throw new Error("Bun native PTY is unavailable in this runtime.
|
|
204
|
+
throw new Error("Bun native PTY is unavailable in this runtime. Pi host requires Bun.Terminal.");
|
|
194
205
|
}
|
|
195
206
|
const spawnOptions = {
|
|
196
207
|
cwd: this.projectRoot,
|
|
@@ -214,7 +225,7 @@ class PiPtyHost {
|
|
|
214
225
|
this.proc = proc;
|
|
215
226
|
this.pty = proc.terminal;
|
|
216
227
|
this.status = "running";
|
|
217
|
-
this.message = "bundled Pi running inside
|
|
228
|
+
this.message = "bundled Pi running inside this app";
|
|
218
229
|
this.emitSnapshotSoon(0);
|
|
219
230
|
proc.exited.then((exitCode) => {
|
|
220
231
|
if (this._disposed)
|
|
@@ -265,7 +276,10 @@ class PiPtyHost {
|
|
|
265
276
|
this.terminal.resize(nextCols, nextRows);
|
|
266
277
|
try {
|
|
267
278
|
this.pty?.resize(nextCols, nextRows);
|
|
268
|
-
|
|
279
|
+
this.lastResizeError = null;
|
|
280
|
+
} catch (error) {
|
|
281
|
+
this.lastResizeError = error instanceof Error ? error.message : String(error);
|
|
282
|
+
}
|
|
269
283
|
this.emitSnapshotSoon(0);
|
|
270
284
|
}
|
|
271
285
|
detach() {
|
|
@@ -307,7 +321,7 @@ class PiPtyHost {
|
|
|
307
321
|
return;
|
|
308
322
|
const text = this.decoder.decode(data, { stream: true });
|
|
309
323
|
this.respondToRawTerminalQueries(text);
|
|
310
|
-
this.terminal.write(data
|
|
324
|
+
this.terminal.write(data);
|
|
311
325
|
}
|
|
312
326
|
emitSnapshotSoon(delayMs = SNAPSHOT_DELAY_MS) {
|
|
313
327
|
if (this._disposed || this.notifyTimer)
|
|
@@ -336,6 +350,7 @@ class PiPtyHost {
|
|
|
336
350
|
lines.push("");
|
|
337
351
|
styledLines[lines.length - 1] = new StyledText([{ __isChunk: true, text: "" }]);
|
|
338
352
|
}
|
|
353
|
+
const resolvedMessage = this.lastResizeError ? `resize degraded: ${this.lastResizeError}` : message;
|
|
339
354
|
return {
|
|
340
355
|
runId: this.runId,
|
|
341
356
|
status: this.status,
|
|
@@ -343,7 +358,8 @@ class PiPtyHost {
|
|
|
343
358
|
rows: this.rows,
|
|
344
359
|
lines,
|
|
345
360
|
styledLines,
|
|
346
|
-
message,
|
|
361
|
+
message: resolvedMessage,
|
|
362
|
+
...this.lastResizeError ? { resizeError: this.lastResizeError } : {},
|
|
347
363
|
...this.exitCode !== undefined ? { exitCode: this.exitCode } : {},
|
|
348
364
|
...this.signal !== undefined ? { signal: this.signal } : {}
|
|
349
365
|
};
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { computeStageLayout } from "../layout";
|
|
2
|
+
import { entryIntentForScene } from "../runtime";
|
|
3
|
+
import { PRIMARY_NAV } from "./nav";
|
|
4
|
+
import type { AppSceneFrame, AppSceneId, AppState } from "../types";
|
|
5
|
+
type SceneRenderer = (state: AppState, layout: ReturnType<typeof computeStageLayout>) => AppSceneFrame;
|
|
6
|
+
export declare function App(props: {
|
|
7
|
+
sceneRenderers?: Partial<Record<AppSceneId, SceneRenderer>>;
|
|
8
|
+
}): React.ReactNode;
|
|
9
|
+
export { PRIMARY_NAV, entryIntentForScene };
|