@h-rig/cli 0.0.6-alpha.83 → 0.0.6-alpha.84

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (163) hide show
  1. package/dist/bin/rig.js +22085 -14618
  2. package/dist/src/app/board.js +155 -28
  3. package/dist/src/app-opentui/adapters/doctor.d.ts +0 -2
  4. package/dist/src/app-opentui/adapters/doctor.js +1 -25
  5. package/dist/src/app-opentui/adapters/family.d.ts +62 -0
  6. package/dist/src/app-opentui/adapters/family.js +14305 -0
  7. package/dist/src/app-opentui/adapters/fleet.d.ts +0 -2
  8. package/dist/src/app-opentui/adapters/fleet.js +6 -40
  9. package/dist/src/app-opentui/adapters/inbox.d.ts +12 -2
  10. package/dist/src/app-opentui/adapters/inbox.js +54 -59
  11. package/dist/src/app-opentui/adapters/init.d.ts +0 -2
  12. package/dist/src/app-opentui/adapters/init.js +0 -26
  13. package/dist/src/app-opentui/adapters/inspect.d.ts +52 -0
  14. package/dist/src/app-opentui/adapters/inspect.js +1024 -0
  15. package/dist/src/app-opentui/adapters/pi-attach.d.ts +15 -6
  16. package/dist/src/app-opentui/adapters/pi-attach.js +348 -93
  17. package/dist/src/app-opentui/adapters/pi.d.ts +23 -0
  18. package/dist/src/app-opentui/adapters/pi.js +363 -0
  19. package/dist/src/app-opentui/adapters/plugin.d.ts +84 -0
  20. package/dist/src/app-opentui/adapters/plugin.js +544 -0
  21. package/dist/src/app-opentui/adapters/repo.d.ts +37 -0
  22. package/dist/src/app-opentui/adapters/repo.js +186 -0
  23. package/dist/src/app-opentui/adapters/run-detail.d.ts +9 -2
  24. package/dist/src/app-opentui/adapters/run-detail.js +97 -44
  25. package/dist/src/app-opentui/adapters/server.d.ts +10 -2
  26. package/dist/src/app-opentui/adapters/server.js +96 -27
  27. package/dist/src/app-opentui/adapters/tasks.d.ts +0 -2
  28. package/dist/src/app-opentui/adapters/tasks.js +437 -105
  29. package/dist/src/app-opentui/adapters/workspace.d.ts +49 -0
  30. package/dist/src/app-opentui/adapters/workspace.js +333 -0
  31. package/dist/src/app-opentui/autocomplete.d.ts +20 -0
  32. package/dist/src/app-opentui/autocomplete.js +576 -0
  33. package/dist/src/app-opentui/bootstrap.js +24296 -16919
  34. package/dist/src/app-opentui/command-palette.d.ts +3 -0
  35. package/dist/src/app-opentui/command-palette.js +1010 -0
  36. package/dist/src/app-opentui/drone.js +8 -6
  37. package/dist/src/app-opentui/fleet-stats.d.ts +32 -0
  38. package/dist/src/app-opentui/fleet-stats.js +114 -0
  39. package/dist/src/app-opentui/index.js +3438 -1724
  40. package/dist/src/app-opentui/intent.js +71 -48
  41. package/dist/src/app-opentui/keymap.d.ts +6 -5
  42. package/dist/src/app-opentui/keymap.js +1064 -23
  43. package/dist/src/app-opentui/layout.d.ts +7 -0
  44. package/dist/src/app-opentui/layout.js +13 -6
  45. package/dist/src/app-opentui/list-search.d.ts +24 -0
  46. package/dist/src/app-opentui/list-search.js +88 -1
  47. package/dist/src/app-opentui/pi-host-child.js +33 -1
  48. package/dist/src/app-opentui/pi-pty-host.d.ts +5 -0
  49. package/dist/src/app-opentui/pi-pty-host.js +21 -3
  50. package/dist/src/app-opentui/react/App.d.ts +9 -0
  51. package/dist/src/app-opentui/react/App.js +5144 -0
  52. package/dist/src/app-opentui/react/Backdrop.d.ts +5 -0
  53. package/dist/src/app-opentui/react/Backdrop.js +1834 -0
  54. package/dist/src/app-opentui/react/ChromeHost.d.ts +5 -0
  55. package/dist/src/app-opentui/react/ChromeHost.js +2942 -0
  56. package/dist/src/app-opentui/react/SceneFrameView.d.ts +7 -0
  57. package/dist/src/app-opentui/react/SceneFrameView.js +529 -0
  58. package/dist/src/app-opentui/react/context.d.ts +17 -0
  59. package/dist/src/app-opentui/react/context.js +37 -0
  60. package/dist/src/app-opentui/react/launch.d.ts +2 -0
  61. package/dist/src/app-opentui/react/launch.js +5743 -0
  62. package/dist/src/app-opentui/react/nav.d.ts +18 -0
  63. package/dist/src/app-opentui/react/nav.js +54 -0
  64. package/dist/src/app-opentui/react/scroll.d.ts +12 -0
  65. package/dist/src/app-opentui/react/scroll.js +21 -0
  66. package/dist/src/app-opentui/react/syntax.d.ts +2 -0
  67. package/dist/src/app-opentui/react/syntax.js +64 -0
  68. package/dist/src/app-opentui/registry.js +19923 -5151
  69. package/dist/src/app-opentui/render/constants.d.ts +31 -0
  70. package/dist/src/app-opentui/render/constants.js +66 -0
  71. package/dist/src/app-opentui/render/graphics.d.ts +2 -1
  72. package/dist/src/app-opentui/render/graphics.js +106 -39
  73. package/dist/src/app-opentui/render/image-visual-layer-worker.js +108 -20
  74. package/dist/src/app-opentui/render/image-visual-layer.d.ts +7 -0
  75. package/dist/src/app-opentui/render/image-visual-layer.js +109 -20
  76. package/dist/src/app-opentui/render/native-host.d.ts +37 -0
  77. package/dist/src/app-opentui/render/native-host.js +179 -0
  78. package/dist/src/app-opentui/render/panels.js +18 -11
  79. package/dist/src/app-opentui/render/preloader.js +8 -6
  80. package/dist/src/app-opentui/render/scene.d.ts +50 -1
  81. package/dist/src/app-opentui/render/scene.js +183 -6
  82. package/dist/src/app-opentui/render/text.d.ts +7 -1
  83. package/dist/src/app-opentui/render/text.js +10 -7
  84. package/dist/src/app-opentui/render/type-bar.js +69 -30
  85. package/dist/src/app-opentui/runtime.d.ts +44 -1
  86. package/dist/src/app-opentui/runtime.js +3231 -1363
  87. package/dist/src/app-opentui/scenes/command.js +20 -6
  88. package/dist/src/app-opentui/scenes/doctor.js +176 -11
  89. package/dist/src/app-opentui/scenes/error.js +20 -10
  90. package/dist/src/app-opentui/scenes/family-domains/agent.d.ts +2 -0
  91. package/dist/src/app-opentui/scenes/family-domains/agent.js +348 -0
  92. package/dist/src/app-opentui/scenes/family-domains/browser.d.ts +2 -0
  93. package/dist/src/app-opentui/scenes/family-domains/browser.js +195 -0
  94. package/dist/src/app-opentui/scenes/family-domains/dist.d.ts +2 -0
  95. package/dist/src/app-opentui/scenes/family-domains/dist.js +243 -0
  96. package/dist/src/app-opentui/scenes/family-domains/git.d.ts +2 -0
  97. package/dist/src/app-opentui/scenes/family-domains/git.js +195 -0
  98. package/dist/src/app-opentui/scenes/family-domains/github.d.ts +2 -0
  99. package/dist/src/app-opentui/scenes/family-domains/github.js +274 -0
  100. package/dist/src/app-opentui/scenes/family-domains/harness.d.ts +2 -0
  101. package/dist/src/app-opentui/scenes/family-domains/harness.js +152 -0
  102. package/dist/src/app-opentui/scenes/family-domains/index.d.ts +4 -0
  103. package/dist/src/app-opentui/scenes/family-domains/index.js +1679 -0
  104. package/dist/src/app-opentui/scenes/family-domains/kit.d.ts +76 -0
  105. package/dist/src/app-opentui/scenes/family-domains/kit.js +305 -0
  106. package/dist/src/app-opentui/scenes/family-domains/profile.d.ts +2 -0
  107. package/dist/src/app-opentui/scenes/family-domains/profile.js +212 -0
  108. package/dist/src/app-opentui/scenes/family-domains/queue.d.ts +2 -0
  109. package/dist/src/app-opentui/scenes/family-domains/queue.js +146 -0
  110. package/dist/src/app-opentui/scenes/family-domains/remote.d.ts +2 -0
  111. package/dist/src/app-opentui/scenes/family-domains/remote.js +518 -0
  112. package/dist/src/app-opentui/scenes/family-domains/review.d.ts +2 -0
  113. package/dist/src/app-opentui/scenes/family-domains/review.js +280 -0
  114. package/dist/src/app-opentui/scenes/family-domains/setup.d.ts +2 -0
  115. package/dist/src/app-opentui/scenes/family-domains/setup.js +267 -0
  116. package/dist/src/app-opentui/scenes/family-domains/stats.d.ts +2 -0
  117. package/dist/src/app-opentui/scenes/family-domains/stats.js +370 -0
  118. package/dist/src/app-opentui/scenes/family.d.ts +3 -0
  119. package/dist/src/app-opentui/scenes/family.js +2144 -0
  120. package/dist/src/app-opentui/scenes/fleet.js +521 -31
  121. package/dist/src/app-opentui/scenes/handoff.js +204 -12
  122. package/dist/src/app-opentui/scenes/help.js +609 -40
  123. package/dist/src/app-opentui/scenes/inbox.js +278 -17
  124. package/dist/src/app-opentui/scenes/init.js +84 -39
  125. package/dist/src/app-opentui/scenes/inspect.d.ts +3 -0
  126. package/dist/src/app-opentui/scenes/inspect.js +793 -0
  127. package/dist/src/app-opentui/scenes/main.js +218 -39
  128. package/dist/src/app-opentui/scenes/pi.d.ts +3 -0
  129. package/dist/src/app-opentui/scenes/pi.js +508 -0
  130. package/dist/src/app-opentui/scenes/plugin.d.ts +3 -0
  131. package/dist/src/app-opentui/scenes/plugin.js +486 -0
  132. package/dist/src/app-opentui/scenes/repo.d.ts +3 -0
  133. package/dist/src/app-opentui/scenes/repo.js +424 -0
  134. package/dist/src/app-opentui/scenes/run-detail.js +333 -20
  135. package/dist/src/app-opentui/scenes/server.js +181 -15
  136. package/dist/src/app-opentui/scenes/tasks.js +486 -31
  137. package/dist/src/app-opentui/scenes/workspace.d.ts +3 -0
  138. package/dist/src/app-opentui/scenes/workspace.js +426 -0
  139. package/dist/src/app-opentui/selectable.d.ts +19 -0
  140. package/dist/src/app-opentui/selectable.js +79 -0
  141. package/dist/src/app-opentui/state.js +83 -30
  142. package/dist/src/app-opentui/surface-catalog.d.ts +20 -0
  143. package/dist/src/app-opentui/surface-catalog.js +540 -0
  144. package/dist/src/app-opentui/theme.d.ts +28 -6
  145. package/dist/src/app-opentui/theme.js +61 -8
  146. package/dist/src/app-opentui/types.d.ts +121 -4
  147. package/dist/src/commands/_authority-runs.d.ts +1 -1
  148. package/dist/src/commands/_authority-runs.js +2 -12
  149. package/dist/src/commands/_help-catalog.js +95 -15
  150. package/dist/src/commands/_operator-view.js +34 -2
  151. package/dist/src/commands/_pi-frontend.d.ts +2 -0
  152. package/dist/src/commands/_pi-frontend.js +34 -0
  153. package/dist/src/commands/_server-events.d.ts +26 -0
  154. package/dist/src/commands/_server-events.js +310 -0
  155. package/dist/src/commands/agent.js +2 -12
  156. package/dist/src/commands/run.js +34 -2
  157. package/dist/src/commands/server.js +2 -12
  158. package/dist/src/commands/stats.js +95 -15
  159. package/dist/src/commands/task-run-driver.js +2 -12
  160. package/dist/src/commands/task.js +131 -29
  161. package/dist/src/commands.js +192 -31
  162. package/dist/src/index.js +192 -31
  163. package/package.json +11 -9
@@ -1,16 +1,25 @@
1
- import type { AppAdapter } from "../types";
2
1
  import { type AppAdapterContext } from "./common";
3
- export declare function createPiAttachAdapter(renderer?: {
4
- suspend?: () => void | Promise<void>;
5
- resume?: () => void | Promise<void>;
6
- destroy?: () => void | Promise<void>;
7
- }): AppAdapter;
2
+ /** One concrete prep step in the attach pipeline (connect → fetch transcript →
3
+ * spawn host → start Pi). Carries a wall-clock start so the handoff scene can
4
+ * show step-by-step progress with elapsed time during the otherwise-blank
5
+ * window before Pi's transcript paints (bug #2). */
6
+ export type PiAttachStep = {
7
+ readonly label: string;
8
+ readonly startedAtMs: number;
9
+ readonly status: "running" | "done" | "failed";
10
+ /** Set when the step settles, so elapsed time freezes instead of ticking. */
11
+ readonly endedAtMs?: number;
12
+ };
8
13
  export type PiAttachHandoffState = {
9
14
  readonly runId: string;
10
15
  readonly status: "preparing" | "entering-pi" | "returned" | "failed";
11
16
  readonly message: string;
12
17
  readonly result?: Record<string, unknown>;
13
18
  readonly ready?: boolean;
19
+ /** Ordered prep steps with timing, surfaced during the blank attach window. */
20
+ readonly steps?: readonly PiAttachStep[];
21
+ /** Operator-facing failure reason when status === "failed". */
22
+ readonly failureReason?: string;
14
23
  };
15
24
  export declare function preparePiAttachHandoff(ctx: AppAdapterContext, runId: string): Promise<PiAttachHandoffState>;
16
25
  export declare function attachRunWithBundledPi(ctx: AppAdapterContext, runId: string): Promise<Record<string, unknown>>;
@@ -15,6 +15,70 @@ var __export = (target, all) => {
15
15
  };
16
16
  var __esm = (fn, res) => () => (fn && (res = fn(fn = 0)), res);
17
17
 
18
+ // packages/cli/src/app-opentui/adapters/common.ts
19
+ function projectRootOf(ctx) {
20
+ const root = ctx.rig?.projectRoot ?? ctx.projectRoot ?? ctx.getState().projectRoot;
21
+ if (typeof root === "string" && root.trim())
22
+ return root;
23
+ throw new Error("App adapter requires a projectRoot.");
24
+ }
25
+ function normalizeAppError(error) {
26
+ const message = error instanceof Error ? error.message : String(error);
27
+ const rawHint = error instanceof Error ? error.hint : undefined;
28
+ return {
29
+ message,
30
+ ...typeof rawHint === "string" && rawHint.trim() ? { hint: rawHint.trim() } : {}
31
+ };
32
+ }
33
+ function emitStarted(ctx, label, optimistic) {
34
+ ctx.emit({ type: "action.started", label, ...optimistic ? { optimistic } : {} });
35
+ }
36
+ function emitProgress(ctx, label, detail, data) {
37
+ ctx.emit({ type: "action.progress", label, ...detail ? { detail } : {}, ...data ? { data } : {} });
38
+ }
39
+ function emitCompleted(ctx, label, data) {
40
+ ctx.emit({ type: "action.completed", label, ...data ? { data } : {} });
41
+ }
42
+ function emitFailed(ctx, label, error, data) {
43
+ if (data)
44
+ patchData(ctx, data);
45
+ const normalized = normalizeAppError(error);
46
+ ctx.emit({
47
+ type: "action.failed",
48
+ label,
49
+ message: normalized.message,
50
+ ...normalized.hint ? { hint: normalized.hint } : {},
51
+ cause: error
52
+ });
53
+ }
54
+ function patchData(ctx, data) {
55
+ ctx.emit({ type: "data.patch", data });
56
+ }
57
+ function patchFooter(ctx, footer) {
58
+ ctx.emit({ type: "footer.patch", footer });
59
+ }
60
+ function arrayFromPayload(value) {
61
+ if (Array.isArray(value)) {
62
+ return value.filter((entry) => Boolean(entry && typeof entry === "object" && !Array.isArray(entry)));
63
+ }
64
+ if (value && typeof value === "object" && !Array.isArray(value)) {
65
+ const entries = value.entries;
66
+ if (Array.isArray(entries))
67
+ return arrayFromPayload(entries);
68
+ }
69
+ return [];
70
+ }
71
+ function stringField(record, keys, fallback = "") {
72
+ for (const key of keys) {
73
+ const value = record[key];
74
+ if (typeof value === "string" && value.trim())
75
+ return value.trim();
76
+ if (typeof value === "number" && Number.isFinite(value))
77
+ return String(value);
78
+ }
79
+ return fallback;
80
+ }
81
+
18
82
  // packages/cli/src/runner.ts
19
83
  import { EventBus } from "@rig/runtime/control-plane/runtime/events";
20
84
  import { CliError as RuntimeCliError } from "@rig/runtime/control-plane/errors";
@@ -766,72 +830,203 @@ var init__server_client = __esm(() => {
766
830
  ]);
767
831
  });
768
832
 
769
- // packages/cli/src/app-opentui/adapters/common.ts
770
- function adapterContextFromRuntime(runtime, renderer) {
771
- return {
772
- projectRoot: runtime.getState().projectRoot,
773
- renderer,
774
- ensureRuntime: () => runtime.getRunnerContext(),
775
- emit: (event) => runtime.emit(event),
776
- getState: () => runtime.getState()
777
- };
778
- }
779
- function projectRootOf(ctx) {
780
- const root = ctx.rig?.projectRoot ?? ctx.projectRoot ?? ctx.getState().projectRoot;
781
- if (typeof root === "string" && root.trim())
782
- return root;
783
- throw new Error("App adapter requires a projectRoot.");
784
- }
785
- function normalizeAppError(error) {
786
- const message = error instanceof Error ? error.message : String(error);
787
- const rawHint = error instanceof Error ? error.hint : undefined;
788
- return {
789
- message,
790
- ...typeof rawHint === "string" && rawHint.trim() ? { hint: rawHint.trim() } : {}
791
- };
792
- }
793
- function emitStarted(ctx, label, optimistic) {
794
- ctx.emit({ type: "action.started", label, ...optimistic ? { optimistic } : {} });
795
- }
796
- function emitProgress(ctx, label, detail, data) {
797
- ctx.emit({ type: "action.progress", label, ...detail ? { detail } : {}, ...data ? { data } : {} });
833
+ // packages/cli/src/app-opentui/adapters/inspect.ts
834
+ var exports_inspect = {};
835
+ __export(exports_inspect, {
836
+ loadInspectRuns: () => loadInspectRuns,
837
+ loadInspect: () => loadInspect,
838
+ INSPECT_VIEWS: () => INSPECT_VIEWS
839
+ });
840
+ import { execFile } from "child_process";
841
+ import { existsSync as existsSync3, readFileSync as readFileSync3, readdirSync, statSync } from "fs";
842
+ import { resolve as resolve3 } from "path";
843
+ import { promisify } from "util";
844
+ import { resolveTaskArtifactDirs } from "@rig/runtime/control-plane/authority-files";
845
+ import { readTaskArtifactPreview } from "@rig/runtime/control-plane/native/workspace-ops";
846
+ import { resolveHarnessPaths, resolveMonorepoRoot, runCapture } from "@rig/runtime/control-plane/native/utils";
847
+ function isFailureEntry(entry) {
848
+ const status = `${entry.status ?? entry.level ?? entry.type ?? ""}`.toLowerCase();
849
+ return status.includes("fail") || status.includes("error");
850
+ }
851
+ async function gitDiff(projectRoot) {
852
+ try {
853
+ const { stdout } = await execFileAsync("git", ["diff", "--no-color"], { cwd: projectRoot, maxBuffer: 8 * 1024 * 1024 });
854
+ return { diff: stdout };
855
+ } catch (error) {
856
+ return { diff: "", error: normalizeAppError(error).message };
857
+ }
798
858
  }
799
- function emitCompleted(ctx, label, data) {
800
- ctx.emit({ type: "action.completed", label, ...data ? { data } : {} });
859
+ function listArtifacts(projectRoot, taskId) {
860
+ if (!taskId)
861
+ return { artifacts: [], error: "run has no task id \u2014 artifacts are resolved per task" };
862
+ try {
863
+ const dirs = resolveTaskArtifactDirs(projectRoot, taskId).filter((dir) => existsSync3(dir));
864
+ const out = [];
865
+ const seen = new Set;
866
+ for (const dir of dirs) {
867
+ for (const name of readdirSync(dir)) {
868
+ if (seen.has(name))
869
+ continue;
870
+ seen.add(name);
871
+ const path = resolve3(dir, name);
872
+ try {
873
+ const stat = statSync(path);
874
+ out.push({ name, path, sizeBytes: stat.size, isDir: stat.isDirectory() });
875
+ } catch {
876
+ out.push({ name, path, sizeBytes: 0, isDir: false });
877
+ }
878
+ }
879
+ }
880
+ out.sort((left, right) => left.name.localeCompare(right.name));
881
+ return { artifacts: out };
882
+ } catch (error) {
883
+ return { artifacts: [], error: normalizeAppError(error).message };
884
+ }
801
885
  }
802
- function emitFailed(ctx, label, error, data) {
803
- if (data)
804
- patchData(ctx, data);
805
- const normalized = normalizeAppError(error);
806
- ctx.emit({
807
- type: "action.failed",
808
- label,
809
- message: normalized.message,
810
- ...normalized.hint ? { hint: normalized.hint } : {},
811
- cause: error
812
- });
886
+ function readArtifact(projectRoot, taskId, fileName) {
887
+ if (!taskId)
888
+ return;
889
+ try {
890
+ const preview = readTaskArtifactPreview(projectRoot, taskId, fileName);
891
+ return { name: fileName, contents: preview.contents, truncated: preview.truncated, sizeBytes: preview.sizeBytes };
892
+ } catch (error) {
893
+ return { name: fileName, contents: `Failed to read artifact: ${normalizeAppError(error).message}`, truncated: false, sizeBytes: 0 };
894
+ }
813
895
  }
814
- function patchData(ctx, data) {
815
- ctx.emit({ type: "data.patch", data });
896
+ function loadGraph(projectRoot) {
897
+ try {
898
+ const result = runCapture(["br", "--no-db", "list", "--pretty"], resolveMonorepoRoot(projectRoot));
899
+ if (result.exitCode !== 0)
900
+ return { graph: "", error: result.stderr || result.stdout || "failed to inspect graph" };
901
+ return { graph: result.stdout };
902
+ } catch (error) {
903
+ return { graph: "", error: normalizeAppError(error).message };
904
+ }
816
905
  }
817
- function patchFooter(ctx, footer) {
818
- ctx.emit({ type: "footer.patch", footer });
906
+ function loadAudit(projectRoot) {
907
+ try {
908
+ const auditPath = resolve3(resolveHarnessPaths(projectRoot).logsDir, "audit.jsonl");
909
+ if (!existsSync3(auditPath))
910
+ return [];
911
+ return readFileSync3(auditPath, "utf-8").split(/\r?\n/).filter(Boolean).slice(-50).flatMap((raw) => {
912
+ try {
913
+ const parsed = JSON.parse(raw);
914
+ return parsed && typeof parsed === "object" && !Array.isArray(parsed) ? [parsed] : [{ detail: raw }];
915
+ } catch {
916
+ return [{ detail: raw }];
917
+ }
918
+ });
919
+ } catch {
920
+ return [];
921
+ }
819
922
  }
820
- function stringField(record, keys, fallback = "") {
821
- for (const key of keys) {
822
- const value = record[key];
823
- if (typeof value === "string" && value.trim())
824
- return value.trim();
825
- if (typeof value === "number" && Number.isFinite(value))
826
- return String(value);
923
+ async function loadInspectRuns(ctx, options = {}) {
924
+ const label = "Loading runs to inspect";
925
+ emitStarted(ctx, label);
926
+ try {
927
+ const projectRoot = projectRootOf(ctx);
928
+ const { listRunsViaServer: listRunsViaServer2 } = await Promise.resolve().then(() => (init__server_client(), exports__server_client));
929
+ emitProgress(ctx, label, "listing recent runs");
930
+ const runs = await listRunsViaServer2({ projectRoot }, { limit: options.limit ?? 50 });
931
+ const options_ = runs.flatMap((record) => {
932
+ const runId = stringField(record, ["runId", "id"]);
933
+ if (!runId)
934
+ return [];
935
+ const taskId = stringField(record, ["taskId"]);
936
+ const updatedAt = stringField(record, ["updatedAt", "createdAt"]);
937
+ return [{
938
+ runId,
939
+ status: stringField(record, ["status", "state"], "unknown"),
940
+ title: stringField(record, ["title", "taskTitle", "taskId"], "untitled run"),
941
+ ...taskId ? { taskId } : {},
942
+ ...updatedAt ? { updatedAt } : {}
943
+ }];
944
+ });
945
+ patchData(ctx, { inspectRuns: options_, lastRefreshError: undefined });
946
+ emitCompleted(ctx, label, { count: options_.length });
947
+ return options_;
948
+ } catch (error) {
949
+ emitFailed(ctx, label, error);
950
+ throw error;
827
951
  }
828
- return fallback;
829
952
  }
830
- function payloadString(intent, key) {
831
- const value = intent.action.payload?.[key];
832
- return typeof value === "string" && value.trim() ? value.trim() : undefined;
953
+ async function loadInspect(ctx, runId) {
954
+ const cleanRunId = runId.trim();
955
+ const label = `Inspecting run ${cleanRunId.slice(0, 8)}`;
956
+ if (!cleanRunId) {
957
+ await loadInspectRuns(ctx).catch(() => []);
958
+ const error = new Error("Inspect requires a run id.");
959
+ throw error;
960
+ }
961
+ emitStarted(ctx, label);
962
+ try {
963
+ const projectRoot = projectRootOf(ctx);
964
+ const { getRunDetailsViaServer: getRunDetailsViaServer2, getRunLogsViaServer: getRunLogsViaServer2, getRunTimelineViaServer: getRunTimelineViaServer2 } = await Promise.resolve().then(() => (init__server_client(), exports__server_client));
965
+ const context = { projectRoot };
966
+ emitProgress(ctx, label, "loading logs, timeline, diff, artifacts, graph, and audit");
967
+ const [runPayload, logsPayload, timelinePayload, diffResult] = await Promise.all([
968
+ getRunDetailsViaServer2(context, cleanRunId).catch((error) => ({ error: normalizeAppError(error).message })),
969
+ getRunLogsViaServer2(context, cleanRunId, { limit: 500 }).catch(() => []),
970
+ getRunTimelineViaServer2(context, cleanRunId, { limit: 500 }).catch(() => []),
971
+ gitDiff(projectRoot)
972
+ ]);
973
+ const run = runPayload && typeof runPayload === "object" && "run" in runPayload && runPayload.run && typeof runPayload.run === "object" ? runPayload.run : runPayload;
974
+ const logs = arrayFromPayload(logsPayload);
975
+ const timeline = arrayFromPayload(timelinePayload);
976
+ const taskId = stringField(run, ["taskId"]) || undefined;
977
+ const { artifacts, error: artifactsError } = listArtifacts(projectRoot, taskId);
978
+ const { graph, error: graphError } = loadGraph(projectRoot);
979
+ const audit = loadAudit(projectRoot);
980
+ const state = ctx.getState();
981
+ const selectedArtifact = typeof state.data?.inspectArtifact === "string" ? state.data.inspectArtifact : undefined;
982
+ const artifactView = selectedArtifact ? readArtifact(projectRoot, taskId, selectedArtifact) : undefined;
983
+ const snapshot = {
984
+ runId: cleanRunId,
985
+ status: stringField(run, ["status", "state"], "unknown"),
986
+ title: stringField(run, ["title", "taskId", "id"], "untitled run"),
987
+ logs,
988
+ timeline,
989
+ diff: diffResult.diff,
990
+ failures: [...logs, ...timeline].filter(isFailureEntry),
991
+ artifacts,
992
+ graph,
993
+ audit,
994
+ refreshedAt: Date.now(),
995
+ ...taskId ? { taskId } : {},
996
+ ...artifactView ? { artifactView } : {},
997
+ ...diffResult.error ? { diffError: diffResult.error } : {},
998
+ ...graphError ? { graphError } : {},
999
+ ...artifactsError ? { artifactsError } : {}
1000
+ };
1001
+ patchData(ctx, { inspect: snapshot, lastRefreshError: undefined });
1002
+ emitCompleted(ctx, label, {
1003
+ runId: cleanRunId,
1004
+ logs: logs.length,
1005
+ timeline: timeline.length,
1006
+ diffBytes: snapshot.diff.length,
1007
+ artifacts: artifacts.length,
1008
+ audit: audit.length
1009
+ });
1010
+ return snapshot;
1011
+ } catch (error) {
1012
+ emitFailed(ctx, label, error, { runId: cleanRunId });
1013
+ throw error;
1014
+ }
833
1015
  }
834
-
1016
+ var execFileAsync, INSPECT_VIEWS;
1017
+ var init_inspect = __esm(() => {
1018
+ execFileAsync = promisify(execFile);
1019
+ INSPECT_VIEWS = [
1020
+ "logs",
1021
+ "timeline",
1022
+ "artifacts",
1023
+ "artifact",
1024
+ "diff",
1025
+ "failures",
1026
+ "graph",
1027
+ "audit"
1028
+ ];
1029
+ });
835
1030
  // packages/cli/src/app-opentui/pi-pty-host.ts
836
1031
  import { fileURLToPath } from "url";
837
1032
  import { basename } from "path";
@@ -983,9 +1178,11 @@ class PiPtyHost {
983
1178
  cols;
984
1179
  rows;
985
1180
  message = "starting bundled Pi";
1181
+ lastResizeError = null;
986
1182
  exitCode;
987
1183
  signal;
988
1184
  notifyTimer = null;
1185
+ lastStreamKey = "";
989
1186
  _disposed = false;
990
1187
  constructor(options) {
991
1188
  this.runId = options.runId;
@@ -1002,6 +1199,17 @@ class PiPtyHost {
1002
1199
  scrollback: 1000
1003
1200
  });
1004
1201
  this.registerTerminalResponders();
1202
+ this.disposables.push(this.terminal.onWriteParsed(() => {
1203
+ if (this._disposed)
1204
+ return;
1205
+ const snapshot = this.createSnapshot();
1206
+ const key = snapshot.lines.join(`
1207
+ `);
1208
+ if (key === this.lastStreamKey)
1209
+ return;
1210
+ this.lastStreamKey = key;
1211
+ this.onSnapshot?.(snapshot);
1212
+ }));
1005
1213
  }
1006
1214
  get disposed() {
1007
1215
  return this._disposed;
@@ -1088,7 +1296,10 @@ class PiPtyHost {
1088
1296
  this.terminal.resize(nextCols, nextRows);
1089
1297
  try {
1090
1298
  this.pty?.resize(nextCols, nextRows);
1091
- } catch {}
1299
+ this.lastResizeError = null;
1300
+ } catch (error) {
1301
+ this.lastResizeError = error instanceof Error ? error.message : String(error);
1302
+ }
1092
1303
  this.emitSnapshotSoon(0);
1093
1304
  }
1094
1305
  detach() {
@@ -1130,7 +1341,7 @@ class PiPtyHost {
1130
1341
  return;
1131
1342
  const text = this.decoder.decode(data, { stream: true });
1132
1343
  this.respondToRawTerminalQueries(text);
1133
- this.terminal.write(data, () => this.emitSnapshotSoon());
1344
+ this.terminal.write(data);
1134
1345
  }
1135
1346
  emitSnapshotSoon(delayMs = SNAPSHOT_DELAY_MS) {
1136
1347
  if (this._disposed || this.notifyTimer)
@@ -1159,6 +1370,7 @@ class PiPtyHost {
1159
1370
  lines.push("");
1160
1371
  styledLines[lines.length - 1] = new StyledText([{ __isChunk: true, text: "" }]);
1161
1372
  }
1373
+ const resolvedMessage = this.lastResizeError ? `resize degraded: ${this.lastResizeError}` : message;
1162
1374
  return {
1163
1375
  runId: this.runId,
1164
1376
  status: this.status,
@@ -1166,7 +1378,8 @@ class PiPtyHost {
1166
1378
  rows: this.rows,
1167
1379
  lines,
1168
1380
  styledLines,
1169
- message,
1381
+ message: resolvedMessage,
1382
+ ...this.lastResizeError ? { resizeError: this.lastResizeError } : {},
1170
1383
  ...this.exitCode !== undefined ? { exitCode: this.exitCode } : {},
1171
1384
  ...this.signal !== undefined ? { signal: this.signal } : {}
1172
1385
  };
@@ -1236,7 +1449,7 @@ async function refreshFleet(ctx, options = {}) {
1236
1449
  const entry = normalizeRunRecord(run);
1237
1450
  return entry ? [entry] : [];
1238
1451
  });
1239
- patchData(ctx, { fleet: { runs: normalized, refreshedAt: new Date().toISOString() } });
1452
+ patchData(ctx, { fleet: { runs: normalized, refreshedAt: new Date().toISOString() }, lastRefreshError: undefined });
1240
1453
  patchFooter(ctx, { server: serverLabel, runs: String(normalized.length) });
1241
1454
  emitCompleted(ctx, label, { count: normalized.length });
1242
1455
  return normalized;
@@ -1247,25 +1460,31 @@ async function refreshFleet(ctx, options = {}) {
1247
1460
  }
1248
1461
 
1249
1462
  // packages/cli/src/app-opentui/adapters/pi-attach.ts
1250
- function createPiAttachAdapter(renderer) {
1251
- return {
1252
- id: "pi-attach",
1253
- async handleIntent(runtime, intent) {
1254
- if (intent.scene !== "handoff" && intent.action.kind !== "run-attach")
1255
- return false;
1256
- const ctx = adapterContextFromRuntime(runtime, renderer);
1257
- const runId = payloadString(intent, "runId") ?? runtime.getState().data.selectedRunId;
1258
- if (intent.action.kind === "run-attach" && runId) {
1259
- await attachRunWithBundledPi(ctx, runId);
1260
- return true;
1261
- }
1262
- if (runId) {
1263
- await preparePiAttachHandoff(ctx, runId);
1264
- return true;
1265
- }
1266
- return false;
1267
- }
1268
- };
1463
+ function currentAttachState(ctx, runId) {
1464
+ const value = ctx.getState().data?.piAttach;
1465
+ if (!value || typeof value !== "object" || Array.isArray(value))
1466
+ return null;
1467
+ const state = value;
1468
+ return state.runId === runId ? state : null;
1469
+ }
1470
+ function recordStep(ctx, runId, label, emitLabel) {
1471
+ const now = Date.now();
1472
+ const previous = currentAttachState(ctx, runId);
1473
+ const priorSteps = previous?.steps ?? [];
1474
+ const settled = priorSteps.map((step) => step.status === "running" ? { ...step, status: "done", endedAtMs: step.endedAtMs ?? now } : step);
1475
+ const steps = [...settled, { label, startedAtMs: now, status: "running" }];
1476
+ patchData(ctx, {
1477
+ piAttach: { ...previous ?? { runId, status: "preparing", message: label }, runId, steps }
1478
+ });
1479
+ emitProgress(ctx, emitLabel, label);
1480
+ }
1481
+ function settleSteps(ctx, runId) {
1482
+ const now = Date.now();
1483
+ const previous = currentAttachState(ctx, runId);
1484
+ if (!previous?.steps)
1485
+ return;
1486
+ const steps = previous.steps.map((step) => step.status === "running" ? { ...step, status: "done", endedAtMs: step.endedAtMs ?? now } : step);
1487
+ patchData(ctx, { piAttach: { ...previous, steps } });
1269
1488
  }
1270
1489
  async function preparePiAttachHandoff(ctx, runId) {
1271
1490
  const cleanRunId = runId.trim();
@@ -1274,7 +1493,7 @@ async function preparePiAttachHandoff(ctx, runId) {
1274
1493
  try {
1275
1494
  const { getRunDetailsViaServer: getRunDetailsViaServer2, getRunPiStatusViaServer: getRunPiStatusViaServer2, resolveServerConnectionLabel: resolveServerConnectionLabel2 } = await Promise.resolve().then(() => (init__server_client(), exports__server_client));
1276
1495
  const projectRoot = projectRootOf(ctx);
1277
- emitProgress(ctx, label, "checking run and Pi session readiness");
1496
+ recordStep(ctx, cleanRunId, "connecting to server \xB7 checking run and Pi session readiness", label);
1278
1497
  const [run, piStatus, server] = await Promise.all([
1279
1498
  getRunDetailsViaServer2({ projectRoot }, cleanRunId).catch((error) => ({ error: error instanceof Error ? error.message : String(error) })),
1280
1499
  getRunPiStatusViaServer2({ projectRoot }, cleanRunId).catch((error) => ({ error: error instanceof Error ? error.message : String(error) })),
@@ -1285,13 +1504,21 @@ async function preparePiAttachHandoff(ctx, runId) {
1285
1504
  status: "preparing",
1286
1505
  message: "ready to enter worker Pi",
1287
1506
  result: { run, piStatus, server },
1288
- ready: true
1507
+ ready: true,
1508
+ ...currentAttachState(ctx, cleanRunId)?.steps ? { steps: currentAttachState(ctx, cleanRunId).steps } : {}
1289
1509
  };
1290
- patchData(ctx, { piAttach: state });
1510
+ patchData(ctx, { piAttach: state, lastRefreshError: undefined });
1291
1511
  emitCompleted(ctx, label, { runId: cleanRunId, server });
1292
1512
  return state;
1293
1513
  } catch (error) {
1294
- const state = { runId: cleanRunId, status: "failed", message: error instanceof Error ? error.message : String(error) };
1514
+ const reason = error instanceof Error ? error.message : String(error);
1515
+ const state = {
1516
+ runId: cleanRunId,
1517
+ status: "failed",
1518
+ message: reason,
1519
+ failureReason: reason,
1520
+ ...currentAttachState(ctx, cleanRunId)?.steps ? { steps: currentAttachState(ctx, cleanRunId).steps } : {}
1521
+ };
1295
1522
  patchData(ctx, { piAttach: state });
1296
1523
  emitFailed(ctx, label, error, { runId: cleanRunId });
1297
1524
  throw error;
@@ -1306,32 +1533,52 @@ async function attachRunWithBundledPi(ctx, runId) {
1306
1533
  throw error;
1307
1534
  }
1308
1535
  emitStarted(ctx, label, { piAttach: { runId: cleanRunId, status: "entering-pi" } });
1309
- patchData(ctx, { piAttach: { runId: cleanRunId, status: "entering-pi", message: "starting bundled Pi" } });
1536
+ patchData(ctx, { piAttach: { runId: cleanRunId, status: "entering-pi", message: "starting bundled Pi", steps: [] } });
1310
1537
  try {
1311
- await preparePiAttachHandoff(ctx, cleanRunId).catch(() => null);
1312
- emitProgress(ctx, label, "starting Bun PTY host for bundled Pi");
1538
+ recordStep(ctx, cleanRunId, "connecting to server and fetching run transcript", label);
1539
+ await preparePiAttachHandoff(ctx, cleanRunId).catch((error) => {
1540
+ patchData(ctx, { lastRefreshError: normalizeAppError(error).message });
1541
+ return null;
1542
+ });
1543
+ recordStep(ctx, cleanRunId, "spawning Bun PTY host for bundled Pi", label);
1313
1544
  const projectRoot = projectRootOf(ctx);
1314
1545
  const cols = typeof process.stdout.columns === "number" ? process.stdout.columns : 100;
1315
1546
  const rows = Math.max(1, (typeof process.stdout.rows === "number" ? process.stdout.rows : 35) - 1);
1316
1547
  const patchTerminal = (snapshot) => {
1548
+ const previousSteps = currentAttachState(ctx, cleanRunId)?.steps;
1549
+ const failed = snapshot.status === "failed";
1550
+ const reason = failed ? snapshot.message ?? `bundled Pi exited with code ${snapshot.exitCode ?? "unknown"}` : undefined;
1317
1551
  patchData(ctx, {
1318
1552
  piTerminal: snapshot,
1319
1553
  piAttach: {
1320
1554
  runId: cleanRunId,
1321
- status: snapshot.status === "failed" ? "failed" : snapshot.status === "exited" ? "returned" : "entering-pi",
1555
+ status: failed ? "failed" : snapshot.status === "exited" ? "returned" : "entering-pi",
1322
1556
  message: snapshot.message ?? "bundled Pi running",
1323
- result: { runId: cleanRunId, status: snapshot.status, exitCode: snapshot.exitCode }
1557
+ result: { runId: cleanRunId, status: snapshot.status, exitCode: snapshot.exitCode },
1558
+ ...previousSteps ? { steps: previousSteps } : {},
1559
+ ...reason ? { failureReason: reason } : {}
1324
1560
  }
1325
1561
  });
1326
1562
  };
1563
+ recordStep(ctx, cleanRunId, "starting bundled Pi", label);
1327
1564
  await startPiPtyHost({
1328
1565
  runId: cleanRunId,
1329
1566
  projectRoot,
1330
1567
  cols,
1331
1568
  rows,
1332
- onSnapshot: patchTerminal,
1569
+ onSnapshot: (snapshot) => {
1570
+ if (snapshot.status === "running" && snapshot.lines.some((entry) => entry.trim().length > 0)) {
1571
+ settleSteps(ctx, cleanRunId);
1572
+ }
1573
+ patchTerminal(snapshot);
1574
+ },
1333
1575
  onExit: (snapshot) => {
1334
1576
  patchTerminal(snapshot);
1577
+ if (snapshot.status === "failed") {
1578
+ Promise.resolve().then(() => (init_inspect(), exports_inspect)).then(({ loadInspect: loadInspect2 }) => loadInspect2(ctx, cleanRunId)).catch(() => null);
1579
+ emitFailed(ctx, label, new Error(snapshot.message ?? `bundled Pi exited with code ${snapshot.exitCode ?? "unknown"}`), { runId: cleanRunId, result: { status: snapshot.status, exitCode: snapshot.exitCode } });
1580
+ return;
1581
+ }
1335
1582
  emitCompleted(ctx, label, { runId: cleanRunId, result: { status: snapshot.status, exitCode: snapshot.exitCode } });
1336
1583
  ctx.emit({ type: "scene.change", scene: "fleet", intent: { scene: "fleet", argv: ["runs"], action: { kind: "refresh", label: "Opening runs" } } });
1337
1584
  refreshFleet(ctx).catch((error) => emitFailed(ctx, "Refresh runs", error));
@@ -1349,13 +1596,21 @@ async function attachRunWithBundledPi(ctx, runId) {
1349
1596
  emitProgress(ctx, label, "bundled Pi is running", { piAttach: { runId: cleanRunId, status: "entering-pi", message: "bundled Pi running", result: record } });
1350
1597
  return record;
1351
1598
  } catch (error) {
1352
- patchData(ctx, { piAttach: { runId: cleanRunId, status: "failed", message: error instanceof Error ? error.message : String(error) } });
1599
+ const reason = error instanceof Error ? error.message : String(error);
1600
+ patchData(ctx, {
1601
+ piAttach: {
1602
+ runId: cleanRunId,
1603
+ status: "failed",
1604
+ message: reason,
1605
+ failureReason: reason,
1606
+ ...currentAttachState(ctx, cleanRunId)?.steps ? { steps: currentAttachState(ctx, cleanRunId).steps } : {}
1607
+ }
1608
+ });
1353
1609
  emitFailed(ctx, label, error, { runId: cleanRunId });
1354
1610
  throw error;
1355
1611
  }
1356
1612
  }
1357
1613
  export {
1358
1614
  preparePiAttachHandoff,
1359
- createPiAttachAdapter,
1360
1615
  attachRunWithBundledPi
1361
1616
  };
@@ -0,0 +1,23 @@
1
+ import { type AppAdapterContext } from "./common";
2
+ export type AppPiProjectPackage = {
3
+ readonly source: string;
4
+ readonly managedByRigConfig: boolean;
5
+ };
6
+ /** One npm search hit from `rig pi search` (details.results[]): a real
7
+ * installable package, rendered as a selectable row that installs inline. */
8
+ export type AppPiSearchResult = {
9
+ readonly name: string;
10
+ readonly version: string;
11
+ readonly description: string;
12
+ };
13
+ export type AppPiSnapshot = {
14
+ readonly projectPackages: readonly AppPiProjectPackage[];
15
+ readonly userPackages: readonly string[];
16
+ /** The term the current search results were produced for (undefined = no
17
+ * search has run this session). */
18
+ readonly searchTerm?: string;
19
+ /** Results of the last `rig pi search` — each is installable inline. */
20
+ readonly searchResults: readonly AppPiSearchResult[];
21
+ readonly refreshedAt: number;
22
+ };
23
+ export declare function refreshPi(ctx: AppAdapterContext): Promise<AppPiSnapshot>;