@h-rig/cli 0.0.6-alpha.80 → 0.0.6-alpha.81
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/bin/rig.js +17034 -11249
- package/dist/src/app/board.js +32 -3
- package/dist/src/app/drone-ui.js +1 -1
- package/dist/src/app/theme.js +1 -0
- package/dist/src/app-opentui/adapters/common.d.ts +53 -0
- package/dist/src/app-opentui/adapters/common.js +149 -0
- package/dist/src/app-opentui/adapters/doctor.d.ts +11 -0
- package/dist/src/app-opentui/adapters/doctor.js +780 -0
- package/dist/src/app-opentui/adapters/fleet.d.ts +16 -0
- package/dist/src/app-opentui/adapters/fleet.js +874 -0
- package/dist/src/app-opentui/adapters/inbox.d.ts +33 -0
- package/dist/src/app-opentui/adapters/inbox.js +1454 -0
- package/dist/src/app-opentui/adapters/init.d.ts +13 -0
- package/dist/src/app-opentui/adapters/init.js +2357 -0
- package/dist/src/app-opentui/adapters/pi-attach.d.ts +16 -0
- package/dist/src/app-opentui/adapters/pi-attach.js +1295 -0
- package/dist/src/app-opentui/adapters/run-detail.d.ts +20 -0
- package/dist/src/app-opentui/adapters/run-detail.js +893 -0
- package/dist/src/app-opentui/adapters/server.d.ts +14 -0
- package/dist/src/app-opentui/adapters/server.js +798 -0
- package/dist/src/app-opentui/adapters/tasks.d.ts +55 -0
- package/dist/src/app-opentui/adapters/tasks.js +3472 -0
- package/dist/src/app-opentui/bootstrap.d.ts +16 -0
- package/dist/src/app-opentui/bootstrap.js +19509 -0
- package/dist/src/app-opentui/drone.d.ts +12 -0
- package/dist/src/app-opentui/drone.js +227 -0
- package/dist/src/app-opentui/events.d.ts +7 -0
- package/dist/src/app-opentui/events.js +28 -0
- package/dist/src/app-opentui/index.d.ts +8 -0
- package/dist/src/app-opentui/index.js +3477 -0
- package/dist/src/app-opentui/intent.d.ts +3 -0
- package/dist/src/app-opentui/intent.js +211 -0
- package/dist/src/app-opentui/layout.d.ts +15 -0
- package/dist/src/app-opentui/layout.js +44 -0
- package/dist/src/app-opentui/list-search.d.ts +8 -0
- package/dist/src/app-opentui/list-search.js +43 -0
- package/dist/src/app-opentui/pi-host-child.d.ts +2 -0
- package/dist/src/app-opentui/pi-host-child.js +778 -0
- package/dist/src/app-opentui/pi-pty-host.d.ts +64 -0
- package/dist/src/app-opentui/pi-pty-host.js +384 -0
- package/dist/src/app-opentui/registry.d.ts +4 -0
- package/dist/src/app-opentui/registry.js +6835 -0
- package/dist/src/app-opentui/render/graphics.d.ts +39 -0
- package/dist/src/app-opentui/render/graphics.js +537 -0
- package/dist/src/app-opentui/render/image-visual-layer-native-canvas.d.ts +2 -0
- package/dist/src/app-opentui/render/image-visual-layer-native-canvas.js +1480 -0
- package/dist/src/app-opentui/render/image-visual-layer-worker.d.ts +1 -0
- package/dist/src/app-opentui/render/image-visual-layer-worker.js +1541 -0
- package/dist/src/app-opentui/render/image-visual-layer.d.ts +93 -0
- package/dist/src/app-opentui/render/image-visual-layer.js +945 -0
- package/dist/src/app-opentui/render/panels.d.ts +10 -0
- package/dist/src/app-opentui/render/panels.js +201 -0
- package/dist/src/app-opentui/render/scene.d.ts +16 -0
- package/dist/src/app-opentui/render/scene.js +110 -0
- package/dist/src/app-opentui/render/text.d.ts +10 -0
- package/dist/src/app-opentui/render/text.js +121 -0
- package/dist/src/app-opentui/render/type-bar.d.ts +14 -0
- package/dist/src/app-opentui/render/type-bar.js +137 -0
- package/dist/src/app-opentui/runtime.d.ts +2 -0
- package/dist/src/app-opentui/runtime.js +3406 -0
- package/dist/src/app-opentui/scenes/doctor.d.ts +2 -0
- package/dist/src/app-opentui/scenes/doctor.js +91 -0
- package/dist/src/app-opentui/scenes/error.d.ts +2 -0
- package/dist/src/app-opentui/scenes/error.js +190 -0
- package/dist/src/app-opentui/scenes/fleet.d.ts +3 -0
- package/dist/src/app-opentui/scenes/fleet.js +207 -0
- package/dist/src/app-opentui/scenes/handoff.d.ts +2 -0
- package/dist/src/app-opentui/scenes/handoff.js +147 -0
- package/dist/src/app-opentui/scenes/help.d.ts +3 -0
- package/dist/src/app-opentui/scenes/help.js +138 -0
- package/dist/src/app-opentui/scenes/inbox.d.ts +2 -0
- package/dist/src/app-opentui/scenes/inbox.js +118 -0
- package/dist/src/app-opentui/scenes/init.d.ts +2 -0
- package/dist/src/app-opentui/scenes/init.js +94 -0
- package/dist/src/app-opentui/scenes/main.d.ts +2 -0
- package/dist/src/app-opentui/scenes/main.js +96 -0
- package/dist/src/app-opentui/scenes/run-detail.d.ts +2 -0
- package/dist/src/app-opentui/scenes/run-detail.js +139 -0
- package/dist/src/app-opentui/scenes/server.d.ts +2 -0
- package/dist/src/app-opentui/scenes/server.js +88 -0
- package/dist/src/app-opentui/scenes/tasks.d.ts +3 -0
- package/dist/src/app-opentui/scenes/tasks.js +230 -0
- package/dist/src/app-opentui/state.d.ts +4 -0
- package/dist/src/app-opentui/state.js +286 -0
- package/dist/src/app-opentui/theme.d.ts +36 -0
- package/dist/src/app-opentui/theme.js +88 -0
- package/dist/src/app-opentui/types.d.ts +188 -0
- package/dist/src/app-opentui/types.js +1 -0
- package/dist/src/commands/_async-ui.js +1 -1
- package/dist/src/commands/_operator-view.js +1 -1
- package/dist/src/commands/_pi-frontend.js +1 -1
- package/dist/src/commands/_spinner.js +1 -1
- package/dist/src/commands/browser.js +1 -1
- package/dist/src/commands/doctor.js +1 -1
- package/dist/src/commands/github.js +1 -1
- package/dist/src/commands/inbox.js +1 -1
- package/dist/src/commands/init.js +1 -1
- package/dist/src/commands/inspect.js +1 -1
- package/dist/src/commands/run.js +1 -1
- package/dist/src/commands/setup.js +1 -1
- package/dist/src/commands/stats.js +1 -1
- package/dist/src/commands/task.js +1 -1
- package/dist/src/commands.js +33 -4
- package/dist/src/index.js +33 -4
- package/package.json +11 -8
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
import { StyledText } from "@opentui/core";
|
|
2
|
+
export type PiPtyHostStatus = "starting" | "running" | "exited" | "failed";
|
|
3
|
+
export type PiPtyHostSnapshot = {
|
|
4
|
+
readonly runId: string;
|
|
5
|
+
readonly status: PiPtyHostStatus;
|
|
6
|
+
readonly cols: number;
|
|
7
|
+
readonly rows: number;
|
|
8
|
+
readonly lines: readonly string[];
|
|
9
|
+
readonly styledLines?: readonly StyledText[];
|
|
10
|
+
readonly message?: string;
|
|
11
|
+
readonly exitCode?: number;
|
|
12
|
+
readonly signal?: string | null;
|
|
13
|
+
};
|
|
14
|
+
type PiPtyInput = string | Uint8Array<ArrayBufferLike> | BufferSource;
|
|
15
|
+
export type PiPtyHostOptions = {
|
|
16
|
+
readonly runId: string;
|
|
17
|
+
readonly projectRoot: string;
|
|
18
|
+
readonly cols: number;
|
|
19
|
+
readonly rows: number;
|
|
20
|
+
readonly env?: NodeJS.ProcessEnv;
|
|
21
|
+
readonly onSnapshot?: (snapshot: PiPtyHostSnapshot) => void;
|
|
22
|
+
readonly onExit?: (snapshot: PiPtyHostSnapshot) => void;
|
|
23
|
+
readonly onError?: (error: unknown, snapshot: PiPtyHostSnapshot) => void;
|
|
24
|
+
};
|
|
25
|
+
export declare function getActivePiHost(): PiPtyHost | null;
|
|
26
|
+
export declare function startPiPtyHost(options: PiPtyHostOptions): Promise<PiPtyHost>;
|
|
27
|
+
export declare function stopActivePiHost(reason?: string): void;
|
|
28
|
+
export declare class PiPtyHost {
|
|
29
|
+
private readonly runId;
|
|
30
|
+
private readonly projectRoot;
|
|
31
|
+
private readonly onSnapshot?;
|
|
32
|
+
private readonly onExit?;
|
|
33
|
+
private readonly onError?;
|
|
34
|
+
private readonly terminal;
|
|
35
|
+
private readonly disposables;
|
|
36
|
+
private readonly decoder;
|
|
37
|
+
private proc;
|
|
38
|
+
private pty;
|
|
39
|
+
private status;
|
|
40
|
+
private cols;
|
|
41
|
+
private rows;
|
|
42
|
+
private message;
|
|
43
|
+
private exitCode;
|
|
44
|
+
private signal;
|
|
45
|
+
private notifyTimer;
|
|
46
|
+
private _disposed;
|
|
47
|
+
constructor(options: PiPtyHostOptions);
|
|
48
|
+
get disposed(): boolean;
|
|
49
|
+
get snapshot(): PiPtyHostSnapshot;
|
|
50
|
+
start(): Promise<void>;
|
|
51
|
+
write(data: PiPtyInput): void;
|
|
52
|
+
resize(cols: number, rows: number): void;
|
|
53
|
+
detach(): PiPtyHostSnapshot;
|
|
54
|
+
dispose(reason?: string, options?: {
|
|
55
|
+
readonly kill?: boolean;
|
|
56
|
+
readonly notify?: boolean;
|
|
57
|
+
}): void;
|
|
58
|
+
private handlePtyData;
|
|
59
|
+
private emitSnapshotSoon;
|
|
60
|
+
private createSnapshot;
|
|
61
|
+
private registerTerminalResponders;
|
|
62
|
+
private respondToRawTerminalQueries;
|
|
63
|
+
}
|
|
64
|
+
export {};
|
|
@@ -0,0 +1,384 @@
|
|
|
1
|
+
// @bun
|
|
2
|
+
// packages/cli/src/app-opentui/pi-pty-host.ts
|
|
3
|
+
import { fileURLToPath } from "url";
|
|
4
|
+
import { basename } from "path";
|
|
5
|
+
import { RGBA, StyledText, TextAttributes } from "@opentui/core";
|
|
6
|
+
import { Terminal as XtermTerminal } from "@xterm/headless";
|
|
7
|
+
var MIN_COLS = 40;
|
|
8
|
+
var MIN_ROWS = 12;
|
|
9
|
+
var MAX_ROWS = 300;
|
|
10
|
+
var MAX_SNAPSHOT_LINES = 1200;
|
|
11
|
+
var fallbackChildScriptPath = fileURLToPath(new URL("./pi-host-child.ts", import.meta.url));
|
|
12
|
+
var activeHost = null;
|
|
13
|
+
function clampCols(cols) {
|
|
14
|
+
return Math.max(MIN_COLS, Math.trunc(cols || 100));
|
|
15
|
+
}
|
|
16
|
+
function clampRows(rows) {
|
|
17
|
+
return Math.max(MIN_ROWS, Math.min(MAX_ROWS, Math.trunc(rows || 35)));
|
|
18
|
+
}
|
|
19
|
+
var XTERM_COLOR_MODE_INDEXED_16 = 16777216;
|
|
20
|
+
var XTERM_COLOR_MODE_INDEXED_256 = 33554432;
|
|
21
|
+
var XTERM_COLOR_MODE_RGB = 50331648;
|
|
22
|
+
function rgbaFromXtermColor(mode, value) {
|
|
23
|
+
if (mode === 1 || mode === 2 || mode === XTERM_COLOR_MODE_INDEXED_16 || mode === XTERM_COLOR_MODE_INDEXED_256) {
|
|
24
|
+
return RGBA.fromIndex(value);
|
|
25
|
+
}
|
|
26
|
+
if (mode === 3 || mode === XTERM_COLOR_MODE_RGB) {
|
|
27
|
+
return RGBA.fromInts(value >> 16 & 255, value >> 8 & 255, value & 255);
|
|
28
|
+
}
|
|
29
|
+
return;
|
|
30
|
+
}
|
|
31
|
+
function textAttributesFromCell(cell) {
|
|
32
|
+
let attributes = TextAttributes.NONE;
|
|
33
|
+
if (cell.isBold())
|
|
34
|
+
attributes |= TextAttributes.BOLD;
|
|
35
|
+
if (cell.isDim())
|
|
36
|
+
attributes |= TextAttributes.DIM;
|
|
37
|
+
if (cell.isItalic())
|
|
38
|
+
attributes |= TextAttributes.ITALIC;
|
|
39
|
+
if (cell.isUnderline())
|
|
40
|
+
attributes |= TextAttributes.UNDERLINE;
|
|
41
|
+
if (cell.isBlink())
|
|
42
|
+
attributes |= TextAttributes.BLINK;
|
|
43
|
+
if (cell.isInverse())
|
|
44
|
+
attributes |= TextAttributes.INVERSE;
|
|
45
|
+
if (cell.isInvisible())
|
|
46
|
+
attributes |= TextAttributes.HIDDEN;
|
|
47
|
+
if (cell.isStrikethrough())
|
|
48
|
+
attributes |= TextAttributes.STRIKETHROUGH;
|
|
49
|
+
return attributes;
|
|
50
|
+
}
|
|
51
|
+
function sameRgba(a, b) {
|
|
52
|
+
if (!a && !b)
|
|
53
|
+
return true;
|
|
54
|
+
return Boolean(a && b && a.equals(b));
|
|
55
|
+
}
|
|
56
|
+
function sameStyle(a, b) {
|
|
57
|
+
return sameRgba(a.fg, b.fg) && sameRgba(a.bg, b.bg) && (a.attributes ?? 0) === (b.attributes ?? 0);
|
|
58
|
+
}
|
|
59
|
+
function styleFromCell(cell) {
|
|
60
|
+
return {
|
|
61
|
+
fg: rgbaFromXtermColor(cell.getFgColorMode(), cell.getFgColor()),
|
|
62
|
+
bg: rgbaFromXtermColor(cell.getBgColorMode(), cell.getBgColor()),
|
|
63
|
+
attributes: textAttributesFromCell(cell)
|
|
64
|
+
};
|
|
65
|
+
}
|
|
66
|
+
function lineToStyledText(line, cols) {
|
|
67
|
+
if (!line)
|
|
68
|
+
return new StyledText([{ __isChunk: true, text: "" }]);
|
|
69
|
+
const chunks = [];
|
|
70
|
+
let run = "";
|
|
71
|
+
let runStyle = null;
|
|
72
|
+
const flush = () => {
|
|
73
|
+
if (!run)
|
|
74
|
+
return;
|
|
75
|
+
chunks.push({
|
|
76
|
+
__isChunk: true,
|
|
77
|
+
text: run,
|
|
78
|
+
...runStyle?.fg ? { fg: runStyle.fg } : {},
|
|
79
|
+
...runStyle?.bg ? { bg: runStyle.bg } : {},
|
|
80
|
+
...(runStyle?.attributes ?? 0) !== 0 ? { attributes: runStyle.attributes } : {}
|
|
81
|
+
});
|
|
82
|
+
run = "";
|
|
83
|
+
};
|
|
84
|
+
for (let index = 0;index < cols; index += 1) {
|
|
85
|
+
const cell = line.getCell(index);
|
|
86
|
+
if (!cell) {
|
|
87
|
+
if (runStyle !== null)
|
|
88
|
+
flush();
|
|
89
|
+
runStyle = null;
|
|
90
|
+
run += " ";
|
|
91
|
+
continue;
|
|
92
|
+
}
|
|
93
|
+
if (cell.getWidth() === 0)
|
|
94
|
+
continue;
|
|
95
|
+
const style = styleFromCell(cell);
|
|
96
|
+
if (!runStyle || !sameStyle(runStyle, style)) {
|
|
97
|
+
flush();
|
|
98
|
+
runStyle = style;
|
|
99
|
+
}
|
|
100
|
+
run += cell.getChars() || " ";
|
|
101
|
+
}
|
|
102
|
+
flush();
|
|
103
|
+
return new StyledText(chunks.length > 0 ? chunks : [{ __isChunk: true, text: "" }]);
|
|
104
|
+
}
|
|
105
|
+
function childCommandPrefix() {
|
|
106
|
+
const execName = basename(process.execPath).toLowerCase();
|
|
107
|
+
const currentEntry = process.argv[1];
|
|
108
|
+
if ((execName === "bun" || execName === "bun.exe") && currentEntry) {
|
|
109
|
+
return [process.execPath, currentEntry, "__opentui-pi-host"];
|
|
110
|
+
}
|
|
111
|
+
if (currentEntry)
|
|
112
|
+
return [process.execPath, "__opentui-pi-host"];
|
|
113
|
+
return [process.execPath, fallbackChildScriptPath];
|
|
114
|
+
}
|
|
115
|
+
function withEnv(base) {
|
|
116
|
+
const env = {};
|
|
117
|
+
for (const [key, value] of Object.entries(base ?? process.env)) {
|
|
118
|
+
if (key === "RIG_PLAIN" || key === "RIG_CLI_PLAIN_HELP")
|
|
119
|
+
continue;
|
|
120
|
+
if (typeof value === "string")
|
|
121
|
+
env[key] = value;
|
|
122
|
+
}
|
|
123
|
+
return {
|
|
124
|
+
...env,
|
|
125
|
+
TERM: "xterm-256color",
|
|
126
|
+
COLORTERM: "truecolor",
|
|
127
|
+
FORCE_COLOR: env.FORCE_COLOR ?? "1",
|
|
128
|
+
RIG_OPENTUI_PI_HOST: "1"
|
|
129
|
+
};
|
|
130
|
+
}
|
|
131
|
+
function getActivePiHost() {
|
|
132
|
+
return activeHost && !activeHost.disposed ? activeHost : null;
|
|
133
|
+
}
|
|
134
|
+
async function startPiPtyHost(options) {
|
|
135
|
+
activeHost?.dispose("replace");
|
|
136
|
+
const host = new PiPtyHost(options);
|
|
137
|
+
activeHost = host;
|
|
138
|
+
await host.start();
|
|
139
|
+
return host;
|
|
140
|
+
}
|
|
141
|
+
function stopActivePiHost(reason = "detach") {
|
|
142
|
+
activeHost?.dispose(reason);
|
|
143
|
+
activeHost = null;
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
class PiPtyHost {
|
|
147
|
+
runId;
|
|
148
|
+
projectRoot;
|
|
149
|
+
onSnapshot;
|
|
150
|
+
onExit;
|
|
151
|
+
onError;
|
|
152
|
+
terminal;
|
|
153
|
+
disposables = [];
|
|
154
|
+
decoder = new TextDecoder("utf-8");
|
|
155
|
+
proc = null;
|
|
156
|
+
pty = null;
|
|
157
|
+
status = "starting";
|
|
158
|
+
cols;
|
|
159
|
+
rows;
|
|
160
|
+
message = "starting bundled Pi";
|
|
161
|
+
exitCode;
|
|
162
|
+
signal;
|
|
163
|
+
notifyTimer = null;
|
|
164
|
+
_disposed = false;
|
|
165
|
+
constructor(options) {
|
|
166
|
+
this.runId = options.runId;
|
|
167
|
+
this.projectRoot = options.projectRoot;
|
|
168
|
+
this.cols = clampCols(options.cols);
|
|
169
|
+
this.rows = clampRows(options.rows);
|
|
170
|
+
this.onSnapshot = options.onSnapshot;
|
|
171
|
+
this.onExit = options.onExit;
|
|
172
|
+
this.onError = options.onError;
|
|
173
|
+
this.terminal = new XtermTerminal({
|
|
174
|
+
allowProposedApi: true,
|
|
175
|
+
cols: this.cols,
|
|
176
|
+
rows: this.rows,
|
|
177
|
+
scrollback: 5000
|
|
178
|
+
});
|
|
179
|
+
this.registerTerminalResponders();
|
|
180
|
+
}
|
|
181
|
+
get disposed() {
|
|
182
|
+
return this._disposed;
|
|
183
|
+
}
|
|
184
|
+
get snapshot() {
|
|
185
|
+
return this.createSnapshot();
|
|
186
|
+
}
|
|
187
|
+
async start() {
|
|
188
|
+
if (this._disposed)
|
|
189
|
+
throw new Error("Pi PTY host is disposed.");
|
|
190
|
+
if (typeof Bun.Terminal !== "function") {
|
|
191
|
+
throw new Error("Bun native PTY is unavailable in this runtime. Rig OpenTUI Pi host requires Bun.Terminal.");
|
|
192
|
+
}
|
|
193
|
+
const spawnOptions = {
|
|
194
|
+
cwd: this.projectRoot,
|
|
195
|
+
env: withEnv(process.env),
|
|
196
|
+
terminal: {
|
|
197
|
+
cols: this.cols,
|
|
198
|
+
rows: this.rows,
|
|
199
|
+
name: "xterm-256color",
|
|
200
|
+
data: (_terminal, data) => this.handlePtyData(data)
|
|
201
|
+
}
|
|
202
|
+
};
|
|
203
|
+
const proc = Bun.spawn([
|
|
204
|
+
...childCommandPrefix(),
|
|
205
|
+
"--run-id",
|
|
206
|
+
this.runId,
|
|
207
|
+
"--project-root",
|
|
208
|
+
this.projectRoot
|
|
209
|
+
], spawnOptions);
|
|
210
|
+
if (!proc.terminal)
|
|
211
|
+
throw new Error("Bun did not attach a terminal to the bundled Pi child process.");
|
|
212
|
+
this.proc = proc;
|
|
213
|
+
this.pty = proc.terminal;
|
|
214
|
+
this.status = "running";
|
|
215
|
+
this.message = "bundled Pi running inside Rig";
|
|
216
|
+
this.emitSnapshotSoon(0);
|
|
217
|
+
proc.exited.then((exitCode) => {
|
|
218
|
+
if (this._disposed)
|
|
219
|
+
return;
|
|
220
|
+
this.status = exitCode === 0 ? "exited" : "failed";
|
|
221
|
+
this.exitCode = exitCode;
|
|
222
|
+
this.signal = null;
|
|
223
|
+
this.message = exitCode === 0 ? "bundled Pi exited" : `bundled Pi exited with code ${exitCode}`;
|
|
224
|
+
const snapshot = this.createSnapshot();
|
|
225
|
+
this.onSnapshot?.(snapshot);
|
|
226
|
+
this.onExit?.(snapshot);
|
|
227
|
+
if (activeHost === this)
|
|
228
|
+
activeHost = null;
|
|
229
|
+
this.dispose("exit", { kill: false, notify: false });
|
|
230
|
+
}).catch((error) => {
|
|
231
|
+
if (this._disposed)
|
|
232
|
+
return;
|
|
233
|
+
this.status = "failed";
|
|
234
|
+
this.message = error instanceof Error ? error.message : String(error);
|
|
235
|
+
const snapshot = this.createSnapshot();
|
|
236
|
+
this.onSnapshot?.(snapshot);
|
|
237
|
+
this.onError?.(error, snapshot);
|
|
238
|
+
if (activeHost === this)
|
|
239
|
+
activeHost = null;
|
|
240
|
+
this.dispose("error", { kill: false, notify: false });
|
|
241
|
+
});
|
|
242
|
+
}
|
|
243
|
+
write(data) {
|
|
244
|
+
if (this._disposed || !this.pty)
|
|
245
|
+
return;
|
|
246
|
+
try {
|
|
247
|
+
this.pty.write(data);
|
|
248
|
+
} catch (error) {
|
|
249
|
+
this.status = "failed";
|
|
250
|
+
this.message = error instanceof Error ? error.message : String(error);
|
|
251
|
+
const snapshot = this.createSnapshot();
|
|
252
|
+
this.onSnapshot?.(snapshot);
|
|
253
|
+
this.onError?.(error, snapshot);
|
|
254
|
+
}
|
|
255
|
+
}
|
|
256
|
+
resize(cols, rows) {
|
|
257
|
+
const nextCols = clampCols(cols);
|
|
258
|
+
const nextRows = clampRows(rows);
|
|
259
|
+
if (nextCols === this.cols && nextRows === this.rows)
|
|
260
|
+
return;
|
|
261
|
+
this.cols = nextCols;
|
|
262
|
+
this.rows = nextRows;
|
|
263
|
+
this.terminal.resize(nextCols, nextRows);
|
|
264
|
+
try {
|
|
265
|
+
this.pty?.resize(nextCols, nextRows);
|
|
266
|
+
} catch {}
|
|
267
|
+
this.emitSnapshotSoon(0);
|
|
268
|
+
}
|
|
269
|
+
detach() {
|
|
270
|
+
this.dispose("detach");
|
|
271
|
+
return this.createSnapshot("detached from bundled Pi");
|
|
272
|
+
}
|
|
273
|
+
dispose(reason = "dispose", options = {}) {
|
|
274
|
+
if (this._disposed)
|
|
275
|
+
return;
|
|
276
|
+
this._disposed = true;
|
|
277
|
+
if (this.notifyTimer)
|
|
278
|
+
clearTimeout(this.notifyTimer);
|
|
279
|
+
this.notifyTimer = null;
|
|
280
|
+
for (const disposable of this.disposables.splice(0)) {
|
|
281
|
+
try {
|
|
282
|
+
disposable.dispose();
|
|
283
|
+
} catch {}
|
|
284
|
+
}
|
|
285
|
+
if (options.kill !== false) {
|
|
286
|
+
try {
|
|
287
|
+
this.proc?.kill("SIGTERM");
|
|
288
|
+
} catch {}
|
|
289
|
+
}
|
|
290
|
+
try {
|
|
291
|
+
this.pty?.close();
|
|
292
|
+
} catch {}
|
|
293
|
+
try {
|
|
294
|
+
this.terminal.dispose();
|
|
295
|
+
} catch {}
|
|
296
|
+
if (activeHost === this)
|
|
297
|
+
activeHost = null;
|
|
298
|
+
if (options.notify) {
|
|
299
|
+
this.message = reason;
|
|
300
|
+
this.onSnapshot?.(this.createSnapshot(reason));
|
|
301
|
+
}
|
|
302
|
+
}
|
|
303
|
+
handlePtyData(data) {
|
|
304
|
+
if (this._disposed)
|
|
305
|
+
return;
|
|
306
|
+
const text = this.decoder.decode(data, { stream: true });
|
|
307
|
+
this.respondToRawTerminalQueries(text);
|
|
308
|
+
this.terminal.write(data, () => this.emitSnapshotSoon());
|
|
309
|
+
}
|
|
310
|
+
emitSnapshotSoon(delayMs = 16) {
|
|
311
|
+
if (this._disposed || this.notifyTimer)
|
|
312
|
+
return;
|
|
313
|
+
this.notifyTimer = setTimeout(() => {
|
|
314
|
+
this.notifyTimer = null;
|
|
315
|
+
if (this._disposed)
|
|
316
|
+
return;
|
|
317
|
+
this.onSnapshot?.(this.createSnapshot());
|
|
318
|
+
}, delayMs);
|
|
319
|
+
}
|
|
320
|
+
createSnapshot(message = this.message) {
|
|
321
|
+
const buffer = this.terminal.buffer.active;
|
|
322
|
+
const end = buffer.length;
|
|
323
|
+
const start = Math.max(0, end - MAX_SNAPSHOT_LINES);
|
|
324
|
+
const lines = [];
|
|
325
|
+
const styledLines = [];
|
|
326
|
+
for (let row = start;row < end; row += 1) {
|
|
327
|
+
const line = buffer.getLine(row);
|
|
328
|
+
lines.push((line?.translateToString(false) ?? "").slice(0, this.cols));
|
|
329
|
+
styledLines.push(lineToStyledText(line, this.cols));
|
|
330
|
+
}
|
|
331
|
+
while (lines.length < this.rows) {
|
|
332
|
+
lines.push("");
|
|
333
|
+
styledLines.push(new StyledText([{ __isChunk: true, text: "" }]));
|
|
334
|
+
}
|
|
335
|
+
return {
|
|
336
|
+
runId: this.runId,
|
|
337
|
+
status: this.status,
|
|
338
|
+
cols: this.cols,
|
|
339
|
+
rows: this.rows,
|
|
340
|
+
lines,
|
|
341
|
+
styledLines,
|
|
342
|
+
message,
|
|
343
|
+
...this.exitCode !== undefined ? { exitCode: this.exitCode } : {},
|
|
344
|
+
...this.signal !== undefined ? { signal: this.signal } : {}
|
|
345
|
+
};
|
|
346
|
+
}
|
|
347
|
+
registerTerminalResponders() {
|
|
348
|
+
this.disposables.push(this.terminal.parser.registerCsiHandler({ final: "c" }, (params) => {
|
|
349
|
+
if (params.length === 0 || params[0] === 0)
|
|
350
|
+
this.write("\x1B[?62;22c");
|
|
351
|
+
return false;
|
|
352
|
+
}));
|
|
353
|
+
this.disposables.push(this.terminal.parser.registerCsiHandler({ prefix: ">", final: "c" }, (params) => {
|
|
354
|
+
if (params.length === 0 || params[0] === 0)
|
|
355
|
+
this.write("\x1B[>0;0;0c");
|
|
356
|
+
return false;
|
|
357
|
+
}));
|
|
358
|
+
this.disposables.push(this.terminal.parser.registerCsiHandler({ final: "n" }, (params) => {
|
|
359
|
+
if (params[0] === 5) {
|
|
360
|
+
this.write("\x1B[0n");
|
|
361
|
+
} else if (params[0] === 6) {
|
|
362
|
+
const row = this.terminal.buffer.active.cursorY + 1;
|
|
363
|
+
const col = this.terminal.buffer.active.cursorX + 1;
|
|
364
|
+
this.write(`\x1B[${row};${col}R`);
|
|
365
|
+
}
|
|
366
|
+
return false;
|
|
367
|
+
}));
|
|
368
|
+
}
|
|
369
|
+
respondToRawTerminalQueries(text) {
|
|
370
|
+
if (!text)
|
|
371
|
+
return;
|
|
372
|
+
const decrqm = /\x1b\[\?(\d+)\$p/g;
|
|
373
|
+
let match;
|
|
374
|
+
while ((match = decrqm.exec(text)) !== null) {
|
|
375
|
+
this.write(`\x1B[?${match[1]};2$y`);
|
|
376
|
+
}
|
|
377
|
+
}
|
|
378
|
+
}
|
|
379
|
+
export {
|
|
380
|
+
stopActivePiHost,
|
|
381
|
+
startPiPtyHost,
|
|
382
|
+
getActivePiHost,
|
|
383
|
+
PiPtyHost
|
|
384
|
+
};
|