@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.
Files changed (100) hide show
  1. package/dist/bin/build-rig-binaries.js +40 -8
  2. package/dist/bin/rig.js +2807 -1443
  3. package/dist/src/app/board.js +62 -13
  4. package/dist/src/app-opentui/adapters/command.d.ts +2 -0
  5. package/dist/src/app-opentui/adapters/command.js +329 -0
  6. package/dist/src/app-opentui/adapters/common.js +2 -2
  7. package/dist/src/app-opentui/adapters/doctor.js +63 -14
  8. package/dist/src/app-opentui/adapters/fleet.js +84 -20
  9. package/dist/src/app-opentui/adapters/inbox.js +83 -19
  10. package/dist/src/app-opentui/adapters/init.js +87 -23
  11. package/dist/src/app-opentui/adapters/pi-attach.js +102 -36
  12. package/dist/src/app-opentui/adapters/run-detail.js +83 -19
  13. package/dist/src/app-opentui/adapters/server.js +100 -23
  14. package/dist/src/app-opentui/adapters/tasks.d.ts +11 -0
  15. package/dist/src/app-opentui/adapters/tasks.js +742 -94
  16. package/dist/src/app-opentui/bootstrap.d.ts +1 -6
  17. package/dist/src/app-opentui/bootstrap.js +13776 -12369
  18. package/dist/src/app-opentui/command-pty-host.d.ts +62 -0
  19. package/dist/src/app-opentui/command-pty-host.js +248 -0
  20. package/dist/src/app-opentui/events.js +1 -1
  21. package/dist/src/app-opentui/focus-manager.d.ts +14 -0
  22. package/dist/src/app-opentui/focus-manager.js +24 -0
  23. package/dist/src/app-opentui/index.js +3806 -2878
  24. package/dist/src/app-opentui/intent.js +154 -48
  25. package/dist/src/app-opentui/keymap.d.ts +20 -0
  26. package/dist/src/app-opentui/keymap.js +707 -0
  27. package/dist/src/app-opentui/launch-routing.d.ts +16 -0
  28. package/dist/src/app-opentui/launch-routing.js +55 -0
  29. package/dist/src/app-opentui/layout.js +2 -2
  30. package/dist/src/app-opentui/pi-host-child.js +66 -16
  31. package/dist/src/app-opentui/pi-pty-host.d.ts +9 -0
  32. package/dist/src/app-opentui/pi-pty-host.js +15 -13
  33. package/dist/src/app-opentui/registry.js +3228 -2396
  34. package/dist/src/app-opentui/render/ascii-fleet.d.ts +15 -0
  35. package/dist/src/app-opentui/render/ascii-fleet.js +82 -0
  36. package/dist/src/app-opentui/render/graphics.d.ts +1 -1
  37. package/dist/src/app-opentui/render/graphics.js +131 -16
  38. package/dist/src/app-opentui/render/image-visual-layer-worker.js +413 -958
  39. package/dist/src/app-opentui/render/image-visual-layer.d.ts +19 -10
  40. package/dist/src/app-opentui/render/image-visual-layer.js +391 -357
  41. package/dist/src/app-opentui/render/panel-layout.d.ts +38 -0
  42. package/dist/src/app-opentui/render/panel-layout.js +48 -0
  43. package/dist/src/app-opentui/render/panels.d.ts +2 -0
  44. package/dist/src/app-opentui/render/panels.js +62 -29
  45. package/dist/src/app-opentui/render/preloader.d.ts +10 -0
  46. package/dist/src/app-opentui/render/preloader.js +163 -0
  47. package/dist/src/app-opentui/render/scene.d.ts +3 -0
  48. package/dist/src/app-opentui/render/scene.js +12 -0
  49. package/dist/src/app-opentui/render/text.js +5 -1
  50. package/dist/src/app-opentui/render/type-bar.d.ts +2 -1
  51. package/dist/src/app-opentui/render/type-bar.js +51 -16
  52. package/dist/src/app-opentui/runtime-resources.d.ts +16 -0
  53. package/dist/src/app-opentui/runtime-resources.js +62 -0
  54. package/dist/src/app-opentui/runtime.js +2329 -1512
  55. package/dist/src/app-opentui/scenes/command.d.ts +3 -0
  56. package/dist/src/app-opentui/scenes/command.js +103 -0
  57. package/dist/src/app-opentui/scenes/doctor.d.ts +2 -1
  58. package/dist/src/app-opentui/scenes/doctor.js +53 -14
  59. package/dist/src/app-opentui/scenes/error.js +44 -14
  60. package/dist/src/app-opentui/scenes/fleet.js +40 -21
  61. package/dist/src/app-opentui/scenes/handoff.js +142 -27
  62. package/dist/src/app-opentui/scenes/help.js +63 -48
  63. package/dist/src/app-opentui/scenes/inbox.d.ts +2 -1
  64. package/dist/src/app-opentui/scenes/inbox.js +64 -17
  65. package/dist/src/app-opentui/scenes/init.d.ts +2 -1
  66. package/dist/src/app-opentui/scenes/init.js +62 -21
  67. package/dist/src/app-opentui/scenes/main.d.ts +2 -1
  68. package/dist/src/app-opentui/scenes/main.js +80 -24
  69. package/dist/src/app-opentui/scenes/run-detail.d.ts +2 -1
  70. package/dist/src/app-opentui/scenes/run-detail.js +39 -25
  71. package/dist/src/app-opentui/scenes/server.d.ts +2 -1
  72. package/dist/src/app-opentui/scenes/server.js +71 -20
  73. package/dist/src/app-opentui/scenes/tasks.js +44 -22
  74. package/dist/src/app-opentui/state.js +70 -30
  75. package/dist/src/app-opentui/terminal-capabilities.d.ts +7 -0
  76. package/dist/src/app-opentui/terminal-capabilities.js +15 -0
  77. package/dist/src/app-opentui/types.d.ts +10 -2
  78. package/dist/src/commands/_doctor-checks.js +62 -13
  79. package/dist/src/commands/_operator-view.js +63 -13
  80. package/dist/src/commands/_pi-frontend.js +63 -13
  81. package/dist/src/commands/_preflight.js +64 -14
  82. package/dist/src/commands/_server-client.js +82 -18
  83. package/dist/src/commands/_snapshot-upload.js +62 -13
  84. package/dist/src/commands/connect.js +7 -1
  85. package/dist/src/commands/doctor.js +62 -13
  86. package/dist/src/commands/github.js +144 -23
  87. package/dist/src/commands/inbox.js +62 -13
  88. package/dist/src/commands/init.js +82 -18
  89. package/dist/src/commands/inspect.js +62 -13
  90. package/dist/src/commands/run.js +66 -13
  91. package/dist/src/commands/server.js +69 -14
  92. package/dist/src/commands/setup.js +62 -13
  93. package/dist/src/commands/stats.js +62 -13
  94. package/dist/src/commands/task-run-driver.js +62 -13
  95. package/dist/src/commands/task.js +65 -14
  96. package/dist/src/commands.js +227 -92
  97. package/dist/src/index.js +234 -99
  98. package/package.json +8 -9
  99. package/dist/src/app-opentui/render/image-visual-layer-native-canvas.d.ts +0 -2
  100. package/dist/src/app-opentui/render/image-visual-layer-native-canvas.js +0 -1480
@@ -0,0 +1,707 @@
1
+ // @bun
2
+ // packages/cli/src/app-opentui/command-pty-host.ts
3
+ import { basename } from "path";
4
+ import { fileURLToPath } from "url";
5
+ import { Terminal as XtermTerminal } from "@xterm/headless";
6
+ var MIN_COLS = 40;
7
+ var MIN_ROWS = 10;
8
+ var MAX_ROWS = 300;
9
+ var MAX_SNAPSHOT_LINES = 420;
10
+ var SNAPSHOT_DELAY_MS = 80;
11
+ var fallbackCliScriptPath = fileURLToPath(new URL("../../bin/rig.ts", import.meta.url));
12
+ var activeHost = null;
13
+ function clampCols(cols) {
14
+ return Math.max(MIN_COLS, Math.trunc(cols || 120));
15
+ }
16
+ function clampRows(rows) {
17
+ return Math.max(MIN_ROWS, Math.min(MAX_ROWS, Math.trunc(rows || 36)));
18
+ }
19
+ function childCommandPrefix() {
20
+ const execName = basename(process.execPath).toLowerCase();
21
+ const currentEntry = process.argv[1];
22
+ if (execName === "bun" || execName === "bun.exe") {
23
+ return currentEntry ? [process.execPath, currentEntry] : [process.execPath, fallbackCliScriptPath];
24
+ }
25
+ return [process.execPath];
26
+ }
27
+ function withEnv(base) {
28
+ const env = {};
29
+ for (const [key, value] of Object.entries(base ?? process.env)) {
30
+ if (key === "NO_COLOR")
31
+ continue;
32
+ if (typeof value === "string")
33
+ env[key] = value;
34
+ }
35
+ return {
36
+ ...env,
37
+ TERM: "xterm-256color",
38
+ COLORTERM: "truecolor",
39
+ FORCE_COLOR: env.FORCE_COLOR ?? "1",
40
+ RIG_PLAIN: "1",
41
+ RIG_OPENTUI_COMMAND_HOST: "1",
42
+ RIG_CLI_PLAIN_HELP: env.RIG_CLI_PLAIN_HELP ?? "1"
43
+ };
44
+ }
45
+ function commandLabel(argv, fallback) {
46
+ const command = argv.join(" ").trim();
47
+ return fallback?.trim() || (command ? `rig ${command}` : "rig");
48
+ }
49
+ function getActiveCommandHost() {
50
+ return activeHost && !activeHost.disposed ? activeHost : null;
51
+ }
52
+ function stopActiveCommandHost(reason = "detach") {
53
+ activeHost?.dispose(reason);
54
+ activeHost = null;
55
+ }
56
+ class CommandPtyHost {
57
+ argv;
58
+ projectRoot;
59
+ label;
60
+ env;
61
+ onSnapshot;
62
+ onExit;
63
+ onError;
64
+ terminal;
65
+ decoder = new TextDecoder("utf-8");
66
+ proc = null;
67
+ pty = null;
68
+ status = "starting";
69
+ cols;
70
+ rows;
71
+ message = "starting command";
72
+ exitCode;
73
+ signal;
74
+ notifyTimer = null;
75
+ _disposed = false;
76
+ constructor(options) {
77
+ this.argv = [...options.argv];
78
+ this.projectRoot = options.projectRoot;
79
+ this.label = commandLabel(options.argv, options.label);
80
+ this.env = options.env;
81
+ this.cols = clampCols(options.cols);
82
+ this.rows = clampRows(options.rows);
83
+ this.onSnapshot = options.onSnapshot;
84
+ this.onExit = options.onExit;
85
+ this.onError = options.onError;
86
+ this.terminal = new XtermTerminal({ allowProposedApi: true, cols: this.cols, rows: this.rows, scrollback: 2000 });
87
+ }
88
+ get disposed() {
89
+ return this._disposed;
90
+ }
91
+ get snapshot() {
92
+ return this.createSnapshot();
93
+ }
94
+ async start() {
95
+ if (this._disposed)
96
+ throw new Error("Command PTY host is disposed.");
97
+ if (typeof Bun.Terminal !== "function") {
98
+ throw new Error("Bun native PTY is unavailable in this runtime. Command host requires Bun.Terminal.");
99
+ }
100
+ const spawnOptions = {
101
+ cwd: this.projectRoot,
102
+ env: withEnv(this.env),
103
+ terminal: {
104
+ cols: this.cols,
105
+ rows: this.rows,
106
+ name: "xterm-256color",
107
+ data: (_terminal, data) => this.handlePtyData(data)
108
+ }
109
+ };
110
+ const proc = Bun.spawn([...childCommandPrefix(), ...this.argv], spawnOptions);
111
+ if (!proc.terminal)
112
+ throw new Error("Bun did not attach a terminal to the command child process.");
113
+ this.proc = proc;
114
+ this.pty = proc.terminal;
115
+ this.status = "running";
116
+ this.message = "running";
117
+ this.emitSnapshotSoon(0);
118
+ proc.exited.then((exitCode) => {
119
+ if (this._disposed)
120
+ return;
121
+ this.status = exitCode === 0 ? "exited" : "failed";
122
+ this.exitCode = exitCode;
123
+ this.signal = null;
124
+ this.message = exitCode === 0 ? "completed" : `exited with code ${exitCode}`;
125
+ const snapshot = this.createSnapshot();
126
+ this.onSnapshot?.(snapshot);
127
+ this.onExit?.(snapshot);
128
+ if (activeHost === this)
129
+ activeHost = null;
130
+ this.dispose("exit", { kill: false, notify: false });
131
+ }).catch((error) => {
132
+ if (this._disposed)
133
+ return;
134
+ this.status = "failed";
135
+ this.message = error instanceof Error ? error.message : String(error);
136
+ const snapshot = this.createSnapshot();
137
+ this.onSnapshot?.(snapshot);
138
+ this.onError?.(error, snapshot);
139
+ if (activeHost === this)
140
+ activeHost = null;
141
+ this.dispose("error", { kill: false, notify: false });
142
+ });
143
+ }
144
+ write(data) {
145
+ if (this._disposed || !this.pty)
146
+ return;
147
+ try {
148
+ this.pty.write(data);
149
+ } catch (error) {
150
+ this.status = "failed";
151
+ this.message = error instanceof Error ? error.message : String(error);
152
+ const snapshot = this.createSnapshot();
153
+ this.onSnapshot?.(snapshot);
154
+ this.onError?.(error, snapshot);
155
+ }
156
+ }
157
+ resize(cols, rows) {
158
+ const nextCols = clampCols(cols);
159
+ const nextRows = clampRows(rows);
160
+ if (nextCols === this.cols && nextRows === this.rows)
161
+ return;
162
+ this.cols = nextCols;
163
+ this.rows = nextRows;
164
+ this.terminal.resize(nextCols, nextRows);
165
+ try {
166
+ this.pty?.resize(nextCols, nextRows);
167
+ } catch {}
168
+ this.emitSnapshotSoon(0);
169
+ }
170
+ dispose(reason = "dispose", options = {}) {
171
+ if (this._disposed)
172
+ return;
173
+ this._disposed = true;
174
+ if (this.notifyTimer)
175
+ clearTimeout(this.notifyTimer);
176
+ this.notifyTimer = null;
177
+ if (options.kill !== false) {
178
+ try {
179
+ this.proc?.kill("SIGTERM");
180
+ } catch {}
181
+ }
182
+ try {
183
+ this.pty?.close();
184
+ } catch {}
185
+ try {
186
+ this.terminal.dispose();
187
+ } catch {}
188
+ if (activeHost === this)
189
+ activeHost = null;
190
+ if (options.notify) {
191
+ this.message = reason;
192
+ this.onSnapshot?.(this.createSnapshot(reason));
193
+ }
194
+ }
195
+ handlePtyData(data) {
196
+ if (this._disposed)
197
+ return;
198
+ this.decoder.decode(data, { stream: true });
199
+ this.terminal.write(data, () => this.emitSnapshotSoon());
200
+ }
201
+ emitSnapshotSoon(delayMs = SNAPSHOT_DELAY_MS) {
202
+ if (this._disposed || this.notifyTimer)
203
+ return;
204
+ this.notifyTimer = setTimeout(() => {
205
+ this.notifyTimer = null;
206
+ if (this._disposed)
207
+ return;
208
+ this.onSnapshot?.(this.createSnapshot());
209
+ }, delayMs);
210
+ }
211
+ createSnapshot(message = this.message) {
212
+ const buffer = this.terminal.buffer.active;
213
+ const end = buffer.length;
214
+ const start = Math.max(0, end - MAX_SNAPSHOT_LINES);
215
+ const lines = [];
216
+ for (let row = start;row < end; row += 1) {
217
+ const line = buffer.getLine(row);
218
+ lines.push((line?.translateToString(false) ?? "").slice(0, this.cols));
219
+ }
220
+ while (lines.length < this.rows)
221
+ lines.push("");
222
+ return {
223
+ command: this.argv,
224
+ label: this.label,
225
+ status: this.status,
226
+ cols: this.cols,
227
+ rows: this.rows,
228
+ lines,
229
+ message,
230
+ ...this.exitCode !== undefined ? { exitCode: this.exitCode } : {},
231
+ ...this.signal !== undefined ? { signal: this.signal } : {}
232
+ };
233
+ }
234
+ }
235
+
236
+ // packages/cli/src/app-opentui/list-search.ts
237
+ function isListSearchScene(scene) {
238
+ return scene === "fleet" || scene === "tasks";
239
+ }
240
+
241
+ // packages/cli/src/app-opentui/pi-pty-host.ts
242
+ import { fileURLToPath as fileURLToPath2 } from "url";
243
+ import { basename as basename2 } from "path";
244
+ import { RGBA, StyledText, TextAttributes } from "@opentui/core";
245
+ import { Terminal as XtermTerminal2 } from "@xterm/headless";
246
+ var MIN_COLS2 = 40;
247
+ var MIN_ROWS2 = 12;
248
+ var MAX_ROWS2 = 300;
249
+ var MAX_SNAPSHOT_LINES2 = 360;
250
+ var STYLED_SNAPSHOT_MARGIN = 6;
251
+ var SNAPSHOT_DELAY_MS2 = 120;
252
+ var fallbackChildScriptPath = fileURLToPath2(new URL("./pi-host-child.ts", import.meta.url));
253
+ var activeHost2 = null;
254
+ function clampCols2(cols) {
255
+ return Math.max(MIN_COLS2, Math.trunc(cols || 100));
256
+ }
257
+ function clampRows2(rows) {
258
+ return Math.max(MIN_ROWS2, Math.min(MAX_ROWS2, Math.trunc(rows || 35)));
259
+ }
260
+ var XTERM_COLOR_MODE_INDEXED_16 = 16777216;
261
+ var XTERM_COLOR_MODE_INDEXED_256 = 33554432;
262
+ var XTERM_COLOR_MODE_RGB = 50331648;
263
+ function rgbaFromXtermColor(mode, value) {
264
+ if (mode === 1 || mode === 2 || mode === XTERM_COLOR_MODE_INDEXED_16 || mode === XTERM_COLOR_MODE_INDEXED_256) {
265
+ return RGBA.fromIndex(value);
266
+ }
267
+ if (mode === 3 || mode === XTERM_COLOR_MODE_RGB) {
268
+ return RGBA.fromInts(value >> 16 & 255, value >> 8 & 255, value & 255);
269
+ }
270
+ return;
271
+ }
272
+ function textAttributesFromCell(cell) {
273
+ let attributes = TextAttributes.NONE;
274
+ if (cell.isBold())
275
+ attributes |= TextAttributes.BOLD;
276
+ if (cell.isDim())
277
+ attributes |= TextAttributes.DIM;
278
+ if (cell.isItalic())
279
+ attributes |= TextAttributes.ITALIC;
280
+ if (cell.isUnderline())
281
+ attributes |= TextAttributes.UNDERLINE;
282
+ if (cell.isBlink())
283
+ attributes |= TextAttributes.BLINK;
284
+ if (cell.isInverse())
285
+ attributes |= TextAttributes.INVERSE;
286
+ if (cell.isInvisible())
287
+ attributes |= TextAttributes.HIDDEN;
288
+ if (cell.isStrikethrough())
289
+ attributes |= TextAttributes.STRIKETHROUGH;
290
+ return attributes;
291
+ }
292
+ function sameRgba(a, b) {
293
+ if (!a && !b)
294
+ return true;
295
+ return Boolean(a && b && a.equals(b));
296
+ }
297
+ function sameStyle(a, b) {
298
+ return sameRgba(a.fg, b.fg) && sameRgba(a.bg, b.bg) && (a.attributes ?? 0) === (b.attributes ?? 0);
299
+ }
300
+ function styleFromCell(cell) {
301
+ return {
302
+ fg: rgbaFromXtermColor(cell.getFgColorMode(), cell.getFgColor()),
303
+ bg: rgbaFromXtermColor(cell.getBgColorMode(), cell.getBgColor()),
304
+ attributes: textAttributesFromCell(cell)
305
+ };
306
+ }
307
+ function lineToStyledText(line, cols) {
308
+ if (!line)
309
+ return new StyledText([{ __isChunk: true, text: "" }]);
310
+ const chunks = [];
311
+ let run = "";
312
+ let runStyle = null;
313
+ const flush = () => {
314
+ if (!run)
315
+ return;
316
+ chunks.push({
317
+ __isChunk: true,
318
+ text: run,
319
+ ...runStyle?.fg ? { fg: runStyle.fg } : {},
320
+ ...runStyle?.bg ? { bg: runStyle.bg } : {},
321
+ ...(runStyle?.attributes ?? 0) !== 0 ? { attributes: runStyle.attributes } : {}
322
+ });
323
+ run = "";
324
+ };
325
+ for (let index = 0;index < cols; index += 1) {
326
+ const cell = line.getCell(index);
327
+ if (!cell) {
328
+ if (runStyle !== null)
329
+ flush();
330
+ runStyle = null;
331
+ run += " ";
332
+ continue;
333
+ }
334
+ if (cell.getWidth() === 0)
335
+ continue;
336
+ const style = styleFromCell(cell);
337
+ if (!runStyle || !sameStyle(runStyle, style)) {
338
+ flush();
339
+ runStyle = style;
340
+ }
341
+ run += cell.getChars() || " ";
342
+ }
343
+ flush();
344
+ return new StyledText(chunks.length > 0 ? chunks : [{ __isChunk: true, text: "" }]);
345
+ }
346
+ function childCommandPrefix2() {
347
+ const execName = basename2(process.execPath).toLowerCase();
348
+ const currentEntry = process.argv[1];
349
+ if (execName === "bun" || execName === "bun.exe") {
350
+ return currentEntry ? [process.execPath, currentEntry, "__opentui-pi-host"] : [process.execPath, fallbackChildScriptPath];
351
+ }
352
+ return [process.execPath, "__opentui-pi-host"];
353
+ }
354
+ function withEnv2(base) {
355
+ const env = {};
356
+ for (const [key, value] of Object.entries(base ?? process.env)) {
357
+ if (key === "RIG_PLAIN" || key === "RIG_CLI_PLAIN_HELP" || key === "NO_COLOR")
358
+ continue;
359
+ if (typeof value === "string")
360
+ env[key] = value;
361
+ }
362
+ return {
363
+ ...env,
364
+ TERM: "xterm-256color",
365
+ COLORTERM: "truecolor",
366
+ FORCE_COLOR: env.FORCE_COLOR ?? "1",
367
+ RIG_OPENTUI_PI_HOST: "1"
368
+ };
369
+ }
370
+ function getActivePiHost() {
371
+ return activeHost2 && !activeHost2.disposed ? activeHost2 : null;
372
+ }
373
+ function stopActivePiHost(reason = "detach") {
374
+ activeHost2?.dispose(reason);
375
+ activeHost2 = null;
376
+ }
377
+
378
+ class PiPtyHost {
379
+ runId;
380
+ projectRoot;
381
+ onSnapshot;
382
+ onExit;
383
+ onError;
384
+ terminal;
385
+ disposables = [];
386
+ decoder = new TextDecoder("utf-8");
387
+ proc = null;
388
+ pty = null;
389
+ status = "starting";
390
+ cols;
391
+ rows;
392
+ message = "starting bundled Pi";
393
+ exitCode;
394
+ signal;
395
+ notifyTimer = null;
396
+ _disposed = false;
397
+ constructor(options) {
398
+ this.runId = options.runId;
399
+ this.projectRoot = options.projectRoot;
400
+ this.cols = clampCols2(options.cols);
401
+ this.rows = clampRows2(options.rows);
402
+ this.onSnapshot = options.onSnapshot;
403
+ this.onExit = options.onExit;
404
+ this.onError = options.onError;
405
+ this.terminal = new XtermTerminal2({
406
+ allowProposedApi: true,
407
+ cols: this.cols,
408
+ rows: this.rows,
409
+ scrollback: 1000
410
+ });
411
+ this.registerTerminalResponders();
412
+ }
413
+ get disposed() {
414
+ return this._disposed;
415
+ }
416
+ get snapshot() {
417
+ return this.createSnapshot();
418
+ }
419
+ async start() {
420
+ if (this._disposed)
421
+ throw new Error("Pi PTY host is disposed.");
422
+ if (typeof Bun.Terminal !== "function") {
423
+ throw new Error("Bun native PTY is unavailable in this runtime. Pi host requires Bun.Terminal.");
424
+ }
425
+ const spawnOptions = {
426
+ cwd: this.projectRoot,
427
+ env: withEnv2(process.env),
428
+ terminal: {
429
+ cols: this.cols,
430
+ rows: this.rows,
431
+ name: "xterm-256color",
432
+ data: (_terminal, data) => this.handlePtyData(data)
433
+ }
434
+ };
435
+ const proc = Bun.spawn([
436
+ ...childCommandPrefix2(),
437
+ "--run-id",
438
+ this.runId,
439
+ "--project-root",
440
+ this.projectRoot
441
+ ], spawnOptions);
442
+ if (!proc.terminal)
443
+ throw new Error("Bun did not attach a terminal to the bundled Pi child process.");
444
+ this.proc = proc;
445
+ this.pty = proc.terminal;
446
+ this.status = "running";
447
+ this.message = "bundled Pi running inside this app";
448
+ this.emitSnapshotSoon(0);
449
+ proc.exited.then((exitCode) => {
450
+ if (this._disposed)
451
+ return;
452
+ this.status = exitCode === 0 ? "exited" : "failed";
453
+ this.exitCode = exitCode;
454
+ this.signal = null;
455
+ this.message = exitCode === 0 ? "bundled Pi exited" : `bundled Pi exited with code ${exitCode}`;
456
+ const snapshot = this.createSnapshot();
457
+ this.onSnapshot?.(snapshot);
458
+ this.onExit?.(snapshot);
459
+ if (activeHost2 === this)
460
+ activeHost2 = null;
461
+ this.dispose("exit", { kill: false, notify: false });
462
+ }).catch((error) => {
463
+ if (this._disposed)
464
+ return;
465
+ this.status = "failed";
466
+ this.message = error instanceof Error ? error.message : String(error);
467
+ const snapshot = this.createSnapshot();
468
+ this.onSnapshot?.(snapshot);
469
+ this.onError?.(error, snapshot);
470
+ if (activeHost2 === this)
471
+ activeHost2 = null;
472
+ this.dispose("error", { kill: false, notify: false });
473
+ });
474
+ }
475
+ write(data) {
476
+ if (this._disposed || !this.pty)
477
+ return;
478
+ try {
479
+ this.pty.write(data);
480
+ } catch (error) {
481
+ this.status = "failed";
482
+ this.message = error instanceof Error ? error.message : String(error);
483
+ const snapshot = this.createSnapshot();
484
+ this.onSnapshot?.(snapshot);
485
+ this.onError?.(error, snapshot);
486
+ }
487
+ }
488
+ resize(cols, rows) {
489
+ const nextCols = clampCols2(cols);
490
+ const nextRows = clampRows2(rows);
491
+ if (nextCols === this.cols && nextRows === this.rows)
492
+ return;
493
+ this.cols = nextCols;
494
+ this.rows = nextRows;
495
+ this.terminal.resize(nextCols, nextRows);
496
+ try {
497
+ this.pty?.resize(nextCols, nextRows);
498
+ } catch {}
499
+ this.emitSnapshotSoon(0);
500
+ }
501
+ detach() {
502
+ this.dispose("detach");
503
+ return this.createSnapshot("detached from bundled Pi");
504
+ }
505
+ dispose(reason = "dispose", options = {}) {
506
+ if (this._disposed)
507
+ return;
508
+ this._disposed = true;
509
+ if (this.notifyTimer)
510
+ clearTimeout(this.notifyTimer);
511
+ this.notifyTimer = null;
512
+ for (const disposable of this.disposables.splice(0)) {
513
+ try {
514
+ disposable.dispose();
515
+ } catch {}
516
+ }
517
+ if (options.kill !== false) {
518
+ try {
519
+ this.proc?.kill("SIGTERM");
520
+ } catch {}
521
+ }
522
+ try {
523
+ this.pty?.close();
524
+ } catch {}
525
+ try {
526
+ this.terminal.dispose();
527
+ } catch {}
528
+ if (activeHost2 === this)
529
+ activeHost2 = null;
530
+ if (options.notify) {
531
+ this.message = reason;
532
+ this.onSnapshot?.(this.createSnapshot(reason));
533
+ }
534
+ }
535
+ handlePtyData(data) {
536
+ if (this._disposed)
537
+ return;
538
+ const text = this.decoder.decode(data, { stream: true });
539
+ this.respondToRawTerminalQueries(text);
540
+ this.terminal.write(data, () => this.emitSnapshotSoon());
541
+ }
542
+ emitSnapshotSoon(delayMs = SNAPSHOT_DELAY_MS2) {
543
+ if (this._disposed || this.notifyTimer)
544
+ return;
545
+ this.notifyTimer = setTimeout(() => {
546
+ this.notifyTimer = null;
547
+ if (this._disposed)
548
+ return;
549
+ this.onSnapshot?.(this.createSnapshot());
550
+ }, delayMs);
551
+ }
552
+ createSnapshot(message = this.message) {
553
+ const buffer = this.terminal.buffer.active;
554
+ const end = buffer.length;
555
+ const start = Math.max(0, end - MAX_SNAPSHOT_LINES2);
556
+ const styledStart = Math.max(start, end - this.rows - STYLED_SNAPSHOT_MARGIN);
557
+ const lines = [];
558
+ const styledLines = [];
559
+ for (let row = start;row < end; row += 1) {
560
+ const line = buffer.getLine(row);
561
+ lines.push((line?.translateToString(false) ?? "").slice(0, this.cols));
562
+ if (row >= styledStart)
563
+ styledLines[lines.length - 1] = lineToStyledText(line, this.cols);
564
+ }
565
+ while (lines.length < this.rows) {
566
+ lines.push("");
567
+ styledLines[lines.length - 1] = new StyledText([{ __isChunk: true, text: "" }]);
568
+ }
569
+ return {
570
+ runId: this.runId,
571
+ status: this.status,
572
+ cols: this.cols,
573
+ rows: this.rows,
574
+ lines,
575
+ styledLines,
576
+ message,
577
+ ...this.exitCode !== undefined ? { exitCode: this.exitCode } : {},
578
+ ...this.signal !== undefined ? { signal: this.signal } : {}
579
+ };
580
+ }
581
+ registerTerminalResponders() {
582
+ this.disposables.push(this.terminal.parser.registerCsiHandler({ final: "c" }, (params) => {
583
+ if (params.length === 0 || params[0] === 0)
584
+ this.write("\x1B[?62;22c");
585
+ return false;
586
+ }));
587
+ this.disposables.push(this.terminal.parser.registerCsiHandler({ prefix: ">", final: "c" }, (params) => {
588
+ if (params.length === 0 || params[0] === 0)
589
+ this.write("\x1B[>0;0;0c");
590
+ return false;
591
+ }));
592
+ this.disposables.push(this.terminal.parser.registerCsiHandler({ final: "n" }, (params) => {
593
+ if (params[0] === 5) {
594
+ this.write("\x1B[0n");
595
+ } else if (params[0] === 6) {
596
+ const row = this.terminal.buffer.active.cursorY + 1;
597
+ const col = this.terminal.buffer.active.cursorX + 1;
598
+ this.write(`\x1B[${row};${col}R`);
599
+ }
600
+ return false;
601
+ }));
602
+ }
603
+ respondToRawTerminalQueries(text) {
604
+ if (!text)
605
+ return;
606
+ const decrqm = /\x1b\[\?(\d+)\$p/g;
607
+ let match;
608
+ while ((match = decrqm.exec(text)) !== null) {
609
+ this.write(`\x1B[?${match[1]};2$y`);
610
+ }
611
+ }
612
+ }
613
+
614
+ // packages/cli/src/app-opentui/keymap.ts
615
+ function clearTypeBar(context, message) {
616
+ const typeBar = context.getTypeBar();
617
+ if (typeBar)
618
+ typeBar.input.value = "";
619
+ context.emitTypeBarPatch({ value: "", message });
620
+ }
621
+ function handleEmbeddedTerminalKey(context, key) {
622
+ const state = context.getState();
623
+ const piHost = getActivePiHost();
624
+ const commandHost = getActiveCommandHost();
625
+ const host = state.scene === "handoff" ? piHost : state.scene === "command" ? commandHost : null;
626
+ if (!host)
627
+ return false;
628
+ if (key.ctrl && key.name === "]") {
629
+ if (state.scene === "handoff") {
630
+ stopActivePiHost("operator detach");
631
+ clearTypeBar(context, "detached from Pi");
632
+ context.runAppAction("Opening runs", () => context.runtime.runIntent({ scene: "fleet", argv: ["runs"], action: { kind: "refresh", label: "Opening runs" } }));
633
+ } else {
634
+ stopActiveCommandHost("operator detach");
635
+ clearTypeBar(context, "closed command");
636
+ context.runAppAction("Project menu", () => context.runtime.runIntent({ scene: "main", argv: ["main"], action: { kind: "none", label: "Project menu" } }));
637
+ }
638
+ return true;
639
+ }
640
+ const sequence = key.raw || key.sequence;
641
+ if (sequence)
642
+ host.write(sequence);
643
+ const typeBar = context.getTypeBar();
644
+ if (typeBar)
645
+ typeBar.input.value = "";
646
+ return true;
647
+ }
648
+ function handleAppKeyPress(context, key) {
649
+ if (handleEmbeddedTerminalKey(context, key))
650
+ return;
651
+ const renderer = context.renderer();
652
+ if (key.ctrl && key.name === "c") {
653
+ renderer?.destroy();
654
+ return;
655
+ }
656
+ const state = context.getState();
657
+ const currentScene = state.scene;
658
+ const searchScene = isListSearchScene(currentScene);
659
+ const typeBar = context.getTypeBar();
660
+ const inputValue = typeBar?.input.value ?? "";
661
+ const typeBarEmpty = inputValue.trim() === "";
662
+ if (typeBarEmpty && (key.name === "right" || key.name === "tab")) {
663
+ context.runAppAction("Navigate", () => context.runtime.runIntent(context.primaryNavIntent(context.nextPrimaryScene(context.getState().scene, 1))));
664
+ return;
665
+ }
666
+ if (typeBarEmpty && key.name === "left") {
667
+ context.runAppAction("Navigate", () => context.runtime.runIntent(context.primaryNavIntent(context.nextPrimaryScene(context.getState().scene, -1))));
668
+ return;
669
+ }
670
+ if (!searchScene && typeBarEmpty && key.name === "r") {
671
+ context.runAppAction("Opening runs", () => context.runtime.runIntent(context.primaryNavIntent("fleet")));
672
+ return;
673
+ }
674
+ if (!searchScene && typeBarEmpty && key.name === "t") {
675
+ context.runAppAction("Opening tasks", () => context.runtime.runIntent(context.primaryNavIntent("tasks")));
676
+ return;
677
+ }
678
+ if (!searchScene && typeBarEmpty && (key.name === "h" || key.sequence === "?")) {
679
+ context.runAppAction("Opening help", () => context.runtime.runIntent(context.primaryNavIntent("help")));
680
+ return;
681
+ }
682
+ if (searchScene && key.name === "up" || !searchScene && typeBarEmpty && (key.name === "up" || key.name === "k")) {
683
+ if (context.moveSelection(-1, context.getRenderedSelectableItems()))
684
+ return;
685
+ }
686
+ if (searchScene && key.name === "down" || !searchScene && typeBarEmpty && (key.name === "down" || key.name === "j")) {
687
+ if (context.moveSelection(1, context.getRenderedSelectableItems()))
688
+ return;
689
+ }
690
+ if (typeBarEmpty && (key.name === "return" || key.name === "enter" || key.name === "space" || key.sequence === " ")) {
691
+ context.runAppAction("Activate selection", () => context.activateSelection(context.getRenderedSelectableItems()));
692
+ return;
693
+ }
694
+ if (!searchScene && key.name === "q" && typeBarEmpty) {
695
+ renderer?.destroy();
696
+ return;
697
+ }
698
+ if (key.name === "escape") {
699
+ if (inputValue)
700
+ clearTypeBar(context);
701
+ else
702
+ context.runAppAction("Project menu", () => context.runtime.runIntent({ scene: "main", argv: ["main"], action: { kind: "none", label: "Project menu" } }));
703
+ }
704
+ }
705
+ export {
706
+ handleAppKeyPress
707
+ };