@h-rig/cli 0.0.6-alpha.86 → 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.
Files changed (80) hide show
  1. package/dist/bin/rig.js +287 -578
  2. package/dist/src/app-opentui/adapters/common.d.ts +3 -0
  3. package/dist/src/app-opentui/adapters/common.js +4 -0
  4. package/dist/src/app-opentui/adapters/family.js +31 -4
  5. package/dist/src/app-opentui/adapters/pi-attach.d.ts +7 -0
  6. package/dist/src/app-opentui/adapters/pi-attach.js +527 -473
  7. package/dist/src/app-opentui/adapters/tasks.js +82 -468
  8. package/dist/src/app-opentui/bootstrap.js +287 -578
  9. package/dist/src/app-opentui/command-palette.js +1 -0
  10. package/dist/src/app-opentui/drone.js +1 -0
  11. package/dist/src/app-opentui/index.js +127 -446
  12. package/dist/src/app-opentui/keymap.js +2 -387
  13. package/dist/src/app-opentui/list-search.d.ts +5 -1
  14. package/dist/src/app-opentui/list-search.js +2 -2
  15. package/dist/src/app-opentui/pi-host-child.js +31 -4
  16. package/dist/src/app-opentui/pi-pty-host.d.ts +14 -64
  17. package/dist/src/app-opentui/pi-pty-host.js +3 -397
  18. package/dist/src/app-opentui/react/App.js +107 -433
  19. package/dist/src/app-opentui/react/Backdrop.js +1 -0
  20. package/dist/src/app-opentui/react/ChromeHost.js +34 -410
  21. package/dist/src/app-opentui/react/SceneFrameView.js +55 -5
  22. package/dist/src/app-opentui/react/launch.js +171 -472
  23. package/dist/src/app-opentui/react/syntax.js +1 -0
  24. package/dist/src/app-opentui/registry.js +99 -487
  25. package/dist/src/app-opentui/render/graphics.js +1 -0
  26. package/dist/src/app-opentui/render/hover.d.ts +11 -0
  27. package/dist/src/app-opentui/render/hover.js +34 -0
  28. package/dist/src/app-opentui/render/native-host.js +1 -0
  29. package/dist/src/app-opentui/render/panels.js +50 -3
  30. package/dist/src/app-opentui/render/preloader.js +1 -0
  31. package/dist/src/app-opentui/render/scene.js +1 -0
  32. package/dist/src/app-opentui/render/terminal-handoff.d.ts +16 -0
  33. package/dist/src/app-opentui/render/terminal-handoff.js +14 -0
  34. package/dist/src/app-opentui/render/text.d.ts +13 -0
  35. package/dist/src/app-opentui/render/text.js +54 -5
  36. package/dist/src/app-opentui/render/type-bar.js +1 -0
  37. package/dist/src/app-opentui/runtime.js +127 -446
  38. package/dist/src/app-opentui/scenes/command.js +1 -0
  39. package/dist/src/app-opentui/scenes/doctor.js +1 -0
  40. package/dist/src/app-opentui/scenes/error.js +1 -0
  41. package/dist/src/app-opentui/scenes/family-domains/agent.js +1 -0
  42. package/dist/src/app-opentui/scenes/family-domains/browser.js +1 -0
  43. package/dist/src/app-opentui/scenes/family-domains/dist.js +1 -0
  44. package/dist/src/app-opentui/scenes/family-domains/git.js +1 -0
  45. package/dist/src/app-opentui/scenes/family-domains/github.js +1 -0
  46. package/dist/src/app-opentui/scenes/family-domains/harness.js +1 -0
  47. package/dist/src/app-opentui/scenes/family-domains/index.js +1 -0
  48. package/dist/src/app-opentui/scenes/family-domains/kit.js +1 -0
  49. package/dist/src/app-opentui/scenes/family-domains/profile.js +1 -0
  50. package/dist/src/app-opentui/scenes/family-domains/queue.js +1 -0
  51. package/dist/src/app-opentui/scenes/family-domains/remote.js +1 -0
  52. package/dist/src/app-opentui/scenes/family-domains/review.js +1 -0
  53. package/dist/src/app-opentui/scenes/family-domains/setup.js +1 -0
  54. package/dist/src/app-opentui/scenes/family-domains/stats.js +1 -0
  55. package/dist/src/app-opentui/scenes/family.js +1 -0
  56. package/dist/src/app-opentui/scenes/fleet.js +3 -5
  57. package/dist/src/app-opentui/scenes/handoff.js +1 -0
  58. package/dist/src/app-opentui/scenes/help.js +1 -0
  59. package/dist/src/app-opentui/scenes/inbox.js +1 -0
  60. package/dist/src/app-opentui/scenes/init.js +1 -0
  61. package/dist/src/app-opentui/scenes/inspect.js +1 -0
  62. package/dist/src/app-opentui/scenes/main.js +1 -0
  63. package/dist/src/app-opentui/scenes/pi.js +1 -0
  64. package/dist/src/app-opentui/scenes/plugin.js +1 -0
  65. package/dist/src/app-opentui/scenes/repo.js +1 -0
  66. package/dist/src/app-opentui/scenes/run-detail.js +1 -0
  67. package/dist/src/app-opentui/scenes/server.js +1 -0
  68. package/dist/src/app-opentui/scenes/tasks.js +3 -5
  69. package/dist/src/app-opentui/scenes/workspace.js +1 -0
  70. package/dist/src/app-opentui/selectable.js +1 -0
  71. package/dist/src/app-opentui/theme.d.ts +1 -0
  72. package/dist/src/app-opentui/theme.js +1 -0
  73. package/dist/src/commands/_operator-view.js +31 -4
  74. package/dist/src/commands/_pi-frontend.d.ts +25 -0
  75. package/dist/src/commands/_pi-frontend.js +32 -4
  76. package/dist/src/commands/run.js +31 -4
  77. package/dist/src/commands/task.js +31 -4
  78. package/dist/src/commands.js +31 -4
  79. package/dist/src/index.js +31 -4
  80. package/package.json +8 -8
@@ -91,6 +91,21 @@ async function captureConsole(fn) {
91
91
  console.error = original.error;
92
92
  }
93
93
  }
94
+ async function releaseRendererForExternalTui(ctx) {
95
+ const renderer = ctx.renderer;
96
+ if (!renderer)
97
+ return;
98
+ if (renderer.suspend) {
99
+ await renderer.suspend();
100
+ return;
101
+ }
102
+ if (renderer.destroy) {
103
+ await renderer.destroy();
104
+ }
105
+ }
106
+ async function resumeRendererAfterExternalTui(ctx) {
107
+ await ctx.renderer?.resume?.();
108
+ }
94
109
  function arrayFromPayload(value) {
95
110
  if (Array.isArray(value)) {
96
111
  return value.filter((entry) => Boolean(entry && typeof entry === "object" && !Array.isArray(entry)));
@@ -9310,6 +9325,14 @@ var init__operator_surface = __esm(() => {
9310
9325
  });
9311
9326
 
9312
9327
  // packages/cli/src/commands/_pi-frontend.ts
9328
+ var exports__pi_frontend = {};
9329
+ __export(exports__pi_frontend, {
9330
+ shouldRequireOperatorTranscript: () => shouldRequireOperatorTranscript,
9331
+ runWithProcessExitGuard: () => runWithProcessExitGuard,
9332
+ missingOperatorTranscriptMessage: () => missingOperatorTranscriptMessage,
9333
+ buildOperatorPiEnv: () => buildOperatorPiEnv,
9334
+ attachRunBundledPiFrontend: () => attachRunBundledPiFrontend
9335
+ });
9313
9336
  import { existsSync as existsSync15, mkdirSync as mkdirSync10, mkdtempSync, readFileSync as readFileSync10, rmSync as rmSync5, writeFileSync as writeFileSync7 } from "fs";
9314
9337
  import { homedir as homedir6, tmpdir } from "os";
9315
9338
  import { join as join3 } from "path";
@@ -9422,6 +9445,29 @@ function installRigPiTheme() {
9422
9445
  }
9423
9446
  } catch {}
9424
9447
  }
9448
+ async function runWithProcessExitGuard(body) {
9449
+ const realExit = process.exit;
9450
+ let exitCode = 0;
9451
+ let signalQuit = () => {};
9452
+ const quit = new Promise((resolve23) => {
9453
+ signalQuit = resolve23;
9454
+ });
9455
+ const guardedExit = (code) => {
9456
+ exitCode = typeof code === "number" ? code : 0;
9457
+ signalQuit();
9458
+ return;
9459
+ };
9460
+ process.exit = guardedExit;
9461
+ try {
9462
+ await Promise.race([Promise.resolve().then(body), quit]);
9463
+ } finally {
9464
+ process.exit = realExit;
9465
+ }
9466
+ return exitCode;
9467
+ }
9468
+ function runPiMainReturningOnQuit(args, options) {
9469
+ return runWithProcessExitGuard(() => runPiMain(args, options));
9470
+ }
9425
9471
  async function attachRunBundledPiFrontend(context, input) {
9426
9472
  const tempSessionDir = mkdtempSync(join3(tmpdir(), "rig-pi-frontend-sessions-"));
9427
9473
  const { server, sessionFileArg } = await withSpinner(`Opening Pi console for run ${input.runId}\u2026`, () => prepareOperatorConsole(context, input.runId, tempSessionDir), { outputMode: context.outputMode });
@@ -9437,16 +9483,20 @@ async function attachRunBundledPiFrontend(context, input) {
9437
9483
  };
9438
9484
  let detached = false;
9439
9485
  try {
9440
- await runPiMain([
9486
+ const piArgs = [
9441
9487
  "--offline",
9442
9488
  "--no-extensions",
9443
9489
  "--no-skills",
9444
9490
  "--no-prompt-templates",
9445
9491
  "--no-context-files",
9446
9492
  ...sessionFileArg
9447
- ], {
9448
- extensionFactories: [piRigExtensionFactory]
9449
- });
9493
+ ];
9494
+ const piOptions = { extensionFactories: [piRigExtensionFactory] };
9495
+ if (input.returnOnQuit) {
9496
+ await runPiMainReturningOnQuit(piArgs, piOptions);
9497
+ } else {
9498
+ await runPiMain(piArgs, piOptions);
9499
+ }
9450
9500
  detached = true;
9451
9501
  } finally {
9452
9502
  restoreEnv();
@@ -14712,397 +14762,6 @@ async function stopFleetRun(ctx, runId) {
14712
14762
  }
14713
14763
  var init_fleet = () => {};
14714
14764
 
14715
- // packages/cli/src/app-opentui/pi-pty-host.ts
14716
- import { fileURLToPath as fileURLToPath2 } from "url";
14717
- import { basename as basename4 } from "path";
14718
- import { RGBA, StyledText, TextAttributes } from "@opentui/core";
14719
- import { Terminal as XtermTerminal2 } from "@xterm/headless";
14720
- function clampCols2(cols) {
14721
- return Math.max(MIN_COLS2, Math.trunc(cols || 100));
14722
- }
14723
- function clampRows2(rows) {
14724
- return Math.max(MIN_ROWS2, Math.min(MAX_ROWS2, Math.trunc(rows || 35)));
14725
- }
14726
- function rgbaFromXtermColor(mode, value) {
14727
- if (mode === 1 || mode === 2 || mode === XTERM_COLOR_MODE_INDEXED_16 || mode === XTERM_COLOR_MODE_INDEXED_256) {
14728
- return RGBA.fromIndex(value);
14729
- }
14730
- if (mode === 3 || mode === XTERM_COLOR_MODE_RGB) {
14731
- return RGBA.fromInts(value >> 16 & 255, value >> 8 & 255, value & 255);
14732
- }
14733
- return;
14734
- }
14735
- function textAttributesFromCell(cell) {
14736
- let attributes = TextAttributes.NONE;
14737
- if (cell.isBold())
14738
- attributes |= TextAttributes.BOLD;
14739
- if (cell.isDim())
14740
- attributes |= TextAttributes.DIM;
14741
- if (cell.isItalic())
14742
- attributes |= TextAttributes.ITALIC;
14743
- if (cell.isUnderline())
14744
- attributes |= TextAttributes.UNDERLINE;
14745
- if (cell.isBlink())
14746
- attributes |= TextAttributes.BLINK;
14747
- if (cell.isInverse())
14748
- attributes |= TextAttributes.INVERSE;
14749
- if (cell.isInvisible())
14750
- attributes |= TextAttributes.HIDDEN;
14751
- if (cell.isStrikethrough())
14752
- attributes |= TextAttributes.STRIKETHROUGH;
14753
- return attributes;
14754
- }
14755
- function sameRgba(a, b) {
14756
- if (!a && !b)
14757
- return true;
14758
- return Boolean(a && b && a.equals(b));
14759
- }
14760
- function sameStyle(a, b) {
14761
- return sameRgba(a.fg, b.fg) && sameRgba(a.bg, b.bg) && (a.attributes ?? 0) === (b.attributes ?? 0);
14762
- }
14763
- function styleFromCell(cell) {
14764
- return {
14765
- fg: rgbaFromXtermColor(cell.getFgColorMode(), cell.getFgColor()),
14766
- bg: rgbaFromXtermColor(cell.getBgColorMode(), cell.getBgColor()),
14767
- attributes: textAttributesFromCell(cell)
14768
- };
14769
- }
14770
- function lineToStyledText(line, cols) {
14771
- if (!line)
14772
- return new StyledText([{ __isChunk: true, text: "" }]);
14773
- const chunks = [];
14774
- let run = "";
14775
- let runStyle = null;
14776
- const flush = () => {
14777
- if (!run)
14778
- return;
14779
- chunks.push({
14780
- __isChunk: true,
14781
- text: run,
14782
- ...runStyle?.fg ? { fg: runStyle.fg } : {},
14783
- ...runStyle?.bg ? { bg: runStyle.bg } : {},
14784
- ...(runStyle?.attributes ?? 0) !== 0 ? { attributes: runStyle.attributes } : {}
14785
- });
14786
- run = "";
14787
- };
14788
- for (let index = 0;index < cols; index += 1) {
14789
- const cell = line.getCell(index);
14790
- if (!cell) {
14791
- if (runStyle !== null)
14792
- flush();
14793
- runStyle = null;
14794
- run += " ";
14795
- continue;
14796
- }
14797
- if (cell.getWidth() === 0)
14798
- continue;
14799
- const style = styleFromCell(cell);
14800
- if (!runStyle || !sameStyle(runStyle, style)) {
14801
- flush();
14802
- runStyle = style;
14803
- }
14804
- run += cell.getChars() || " ";
14805
- }
14806
- flush();
14807
- return new StyledText(chunks.length > 0 ? chunks : [{ __isChunk: true, text: "" }]);
14808
- }
14809
- function childCommandPrefix2() {
14810
- const execName = basename4(process.execPath).toLowerCase();
14811
- const currentEntry = process.argv[1];
14812
- if (execName === "bun" || execName === "bun.exe") {
14813
- return currentEntry ? [process.execPath, currentEntry, "__opentui-pi-host"] : [process.execPath, fallbackChildScriptPath];
14814
- }
14815
- return [process.execPath, "__opentui-pi-host"];
14816
- }
14817
- function withEnv2(base) {
14818
- const env = {};
14819
- for (const [key, value] of Object.entries(base ?? process.env)) {
14820
- if (key === "RIG_PLAIN" || key === "RIG_CLI_PLAIN_HELP" || key === "NO_COLOR")
14821
- continue;
14822
- if (typeof value === "string")
14823
- env[key] = value;
14824
- }
14825
- return {
14826
- ...env,
14827
- TERM: "xterm-256color",
14828
- COLORTERM: "truecolor",
14829
- FORCE_COLOR: env.FORCE_COLOR ?? "1",
14830
- RIG_OPENTUI_PI_HOST: "1"
14831
- };
14832
- }
14833
- function getActivePiHost() {
14834
- return activeHost2 && !activeHost2.disposed ? activeHost2 : null;
14835
- }
14836
- async function startPiPtyHost(options) {
14837
- activeHost2?.dispose("replace");
14838
- const host = new PiPtyHost(options);
14839
- activeHost2 = host;
14840
- await host.start();
14841
- return host;
14842
- }
14843
- function stopActivePiHost(reason = "detach") {
14844
- activeHost2?.dispose(reason);
14845
- activeHost2 = null;
14846
- }
14847
-
14848
- class PiPtyHost {
14849
- runId;
14850
- projectRoot;
14851
- onSnapshot;
14852
- onExit;
14853
- onError;
14854
- terminal;
14855
- disposables = [];
14856
- decoder = new TextDecoder("utf-8");
14857
- proc = null;
14858
- pty = null;
14859
- status = "starting";
14860
- cols;
14861
- rows;
14862
- message = "starting bundled Pi";
14863
- lastResizeError = null;
14864
- exitCode;
14865
- signal;
14866
- notifyTimer = null;
14867
- lastStreamKey = "";
14868
- _disposed = false;
14869
- constructor(options) {
14870
- this.runId = options.runId;
14871
- this.projectRoot = options.projectRoot;
14872
- this.cols = clampCols2(options.cols);
14873
- this.rows = clampRows2(options.rows);
14874
- this.onSnapshot = options.onSnapshot;
14875
- this.onExit = options.onExit;
14876
- this.onError = options.onError;
14877
- this.terminal = new XtermTerminal2({
14878
- allowProposedApi: true,
14879
- cols: this.cols,
14880
- rows: this.rows,
14881
- scrollback: 1000
14882
- });
14883
- this.registerTerminalResponders();
14884
- this.disposables.push(this.terminal.onWriteParsed(() => {
14885
- if (this._disposed)
14886
- return;
14887
- const snapshot = this.createSnapshot();
14888
- const key = snapshot.lines.join(`
14889
- `);
14890
- if (key === this.lastStreamKey)
14891
- return;
14892
- this.lastStreamKey = key;
14893
- this.onSnapshot?.(snapshot);
14894
- }));
14895
- }
14896
- get disposed() {
14897
- return this._disposed;
14898
- }
14899
- get snapshot() {
14900
- return this.createSnapshot();
14901
- }
14902
- async start() {
14903
- if (this._disposed)
14904
- throw new Error("Pi PTY host is disposed.");
14905
- if (typeof Bun.Terminal !== "function") {
14906
- throw new Error("Bun native PTY is unavailable in this runtime. Pi host requires Bun.Terminal.");
14907
- }
14908
- const spawnOptions = {
14909
- cwd: this.projectRoot,
14910
- env: withEnv2(process.env),
14911
- terminal: {
14912
- cols: this.cols,
14913
- rows: this.rows,
14914
- name: "xterm-256color",
14915
- data: (_terminal, data) => this.handlePtyData(data)
14916
- }
14917
- };
14918
- const proc = Bun.spawn([
14919
- ...childCommandPrefix2(),
14920
- "--run-id",
14921
- this.runId,
14922
- "--project-root",
14923
- this.projectRoot
14924
- ], spawnOptions);
14925
- if (!proc.terminal)
14926
- throw new Error("Bun did not attach a terminal to the bundled Pi child process.");
14927
- this.proc = proc;
14928
- this.pty = proc.terminal;
14929
- this.status = "running";
14930
- this.message = "bundled Pi running inside this app";
14931
- this.emitSnapshotSoon(0);
14932
- proc.exited.then((exitCode) => {
14933
- if (this._disposed)
14934
- return;
14935
- this.status = exitCode === 0 ? "exited" : "failed";
14936
- this.exitCode = exitCode;
14937
- this.signal = null;
14938
- this.message = exitCode === 0 ? "bundled Pi exited" : `bundled Pi exited with code ${exitCode}`;
14939
- const snapshot = this.createSnapshot();
14940
- this.onSnapshot?.(snapshot);
14941
- this.onExit?.(snapshot);
14942
- if (activeHost2 === this)
14943
- activeHost2 = null;
14944
- this.dispose("exit", { kill: false, notify: false });
14945
- }).catch((error) => {
14946
- if (this._disposed)
14947
- return;
14948
- this.status = "failed";
14949
- this.message = error instanceof Error ? error.message : String(error);
14950
- const snapshot = this.createSnapshot();
14951
- this.onSnapshot?.(snapshot);
14952
- this.onError?.(error, snapshot);
14953
- if (activeHost2 === this)
14954
- activeHost2 = null;
14955
- this.dispose("error", { kill: false, notify: false });
14956
- });
14957
- }
14958
- write(data) {
14959
- if (this._disposed || !this.pty)
14960
- return;
14961
- try {
14962
- this.pty.write(data);
14963
- } catch (error) {
14964
- this.status = "failed";
14965
- this.message = error instanceof Error ? error.message : String(error);
14966
- const snapshot = this.createSnapshot();
14967
- this.onSnapshot?.(snapshot);
14968
- this.onError?.(error, snapshot);
14969
- }
14970
- }
14971
- resize(cols, rows) {
14972
- const nextCols = clampCols2(cols);
14973
- const nextRows = clampRows2(rows);
14974
- if (nextCols === this.cols && nextRows === this.rows)
14975
- return;
14976
- this.cols = nextCols;
14977
- this.rows = nextRows;
14978
- this.terminal.resize(nextCols, nextRows);
14979
- try {
14980
- this.pty?.resize(nextCols, nextRows);
14981
- this.lastResizeError = null;
14982
- } catch (error) {
14983
- this.lastResizeError = error instanceof Error ? error.message : String(error);
14984
- }
14985
- this.emitSnapshotSoon(0);
14986
- }
14987
- detach() {
14988
- this.dispose("detach");
14989
- return this.createSnapshot("detached from bundled Pi");
14990
- }
14991
- dispose(reason = "dispose", options = {}) {
14992
- if (this._disposed)
14993
- return;
14994
- this._disposed = true;
14995
- if (this.notifyTimer)
14996
- clearTimeout(this.notifyTimer);
14997
- this.notifyTimer = null;
14998
- for (const disposable of this.disposables.splice(0)) {
14999
- try {
15000
- disposable.dispose();
15001
- } catch {}
15002
- }
15003
- if (options.kill !== false) {
15004
- try {
15005
- this.proc?.kill("SIGTERM");
15006
- } catch {}
15007
- }
15008
- try {
15009
- this.pty?.close();
15010
- } catch {}
15011
- try {
15012
- this.terminal.dispose();
15013
- } catch {}
15014
- if (activeHost2 === this)
15015
- activeHost2 = null;
15016
- if (options.notify) {
15017
- this.message = reason;
15018
- this.onSnapshot?.(this.createSnapshot(reason));
15019
- }
15020
- }
15021
- handlePtyData(data) {
15022
- if (this._disposed)
15023
- return;
15024
- const text2 = this.decoder.decode(data, { stream: true });
15025
- this.respondToRawTerminalQueries(text2);
15026
- this.terminal.write(data);
15027
- }
15028
- emitSnapshotSoon(delayMs = SNAPSHOT_DELAY_MS2) {
15029
- if (this._disposed || this.notifyTimer)
15030
- return;
15031
- this.notifyTimer = setTimeout(() => {
15032
- this.notifyTimer = null;
15033
- if (this._disposed)
15034
- return;
15035
- this.onSnapshot?.(this.createSnapshot());
15036
- }, delayMs);
15037
- }
15038
- createSnapshot(message2 = this.message) {
15039
- const buffer = this.terminal.buffer.active;
15040
- const end = buffer.length;
15041
- const start = Math.max(0, end - MAX_SNAPSHOT_LINES2);
15042
- const styledStart = Math.max(start, end - this.rows - STYLED_SNAPSHOT_MARGIN);
15043
- const lines = [];
15044
- const styledLines = [];
15045
- for (let row = start;row < end; row += 1) {
15046
- const line = buffer.getLine(row);
15047
- lines.push((line?.translateToString(false) ?? "").slice(0, this.cols));
15048
- if (row >= styledStart)
15049
- styledLines[lines.length - 1] = lineToStyledText(line, this.cols);
15050
- }
15051
- while (lines.length < this.rows) {
15052
- lines.push("");
15053
- styledLines[lines.length - 1] = new StyledText([{ __isChunk: true, text: "" }]);
15054
- }
15055
- const resolvedMessage = this.lastResizeError ? `resize degraded: ${this.lastResizeError}` : message2;
15056
- return {
15057
- runId: this.runId,
15058
- status: this.status,
15059
- cols: this.cols,
15060
- rows: this.rows,
15061
- lines,
15062
- styledLines,
15063
- message: resolvedMessage,
15064
- ...this.lastResizeError ? { resizeError: this.lastResizeError } : {},
15065
- ...this.exitCode !== undefined ? { exitCode: this.exitCode } : {},
15066
- ...this.signal !== undefined ? { signal: this.signal } : {}
15067
- };
15068
- }
15069
- registerTerminalResponders() {
15070
- this.disposables.push(this.terminal.parser.registerCsiHandler({ final: "c" }, (params) => {
15071
- if (params.length === 0 || params[0] === 0)
15072
- this.write("\x1B[?62;22c");
15073
- return false;
15074
- }));
15075
- this.disposables.push(this.terminal.parser.registerCsiHandler({ prefix: ">", final: "c" }, (params) => {
15076
- if (params.length === 0 || params[0] === 0)
15077
- this.write("\x1B[>0;0;0c");
15078
- return false;
15079
- }));
15080
- this.disposables.push(this.terminal.parser.registerCsiHandler({ final: "n" }, (params) => {
15081
- if (params[0] === 5) {
15082
- this.write("\x1B[0n");
15083
- } else if (params[0] === 6) {
15084
- const row = this.terminal.buffer.active.cursorY + 1;
15085
- const col = this.terminal.buffer.active.cursorX + 1;
15086
- this.write(`\x1B[${row};${col}R`);
15087
- }
15088
- return false;
15089
- }));
15090
- }
15091
- respondToRawTerminalQueries(text2) {
15092
- if (!text2)
15093
- return;
15094
- const decrqm = /\x1b\[\?(\d+)\$p/g;
15095
- let match;
15096
- while ((match = decrqm.exec(text2)) !== null) {
15097
- this.write(`\x1B[?${match[1]};2$y`);
15098
- }
15099
- }
15100
- }
15101
- 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;
15102
- var init_pi_pty_host = __esm(() => {
15103
- fallbackChildScriptPath = fileURLToPath2(new URL("./pi-host-child.ts", import.meta.url));
15104
- });
15105
-
15106
14765
  // packages/cli/src/app-opentui/adapters/inspect.ts
15107
14766
  var exports_inspect = {};
15108
14767
  __export(exports_inspect, {
@@ -15325,14 +14984,6 @@ function recordStep(ctx, runId, label, emitLabel) {
15325
14984
  });
15326
14985
  emitProgress(ctx, emitLabel, label);
15327
14986
  }
15328
- function settleSteps(ctx, runId) {
15329
- const now = Date.now();
15330
- const previous = currentAttachState(ctx, runId);
15331
- if (!previous?.steps)
15332
- return;
15333
- const steps = previous.steps.map((step) => step.status === "running" ? { ...step, status: "done", endedAtMs: step.endedAtMs ?? now } : step);
15334
- patchData(ctx, { piAttach: { ...previous, steps } });
15335
- }
15336
14987
  async function preparePiAttachHandoff(ctx, runId) {
15337
14988
  const cleanRunId = runId.trim();
15338
14989
  const label = `Preparing Pi ${cleanRunId.slice(0, 8)}`;
@@ -15380,85 +15031,41 @@ async function attachRunWithBundledPi(ctx, runId) {
15380
15031
  throw error;
15381
15032
  }
15382
15033
  emitStarted(ctx, label, { piAttach: { runId: cleanRunId, status: "entering-pi" } });
15383
- patchData(ctx, { piAttach: { runId: cleanRunId, status: "entering-pi", message: "starting bundled Pi", steps: [] } });
15034
+ patchData(ctx, { piAttach: { runId: cleanRunId, status: "entering-pi", message: "handing the terminal to worker Pi\u2026" } });
15035
+ const projectRoot = projectRootOf(ctx);
15036
+ const outputMode = ctx.rig?.outputMode ?? "text";
15037
+ await releaseRendererForExternalTui(ctx);
15038
+ let outcome = null;
15039
+ let attachError = null;
15384
15040
  try {
15385
- recordStep(ctx, cleanRunId, "connecting to server and fetching run transcript", label);
15386
- await preparePiAttachHandoff(ctx, cleanRunId).catch((error) => {
15387
- patchData(ctx, { lastRefreshError: normalizeAppError(error).message });
15388
- return null;
15389
- });
15390
- recordStep(ctx, cleanRunId, "spawning Bun PTY host for bundled Pi", label);
15391
- const projectRoot = projectRootOf(ctx);
15392
- const cols = typeof process.stdout.columns === "number" ? process.stdout.columns : 100;
15393
- const rows = Math.max(1, (typeof process.stdout.rows === "number" ? process.stdout.rows : 35) - 1);
15394
- const patchTerminal = (snapshot) => {
15395
- const previousSteps = currentAttachState(ctx, cleanRunId)?.steps;
15396
- const failed = snapshot.status === "failed";
15397
- const reason = failed ? snapshot.message ?? `bundled Pi exited with code ${snapshot.exitCode ?? "unknown"}` : undefined;
15398
- patchData(ctx, {
15399
- piTerminal: snapshot,
15400
- piAttach: {
15401
- runId: cleanRunId,
15402
- status: failed ? "failed" : snapshot.status === "exited" ? "returned" : "entering-pi",
15403
- message: snapshot.message ?? "bundled Pi running",
15404
- result: { runId: cleanRunId, status: snapshot.status, exitCode: snapshot.exitCode },
15405
- ...previousSteps ? { steps: previousSteps } : {},
15406
- ...reason ? { failureReason: reason } : {}
15407
- }
15408
- });
15409
- };
15410
- recordStep(ctx, cleanRunId, "starting bundled Pi", label);
15411
- await startPiPtyHost({
15412
- runId: cleanRunId,
15413
- projectRoot,
15414
- cols,
15415
- rows,
15416
- onSnapshot: (snapshot) => {
15417
- if (snapshot.status === "running" && snapshot.lines.some((entry) => entry.trim().length > 0)) {
15418
- settleSteps(ctx, cleanRunId);
15419
- }
15420
- patchTerminal(snapshot);
15421
- },
15422
- onExit: (snapshot) => {
15423
- patchTerminal(snapshot);
15424
- if (snapshot.status === "failed") {
15425
- Promise.resolve().then(() => (init_inspect2(), exports_inspect)).then(({ loadInspect: loadInspect2 }) => loadInspect2(ctx, cleanRunId)).catch(() => null);
15426
- emitFailed(ctx, label, new Error(snapshot.message ?? `bundled Pi exited with code ${snapshot.exitCode ?? "unknown"}`), { runId: cleanRunId, result: { status: snapshot.status, exitCode: snapshot.exitCode } });
15427
- return;
15428
- }
15429
- emitCompleted(ctx, label, { runId: cleanRunId, result: { status: snapshot.status, exitCode: snapshot.exitCode } });
15430
- ctx.emit({ type: "scene.change", scene: "fleet", intent: { scene: "fleet", argv: ["runs"], action: { kind: "refresh", label: "Opening runs" } } });
15431
- refreshFleet(ctx).catch((error) => emitFailed(ctx, "Refresh runs", error));
15432
- },
15433
- onError: (error, snapshot) => {
15434
- patchTerminal(snapshot);
15435
- emitFailed(ctx, label, error, { runId: cleanRunId });
15436
- }
15437
- });
15438
- const record = {
15439
- runId: cleanRunId,
15440
- status: "running",
15441
- rendered: "bundled Pi embedded via Bun.Terminal + @xterm/headless"
15442
- };
15443
- emitProgress(ctx, label, "bundled Pi is running", { piAttach: { runId: cleanRunId, status: "entering-pi", message: "bundled Pi running", result: record } });
15444
- return record;
15041
+ const { attachRunBundledPiFrontend: attachRunBundledPiFrontend2 } = await Promise.resolve().then(() => (init__pi_frontend(), exports__pi_frontend));
15042
+ outcome = await attachRunBundledPiFrontend2({ projectRoot, outputMode }, { runId: cleanRunId, returnOnQuit: true });
15445
15043
  } catch (error) {
15446
- const reason = error instanceof Error ? error.message : String(error);
15044
+ attachError = error;
15045
+ } finally {
15046
+ await resumeRendererAfterExternalTui(ctx);
15047
+ }
15048
+ if (attachError) {
15049
+ const reason = normalizeAppError(attachError).message;
15447
15050
  patchData(ctx, {
15448
- piAttach: {
15449
- runId: cleanRunId,
15450
- status: "failed",
15451
- message: reason,
15452
- failureReason: reason,
15453
- ...currentAttachState(ctx, cleanRunId)?.steps ? { steps: currentAttachState(ctx, cleanRunId).steps } : {}
15454
- }
15051
+ piTerminal: undefined,
15052
+ piAttach: { runId: cleanRunId, status: "failed", message: reason, failureReason: reason }
15455
15053
  });
15456
- emitFailed(ctx, label, error, { runId: cleanRunId });
15457
- throw error;
15054
+ Promise.resolve().then(() => (init_inspect2(), exports_inspect)).then(({ loadInspect: loadInspect2 }) => loadInspect2(ctx, cleanRunId)).catch(() => null);
15055
+ emitFailed(ctx, label, attachError, { runId: cleanRunId });
15056
+ throw attachError;
15458
15057
  }
15058
+ const detached = outcome?.detached === true;
15059
+ patchData(ctx, {
15060
+ piTerminal: undefined,
15061
+ piAttach: { runId: cleanRunId, status: "returned", message: detached ? "detached from worker Pi" : "returned from worker Pi" }
15062
+ });
15063
+ emitCompleted(ctx, label, { runId: cleanRunId, result: { runId: cleanRunId, detached } });
15064
+ ctx.emit({ type: "scene.change", scene: "fleet", intent: { scene: "fleet", argv: ["runs"], action: { kind: "refresh", label: "Opening runs" } } });
15065
+ refreshFleet(ctx).catch((error) => emitFailed(ctx, "Refresh runs", error));
15066
+ return { runId: cleanRunId, status: "returned", detached };
15459
15067
  }
15460
15068
  var init_pi_attach = __esm(() => {
15461
- init_pi_pty_host();
15462
15069
  init_fleet();
15463
15070
  });
15464
15071
 
@@ -15510,7 +15117,7 @@ import {
15510
15117
  dim as otuiDim,
15511
15118
  fg as otuiFg,
15512
15119
  t,
15513
- TextAttributes as TextAttributes2
15120
+ TextAttributes
15514
15121
  } from "@opentui/core";
15515
15122
  function statusColor3(status) {
15516
15123
  switch (status) {
@@ -15585,6 +15192,7 @@ var init_theme2 = __esm(() => {
15585
15192
  bg2: "#0b0c0e",
15586
15193
  panel: "#101115",
15587
15194
  panel2: "#14161b",
15195
+ hover: "#181b24",
15588
15196
  glass: "#1e2230",
15589
15197
  border: "#2a2e3a",
15590
15198
  ink: "#f4f5f8",
@@ -15614,7 +15222,7 @@ var init_theme2 = __esm(() => {
15614
15222
  });
15615
15223
 
15616
15224
  // packages/cli/src/app-opentui/drone.ts
15617
- import { RGBA as RGBA2, StyledText as StyledText2, TextAttributes as TextAttributes3 } from "@opentui/core";
15225
+ import { RGBA, StyledText, TextAttributes as TextAttributes2 } from "@opentui/core";
15618
15226
  function bladeForTick(tick, phase = 0) {
15619
15227
  return BLADE_FRAMES2[(Math.floor(tick / 3) + phase) % BLADE_FRAMES2.length];
15620
15228
  }
@@ -15623,12 +15231,12 @@ function eyeForTick(tick, phase = 0) {
15623
15231
  return pulse > 0.55 ? EYE_FRAMES2[1] : pulse > 0.1 ? EYE_FRAMES2[0] : pulse > -0.45 ? EYE_FRAMES2[2] : EYE_FRAMES2[3];
15624
15232
  }
15625
15233
  function chunk(text2, fg2, bold2 = false, dim = false) {
15626
- let attributes = TextAttributes3.NONE;
15234
+ let attributes = TextAttributes2.NONE;
15627
15235
  if (bold2)
15628
- attributes |= TextAttributes3.BOLD;
15236
+ attributes |= TextAttributes2.BOLD;
15629
15237
  if (dim)
15630
- attributes |= TextAttributes3.DIM;
15631
- return { __isChunk: true, text: text2, fg: fg2, ...attributes !== TextAttributes3.NONE ? { attributes } : {} };
15238
+ attributes |= TextAttributes2.DIM;
15239
+ return { __isChunk: true, text: text2, fg: fg2, ...attributes !== TextAttributes2.NONE ? { attributes } : {} };
15632
15240
  }
15633
15241
  function styledLine(text2, colorFor) {
15634
15242
  const chunks = [];
@@ -15649,7 +15257,7 @@ function styledLine(text2, colorFor) {
15649
15257
  run += char;
15650
15258
  }
15651
15259
  flush();
15652
- return new StyledText2(chunks.length > 0 ? chunks : [chunk("", COLOR.ink)]);
15260
+ return new StyledText(chunks.length > 0 ? chunks : [chunk("", COLOR.ink)]);
15653
15261
  }
15654
15262
  function droneColor(char) {
15655
15263
  if (char === "?" || char === "o" || char === "@" || char === "\u2022")
@@ -15741,13 +15349,13 @@ var init_drone = __esm(() => {
15741
15349
  BLADE_FRAMES2 = ["---", "\\\\\\", "|||", "///"];
15742
15350
  EYE_FRAMES2 = ["o", "@", "\u2022", "."];
15743
15351
  COLOR = {
15744
- body: RGBA2.fromHex(RIG_UI.lime),
15745
- mini: RGBA2.fromHex(RIG_UI.limeDim),
15746
- rotor: RGBA2.fromHex(RIG_UI.cyan),
15747
- path: RGBA2.fromHex(RIG_UI.cyan),
15748
- eye: RGBA2.fromHex(RIG_UI.ink),
15749
- dim: RGBA2.fromHex(RIG_UI.ink4),
15750
- ink: RGBA2.fromHex(RIG_UI.ink2)
15352
+ body: RGBA.fromHex(RIG_UI.lime),
15353
+ mini: RGBA.fromHex(RIG_UI.limeDim),
15354
+ rotor: RGBA.fromHex(RIG_UI.cyan),
15355
+ path: RGBA.fromHex(RIG_UI.cyan),
15356
+ eye: RGBA.fromHex(RIG_UI.ink),
15357
+ dim: RGBA.fromHex(RIG_UI.ink4),
15358
+ ink: RGBA.fromHex(RIG_UI.ink2)
15751
15359
  };
15752
15360
  });
15753
15361
 
@@ -16057,7 +15665,7 @@ var init__json_output = __esm(() => {
16057
15665
 
16058
15666
  // packages/cli/src/launcher.ts
16059
15667
  import { existsSync as existsSync22 } from "fs";
16060
- import { basename as basename5, resolve as resolve29 } from "path";
15668
+ import { basename as basename4, resolve as resolve29 } from "path";
16061
15669
  import { loadDotEnvSecrets } from "@rig/runtime/baked-secrets";
16062
15670
  import { RIG_DEFINITION_DIRNAME, RIG_STATE_DIRNAME, resolveNearestRigProjectRoot } from "@rig/runtime/layout";
16063
15671
  function parsePolicyMode(value) {
@@ -16082,7 +15690,7 @@ function resolveProjectRoot({
16082
15690
  return resolve29(cwd, envProjectRoot);
16083
15691
  }
16084
15692
  const fallbackImportDir = importDir ?? cwd;
16085
- const execName = basename5(execPath).toLowerCase();
15693
+ const execName = basename4(execPath).toLowerCase();
16086
15694
  const execCandidates = execName === "rig" || execName === "rig.exe" ? [resolve29(execPath, "..", "..")] : [];
16087
15695
  const candidates = [cwd, ...execCandidates, resolve29(fallbackImportDir, "..")];
16088
15696
  for (const candidate of candidates) {
@@ -16951,6 +16559,12 @@ var init_autocomplete = __esm(() => {
16951
16559
  NAV_VERBS = ["runs", "tasks", "inbox", "server", "doctor", "help", "init", "main"];
16952
16560
  });
16953
16561
 
16562
+ // packages/cli/src/app-opentui/pi-pty-host.ts
16563
+ function getActivePiHost() {
16564
+ return null;
16565
+ }
16566
+ function stopActivePiHost(_reason) {}
16567
+
16954
16568
  // packages/cli/src/app-opentui/keymap.ts
16955
16569
  function clearTypeBar(context, message2) {
16956
16570
  const typeBar = context.getTypeBar();
@@ -17183,7 +16797,6 @@ var init_keymap = __esm(() => {
17183
16797
  init_autocomplete();
17184
16798
  init_command_pty_host();
17185
16799
  init_intent();
17186
- init_pi_pty_host();
17187
16800
  });
17188
16801
 
17189
16802
  // packages/cli/src/app-opentui/runtime-resources.ts
@@ -17340,7 +16953,7 @@ var init_constants = __esm(() => {
17340
16953
  });
17341
16954
 
17342
16955
  // packages/cli/src/app-opentui/render/graphics.ts
17343
- import { FrameBufferRenderable, RGBA as RGBA3 } from "@opentui/core";
16956
+ import { FrameBufferRenderable, RGBA as RGBA2 } from "@opentui/core";
17344
16957
  function sceneKind(scene) {
17345
16958
  if (scene === "tasks")
17346
16959
  return "loop";
@@ -17435,7 +17048,7 @@ function paletteColor(ac, c2, mg, ink5) {
17435
17048
  const b = (AC_RGB[2] * ac + C2_RGB[2] * c2 + MG_RGB[2] * mg + INK_RGB[2] * ink5) / total;
17436
17049
  const intensity = Math.min(1, total / 4);
17437
17050
  const lift = 0.34 + intensity * 0.66;
17438
- return RGBA3.fromInts(Math.round(r * lift), Math.round(g * lift), Math.round(b * lift), 255);
17051
+ return RGBA2.fromInts(Math.round(r * lift), Math.round(g * lift), Math.round(b * lift), 255);
17439
17052
  }
17440
17053
  function clearCanvas(canvas) {
17441
17054
  canvas.ac.fill(0);
@@ -17790,8 +17403,8 @@ function staticFleetTick(scene) {
17790
17403
  }
17791
17404
  function withAlpha(hex, alpha) {
17792
17405
  const a = Math.max(0, Math.min(255, Math.round(alpha * 255)));
17793
- const [r, g, b] = RGBA3.fromHex(hex).toInts();
17794
- return RGBA3.fromInts(r, g, b, a);
17406
+ const [r, g, b] = RGBA2.fromHex(hex).toInts();
17407
+ return RGBA2.fromInts(r, g, b, a);
17795
17408
  }
17796
17409
  function asciiFleetColor(char, row, alpha) {
17797
17410
  if (char === "@" || char === "$" || char === "o")
@@ -17799,7 +17412,7 @@ function asciiFleetColor(char, row, alpha) {
17799
17412
  if (char === "%" || char === "!" || char === "/" || char === "\\" || char === "|")
17800
17413
  return withAlpha(RIG_UI.cyan, alpha);
17801
17414
  if (char === "." || char === "'" || char === "_" || row < 3 || row > FLEET_GRID_HEIGHT - 4) {
17802
- return RGBA3.fromInts(108, 110, 121, Math.min(170, Math.max(0, Math.min(255, Math.round(alpha * 255)))));
17415
+ return RGBA2.fromInts(108, 110, 121, Math.min(170, Math.max(0, Math.min(255, Math.round(alpha * 255)))));
17803
17416
  }
17804
17417
  return withAlpha(RIG_UI.limeDim, alpha);
17805
17418
  }
@@ -17853,13 +17466,13 @@ var init_graphics = __esm(() => {
17853
17466
  init_theme2();
17854
17467
  init_ascii_fleet();
17855
17468
  init_constants();
17856
- TRANSPARENT = RGBA3.fromValues(0, 0, 0, 0);
17857
- BACKDROP = RGBA3.fromHex(RIG_UI.bg);
17858
- PANEL_BG = RGBA3.fromInts(16, 17, 21, 184);
17859
- PANEL_HEADER_BG = RGBA3.fromInts(16, 17, 21, 188);
17860
- PANEL_LINE = RGBA3.fromInts(255, 255, 255, 20);
17861
- PANEL_LINE_DIM = RGBA3.fromInts(255, 255, 255, 8);
17862
- PANEL_TEXT_DIM = RGBA3.fromInts(108, 110, 121, 255);
17469
+ TRANSPARENT = RGBA2.fromValues(0, 0, 0, 0);
17470
+ BACKDROP = RGBA2.fromHex(RIG_UI.bg);
17471
+ PANEL_BG = RGBA2.fromInts(16, 17, 21, 184);
17472
+ PANEL_HEADER_BG = RGBA2.fromInts(16, 17, 21, 188);
17473
+ PANEL_LINE = RGBA2.fromInts(255, 255, 255, 20);
17474
+ PANEL_LINE_DIM = RGBA2.fromInts(255, 255, 255, 8);
17475
+ PANEL_TEXT_DIM = RGBA2.fromInts(108, 110, 121, 255);
17863
17476
  AC_RGB = [204, 255, 77];
17864
17477
  C2_RGB = [86, 216, 255];
17865
17478
  MG_RGB = [255, 121, 176];
@@ -17872,16 +17485,69 @@ var init_graphics = __esm(() => {
17872
17485
  ];
17873
17486
  });
17874
17487
 
17488
+ // packages/cli/src/app-opentui/render/hover.ts
17489
+ function getHoveredSelectableId() {
17490
+ return hoveredId;
17491
+ }
17492
+ function isHovered(id) {
17493
+ return id !== undefined && id === hoveredId;
17494
+ }
17495
+ function setHoveredSelectableId(id) {
17496
+ if (hoveredId === id)
17497
+ return;
17498
+ hoveredId = id;
17499
+ for (const listener of listeners)
17500
+ listener();
17501
+ }
17502
+ function clearHoveredSelectableId(id) {
17503
+ if (id !== undefined && hoveredId === id)
17504
+ setHoveredSelectableId(undefined);
17505
+ }
17506
+ function subscribeHover(listener) {
17507
+ listeners.add(listener);
17508
+ return () => {
17509
+ listeners.delete(listener);
17510
+ };
17511
+ }
17512
+ var hoveredId, listeners;
17513
+ var init_hover = __esm(() => {
17514
+ listeners = new Set;
17515
+ });
17516
+
17875
17517
  // packages/cli/src/app-opentui/render/text.ts
17876
- import { RGBA as RGBA4, TextAttributes as TextAttributes4, TextRenderable } from "@opentui/core";
17518
+ import { RGBA as RGBA3, TextAttributes as TextAttributes3, TextRenderable } from "@opentui/core";
17519
+ function lineIsClickable(line2) {
17520
+ return line2?.selectable !== undefined || line2?.selectableIndex !== undefined;
17521
+ }
17522
+ function lineSelectableId(line2) {
17523
+ return line2?.selectable?.id ?? line2?.selectableId;
17524
+ }
17525
+ function lineBackground(line2) {
17526
+ if (line2?.bg)
17527
+ return RGBA3.fromHex(line2.bg);
17528
+ const id = lineSelectableId(line2);
17529
+ if (id !== undefined && isHovered(id))
17530
+ return HOVER_BG;
17531
+ return TRANSPARENT2;
17532
+ }
17877
17533
  function selectableMouseHandler(currentLineRef, onLineMouseDown) {
17878
17534
  return (event) => {
17879
17535
  const currentLine = currentLineRef();
17880
- if (currentLine?.selectableIndex === undefined)
17536
+ if (!lineIsClickable(currentLine))
17881
17537
  return;
17882
17538
  onLineMouseDown?.(currentLine, event);
17883
17539
  };
17884
17540
  }
17541
+ function hoverHandlers(currentLineRef) {
17542
+ return {
17543
+ onMouseOver: () => {
17544
+ const id = lineSelectableId(currentLineRef());
17545
+ if (id !== undefined)
17546
+ setHoveredSelectableId(id);
17547
+ },
17548
+ onMouseOut: () => clearHoveredSelectableId(lineSelectableId(currentLineRef()))
17549
+ };
17550
+ }
17885
17551
  function createTextLine(renderer, id, top, onLineMouseDown) {
17886
17552
  let currentLine;
17887
17553
  const renderable = new TextRenderable(renderer, {
@@ -17893,7 +17559,8 @@ function createTextLine(renderer, id, top, onLineMouseDown) {
17893
17559
  width: 1,
17894
17560
  fg: RIG_UI.ink2,
17895
17561
  selectable: true,
17896
- onMouseDown: selectableMouseHandler(() => currentLine, onLineMouseDown)
17562
+ onMouseDown: selectableMouseHandler(() => currentLine, onLineMouseDown),
17563
+ ...hoverHandlers(() => currentLine)
17897
17564
  });
17898
17565
  renderable.setRigSceneLine = (line2) => {
17899
17566
  currentLine = line2;
@@ -17910,7 +17577,8 @@ function createFlowTextLine(renderer, id, onLineMouseDown) {
17910
17577
  flexShrink: 0,
17911
17578
  fg: RIG_UI.ink2,
17912
17579
  selectable: true,
17913
- onMouseDown: selectableMouseHandler(() => currentLine, onLineMouseDown)
17580
+ onMouseDown: selectableMouseHandler(() => currentLine, onLineMouseDown),
17581
+ ...hoverHandlers(() => currentLine)
17914
17582
  });
17915
17583
  renderable.setRigSceneLine = (line2) => {
17916
17584
  currentLine = line2;
@@ -17924,8 +17592,8 @@ function applyTextLine(renderable, line2, top, left, width) {
17924
17592
  renderable.width = width;
17925
17593
  renderable.content = line2?.styledText ?? line2?.text ?? "";
17926
17594
  renderable.fg = line2?.fg ?? RIG_UI.ink2;
17927
- renderable.bg = line2?.bg ?? TRANSPARENT2;
17928
- renderable.attributes = line2?.bold ? TextAttributes4.BOLD : line2?.dim ? TextAttributes4.DIM : 0;
17595
+ renderable.bg = lineBackground(line2);
17596
+ renderable.attributes = line2?.bold ? TextAttributes3.BOLD : line2?.dim ? TextAttributes3.DIM : 0;
17929
17597
  }
17930
17598
  function applyFlowTextLine(renderable, line2, width) {
17931
17599
  renderable.setRigSceneLine?.(line2);
@@ -17934,8 +17602,8 @@ function applyFlowTextLine(renderable, line2, width) {
17934
17602
  renderable.width = Math.max(1, width);
17935
17603
  renderable.content = line2?.styledText ?? line2?.text ?? "";
17936
17604
  renderable.fg = line2?.fg ?? RIG_UI.ink2;
17937
- renderable.bg = line2?.bg ?? TRANSPARENT2;
17938
- renderable.attributes = line2?.bold ? TextAttributes4.BOLD : line2?.dim ? TextAttributes4.DIM : 0;
17605
+ renderable.bg = lineBackground(line2);
17606
+ renderable.attributes = line2?.bold ? TextAttributes3.BOLD : line2?.dim ? TextAttributes3.DIM : 0;
17939
17607
  }
17940
17608
  function clearTextLines(renderables, from = 0) {
17941
17609
  for (let index = from;index < renderables.length; index += 1) {
@@ -17948,21 +17616,34 @@ function clearTextLines(renderables, from = 0) {
17948
17616
  renderable.bg = TRANSPARENT2;
17949
17617
  }
17950
17618
  }
17951
- var TRANSPARENT2;
17619
+ var TRANSPARENT2, HOVER_BG;
17952
17620
  var init_text = __esm(() => {
17953
17621
  init_theme2();
17954
- TRANSPARENT2 = RGBA4.fromInts(0, 0, 0, 0);
17622
+ init_hover();
17623
+ TRANSPARENT2 = RGBA3.fromInts(0, 0, 0, 0);
17624
+ HOVER_BG = RGBA3.fromHex(RIG_UI.hover);
17955
17625
  });
17956
17626
 
17627
+ // packages/cli/src/app-opentui/render/terminal-handoff.ts
17628
+ function resumeRendererClean(renderer) {
17629
+ try {
17630
+ renderer.pendingSuspendedTerminalSetup = true;
17631
+ } catch {}
17632
+ renderer.resume();
17633
+ try {
17634
+ renderer.requestRender?.();
17635
+ } catch {}
17636
+ }
17637
+
17957
17638
  // packages/cli/src/app-opentui/render/panels.ts
17958
- import { RGBA as RGBA5, ScrollBoxRenderable } from "@opentui/core";
17639
+ import { RGBA as RGBA4, ScrollBoxRenderable } from "@opentui/core";
17959
17640
  function hexToRgba(hex, alpha) {
17960
17641
  const clean = hex.replace(/^#/, "");
17961
17642
  const full = clean.length === 3 ? clean.split("").map((part) => `${part}${part}`).join("") : clean;
17962
17643
  const value = Number.parseInt(full, 16);
17963
17644
  if (!Number.isFinite(value))
17964
- return RGBA5.fromInts(14, 15, 17, alpha);
17965
- return RGBA5.fromInts(value >> 16 & 255, value >> 8 & 255, value & 255, alpha);
17645
+ return RGBA4.fromInts(14, 15, 17, alpha);
17646
+ return RGBA4.fromInts(value >> 16 & 255, value >> 8 & 255, value & 255, alpha);
17966
17647
  }
17967
17648
  function panelBackground(panel) {
17968
17649
  return hexToRgba(panel.backgroundColor ?? RIG_UI.panel, PANEL_OPAQUE_ALPHA);
@@ -18079,12 +17760,12 @@ var TRANSPARENT3, MAX_PANEL_LINES = 420, PANEL_OPAQUE_ALPHA = 255, PANEL_BORDER;
18079
17760
  var init_panels = __esm(() => {
18080
17761
  init_theme2();
18081
17762
  init_text();
18082
- TRANSPARENT3 = RGBA5.fromInts(0, 0, 0, 0);
17763
+ TRANSPARENT3 = RGBA4.fromInts(0, 0, 0, 0);
18083
17764
  PANEL_BORDER = hexToRgba(RIG_UI.border, 255);
18084
17765
  });
18085
17766
 
18086
17767
  // packages/cli/src/app-opentui/render/type-bar.ts
18087
- import { BoxRenderable, InputRenderable, InputRenderableEvents, RGBA as RGBA6, StyledText as StyledText3, TextRenderable as TextRenderable2 } from "@opentui/core";
17768
+ import { BoxRenderable, InputRenderable, InputRenderableEvents, RGBA as RGBA5, StyledText as StyledText2, TextRenderable as TextRenderable2 } from "@opentui/core";
18088
17769
  function createTypeBar(renderer) {
18089
17770
  const background = new BoxRenderable(renderer, {
18090
17771
  id: "typebar-card",
@@ -18231,13 +17912,13 @@ function formatFooter(footer, width) {
18231
17912
  chunks.push(cell4.style(text2));
18232
17913
  used += visibleWidth(text2);
18233
17914
  });
18234
- return new StyledText3(chunks);
17915
+ return new StyledText2(chunks);
18235
17916
  }
18236
17917
  var TYPEBAR_BG, TYPEBAR_BORDER, FOOTER_SEPARATOR = " \xB7 ";
18237
17918
  var init_type_bar = __esm(() => {
18238
17919
  init_theme2();
18239
- TYPEBAR_BG = RGBA6.fromInts(20, 25, 14, 224);
18240
- TYPEBAR_BORDER = RGBA6.fromInts(204, 255, 77, 92);
17920
+ TYPEBAR_BG = RGBA5.fromInts(20, 25, 14, 224);
17921
+ TYPEBAR_BORDER = RGBA5.fromInts(204, 255, 77, 92);
18241
17922
  });
18242
17923
 
18243
17924
  // packages/cli/src/app-opentui/render/native-host.ts
@@ -18245,7 +17926,7 @@ import {
18245
17926
  BoxRenderable as BoxRenderable2,
18246
17927
  CodeRenderable,
18247
17928
  DiffRenderable,
18248
- RGBA as RGBA7,
17929
+ RGBA as RGBA6,
18249
17930
  ScrollBoxRenderable as ScrollBoxRenderable2,
18250
17931
  SyntaxStyle,
18251
17932
  TextRenderable as TextRenderable3,
@@ -18255,19 +17936,19 @@ function rigSyntaxStyle() {
18255
17936
  if (syntaxStyle)
18256
17937
  return syntaxStyle;
18257
17938
  syntaxStyle = SyntaxStyle.fromStyles({
18258
- keyword: { fg: RGBA7.fromHex(RIG_UI.magenta), bold: true },
18259
- string: { fg: RGBA7.fromHex(RIG_UI.lime) },
18260
- number: { fg: RGBA7.fromHex(RIG_UI.cyan) },
18261
- comment: { fg: RGBA7.fromHex(RIG_UI.ink4), italic: true },
18262
- function: { fg: RGBA7.fromHex(RIG_UI.cyan) },
18263
- type: { fg: RGBA7.fromHex(RIG_UI.limeDim) },
18264
- constant: { fg: RGBA7.fromHex(RIG_UI.yellow) },
18265
- default: { fg: RGBA7.fromHex(RIG_UI.ink2) }
17939
+ keyword: { fg: RGBA6.fromHex(RIG_UI.magenta), bold: true },
17940
+ string: { fg: RGBA6.fromHex(RIG_UI.lime) },
17941
+ number: { fg: RGBA6.fromHex(RIG_UI.cyan) },
17942
+ comment: { fg: RGBA6.fromHex(RIG_UI.ink4), italic: true },
17943
+ function: { fg: RGBA6.fromHex(RIG_UI.cyan) },
17944
+ type: { fg: RGBA6.fromHex(RIG_UI.limeDim) },
17945
+ constant: { fg: RGBA6.fromHex(RIG_UI.yellow) },
17946
+ default: { fg: RGBA6.fromHex(RIG_UI.ink2) }
18266
17947
  });
18267
17948
  return syntaxStyle;
18268
17949
  }
18269
17950
  function tableContent(rows) {
18270
- return rows.map((row, rowIndex) => row.map((cell4) => [{ __isChunk: true, text: cell4, fg: RGBA7.fromHex(rowIndex === 0 ? RIG_UI.ink3 : RIG_UI.ink2) }]));
17951
+ return rows.map((row, rowIndex) => row.map((cell4) => [{ __isChunk: true, text: cell4, fg: RGBA6.fromHex(rowIndex === 0 ? RIG_UI.ink3 : RIG_UI.ink2) }]));
18271
17952
  }
18272
17953
  function createNativeHost(renderer) {
18273
17954
  try {
@@ -18795,7 +18476,7 @@ __export(exports_runtime, {
18795
18476
  });
18796
18477
  import { existsSync as existsSync23 } from "fs";
18797
18478
  import { resolve as resolve30 } from "path";
18798
- import { BoxRenderable as BoxRenderable3, RGBA as RGBA8, StyledText as StyledText4, TextRenderable as TextRenderable4, createCliRenderer } from "@opentui/core";
18479
+ import { BoxRenderable as BoxRenderable3, RGBA as RGBA7, StyledText as StyledText3, TextRenderable as TextRenderable4, createCliRenderer } from "@opentui/core";
18799
18480
  function inboxPendingCount(state) {
18800
18481
  const inbox = state.data.inbox;
18801
18482
  if (!inbox || typeof inbox !== "object" || Array.isArray(inbox))
@@ -18964,7 +18645,7 @@ function buildNavStrip(state) {
18964
18645
  chunks.push(otuiBold(styles.yellow(String(badgeCount))));
18965
18646
  }
18966
18647
  });
18967
- return new StyledText4(chunks);
18648
+ return new StyledText3(chunks);
18968
18649
  }
18969
18650
  function requestRender(renderer) {
18970
18651
  renderer.requestRender?.();
@@ -19192,7 +18873,8 @@ async function launchRigOpenTuiApp(options) {
19192
18873
  },
19193
18874
  suspend: () => renderer?.suspend(),
19194
18875
  resume: () => {
19195
- renderer?.resume();
18876
+ if (renderer)
18877
+ resumeRendererClean(renderer);
19196
18878
  renderApp();
19197
18879
  },
19198
18880
  destroy: () => renderer?.destroy()
@@ -19299,8 +18981,7 @@ async function launchRigOpenTuiApp(options) {
19299
18981
  } else if (!selectById(store, id, renderedSelectableItems)) {
19300
18982
  return;
19301
18983
  }
19302
- if (line2.activateOnClick)
19303
- runAppAction("Activate selection", () => activateSelection(runtime, renderedSelectableItems));
18984
+ runAppAction("Activate selection", () => activateSelection(runtime, renderedSelectableItems));
19304
18985
  };
19305
18986
  for (let index = 0;index < MAX_SCENE_PANELS; index += 1) {
19306
18987
  const panel = createScrollPanelLayer(renderer, `rig-scroll-panel-${index}`, onLineClick);
@@ -19367,6 +19048,7 @@ async function launchRigOpenTuiApp(options) {
19367
19048
  };
19368
19049
  renderer.keyInput.on("keypress", onKeyPress);
19369
19050
  resources.add(() => renderer?.keyInput.off("keypress", onKeyPress));
19051
+ resources.add(subscribeHover(() => scheduleAppRender(0)));
19370
19052
  const onPaste = (event) => {
19371
19053
  const state = store.getState();
19372
19054
  if (state.scene === "handoff") {
@@ -19608,6 +19290,7 @@ var init_runtime = __esm(() => {
19608
19290
  init_graphics();
19609
19291
  init_fleet_stats();
19610
19292
  init_text();
19293
+ init_hover();
19611
19294
  init_panels();
19612
19295
  init_type_bar();
19613
19296
  init_scene();
@@ -19617,8 +19300,7 @@ var init_runtime = __esm(() => {
19617
19300
  init_help();
19618
19301
  init_main();
19619
19302
  init_selectable();
19620
- init_pi_pty_host();
19621
- PRELOADER_BACKDROP = RGBA8.fromHex(RIG_UI.bg);
19303
+ PRELOADER_BACKDROP = RGBA7.fromHex(RIG_UI.bg);
19622
19304
  PRIMARY_NAV = [
19623
19305
  { id: "cockpit", label: "Cockpit", scene: "main", argv: ["main"], enterLabel: "Cockpit", member: ["main"] },
19624
19306
  { id: "runs", label: "Runs", scene: "fleet", argv: ["runs"], enterLabel: "Runs", member: ["fleet", "run-detail", "inspect", "handoff"] },
@@ -19735,23 +19417,26 @@ var init_scroll = __esm(() => {
19735
19417
  // packages/cli/src/app-opentui/react/SceneFrameView.tsx
19736
19418
  import { useEffect as useEffect2, useRef as useRef2 } from "react";
19737
19419
  import { useRenderer as useRenderer2 } from "@opentui/react";
19738
- import { TextAttributes as TextAttributes5 } from "@opentui/core";
19420
+ import { TextAttributes as TextAttributes4 } from "@opentui/core";
19739
19421
  import { jsxDEV, Fragment } from "@opentui/react/jsx-dev-runtime";
19740
19422
  function lineAttributes(line2) {
19741
- return line2?.bold ? TextAttributes5.BOLD : line2?.dim ? TextAttributes5.DIM : 0;
19423
+ return line2?.bold ? TextAttributes4.BOLD : line2?.dim ? TextAttributes4.DIM : 0;
19742
19424
  }
19743
19425
  function lineContent(line2) {
19744
19426
  return line2.styledText ?? line2.text ?? "";
19745
19427
  }
19746
19428
  function LineView(props) {
19747
19429
  const { line: line2, onLineClick } = props;
19430
+ const id = lineSelectableId(line2);
19748
19431
  return /* @__PURE__ */ jsxDEV("text", {
19749
19432
  style: { position: "absolute", left: props.left, top: props.top, width: props.width, zIndex: 5 },
19750
19433
  fg: line2.fg ?? RIG_UI.ink2,
19751
- bg: line2.bg ?? TRANSPARENT2,
19434
+ bg: lineBackground(line2),
19752
19435
  attributes: lineAttributes(line2),
19753
19436
  content: lineContent(line2),
19754
- onMouseDown: onLineClick ? () => onLineClick(line2) : undefined
19437
+ onMouseDown: onLineClick ? () => onLineClick(line2) : undefined,
19438
+ onMouseOver: id !== undefined ? () => setHoveredSelectableId(id) : undefined,
19439
+ onMouseOut: id !== undefined ? () => clearHoveredSelectableId(id) : undefined
19755
19440
  }, undefined, false, undefined, this);
19756
19441
  }
19757
19442
  function PanelHost(props) {
@@ -19838,6 +19523,7 @@ var init_SceneFrameView = __esm(() => {
19838
19523
  init_theme2();
19839
19524
  init_scene();
19840
19525
  init_text();
19526
+ init_hover();
19841
19527
  init_panels();
19842
19528
  init_native_host();
19843
19529
  init_scroll();
@@ -19991,7 +19677,7 @@ var init_nav = __esm(() => {
19991
19677
  });
19992
19678
 
19993
19679
  // packages/cli/src/app-opentui/react/App.tsx
19994
- import { useMemo, useRef as useRef4 } from "react";
19680
+ import { useMemo, useRef as useRef4, useSyncExternalStore as useSyncExternalStore2 } from "react";
19995
19681
  import { useKeyboard, useRenderer as useRenderer4, useTerminalDimensions as useTerminalDimensions2 } from "@opentui/react";
19996
19682
  import { jsxDEV as jsxDEV3 } from "@opentui/react/jsx-dev-runtime";
19997
19683
  function defaultFrame(state) {
@@ -20009,6 +19695,7 @@ function App(props) {
20009
19695
  const runAppAction = useRunAppAction();
20010
19696
  const store = useStore();
20011
19697
  const renderer = useRenderer4();
19698
+ useSyncExternalStore2(subscribeHover, getHoveredSelectableId, getHoveredSelectableId);
20012
19699
  const dims = useTerminalDimensions2();
20013
19700
  const layout = computeStageLayout(dims.width, dims.height);
20014
19701
  const registry = useMemo(() => ({ ...BUILTIN, ...props.sceneRenderers ?? {} }), [props.sceneRenderers]);
@@ -20043,8 +19730,7 @@ function App(props) {
20043
19730
  } else {
20044
19731
  return;
20045
19732
  }
20046
- if (line2.activateOnClick)
20047
- runAppAction("Activate selection", () => activateSelection(runtime, items));
19733
+ runAppAction("Activate selection", () => activateSelection(runtime, items));
20048
19734
  };
20049
19735
  return /* @__PURE__ */ jsxDEV3("box", {
20050
19736
  style: { position: "absolute", left: 0, top: 0, width: "100%", height: "100%" },
@@ -20094,6 +19780,7 @@ var init_App = __esm(() => {
20094
19780
  init_SceneFrameView();
20095
19781
  init_ChromeHost();
20096
19782
  init_scroll();
19783
+ init_hover();
20097
19784
  init_context();
20098
19785
  init_nav();
20099
19786
  BUILTIN = {
@@ -20116,8 +19803,48 @@ async function launchRigReactApp(options) {
20116
19803
  const store = createAppStore(initialState);
20117
19804
  const events = createAppEventBus();
20118
19805
  let renderer = null;
19806
+ let root = null;
20119
19807
  let runnerPromise = null;
20120
19808
  let suppressHistoryPush = false;
19809
+ let resolveExit = () => {};
19810
+ const appExit = new Promise((resolve31) => {
19811
+ resolveExit = resolve31;
19812
+ });
19813
+ let relaunching = false;
19814
+ const mountRenderer = async () => {
19815
+ renderer = await createCliRenderer2({
19816
+ screenMode: "alternate-screen",
19817
+ exitOnCtrlC: false,
19818
+ targetFps: 30,
19819
+ maxFps: 30,
19820
+ useMouse: true,
19821
+ autoFocus: false,
19822
+ useKittyKeyboard: { disambiguate: true }
19823
+ });
19824
+ renderer.on("destroy", () => {
19825
+ if (!relaunching)
19826
+ resolveExit();
19827
+ });
19828
+ root = createRoot(renderer);
19829
+ root.render(/* @__PURE__ */ jsxDEV4(RigAppProvider, {
19830
+ value: { store, runtime, runAppAction },
19831
+ children: /* @__PURE__ */ jsxDEV4(App, {
19832
+ sceneRenderers: options.sceneRenderers
19833
+ }, undefined, false, undefined, this)
19834
+ }, undefined, false, undefined, this));
19835
+ relaunching = false;
19836
+ };
19837
+ const teardownRenderer = () => {
19838
+ relaunching = true;
19839
+ try {
19840
+ root?.unmount();
19841
+ } catch {}
19842
+ root = null;
19843
+ try {
19844
+ renderer?.destroy();
19845
+ } catch {}
19846
+ renderer = null;
19847
+ };
20121
19848
  const runAppAction = (label, action) => {
20122
19849
  action().catch((error) => {
20123
19850
  const n = normalizeError(error);
@@ -20159,8 +19886,10 @@ async function launchRigReactApp(options) {
20159
19886
  runnerPromise ??= options.initializeRuntime();
20160
19887
  return runnerPromise;
20161
19888
  },
20162
- suspend: () => renderer?.suspend(),
20163
- resume: () => renderer?.resume(),
19889
+ suspend: () => teardownRenderer(),
19890
+ resume: async () => {
19891
+ await mountRenderer();
19892
+ },
20164
19893
  destroy: () => renderer?.destroy()
20165
19894
  };
20166
19895
  events.subscribe((event) => store.patch(reduceAppEvent(store.getState(), event)));
@@ -20168,15 +19897,7 @@ async function launchRigReactApp(options) {
20168
19897
  if (typeof tick.unref === "function") {
20169
19898
  tick.unref();
20170
19899
  }
20171
- renderer = await createCliRenderer2({
20172
- screenMode: "alternate-screen",
20173
- exitOnCtrlC: false,
20174
- targetFps: 30,
20175
- maxFps: 30,
20176
- useMouse: true,
20177
- autoFocus: false,
20178
- useKittyKeyboard: { disambiguate: true }
20179
- });
19900
+ await mountRenderer();
20180
19901
  let liveEvents = null;
20181
19902
  const canAutoRefresh = (state) => {
20182
19903
  if (state.status === "action" || state.status === "loading")
@@ -20207,12 +19928,6 @@ async function launchRigReactApp(options) {
20207
19928
  }, FALLBACK_REFRESH_MS);
20208
19929
  if (typeof poll.unref === "function")
20209
19930
  poll.unref();
20210
- createRoot(renderer).render(/* @__PURE__ */ jsxDEV4(RigAppProvider, {
20211
- value: { store, runtime, runAppAction },
20212
- children: /* @__PURE__ */ jsxDEV4(App, {
20213
- sceneRenderers: options.sceneRenderers
20214
- }, undefined, false, undefined, this)
20215
- }, undefined, false, undefined, this));
20216
19931
  queueMicrotask(() => {
20217
19932
  (async () => {
20218
19933
  try {
@@ -20240,14 +19955,11 @@ async function launchRigReactApp(options) {
20240
19955
  }
20241
19956
  })();
20242
19957
  });
20243
- await new Promise((resolve31) => {
20244
- renderer?.on("destroy", () => {
20245
- clearInterval(tick);
20246
- clearInterval(poll);
20247
- liveEvents?.close();
20248
- resolve31();
20249
- });
20250
- });
19958
+ await appExit;
19959
+ clearInterval(tick);
19960
+ clearInterval(poll);
19961
+ liveEvents?.close();
19962
+ teardownRenderer();
20251
19963
  }
20252
19964
  var POLLABLE_SCENES;
20253
19965
  var init_launch = __esm(() => {
@@ -20261,7 +19973,7 @@ var init_launch = __esm(() => {
20261
19973
 
20262
19974
  // packages/cli/src/app-opentui/bootstrap.ts
20263
19975
  import { existsSync as existsSync24, readFileSync as readFileSync15 } from "fs";
20264
- import { basename as basename6, resolve as resolve31 } from "path";
19976
+ import { basename as basename5, resolve as resolve31 } from "path";
20265
19977
 
20266
19978
  // packages/cli/src/app-opentui/adapters/command.ts
20267
19979
  init_command_pty_host();
@@ -23990,11 +23702,8 @@ init_scene();
23990
23702
  init_selectable();
23991
23703
 
23992
23704
  // packages/cli/src/app-opentui/list-search.ts
23993
- function isListSearchScene(scene) {
23994
- return scene === "fleet" || scene === "tasks";
23995
- }
23996
- function listSearchQuery(state) {
23997
- return isListSearchScene(state.scene) ? state.typeBar.value.trim() : "";
23705
+ function listSearchQuery(_state) {
23706
+ return "";
23998
23707
  }
23999
23708
  function valueText(value) {
24000
23709
  if (value === undefined || value === null)
@@ -27195,7 +26904,7 @@ function parsePolicyMode2(value) {
27195
26904
  function resolveProjectRoot2(input) {
27196
26905
  if (input.envProjectRoot)
27197
26906
  return resolve31(input.cwd, input.envProjectRoot);
27198
- const execName = basename6(input.execPath).toLowerCase();
26907
+ const execName = basename5(input.execPath).toLowerCase();
27199
26908
  const execCandidates = execName === "rig" || execName === "rig.exe" ? [resolve31(input.execPath, "..", "..")] : [];
27200
26909
  const candidates = [input.cwd, ...execCandidates, resolve31(input.importDir, "..")];
27201
26910
  for (const candidate of candidates) {