@h-rig/cli 0.0.6-alpha.87 → 0.0.6-alpha.88

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 CHANGED
@@ -1346,6 +1346,14 @@ var init__async_ui = __esm(() => {
1346
1346
  });
1347
1347
 
1348
1348
  // packages/cli/src/commands/_pi-frontend.ts
1349
+ var exports__pi_frontend = {};
1350
+ __export(exports__pi_frontend, {
1351
+ shouldRequireOperatorTranscript: () => shouldRequireOperatorTranscript,
1352
+ runWithProcessExitGuard: () => runWithProcessExitGuard,
1353
+ missingOperatorTranscriptMessage: () => missingOperatorTranscriptMessage,
1354
+ buildOperatorPiEnv: () => buildOperatorPiEnv,
1355
+ attachRunBundledPiFrontend: () => attachRunBundledPiFrontend
1356
+ });
1349
1357
  import { existsSync as existsSync4, mkdirSync as mkdirSync3, mkdtempSync, readFileSync as readFileSync3, rmSync, writeFileSync as writeFileSync2 } from "fs";
1350
1358
  import { homedir as homedir2, tmpdir } from "os";
1351
1359
  import { join } from "path";
@@ -1458,6 +1466,29 @@ function installRigPiTheme() {
1458
1466
  }
1459
1467
  } catch {}
1460
1468
  }
1469
+ async function runWithProcessExitGuard(body) {
1470
+ const realExit = process.exit;
1471
+ let exitCode = 0;
1472
+ let signalQuit = () => {};
1473
+ const quit = new Promise((resolve4) => {
1474
+ signalQuit = resolve4;
1475
+ });
1476
+ const guardedExit = (code) => {
1477
+ exitCode = typeof code === "number" ? code : 0;
1478
+ signalQuit();
1479
+ return;
1480
+ };
1481
+ process.exit = guardedExit;
1482
+ try {
1483
+ await Promise.race([Promise.resolve().then(body), quit]);
1484
+ } finally {
1485
+ process.exit = realExit;
1486
+ }
1487
+ return exitCode;
1488
+ }
1489
+ function runPiMainReturningOnQuit(args, options) {
1490
+ return runWithProcessExitGuard(() => runPiMain(args, options));
1491
+ }
1461
1492
  async function attachRunBundledPiFrontend(context, input) {
1462
1493
  const tempSessionDir = mkdtempSync(join(tmpdir(), "rig-pi-frontend-sessions-"));
1463
1494
  const { server, sessionFileArg } = await withSpinner(`Opening Pi console for run ${input.runId}\u2026`, () => prepareOperatorConsole(context, input.runId, tempSessionDir), { outputMode: context.outputMode });
@@ -1473,16 +1504,20 @@ async function attachRunBundledPiFrontend(context, input) {
1473
1504
  };
1474
1505
  let detached = false;
1475
1506
  try {
1476
- await runPiMain([
1507
+ const piArgs = [
1477
1508
  "--offline",
1478
1509
  "--no-extensions",
1479
1510
  "--no-skills",
1480
1511
  "--no-prompt-templates",
1481
1512
  "--no-context-files",
1482
1513
  ...sessionFileArg
1483
- ], {
1484
- extensionFactories: [piRigExtensionFactory]
1485
- });
1514
+ ];
1515
+ const piOptions = { extensionFactories: [piRigExtensionFactory] };
1516
+ if (input.returnOnQuit) {
1517
+ await runPiMainReturningOnQuit(piArgs, piOptions);
1518
+ } else {
1519
+ await runPiMain(piArgs, piOptions);
1520
+ }
1486
1521
  detached = true;
1487
1522
  } finally {
1488
1523
  restoreEnv();
@@ -1695,6 +1730,21 @@ async function captureConsole(fn) {
1695
1730
  console.error = original.error;
1696
1731
  }
1697
1732
  }
1733
+ async function releaseRendererForExternalTui(ctx) {
1734
+ const renderer = ctx.renderer;
1735
+ if (!renderer)
1736
+ return;
1737
+ if (renderer.suspend) {
1738
+ await renderer.suspend();
1739
+ return;
1740
+ }
1741
+ if (renderer.destroy) {
1742
+ await renderer.destroy();
1743
+ }
1744
+ }
1745
+ async function resumeRendererAfterExternalTui(ctx) {
1746
+ await ctx.renderer?.resume?.();
1747
+ }
1698
1748
  function arrayFromPayload(value) {
1699
1749
  if (Array.isArray(value)) {
1700
1750
  return value.filter((entry) => Boolean(entry && typeof entry === "object" && !Array.isArray(entry)));
@@ -15341,397 +15391,6 @@ async function runInitYes(ctx, args = ["--yes"]) {
15341
15391
  }
15342
15392
  var init_init2 = () => {};
15343
15393
 
15344
- // packages/cli/src/app-opentui/pi-pty-host.ts
15345
- import { fileURLToPath as fileURLToPath2 } from "url";
15346
- import { basename as basename4 } from "path";
15347
- import { RGBA, StyledText, TextAttributes } from "@opentui/core";
15348
- import { Terminal as XtermTerminal2 } from "@xterm/headless";
15349
- function clampCols2(cols) {
15350
- return Math.max(MIN_COLS2, Math.trunc(cols || 100));
15351
- }
15352
- function clampRows2(rows) {
15353
- return Math.max(MIN_ROWS2, Math.min(MAX_ROWS2, Math.trunc(rows || 35)));
15354
- }
15355
- function rgbaFromXtermColor(mode, value) {
15356
- if (mode === 1 || mode === 2 || mode === XTERM_COLOR_MODE_INDEXED_16 || mode === XTERM_COLOR_MODE_INDEXED_256) {
15357
- return RGBA.fromIndex(value);
15358
- }
15359
- if (mode === 3 || mode === XTERM_COLOR_MODE_RGB) {
15360
- return RGBA.fromInts(value >> 16 & 255, value >> 8 & 255, value & 255);
15361
- }
15362
- return;
15363
- }
15364
- function textAttributesFromCell(cell) {
15365
- let attributes = TextAttributes.NONE;
15366
- if (cell.isBold())
15367
- attributes |= TextAttributes.BOLD;
15368
- if (cell.isDim())
15369
- attributes |= TextAttributes.DIM;
15370
- if (cell.isItalic())
15371
- attributes |= TextAttributes.ITALIC;
15372
- if (cell.isUnderline())
15373
- attributes |= TextAttributes.UNDERLINE;
15374
- if (cell.isBlink())
15375
- attributes |= TextAttributes.BLINK;
15376
- if (cell.isInverse())
15377
- attributes |= TextAttributes.INVERSE;
15378
- if (cell.isInvisible())
15379
- attributes |= TextAttributes.HIDDEN;
15380
- if (cell.isStrikethrough())
15381
- attributes |= TextAttributes.STRIKETHROUGH;
15382
- return attributes;
15383
- }
15384
- function sameRgba(a, b) {
15385
- if (!a && !b)
15386
- return true;
15387
- return Boolean(a && b && a.equals(b));
15388
- }
15389
- function sameStyle(a, b) {
15390
- return sameRgba(a.fg, b.fg) && sameRgba(a.bg, b.bg) && (a.attributes ?? 0) === (b.attributes ?? 0);
15391
- }
15392
- function styleFromCell(cell) {
15393
- return {
15394
- fg: rgbaFromXtermColor(cell.getFgColorMode(), cell.getFgColor()),
15395
- bg: rgbaFromXtermColor(cell.getBgColorMode(), cell.getBgColor()),
15396
- attributes: textAttributesFromCell(cell)
15397
- };
15398
- }
15399
- function lineToStyledText(line, cols) {
15400
- if (!line)
15401
- return new StyledText([{ __isChunk: true, text: "" }]);
15402
- const chunks = [];
15403
- let run = "";
15404
- let runStyle = null;
15405
- const flush = () => {
15406
- if (!run)
15407
- return;
15408
- chunks.push({
15409
- __isChunk: true,
15410
- text: run,
15411
- ...runStyle?.fg ? { fg: runStyle.fg } : {},
15412
- ...runStyle?.bg ? { bg: runStyle.bg } : {},
15413
- ...(runStyle?.attributes ?? 0) !== 0 ? { attributes: runStyle.attributes } : {}
15414
- });
15415
- run = "";
15416
- };
15417
- for (let index = 0;index < cols; index += 1) {
15418
- const cell = line.getCell(index);
15419
- if (!cell) {
15420
- if (runStyle !== null)
15421
- flush();
15422
- runStyle = null;
15423
- run += " ";
15424
- continue;
15425
- }
15426
- if (cell.getWidth() === 0)
15427
- continue;
15428
- const style = styleFromCell(cell);
15429
- if (!runStyle || !sameStyle(runStyle, style)) {
15430
- flush();
15431
- runStyle = style;
15432
- }
15433
- run += cell.getChars() || " ";
15434
- }
15435
- flush();
15436
- return new StyledText(chunks.length > 0 ? chunks : [{ __isChunk: true, text: "" }]);
15437
- }
15438
- function childCommandPrefix2() {
15439
- const execName = basename4(process.execPath).toLowerCase();
15440
- const currentEntry = process.argv[1];
15441
- if (execName === "bun" || execName === "bun.exe") {
15442
- return currentEntry ? [process.execPath, currentEntry, "__opentui-pi-host"] : [process.execPath, fallbackChildScriptPath];
15443
- }
15444
- return [process.execPath, "__opentui-pi-host"];
15445
- }
15446
- function withEnv2(base) {
15447
- const env = {};
15448
- for (const [key, value] of Object.entries(base ?? process.env)) {
15449
- if (key === "RIG_PLAIN" || key === "RIG_CLI_PLAIN_HELP" || key === "NO_COLOR")
15450
- continue;
15451
- if (typeof value === "string")
15452
- env[key] = value;
15453
- }
15454
- return {
15455
- ...env,
15456
- TERM: "xterm-256color",
15457
- COLORTERM: "truecolor",
15458
- FORCE_COLOR: env.FORCE_COLOR ?? "1",
15459
- RIG_OPENTUI_PI_HOST: "1"
15460
- };
15461
- }
15462
- function getActivePiHost() {
15463
- return activeHost2 && !activeHost2.disposed ? activeHost2 : null;
15464
- }
15465
- async function startPiPtyHost(options) {
15466
- activeHost2?.dispose("replace");
15467
- const host = new PiPtyHost(options);
15468
- activeHost2 = host;
15469
- await host.start();
15470
- return host;
15471
- }
15472
- function stopActivePiHost(reason = "detach") {
15473
- activeHost2?.dispose(reason);
15474
- activeHost2 = null;
15475
- }
15476
-
15477
- class PiPtyHost {
15478
- runId;
15479
- projectRoot;
15480
- onSnapshot;
15481
- onExit;
15482
- onError;
15483
- terminal;
15484
- disposables = [];
15485
- decoder = new TextDecoder("utf-8");
15486
- proc = null;
15487
- pty = null;
15488
- status = "starting";
15489
- cols;
15490
- rows;
15491
- message = "starting bundled Pi";
15492
- lastResizeError = null;
15493
- exitCode;
15494
- signal;
15495
- notifyTimer = null;
15496
- lastStreamKey = "";
15497
- _disposed = false;
15498
- constructor(options) {
15499
- this.runId = options.runId;
15500
- this.projectRoot = options.projectRoot;
15501
- this.cols = clampCols2(options.cols);
15502
- this.rows = clampRows2(options.rows);
15503
- this.onSnapshot = options.onSnapshot;
15504
- this.onExit = options.onExit;
15505
- this.onError = options.onError;
15506
- this.terminal = new XtermTerminal2({
15507
- allowProposedApi: true,
15508
- cols: this.cols,
15509
- rows: this.rows,
15510
- scrollback: 1000
15511
- });
15512
- this.registerTerminalResponders();
15513
- this.disposables.push(this.terminal.onWriteParsed(() => {
15514
- if (this._disposed)
15515
- return;
15516
- const snapshot = this.createSnapshot();
15517
- const key = snapshot.lines.join(`
15518
- `);
15519
- if (key === this.lastStreamKey)
15520
- return;
15521
- this.lastStreamKey = key;
15522
- this.onSnapshot?.(snapshot);
15523
- }));
15524
- }
15525
- get disposed() {
15526
- return this._disposed;
15527
- }
15528
- get snapshot() {
15529
- return this.createSnapshot();
15530
- }
15531
- async start() {
15532
- if (this._disposed)
15533
- throw new Error("Pi PTY host is disposed.");
15534
- if (typeof Bun.Terminal !== "function") {
15535
- throw new Error("Bun native PTY is unavailable in this runtime. Pi host requires Bun.Terminal.");
15536
- }
15537
- const spawnOptions = {
15538
- cwd: this.projectRoot,
15539
- env: withEnv2(process.env),
15540
- terminal: {
15541
- cols: this.cols,
15542
- rows: this.rows,
15543
- name: "xterm-256color",
15544
- data: (_terminal, data) => this.handlePtyData(data)
15545
- }
15546
- };
15547
- const proc = Bun.spawn([
15548
- ...childCommandPrefix2(),
15549
- "--run-id",
15550
- this.runId,
15551
- "--project-root",
15552
- this.projectRoot
15553
- ], spawnOptions);
15554
- if (!proc.terminal)
15555
- throw new Error("Bun did not attach a terminal to the bundled Pi child process.");
15556
- this.proc = proc;
15557
- this.pty = proc.terminal;
15558
- this.status = "running";
15559
- this.message = "bundled Pi running inside this app";
15560
- this.emitSnapshotSoon(0);
15561
- proc.exited.then((exitCode) => {
15562
- if (this._disposed)
15563
- return;
15564
- this.status = exitCode === 0 ? "exited" : "failed";
15565
- this.exitCode = exitCode;
15566
- this.signal = null;
15567
- this.message = exitCode === 0 ? "bundled Pi exited" : `bundled Pi exited with code ${exitCode}`;
15568
- const snapshot = this.createSnapshot();
15569
- this.onSnapshot?.(snapshot);
15570
- this.onExit?.(snapshot);
15571
- if (activeHost2 === this)
15572
- activeHost2 = null;
15573
- this.dispose("exit", { kill: false, notify: false });
15574
- }).catch((error) => {
15575
- if (this._disposed)
15576
- return;
15577
- this.status = "failed";
15578
- this.message = error instanceof Error ? error.message : String(error);
15579
- const snapshot = this.createSnapshot();
15580
- this.onSnapshot?.(snapshot);
15581
- this.onError?.(error, snapshot);
15582
- if (activeHost2 === this)
15583
- activeHost2 = null;
15584
- this.dispose("error", { kill: false, notify: false });
15585
- });
15586
- }
15587
- write(data) {
15588
- if (this._disposed || !this.pty)
15589
- return;
15590
- try {
15591
- this.pty.write(data);
15592
- } catch (error) {
15593
- this.status = "failed";
15594
- this.message = error instanceof Error ? error.message : String(error);
15595
- const snapshot = this.createSnapshot();
15596
- this.onSnapshot?.(snapshot);
15597
- this.onError?.(error, snapshot);
15598
- }
15599
- }
15600
- resize(cols, rows) {
15601
- const nextCols = clampCols2(cols);
15602
- const nextRows = clampRows2(rows);
15603
- if (nextCols === this.cols && nextRows === this.rows)
15604
- return;
15605
- this.cols = nextCols;
15606
- this.rows = nextRows;
15607
- this.terminal.resize(nextCols, nextRows);
15608
- try {
15609
- this.pty?.resize(nextCols, nextRows);
15610
- this.lastResizeError = null;
15611
- } catch (error) {
15612
- this.lastResizeError = error instanceof Error ? error.message : String(error);
15613
- }
15614
- this.emitSnapshotSoon(0);
15615
- }
15616
- detach() {
15617
- this.dispose("detach");
15618
- return this.createSnapshot("detached from bundled Pi");
15619
- }
15620
- dispose(reason = "dispose", options = {}) {
15621
- if (this._disposed)
15622
- return;
15623
- this._disposed = true;
15624
- if (this.notifyTimer)
15625
- clearTimeout(this.notifyTimer);
15626
- this.notifyTimer = null;
15627
- for (const disposable of this.disposables.splice(0)) {
15628
- try {
15629
- disposable.dispose();
15630
- } catch {}
15631
- }
15632
- if (options.kill !== false) {
15633
- try {
15634
- this.proc?.kill("SIGTERM");
15635
- } catch {}
15636
- }
15637
- try {
15638
- this.pty?.close();
15639
- } catch {}
15640
- try {
15641
- this.terminal.dispose();
15642
- } catch {}
15643
- if (activeHost2 === this)
15644
- activeHost2 = null;
15645
- if (options.notify) {
15646
- this.message = reason;
15647
- this.onSnapshot?.(this.createSnapshot(reason));
15648
- }
15649
- }
15650
- handlePtyData(data) {
15651
- if (this._disposed)
15652
- return;
15653
- const text2 = this.decoder.decode(data, { stream: true });
15654
- this.respondToRawTerminalQueries(text2);
15655
- this.terminal.write(data);
15656
- }
15657
- emitSnapshotSoon(delayMs = SNAPSHOT_DELAY_MS2) {
15658
- if (this._disposed || this.notifyTimer)
15659
- return;
15660
- this.notifyTimer = setTimeout(() => {
15661
- this.notifyTimer = null;
15662
- if (this._disposed)
15663
- return;
15664
- this.onSnapshot?.(this.createSnapshot());
15665
- }, delayMs);
15666
- }
15667
- createSnapshot(message2 = this.message) {
15668
- const buffer = this.terminal.buffer.active;
15669
- const end = buffer.length;
15670
- const start = Math.max(0, end - MAX_SNAPSHOT_LINES2);
15671
- const styledStart = Math.max(start, end - this.rows - STYLED_SNAPSHOT_MARGIN);
15672
- const lines = [];
15673
- const styledLines = [];
15674
- for (let row = start;row < end; row += 1) {
15675
- const line = buffer.getLine(row);
15676
- lines.push((line?.translateToString(false) ?? "").slice(0, this.cols));
15677
- if (row >= styledStart)
15678
- styledLines[lines.length - 1] = lineToStyledText(line, this.cols);
15679
- }
15680
- while (lines.length < this.rows) {
15681
- lines.push("");
15682
- styledLines[lines.length - 1] = new StyledText([{ __isChunk: true, text: "" }]);
15683
- }
15684
- const resolvedMessage = this.lastResizeError ? `resize degraded: ${this.lastResizeError}` : message2;
15685
- return {
15686
- runId: this.runId,
15687
- status: this.status,
15688
- cols: this.cols,
15689
- rows: this.rows,
15690
- lines,
15691
- styledLines,
15692
- message: resolvedMessage,
15693
- ...this.lastResizeError ? { resizeError: this.lastResizeError } : {},
15694
- ...this.exitCode !== undefined ? { exitCode: this.exitCode } : {},
15695
- ...this.signal !== undefined ? { signal: this.signal } : {}
15696
- };
15697
- }
15698
- registerTerminalResponders() {
15699
- this.disposables.push(this.terminal.parser.registerCsiHandler({ final: "c" }, (params) => {
15700
- if (params.length === 0 || params[0] === 0)
15701
- this.write("\x1B[?62;22c");
15702
- return false;
15703
- }));
15704
- this.disposables.push(this.terminal.parser.registerCsiHandler({ prefix: ">", final: "c" }, (params) => {
15705
- if (params.length === 0 || params[0] === 0)
15706
- this.write("\x1B[>0;0;0c");
15707
- return false;
15708
- }));
15709
- this.disposables.push(this.terminal.parser.registerCsiHandler({ final: "n" }, (params) => {
15710
- if (params[0] === 5) {
15711
- this.write("\x1B[0n");
15712
- } else if (params[0] === 6) {
15713
- const row = this.terminal.buffer.active.cursorY + 1;
15714
- const col = this.terminal.buffer.active.cursorX + 1;
15715
- this.write(`\x1B[${row};${col}R`);
15716
- }
15717
- return false;
15718
- }));
15719
- }
15720
- respondToRawTerminalQueries(text2) {
15721
- if (!text2)
15722
- return;
15723
- const decrqm = /\x1b\[\?(\d+)\$p/g;
15724
- let match;
15725
- while ((match = decrqm.exec(text2)) !== null) {
15726
- this.write(`\x1B[?${match[1]};2$y`);
15727
- }
15728
- }
15729
- }
15730
- var MIN_COLS2 = 40, MIN_ROWS2 = 12, MAX_ROWS2 = 300, MAX_SNAPSHOT_LINES2 = 360, STYLED_SNAPSHOT_MARGIN = 6, SNAPSHOT_DELAY_MS2 = 120, fallbackChildScriptPath, activeHost2 = null, XTERM_COLOR_MODE_INDEXED_16 = 16777216, XTERM_COLOR_MODE_INDEXED_256 = 33554432, XTERM_COLOR_MODE_RGB = 50331648;
15731
- var init_pi_pty_host = __esm(() => {
15732
- fallbackChildScriptPath = fileURLToPath2(new URL("./pi-host-child.ts", import.meta.url));
15733
- });
15734
-
15735
15394
  // packages/cli/src/app-opentui/adapters/inspect.ts
15736
15395
  var exports_inspect = {};
15737
15396
  __export(exports_inspect, {
@@ -15954,14 +15613,6 @@ function recordStep(ctx, runId, label, emitLabel) {
15954
15613
  });
15955
15614
  emitProgress(ctx, emitLabel, label);
15956
15615
  }
15957
- function settleSteps(ctx, runId) {
15958
- const now = Date.now();
15959
- const previous = currentAttachState(ctx, runId);
15960
- if (!previous?.steps)
15961
- return;
15962
- const steps = previous.steps.map((step) => step.status === "running" ? { ...step, status: "done", endedAtMs: step.endedAtMs ?? now } : step);
15963
- patchData(ctx, { piAttach: { ...previous, steps } });
15964
- }
15965
15616
  async function preparePiAttachHandoff(ctx, runId) {
15966
15617
  const cleanRunId = runId.trim();
15967
15618
  const label = `Preparing Pi ${cleanRunId.slice(0, 8)}`;
@@ -16009,85 +15660,41 @@ async function attachRunWithBundledPi(ctx, runId) {
16009
15660
  throw error;
16010
15661
  }
16011
15662
  emitStarted(ctx, label, { piAttach: { runId: cleanRunId, status: "entering-pi" } });
16012
- patchData(ctx, { piAttach: { runId: cleanRunId, status: "entering-pi", message: "starting bundled Pi", steps: [] } });
15663
+ patchData(ctx, { piAttach: { runId: cleanRunId, status: "entering-pi", message: "handing the terminal to worker Pi\u2026" } });
15664
+ const projectRoot = projectRootOf(ctx);
15665
+ const outputMode = ctx.rig?.outputMode ?? "text";
15666
+ await releaseRendererForExternalTui(ctx);
15667
+ let outcome = null;
15668
+ let attachError = null;
16013
15669
  try {
16014
- recordStep(ctx, cleanRunId, "connecting to server and fetching run transcript", label);
16015
- await preparePiAttachHandoff(ctx, cleanRunId).catch((error) => {
16016
- patchData(ctx, { lastRefreshError: normalizeAppError(error).message });
16017
- return null;
16018
- });
16019
- recordStep(ctx, cleanRunId, "spawning Bun PTY host for bundled Pi", label);
16020
- const projectRoot = projectRootOf(ctx);
16021
- const cols = typeof process.stdout.columns === "number" ? process.stdout.columns : 100;
16022
- const rows = Math.max(1, (typeof process.stdout.rows === "number" ? process.stdout.rows : 35) - 1);
16023
- const patchTerminal = (snapshot) => {
16024
- const previousSteps = currentAttachState(ctx, cleanRunId)?.steps;
16025
- const failed = snapshot.status === "failed";
16026
- const reason = failed ? snapshot.message ?? `bundled Pi exited with code ${snapshot.exitCode ?? "unknown"}` : undefined;
16027
- patchData(ctx, {
16028
- piTerminal: snapshot,
16029
- piAttach: {
16030
- runId: cleanRunId,
16031
- status: failed ? "failed" : snapshot.status === "exited" ? "returned" : "entering-pi",
16032
- message: snapshot.message ?? "bundled Pi running",
16033
- result: { runId: cleanRunId, status: snapshot.status, exitCode: snapshot.exitCode },
16034
- ...previousSteps ? { steps: previousSteps } : {},
16035
- ...reason ? { failureReason: reason } : {}
16036
- }
16037
- });
16038
- };
16039
- recordStep(ctx, cleanRunId, "starting bundled Pi", label);
16040
- await startPiPtyHost({
16041
- runId: cleanRunId,
16042
- projectRoot,
16043
- cols,
16044
- rows,
16045
- onSnapshot: (snapshot) => {
16046
- if (snapshot.status === "running" && snapshot.lines.some((entry) => entry.trim().length > 0)) {
16047
- settleSteps(ctx, cleanRunId);
16048
- }
16049
- patchTerminal(snapshot);
16050
- },
16051
- onExit: (snapshot) => {
16052
- patchTerminal(snapshot);
16053
- if (snapshot.status === "failed") {
16054
- Promise.resolve().then(() => (init_inspect2(), exports_inspect)).then(({ loadInspect: loadInspect2 }) => loadInspect2(ctx, cleanRunId)).catch(() => null);
16055
- emitFailed(ctx, label, new Error(snapshot.message ?? `bundled Pi exited with code ${snapshot.exitCode ?? "unknown"}`), { runId: cleanRunId, result: { status: snapshot.status, exitCode: snapshot.exitCode } });
16056
- return;
16057
- }
16058
- emitCompleted(ctx, label, { runId: cleanRunId, result: { status: snapshot.status, exitCode: snapshot.exitCode } });
16059
- ctx.emit({ type: "scene.change", scene: "fleet", intent: { scene: "fleet", argv: ["runs"], action: { kind: "refresh", label: "Opening runs" } } });
16060
- refreshFleet(ctx).catch((error) => emitFailed(ctx, "Refresh runs", error));
16061
- },
16062
- onError: (error, snapshot) => {
16063
- patchTerminal(snapshot);
16064
- emitFailed(ctx, label, error, { runId: cleanRunId });
16065
- }
16066
- });
16067
- const record = {
16068
- runId: cleanRunId,
16069
- status: "running",
16070
- rendered: "bundled Pi embedded via Bun.Terminal + @xterm/headless"
16071
- };
16072
- emitProgress(ctx, label, "bundled Pi is running", { piAttach: { runId: cleanRunId, status: "entering-pi", message: "bundled Pi running", result: record } });
16073
- return record;
15670
+ const { attachRunBundledPiFrontend: attachRunBundledPiFrontend2 } = await Promise.resolve().then(() => (init__pi_frontend(), exports__pi_frontend));
15671
+ outcome = await attachRunBundledPiFrontend2({ projectRoot, outputMode }, { runId: cleanRunId, returnOnQuit: true });
16074
15672
  } catch (error) {
16075
- const reason = error instanceof Error ? error.message : String(error);
15673
+ attachError = error;
15674
+ } finally {
15675
+ await resumeRendererAfterExternalTui(ctx);
15676
+ }
15677
+ if (attachError) {
15678
+ const reason = normalizeAppError(attachError).message;
16076
15679
  patchData(ctx, {
16077
- piAttach: {
16078
- runId: cleanRunId,
16079
- status: "failed",
16080
- message: reason,
16081
- failureReason: reason,
16082
- ...currentAttachState(ctx, cleanRunId)?.steps ? { steps: currentAttachState(ctx, cleanRunId).steps } : {}
16083
- }
15680
+ piTerminal: undefined,
15681
+ piAttach: { runId: cleanRunId, status: "failed", message: reason, failureReason: reason }
16084
15682
  });
16085
- emitFailed(ctx, label, error, { runId: cleanRunId });
16086
- throw error;
15683
+ Promise.resolve().then(() => (init_inspect2(), exports_inspect)).then(({ loadInspect: loadInspect2 }) => loadInspect2(ctx, cleanRunId)).catch(() => null);
15684
+ emitFailed(ctx, label, attachError, { runId: cleanRunId });
15685
+ throw attachError;
16087
15686
  }
15687
+ const detached = outcome?.detached === true;
15688
+ patchData(ctx, {
15689
+ piTerminal: undefined,
15690
+ piAttach: { runId: cleanRunId, status: "returned", message: detached ? "detached from worker Pi" : "returned from worker Pi" }
15691
+ });
15692
+ emitCompleted(ctx, label, { runId: cleanRunId, result: { runId: cleanRunId, detached } });
15693
+ ctx.emit({ type: "scene.change", scene: "fleet", intent: { scene: "fleet", argv: ["runs"], action: { kind: "refresh", label: "Opening runs" } } });
15694
+ refreshFleet(ctx).catch((error) => emitFailed(ctx, "Refresh runs", error));
15695
+ return { runId: cleanRunId, status: "returned", detached };
16088
15696
  }
16089
15697
  var init_pi_attach = __esm(() => {
16090
- init_pi_pty_host();
16091
15698
  init_fleet();
16092
15699
  });
16093
15700
 
@@ -17378,7 +16985,7 @@ import {
17378
16985
  dim as otuiDim,
17379
16986
  fg as otuiFg,
17380
16987
  t,
17381
- TextAttributes as TextAttributes2
16988
+ TextAttributes
17382
16989
  } from "@opentui/core";
17383
16990
  function statusColor3(status) {
17384
16991
  switch (status) {
@@ -17483,7 +17090,7 @@ var init_theme2 = __esm(() => {
17483
17090
  });
17484
17091
 
17485
17092
  // packages/cli/src/app-opentui/drone.ts
17486
- import { RGBA as RGBA2, StyledText as StyledText2, TextAttributes as TextAttributes3 } from "@opentui/core";
17093
+ import { RGBA, StyledText, TextAttributes as TextAttributes2 } from "@opentui/core";
17487
17094
  function bladeForTick(tick, phase = 0) {
17488
17095
  return BLADE_FRAMES2[(Math.floor(tick / 3) + phase) % BLADE_FRAMES2.length];
17489
17096
  }
@@ -17492,12 +17099,12 @@ function eyeForTick(tick, phase = 0) {
17492
17099
  return pulse > 0.55 ? EYE_FRAMES2[1] : pulse > 0.1 ? EYE_FRAMES2[0] : pulse > -0.45 ? EYE_FRAMES2[2] : EYE_FRAMES2[3];
17493
17100
  }
17494
17101
  function chunk(text2, fg2, bold2 = false, dim = false) {
17495
- let attributes = TextAttributes3.NONE;
17102
+ let attributes = TextAttributes2.NONE;
17496
17103
  if (bold2)
17497
- attributes |= TextAttributes3.BOLD;
17104
+ attributes |= TextAttributes2.BOLD;
17498
17105
  if (dim)
17499
- attributes |= TextAttributes3.DIM;
17500
- return { __isChunk: true, text: text2, fg: fg2, ...attributes !== TextAttributes3.NONE ? { attributes } : {} };
17106
+ attributes |= TextAttributes2.DIM;
17107
+ return { __isChunk: true, text: text2, fg: fg2, ...attributes !== TextAttributes2.NONE ? { attributes } : {} };
17501
17108
  }
17502
17109
  function styledLine(text2, colorFor) {
17503
17110
  const chunks = [];
@@ -17518,7 +17125,7 @@ function styledLine(text2, colorFor) {
17518
17125
  run += char;
17519
17126
  }
17520
17127
  flush();
17521
- return new StyledText2(chunks.length > 0 ? chunks : [chunk("", COLOR.ink)]);
17128
+ return new StyledText(chunks.length > 0 ? chunks : [chunk("", COLOR.ink)]);
17522
17129
  }
17523
17130
  function droneColor(char) {
17524
17131
  if (char === "?" || char === "o" || char === "@" || char === "\u2022")
@@ -17610,13 +17217,13 @@ var init_drone = __esm(() => {
17610
17217
  BLADE_FRAMES2 = ["---", "\\\\\\", "|||", "///"];
17611
17218
  EYE_FRAMES2 = ["o", "@", "\u2022", "."];
17612
17219
  COLOR = {
17613
- body: RGBA2.fromHex(RIG_UI.lime),
17614
- mini: RGBA2.fromHex(RIG_UI.limeDim),
17615
- rotor: RGBA2.fromHex(RIG_UI.cyan),
17616
- path: RGBA2.fromHex(RIG_UI.cyan),
17617
- eye: RGBA2.fromHex(RIG_UI.ink),
17618
- dim: RGBA2.fromHex(RIG_UI.ink4),
17619
- ink: RGBA2.fromHex(RIG_UI.ink2)
17220
+ body: RGBA.fromHex(RIG_UI.lime),
17221
+ mini: RGBA.fromHex(RIG_UI.limeDim),
17222
+ rotor: RGBA.fromHex(RIG_UI.cyan),
17223
+ path: RGBA.fromHex(RIG_UI.cyan),
17224
+ eye: RGBA.fromHex(RIG_UI.ink),
17225
+ dim: RGBA.fromHex(RIG_UI.ink4),
17226
+ ink: RGBA.fromHex(RIG_UI.ink2)
17620
17227
  };
17621
17228
  });
17622
17229
 
@@ -23046,7 +22653,7 @@ var init__json_output = __esm(() => {
23046
22653
 
23047
22654
  // packages/cli/src/launcher.ts
23048
22655
  import { existsSync as existsSync22 } from "fs";
23049
- import { basename as basename5, resolve as resolve29 } from "path";
22656
+ import { basename as basename4, resolve as resolve29 } from "path";
23050
22657
  import { loadDotEnvSecrets } from "@rig/runtime/baked-secrets";
23051
22658
  import { RIG_DEFINITION_DIRNAME, RIG_STATE_DIRNAME, resolveNearestRigProjectRoot } from "@rig/runtime/layout";
23052
22659
  function parsePolicyMode(value) {
@@ -23071,7 +22678,7 @@ function resolveProjectRoot({
23071
22678
  return resolve29(cwd, envProjectRoot);
23072
22679
  }
23073
22680
  const fallbackImportDir = importDir ?? cwd;
23074
- const execName = basename5(execPath).toLowerCase();
22681
+ const execName = basename4(execPath).toLowerCase();
23075
22682
  const execCandidates = execName === "rig" || execName === "rig.exe" ? [resolve29(execPath, "..", "..")] : [];
23076
22683
  const candidates = [cwd, ...execCandidates, resolve29(fallbackImportDir, "..")];
23077
22684
  for (const candidate of candidates) {
@@ -23940,6 +23547,12 @@ var init_autocomplete = __esm(() => {
23940
23547
  NAV_VERBS = ["runs", "tasks", "inbox", "server", "doctor", "help", "init", "main"];
23941
23548
  });
23942
23549
 
23550
+ // packages/cli/src/app-opentui/pi-pty-host.ts
23551
+ function getActivePiHost() {
23552
+ return null;
23553
+ }
23554
+ function stopActivePiHost(_reason) {}
23555
+
23943
23556
  // packages/cli/src/app-opentui/keymap.ts
23944
23557
  function clearTypeBar(context, message2) {
23945
23558
  const typeBar = context.getTypeBar();
@@ -24172,7 +23785,6 @@ var init_keymap = __esm(() => {
24172
23785
  init_autocomplete();
24173
23786
  init_command_pty_host();
24174
23787
  init_intent();
24175
- init_pi_pty_host();
24176
23788
  });
24177
23789
 
24178
23790
  // packages/cli/src/app-opentui/runtime-resources.ts
@@ -24329,7 +23941,7 @@ var init_constants = __esm(() => {
24329
23941
  });
24330
23942
 
24331
23943
  // packages/cli/src/app-opentui/render/graphics.ts
24332
- import { FrameBufferRenderable, RGBA as RGBA3 } from "@opentui/core";
23944
+ import { FrameBufferRenderable, RGBA as RGBA2 } from "@opentui/core";
24333
23945
  function sceneKind(scene) {
24334
23946
  if (scene === "tasks")
24335
23947
  return "loop";
@@ -24424,7 +24036,7 @@ function paletteColor(ac, c2, mg, ink5) {
24424
24036
  const b = (AC_RGB[2] * ac + C2_RGB[2] * c2 + MG_RGB[2] * mg + INK_RGB[2] * ink5) / total;
24425
24037
  const intensity = Math.min(1, total / 4);
24426
24038
  const lift = 0.34 + intensity * 0.66;
24427
- return RGBA3.fromInts(Math.round(r * lift), Math.round(g * lift), Math.round(b * lift), 255);
24039
+ return RGBA2.fromInts(Math.round(r * lift), Math.round(g * lift), Math.round(b * lift), 255);
24428
24040
  }
24429
24041
  function clearCanvas(canvas) {
24430
24042
  canvas.ac.fill(0);
@@ -24779,8 +24391,8 @@ function staticFleetTick(scene) {
24779
24391
  }
24780
24392
  function withAlpha(hex, alpha) {
24781
24393
  const a = Math.max(0, Math.min(255, Math.round(alpha * 255)));
24782
- const [r, g, b] = RGBA3.fromHex(hex).toInts();
24783
- return RGBA3.fromInts(r, g, b, a);
24394
+ const [r, g, b] = RGBA2.fromHex(hex).toInts();
24395
+ return RGBA2.fromInts(r, g, b, a);
24784
24396
  }
24785
24397
  function asciiFleetColor(char, row, alpha) {
24786
24398
  if (char === "@" || char === "$" || char === "o")
@@ -24788,7 +24400,7 @@ function asciiFleetColor(char, row, alpha) {
24788
24400
  if (char === "%" || char === "!" || char === "/" || char === "\\" || char === "|")
24789
24401
  return withAlpha(RIG_UI.cyan, alpha);
24790
24402
  if (char === "." || char === "'" || char === "_" || row < 3 || row > FLEET_GRID_HEIGHT - 4) {
24791
- return RGBA3.fromInts(108, 110, 121, Math.min(170, Math.max(0, Math.min(255, Math.round(alpha * 255)))));
24403
+ return RGBA2.fromInts(108, 110, 121, Math.min(170, Math.max(0, Math.min(255, Math.round(alpha * 255)))));
24792
24404
  }
24793
24405
  return withAlpha(RIG_UI.limeDim, alpha);
24794
24406
  }
@@ -24842,13 +24454,13 @@ var init_graphics = __esm(() => {
24842
24454
  init_theme2();
24843
24455
  init_ascii_fleet();
24844
24456
  init_constants();
24845
- TRANSPARENT = RGBA3.fromValues(0, 0, 0, 0);
24846
- BACKDROP = RGBA3.fromHex(RIG_UI.bg);
24847
- PANEL_BG = RGBA3.fromInts(16, 17, 21, 184);
24848
- PANEL_HEADER_BG = RGBA3.fromInts(16, 17, 21, 188);
24849
- PANEL_LINE = RGBA3.fromInts(255, 255, 255, 20);
24850
- PANEL_LINE_DIM = RGBA3.fromInts(255, 255, 255, 8);
24851
- PANEL_TEXT_DIM = RGBA3.fromInts(108, 110, 121, 255);
24457
+ TRANSPARENT = RGBA2.fromValues(0, 0, 0, 0);
24458
+ BACKDROP = RGBA2.fromHex(RIG_UI.bg);
24459
+ PANEL_BG = RGBA2.fromInts(16, 17, 21, 184);
24460
+ PANEL_HEADER_BG = RGBA2.fromInts(16, 17, 21, 188);
24461
+ PANEL_LINE = RGBA2.fromInts(255, 255, 255, 20);
24462
+ PANEL_LINE_DIM = RGBA2.fromInts(255, 255, 255, 8);
24463
+ PANEL_TEXT_DIM = RGBA2.fromInts(108, 110, 121, 255);
24852
24464
  AC_RGB = [204, 255, 77];
24853
24465
  C2_RGB = [86, 216, 255];
24854
24466
  MG_RGB = [255, 121, 176];
@@ -24891,7 +24503,7 @@ var init_hover = __esm(() => {
24891
24503
  });
24892
24504
 
24893
24505
  // packages/cli/src/app-opentui/render/text.ts
24894
- import { RGBA as RGBA4, TextAttributes as TextAttributes4, TextRenderable } from "@opentui/core";
24506
+ import { RGBA as RGBA3, TextAttributes as TextAttributes3, TextRenderable } from "@opentui/core";
24895
24507
  function lineIsClickable(line2) {
24896
24508
  return line2?.selectable !== undefined || line2?.selectableIndex !== undefined;
24897
24509
  }
@@ -24900,7 +24512,7 @@ function lineSelectableId(line2) {
24900
24512
  }
24901
24513
  function lineBackground(line2) {
24902
24514
  if (line2?.bg)
24903
- return RGBA4.fromHex(line2.bg);
24515
+ return RGBA3.fromHex(line2.bg);
24904
24516
  const id = lineSelectableId(line2);
24905
24517
  if (id !== undefined && isHovered(id))
24906
24518
  return HOVER_BG;
@@ -24969,7 +24581,7 @@ function applyTextLine(renderable, line2, top, left, width) {
24969
24581
  renderable.content = line2?.styledText ?? line2?.text ?? "";
24970
24582
  renderable.fg = line2?.fg ?? RIG_UI.ink2;
24971
24583
  renderable.bg = lineBackground(line2);
24972
- renderable.attributes = line2?.bold ? TextAttributes4.BOLD : line2?.dim ? TextAttributes4.DIM : 0;
24584
+ renderable.attributes = line2?.bold ? TextAttributes3.BOLD : line2?.dim ? TextAttributes3.DIM : 0;
24973
24585
  }
24974
24586
  function applyFlowTextLine(renderable, line2, width) {
24975
24587
  renderable.setRigSceneLine?.(line2);
@@ -24979,7 +24591,7 @@ function applyFlowTextLine(renderable, line2, width) {
24979
24591
  renderable.content = line2?.styledText ?? line2?.text ?? "";
24980
24592
  renderable.fg = line2?.fg ?? RIG_UI.ink2;
24981
24593
  renderable.bg = lineBackground(line2);
24982
- renderable.attributes = line2?.bold ? TextAttributes4.BOLD : line2?.dim ? TextAttributes4.DIM : 0;
24594
+ renderable.attributes = line2?.bold ? TextAttributes3.BOLD : line2?.dim ? TextAttributes3.DIM : 0;
24983
24595
  }
24984
24596
  function clearTextLines(renderables, from = 0) {
24985
24597
  for (let index = from;index < renderables.length; index += 1) {
@@ -24996,19 +24608,30 @@ var TRANSPARENT2, HOVER_BG;
24996
24608
  var init_text = __esm(() => {
24997
24609
  init_theme2();
24998
24610
  init_hover();
24999
- TRANSPARENT2 = RGBA4.fromInts(0, 0, 0, 0);
25000
- HOVER_BG = RGBA4.fromHex(RIG_UI.hover);
24611
+ TRANSPARENT2 = RGBA3.fromInts(0, 0, 0, 0);
24612
+ HOVER_BG = RGBA3.fromHex(RIG_UI.hover);
25001
24613
  });
25002
24614
 
24615
+ // packages/cli/src/app-opentui/render/terminal-handoff.ts
24616
+ function resumeRendererClean(renderer) {
24617
+ try {
24618
+ renderer.pendingSuspendedTerminalSetup = true;
24619
+ } catch {}
24620
+ renderer.resume();
24621
+ try {
24622
+ renderer.requestRender?.();
24623
+ } catch {}
24624
+ }
24625
+
25003
24626
  // packages/cli/src/app-opentui/render/panels.ts
25004
- import { RGBA as RGBA5, ScrollBoxRenderable } from "@opentui/core";
24627
+ import { RGBA as RGBA4, ScrollBoxRenderable } from "@opentui/core";
25005
24628
  function hexToRgba(hex, alpha) {
25006
24629
  const clean = hex.replace(/^#/, "");
25007
24630
  const full = clean.length === 3 ? clean.split("").map((part) => `${part}${part}`).join("") : clean;
25008
24631
  const value = Number.parseInt(full, 16);
25009
24632
  if (!Number.isFinite(value))
25010
- return RGBA5.fromInts(14, 15, 17, alpha);
25011
- return RGBA5.fromInts(value >> 16 & 255, value >> 8 & 255, value & 255, alpha);
24633
+ return RGBA4.fromInts(14, 15, 17, alpha);
24634
+ return RGBA4.fromInts(value >> 16 & 255, value >> 8 & 255, value & 255, alpha);
25012
24635
  }
25013
24636
  function panelBackground(panel) {
25014
24637
  return hexToRgba(panel.backgroundColor ?? RIG_UI.panel, PANEL_OPAQUE_ALPHA);
@@ -25125,12 +24748,12 @@ var TRANSPARENT3, MAX_PANEL_LINES = 420, PANEL_OPAQUE_ALPHA = 255, PANEL_BORDER;
25125
24748
  var init_panels = __esm(() => {
25126
24749
  init_theme2();
25127
24750
  init_text();
25128
- TRANSPARENT3 = RGBA5.fromInts(0, 0, 0, 0);
24751
+ TRANSPARENT3 = RGBA4.fromInts(0, 0, 0, 0);
25129
24752
  PANEL_BORDER = hexToRgba(RIG_UI.border, 255);
25130
24753
  });
25131
24754
 
25132
24755
  // packages/cli/src/app-opentui/render/type-bar.ts
25133
- import { BoxRenderable, InputRenderable, InputRenderableEvents, RGBA as RGBA6, StyledText as StyledText3, TextRenderable as TextRenderable2 } from "@opentui/core";
24756
+ import { BoxRenderable, InputRenderable, InputRenderableEvents, RGBA as RGBA5, StyledText as StyledText2, TextRenderable as TextRenderable2 } from "@opentui/core";
25134
24757
  function createTypeBar(renderer) {
25135
24758
  const background = new BoxRenderable(renderer, {
25136
24759
  id: "typebar-card",
@@ -25277,13 +24900,13 @@ function formatFooter(footer, width) {
25277
24900
  chunks.push(cell4.style(text2));
25278
24901
  used += visibleWidth(text2);
25279
24902
  });
25280
- return new StyledText3(chunks);
24903
+ return new StyledText2(chunks);
25281
24904
  }
25282
24905
  var TYPEBAR_BG, TYPEBAR_BORDER, FOOTER_SEPARATOR = " \xB7 ";
25283
24906
  var init_type_bar = __esm(() => {
25284
24907
  init_theme2();
25285
- TYPEBAR_BG = RGBA6.fromInts(20, 25, 14, 224);
25286
- TYPEBAR_BORDER = RGBA6.fromInts(204, 255, 77, 92);
24908
+ TYPEBAR_BG = RGBA5.fromInts(20, 25, 14, 224);
24909
+ TYPEBAR_BORDER = RGBA5.fromInts(204, 255, 77, 92);
25287
24910
  });
25288
24911
 
25289
24912
  // packages/cli/src/app-opentui/render/native-host.ts
@@ -25291,7 +24914,7 @@ import {
25291
24914
  BoxRenderable as BoxRenderable2,
25292
24915
  CodeRenderable,
25293
24916
  DiffRenderable,
25294
- RGBA as RGBA7,
24917
+ RGBA as RGBA6,
25295
24918
  ScrollBoxRenderable as ScrollBoxRenderable2,
25296
24919
  SyntaxStyle,
25297
24920
  TextRenderable as TextRenderable3,
@@ -25301,19 +24924,19 @@ function rigSyntaxStyle() {
25301
24924
  if (syntaxStyle)
25302
24925
  return syntaxStyle;
25303
24926
  syntaxStyle = SyntaxStyle.fromStyles({
25304
- keyword: { fg: RGBA7.fromHex(RIG_UI.magenta), bold: true },
25305
- string: { fg: RGBA7.fromHex(RIG_UI.lime) },
25306
- number: { fg: RGBA7.fromHex(RIG_UI.cyan) },
25307
- comment: { fg: RGBA7.fromHex(RIG_UI.ink4), italic: true },
25308
- function: { fg: RGBA7.fromHex(RIG_UI.cyan) },
25309
- type: { fg: RGBA7.fromHex(RIG_UI.limeDim) },
25310
- constant: { fg: RGBA7.fromHex(RIG_UI.yellow) },
25311
- default: { fg: RGBA7.fromHex(RIG_UI.ink2) }
24927
+ keyword: { fg: RGBA6.fromHex(RIG_UI.magenta), bold: true },
24928
+ string: { fg: RGBA6.fromHex(RIG_UI.lime) },
24929
+ number: { fg: RGBA6.fromHex(RIG_UI.cyan) },
24930
+ comment: { fg: RGBA6.fromHex(RIG_UI.ink4), italic: true },
24931
+ function: { fg: RGBA6.fromHex(RIG_UI.cyan) },
24932
+ type: { fg: RGBA6.fromHex(RIG_UI.limeDim) },
24933
+ constant: { fg: RGBA6.fromHex(RIG_UI.yellow) },
24934
+ default: { fg: RGBA6.fromHex(RIG_UI.ink2) }
25312
24935
  });
25313
24936
  return syntaxStyle;
25314
24937
  }
25315
24938
  function tableContent(rows) {
25316
- return rows.map((row, rowIndex) => row.map((cell4) => [{ __isChunk: true, text: cell4, fg: RGBA7.fromHex(rowIndex === 0 ? RIG_UI.ink3 : RIG_UI.ink2) }]));
24939
+ return rows.map((row, rowIndex) => row.map((cell4) => [{ __isChunk: true, text: cell4, fg: RGBA6.fromHex(rowIndex === 0 ? RIG_UI.ink3 : RIG_UI.ink2) }]));
25317
24940
  }
25318
24941
  function createNativeHost(renderer) {
25319
24942
  try {
@@ -25841,7 +25464,7 @@ __export(exports_runtime, {
25841
25464
  });
25842
25465
  import { existsSync as existsSync23 } from "fs";
25843
25466
  import { resolve as resolve30 } from "path";
25844
- import { BoxRenderable as BoxRenderable3, RGBA as RGBA8, StyledText as StyledText4, TextRenderable as TextRenderable4, createCliRenderer } from "@opentui/core";
25467
+ import { BoxRenderable as BoxRenderable3, RGBA as RGBA7, StyledText as StyledText3, TextRenderable as TextRenderable4, createCliRenderer } from "@opentui/core";
25845
25468
  function inboxPendingCount(state) {
25846
25469
  const inbox = state.data.inbox;
25847
25470
  if (!inbox || typeof inbox !== "object" || Array.isArray(inbox))
@@ -26010,7 +25633,7 @@ function buildNavStrip(state) {
26010
25633
  chunks.push(otuiBold(styles.yellow(String(badgeCount))));
26011
25634
  }
26012
25635
  });
26013
- return new StyledText4(chunks);
25636
+ return new StyledText3(chunks);
26014
25637
  }
26015
25638
  function requestRender(renderer) {
26016
25639
  renderer.requestRender?.();
@@ -26238,7 +25861,8 @@ async function launchRigOpenTuiApp(options) {
26238
25861
  },
26239
25862
  suspend: () => renderer?.suspend(),
26240
25863
  resume: () => {
26241
- renderer?.resume();
25864
+ if (renderer)
25865
+ resumeRendererClean(renderer);
26242
25866
  renderApp();
26243
25867
  },
26244
25868
  destroy: () => renderer?.destroy()
@@ -26664,8 +26288,7 @@ var init_runtime = __esm(() => {
26664
26288
  init_help();
26665
26289
  init_main();
26666
26290
  init_selectable();
26667
- init_pi_pty_host();
26668
- PRELOADER_BACKDROP = RGBA8.fromHex(RIG_UI.bg);
26291
+ PRELOADER_BACKDROP = RGBA7.fromHex(RIG_UI.bg);
26669
26292
  PRIMARY_NAV = [
26670
26293
  { id: "cockpit", label: "Cockpit", scene: "main", argv: ["main"], enterLabel: "Cockpit", member: ["main"] },
26671
26294
  { id: "runs", label: "Runs", scene: "fleet", argv: ["runs"], enterLabel: "Runs", member: ["fleet", "run-detail", "inspect", "handoff"] },
@@ -26782,10 +26405,10 @@ var init_scroll = __esm(() => {
26782
26405
  // packages/cli/src/app-opentui/react/SceneFrameView.tsx
26783
26406
  import { useEffect as useEffect2, useRef as useRef2 } from "react";
26784
26407
  import { useRenderer as useRenderer2 } from "@opentui/react";
26785
- import { TextAttributes as TextAttributes5 } from "@opentui/core";
26408
+ import { TextAttributes as TextAttributes4 } from "@opentui/core";
26786
26409
  import { jsxDEV, Fragment } from "@opentui/react/jsx-dev-runtime";
26787
26410
  function lineAttributes(line2) {
26788
- return line2?.bold ? TextAttributes5.BOLD : line2?.dim ? TextAttributes5.DIM : 0;
26411
+ return line2?.bold ? TextAttributes4.BOLD : line2?.dim ? TextAttributes4.DIM : 0;
26789
26412
  }
26790
26413
  function lineContent(line2) {
26791
26414
  return line2.styledText ?? line2.text ?? "";
@@ -27168,8 +26791,48 @@ async function launchRigReactApp(options) {
27168
26791
  const store = createAppStore(initialState);
27169
26792
  const events = createAppEventBus();
27170
26793
  let renderer = null;
26794
+ let root = null;
27171
26795
  let runnerPromise = null;
27172
26796
  let suppressHistoryPush = false;
26797
+ let resolveExit = () => {};
26798
+ const appExit = new Promise((resolve31) => {
26799
+ resolveExit = resolve31;
26800
+ });
26801
+ let relaunching = false;
26802
+ const mountRenderer = async () => {
26803
+ renderer = await createCliRenderer2({
26804
+ screenMode: "alternate-screen",
26805
+ exitOnCtrlC: false,
26806
+ targetFps: 30,
26807
+ maxFps: 30,
26808
+ useMouse: true,
26809
+ autoFocus: false,
26810
+ useKittyKeyboard: { disambiguate: true }
26811
+ });
26812
+ renderer.on("destroy", () => {
26813
+ if (!relaunching)
26814
+ resolveExit();
26815
+ });
26816
+ root = createRoot(renderer);
26817
+ root.render(/* @__PURE__ */ jsxDEV4(RigAppProvider, {
26818
+ value: { store, runtime, runAppAction },
26819
+ children: /* @__PURE__ */ jsxDEV4(App, {
26820
+ sceneRenderers: options.sceneRenderers
26821
+ }, undefined, false, undefined, this)
26822
+ }, undefined, false, undefined, this));
26823
+ relaunching = false;
26824
+ };
26825
+ const teardownRenderer = () => {
26826
+ relaunching = true;
26827
+ try {
26828
+ root?.unmount();
26829
+ } catch {}
26830
+ root = null;
26831
+ try {
26832
+ renderer?.destroy();
26833
+ } catch {}
26834
+ renderer = null;
26835
+ };
27173
26836
  const runAppAction = (label, action) => {
27174
26837
  action().catch((error) => {
27175
26838
  const n = normalizeError(error);
@@ -27211,8 +26874,10 @@ async function launchRigReactApp(options) {
27211
26874
  runnerPromise ??= options.initializeRuntime();
27212
26875
  return runnerPromise;
27213
26876
  },
27214
- suspend: () => renderer?.suspend(),
27215
- resume: () => renderer?.resume(),
26877
+ suspend: () => teardownRenderer(),
26878
+ resume: async () => {
26879
+ await mountRenderer();
26880
+ },
27216
26881
  destroy: () => renderer?.destroy()
27217
26882
  };
27218
26883
  events.subscribe((event) => store.patch(reduceAppEvent(store.getState(), event)));
@@ -27220,15 +26885,7 @@ async function launchRigReactApp(options) {
27220
26885
  if (typeof tick.unref === "function") {
27221
26886
  tick.unref();
27222
26887
  }
27223
- renderer = await createCliRenderer2({
27224
- screenMode: "alternate-screen",
27225
- exitOnCtrlC: false,
27226
- targetFps: 30,
27227
- maxFps: 30,
27228
- useMouse: true,
27229
- autoFocus: false,
27230
- useKittyKeyboard: { disambiguate: true }
27231
- });
26888
+ await mountRenderer();
27232
26889
  let liveEvents = null;
27233
26890
  const canAutoRefresh = (state) => {
27234
26891
  if (state.status === "action" || state.status === "loading")
@@ -27259,12 +26916,6 @@ async function launchRigReactApp(options) {
27259
26916
  }, FALLBACK_REFRESH_MS);
27260
26917
  if (typeof poll.unref === "function")
27261
26918
  poll.unref();
27262
- createRoot(renderer).render(/* @__PURE__ */ jsxDEV4(RigAppProvider, {
27263
- value: { store, runtime, runAppAction },
27264
- children: /* @__PURE__ */ jsxDEV4(App, {
27265
- sceneRenderers: options.sceneRenderers
27266
- }, undefined, false, undefined, this)
27267
- }, undefined, false, undefined, this));
27268
26919
  queueMicrotask(() => {
27269
26920
  (async () => {
27270
26921
  try {
@@ -27292,14 +26943,11 @@ async function launchRigReactApp(options) {
27292
26943
  }
27293
26944
  })();
27294
26945
  });
27295
- await new Promise((resolve31) => {
27296
- renderer?.on("destroy", () => {
27297
- clearInterval(tick);
27298
- clearInterval(poll);
27299
- liveEvents?.close();
27300
- resolve31();
27301
- });
27302
- });
26946
+ await appExit;
26947
+ clearInterval(tick);
26948
+ clearInterval(poll);
26949
+ liveEvents?.close();
26950
+ teardownRenderer();
27303
26951
  }
27304
26952
  var POLLABLE_SCENES;
27305
26953
  var init_launch = __esm(() => {
@@ -27319,7 +26967,7 @@ __export(exports_bootstrap, {
27319
26967
  buildAppLaunchOptions: () => buildAppLaunchOptions
27320
26968
  });
27321
26969
  import { existsSync as existsSync24, readFileSync as readFileSync15 } from "fs";
27322
- import { basename as basename6, resolve as resolve31 } from "path";
26970
+ import { basename as basename5, resolve as resolve31 } from "path";
27323
26971
  async function launchRigOpenTuiBootstrap(options) {
27324
26972
  const launchOptions = buildAppLaunchOptions(options);
27325
26973
  await launchOpenTuiApp(launchOptions);
@@ -27425,7 +27073,7 @@ function parsePolicyMode2(value) {
27425
27073
  function resolveProjectRoot2(input) {
27426
27074
  if (input.envProjectRoot)
27427
27075
  return resolve31(input.cwd, input.envProjectRoot);
27428
- const execName = basename6(input.execPath).toLowerCase();
27076
+ const execName = basename5(input.execPath).toLowerCase();
27429
27077
  const execCandidates = execName === "rig" || execName === "rig.exe" ? [resolve31(input.execPath, "..", "..")] : [];
27430
27078
  const candidates = [input.cwd, ...execCandidates, resolve31(input.importDir, "..")];
27431
27079
  for (const candidate of candidates) {