@h-rig/cli 0.0.6-alpha.82 → 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 (206) hide show
  1. package/dist/bin/build-rig-binaries.js +40 -8
  2. package/dist/bin/rig.js +23402 -14577
  3. package/dist/src/app/board.js +217 -41
  4. package/dist/src/app-opentui/adapters/command.d.ts +2 -0
  5. package/dist/src/app-opentui/adapters/command.js +329 -0
  6. package/dist/src/app-opentui/adapters/common.js +2 -2
  7. package/dist/src/app-opentui/adapters/doctor.d.ts +0 -2
  8. package/dist/src/app-opentui/adapters/doctor.js +64 -39
  9. package/dist/src/app-opentui/adapters/family.d.ts +62 -0
  10. package/dist/src/app-opentui/adapters/family.js +14305 -0
  11. package/dist/src/app-opentui/adapters/fleet.d.ts +0 -2
  12. package/dist/src/app-opentui/adapters/fleet.js +90 -60
  13. package/dist/src/app-opentui/adapters/inbox.d.ts +12 -2
  14. package/dist/src/app-opentui/adapters/inbox.js +137 -78
  15. package/dist/src/app-opentui/adapters/init.d.ts +0 -2
  16. package/dist/src/app-opentui/adapters/init.js +85 -47
  17. package/dist/src/app-opentui/adapters/inspect.d.ts +52 -0
  18. package/dist/src/app-opentui/adapters/inspect.js +1024 -0
  19. package/dist/src/app-opentui/adapters/pi-attach.d.ts +15 -6
  20. package/dist/src/app-opentui/adapters/pi-attach.js +442 -125
  21. package/dist/src/app-opentui/adapters/pi.d.ts +23 -0
  22. package/dist/src/app-opentui/adapters/pi.js +363 -0
  23. package/dist/src/app-opentui/adapters/plugin.d.ts +84 -0
  24. package/dist/src/app-opentui/adapters/plugin.js +544 -0
  25. package/dist/src/app-opentui/adapters/repo.d.ts +37 -0
  26. package/dist/src/app-opentui/adapters/repo.js +186 -0
  27. package/dist/src/app-opentui/adapters/run-detail.d.ts +9 -2
  28. package/dist/src/app-opentui/adapters/run-detail.js +180 -63
  29. package/dist/src/app-opentui/adapters/server.d.ts +10 -2
  30. package/dist/src/app-opentui/adapters/server.js +191 -45
  31. package/dist/src/app-opentui/adapters/tasks.d.ts +11 -2
  32. package/dist/src/app-opentui/adapters/tasks.js +1123 -143
  33. package/dist/src/app-opentui/adapters/workspace.d.ts +49 -0
  34. package/dist/src/app-opentui/adapters/workspace.js +333 -0
  35. package/dist/src/app-opentui/autocomplete.d.ts +20 -0
  36. package/dist/src/app-opentui/autocomplete.js +576 -0
  37. package/dist/src/app-opentui/bootstrap.d.ts +1 -6
  38. package/dist/src/app-opentui/bootstrap.js +25252 -16474
  39. package/dist/src/app-opentui/command-palette.d.ts +3 -0
  40. package/dist/src/app-opentui/command-palette.js +1010 -0
  41. package/dist/src/app-opentui/command-pty-host.d.ts +62 -0
  42. package/dist/src/app-opentui/command-pty-host.js +248 -0
  43. package/dist/src/app-opentui/drone.js +8 -6
  44. package/dist/src/app-opentui/events.js +1 -1
  45. package/dist/src/app-opentui/fleet-stats.d.ts +32 -0
  46. package/dist/src/app-opentui/fleet-stats.js +114 -0
  47. package/dist/src/app-opentui/focus-manager.d.ts +14 -0
  48. package/dist/src/app-opentui/focus-manager.js +24 -0
  49. package/dist/src/app-opentui/index.js +5431 -2797
  50. package/dist/src/app-opentui/intent.js +179 -50
  51. package/dist/src/app-opentui/keymap.d.ts +21 -0
  52. package/dist/src/app-opentui/keymap.js +1748 -0
  53. package/dist/src/app-opentui/launch-routing.d.ts +16 -0
  54. package/dist/src/app-opentui/launch-routing.js +55 -0
  55. package/dist/src/app-opentui/layout.d.ts +7 -0
  56. package/dist/src/app-opentui/layout.js +13 -6
  57. package/dist/src/app-opentui/list-search.d.ts +24 -0
  58. package/dist/src/app-opentui/list-search.js +88 -1
  59. package/dist/src/app-opentui/pi-host-child.js +99 -17
  60. package/dist/src/app-opentui/pi-pty-host.d.ts +14 -0
  61. package/dist/src/app-opentui/pi-pty-host.js +30 -14
  62. package/dist/src/app-opentui/react/App.d.ts +9 -0
  63. package/dist/src/app-opentui/react/App.js +5144 -0
  64. package/dist/src/app-opentui/react/Backdrop.d.ts +5 -0
  65. package/dist/src/app-opentui/react/Backdrop.js +1834 -0
  66. package/dist/src/app-opentui/react/ChromeHost.d.ts +5 -0
  67. package/dist/src/app-opentui/react/ChromeHost.js +2942 -0
  68. package/dist/src/app-opentui/react/SceneFrameView.d.ts +7 -0
  69. package/dist/src/app-opentui/react/SceneFrameView.js +529 -0
  70. package/dist/src/app-opentui/react/context.d.ts +17 -0
  71. package/dist/src/app-opentui/react/context.js +37 -0
  72. package/dist/src/app-opentui/react/launch.d.ts +2 -0
  73. package/dist/src/app-opentui/react/launch.js +5743 -0
  74. package/dist/src/app-opentui/react/nav.d.ts +18 -0
  75. package/dist/src/app-opentui/react/nav.js +54 -0
  76. package/dist/src/app-opentui/react/scroll.d.ts +12 -0
  77. package/dist/src/app-opentui/react/scroll.js +21 -0
  78. package/dist/src/app-opentui/react/syntax.d.ts +2 -0
  79. package/dist/src/app-opentui/react/syntax.js +64 -0
  80. package/dist/src/app-opentui/registry.js +20428 -4828
  81. package/dist/src/app-opentui/render/ascii-fleet.d.ts +15 -0
  82. package/dist/src/app-opentui/render/ascii-fleet.js +82 -0
  83. package/dist/src/app-opentui/render/constants.d.ts +31 -0
  84. package/dist/src/app-opentui/render/constants.js +66 -0
  85. package/dist/src/app-opentui/render/graphics.d.ts +2 -1
  86. package/dist/src/app-opentui/render/graphics.js +228 -46
  87. package/dist/src/app-opentui/render/image-visual-layer-worker.js +469 -930
  88. package/dist/src/app-opentui/render/image-visual-layer.d.ts +25 -10
  89. package/dist/src/app-opentui/render/image-visual-layer.js +448 -329
  90. package/dist/src/app-opentui/render/native-host.d.ts +37 -0
  91. package/dist/src/app-opentui/render/native-host.js +179 -0
  92. package/dist/src/app-opentui/render/panel-layout.d.ts +38 -0
  93. package/dist/src/app-opentui/render/panel-layout.js +48 -0
  94. package/dist/src/app-opentui/render/panels.d.ts +2 -0
  95. package/dist/src/app-opentui/render/panels.js +78 -38
  96. package/dist/src/app-opentui/render/preloader.d.ts +10 -0
  97. package/dist/src/app-opentui/render/preloader.js +165 -0
  98. package/dist/src/app-opentui/render/scene.d.ts +53 -1
  99. package/dist/src/app-opentui/render/scene.js +195 -6
  100. package/dist/src/app-opentui/render/text.d.ts +7 -1
  101. package/dist/src/app-opentui/render/text.js +15 -8
  102. package/dist/src/app-opentui/render/type-bar.d.ts +2 -1
  103. package/dist/src/app-opentui/render/type-bar.js +113 -39
  104. package/dist/src/app-opentui/runtime-resources.d.ts +16 -0
  105. package/dist/src/app-opentui/runtime-resources.js +62 -0
  106. package/dist/src/app-opentui/runtime.d.ts +44 -1
  107. package/dist/src/app-opentui/runtime.js +5415 -2738
  108. package/dist/src/app-opentui/scenes/command.d.ts +3 -0
  109. package/dist/src/app-opentui/scenes/command.js +117 -0
  110. package/dist/src/app-opentui/scenes/doctor.d.ts +2 -1
  111. package/dist/src/app-opentui/scenes/doctor.js +221 -17
  112. package/dist/src/app-opentui/scenes/error.js +60 -20
  113. package/dist/src/app-opentui/scenes/family-domains/agent.d.ts +2 -0
  114. package/dist/src/app-opentui/scenes/family-domains/agent.js +348 -0
  115. package/dist/src/app-opentui/scenes/family-domains/browser.d.ts +2 -0
  116. package/dist/src/app-opentui/scenes/family-domains/browser.js +195 -0
  117. package/dist/src/app-opentui/scenes/family-domains/dist.d.ts +2 -0
  118. package/dist/src/app-opentui/scenes/family-domains/dist.js +243 -0
  119. package/dist/src/app-opentui/scenes/family-domains/git.d.ts +2 -0
  120. package/dist/src/app-opentui/scenes/family-domains/git.js +195 -0
  121. package/dist/src/app-opentui/scenes/family-domains/github.d.ts +2 -0
  122. package/dist/src/app-opentui/scenes/family-domains/github.js +274 -0
  123. package/dist/src/app-opentui/scenes/family-domains/harness.d.ts +2 -0
  124. package/dist/src/app-opentui/scenes/family-domains/harness.js +152 -0
  125. package/dist/src/app-opentui/scenes/family-domains/index.d.ts +4 -0
  126. package/dist/src/app-opentui/scenes/family-domains/index.js +1679 -0
  127. package/dist/src/app-opentui/scenes/family-domains/kit.d.ts +76 -0
  128. package/dist/src/app-opentui/scenes/family-domains/kit.js +305 -0
  129. package/dist/src/app-opentui/scenes/family-domains/profile.d.ts +2 -0
  130. package/dist/src/app-opentui/scenes/family-domains/profile.js +212 -0
  131. package/dist/src/app-opentui/scenes/family-domains/queue.d.ts +2 -0
  132. package/dist/src/app-opentui/scenes/family-domains/queue.js +146 -0
  133. package/dist/src/app-opentui/scenes/family-domains/remote.d.ts +2 -0
  134. package/dist/src/app-opentui/scenes/family-domains/remote.js +518 -0
  135. package/dist/src/app-opentui/scenes/family-domains/review.d.ts +2 -0
  136. package/dist/src/app-opentui/scenes/family-domains/review.js +280 -0
  137. package/dist/src/app-opentui/scenes/family-domains/setup.d.ts +2 -0
  138. package/dist/src/app-opentui/scenes/family-domains/setup.js +267 -0
  139. package/dist/src/app-opentui/scenes/family-domains/stats.d.ts +2 -0
  140. package/dist/src/app-opentui/scenes/family-domains/stats.js +370 -0
  141. package/dist/src/app-opentui/scenes/family.d.ts +3 -0
  142. package/dist/src/app-opentui/scenes/family.js +2144 -0
  143. package/dist/src/app-opentui/scenes/fleet.js +552 -43
  144. package/dist/src/app-opentui/scenes/handoff.js +342 -35
  145. package/dist/src/app-opentui/scenes/help.js +640 -56
  146. package/dist/src/app-opentui/scenes/inbox.d.ts +2 -1
  147. package/dist/src/app-opentui/scenes/inbox.js +329 -21
  148. package/dist/src/app-opentui/scenes/init.d.ts +2 -1
  149. package/dist/src/app-opentui/scenes/init.js +120 -34
  150. package/dist/src/app-opentui/scenes/inspect.d.ts +3 -0
  151. package/dist/src/app-opentui/scenes/inspect.js +793 -0
  152. package/dist/src/app-opentui/scenes/main.d.ts +2 -1
  153. package/dist/src/app-opentui/scenes/main.js +264 -29
  154. package/dist/src/app-opentui/scenes/pi.d.ts +3 -0
  155. package/dist/src/app-opentui/scenes/pi.js +508 -0
  156. package/dist/src/app-opentui/scenes/plugin.d.ts +3 -0
  157. package/dist/src/app-opentui/scenes/plugin.js +486 -0
  158. package/dist/src/app-opentui/scenes/repo.d.ts +3 -0
  159. package/dist/src/app-opentui/scenes/repo.js +424 -0
  160. package/dist/src/app-opentui/scenes/run-detail.d.ts +2 -1
  161. package/dist/src/app-opentui/scenes/run-detail.js +362 -35
  162. package/dist/src/app-opentui/scenes/server.d.ts +2 -1
  163. package/dist/src/app-opentui/scenes/server.js +243 -26
  164. package/dist/src/app-opentui/scenes/tasks.js +518 -41
  165. package/dist/src/app-opentui/scenes/workspace.d.ts +3 -0
  166. package/dist/src/app-opentui/scenes/workspace.js +426 -0
  167. package/dist/src/app-opentui/selectable.d.ts +19 -0
  168. package/dist/src/app-opentui/selectable.js +79 -0
  169. package/dist/src/app-opentui/state.js +129 -36
  170. package/dist/src/app-opentui/surface-catalog.d.ts +20 -0
  171. package/dist/src/app-opentui/surface-catalog.js +540 -0
  172. package/dist/src/app-opentui/terminal-capabilities.d.ts +7 -0
  173. package/dist/src/app-opentui/terminal-capabilities.js +15 -0
  174. package/dist/src/app-opentui/theme.d.ts +28 -6
  175. package/dist/src/app-opentui/theme.js +61 -8
  176. package/dist/src/app-opentui/types.d.ts +130 -5
  177. package/dist/src/commands/_authority-runs.d.ts +1 -1
  178. package/dist/src/commands/_authority-runs.js +2 -12
  179. package/dist/src/commands/_doctor-checks.js +62 -13
  180. package/dist/src/commands/_help-catalog.js +95 -15
  181. package/dist/src/commands/_operator-view.js +97 -15
  182. package/dist/src/commands/_pi-frontend.d.ts +2 -0
  183. package/dist/src/commands/_pi-frontend.js +97 -13
  184. package/dist/src/commands/_preflight.js +64 -14
  185. package/dist/src/commands/_server-client.js +82 -18
  186. package/dist/src/commands/_server-events.d.ts +26 -0
  187. package/dist/src/commands/_server-events.js +310 -0
  188. package/dist/src/commands/_snapshot-upload.js +62 -13
  189. package/dist/src/commands/agent.js +2 -12
  190. package/dist/src/commands/connect.js +7 -1
  191. package/dist/src/commands/doctor.js +62 -13
  192. package/dist/src/commands/github.js +144 -23
  193. package/dist/src/commands/inbox.js +62 -13
  194. package/dist/src/commands/init.js +82 -18
  195. package/dist/src/commands/inspect.js +62 -13
  196. package/dist/src/commands/run.js +100 -15
  197. package/dist/src/commands/server.js +71 -26
  198. package/dist/src/commands/setup.js +62 -13
  199. package/dist/src/commands/stats.js +157 -28
  200. package/dist/src/commands/task-run-driver.js +64 -25
  201. package/dist/src/commands/task.js +196 -43
  202. package/dist/src/commands.js +419 -123
  203. package/dist/src/index.js +426 -130
  204. package/package.json +11 -10
  205. package/dist/src/app-opentui/render/image-visual-layer-native-canvas.d.ts +0 -2
  206. package/dist/src/app-opentui/render/image-visual-layer-native-canvas.js +0 -1484
@@ -15,6 +15,87 @@ 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
+ async function runtimeOf(ctx) {
26
+ if (ctx.rig && "runId" in ctx.rig && ctx.ensureRuntime === undefined) {
27
+ return ctx.rig;
28
+ }
29
+ if (ctx.ensureRuntime)
30
+ return ctx.ensureRuntime();
31
+ throw new Error("App adapter requires ensureRuntime() before this action can run.");
32
+ }
33
+ function normalizeAppError(error) {
34
+ const message = error instanceof Error ? error.message : String(error);
35
+ const rawHint = error instanceof Error ? error.hint : undefined;
36
+ return {
37
+ message,
38
+ ...typeof rawHint === "string" && rawHint.trim() ? { hint: rawHint.trim() } : {}
39
+ };
40
+ }
41
+ function emitStarted(ctx, label, optimistic) {
42
+ ctx.emit({ type: "action.started", label, ...optimistic ? { optimistic } : {} });
43
+ }
44
+ function emitProgress(ctx, label, detail, data) {
45
+ ctx.emit({ type: "action.progress", label, ...detail ? { detail } : {}, ...data ? { data } : {} });
46
+ }
47
+ function emitCompleted(ctx, label, data) {
48
+ ctx.emit({ type: "action.completed", label, ...data ? { data } : {} });
49
+ }
50
+ function emitFailed(ctx, label, error, data) {
51
+ if (data)
52
+ patchData(ctx, data);
53
+ const normalized = normalizeAppError(error);
54
+ ctx.emit({
55
+ type: "action.failed",
56
+ label,
57
+ message: normalized.message,
58
+ ...normalized.hint ? { hint: normalized.hint } : {},
59
+ cause: error
60
+ });
61
+ }
62
+ function patchData(ctx, data) {
63
+ ctx.emit({ type: "data.patch", data });
64
+ }
65
+ function patchFooter(ctx, footer) {
66
+ ctx.emit({ type: "footer.patch", footer });
67
+ }
68
+ function arrayFromPayload(value) {
69
+ if (Array.isArray(value)) {
70
+ return value.filter((entry) => Boolean(entry && typeof entry === "object" && !Array.isArray(entry)));
71
+ }
72
+ if (value && typeof value === "object" && !Array.isArray(value)) {
73
+ const entries = value.entries;
74
+ if (Array.isArray(entries))
75
+ return arrayFromPayload(entries);
76
+ }
77
+ return [];
78
+ }
79
+ function stringField(record, keys, fallback = "") {
80
+ for (const key of keys) {
81
+ const value = record[key];
82
+ if (typeof value === "string" && value.trim())
83
+ return value.trim();
84
+ if (typeof value === "number" && Number.isFinite(value))
85
+ return String(value);
86
+ }
87
+ return fallback;
88
+ }
89
+ function compactRecord(record, keys) {
90
+ const out = {};
91
+ for (const key of keys) {
92
+ const value = record[key];
93
+ if (value !== undefined && value !== null && value !== "")
94
+ out[key] = value;
95
+ }
96
+ return out;
97
+ }
98
+
18
99
  // packages/cli/src/runner.ts
19
100
  import { EventBus } from "@rig/runtime/control-plane/runtime/events";
20
101
  import { CliError as RuntimeCliError } from "@rig/runtime/control-plane/errors";
@@ -237,17 +318,21 @@ function setGitHubBearerTokenForCurrentProcess(token, projectRoot) {
237
318
  const scopedKey = resolve2(projectRoot ?? process.cwd());
238
319
  scopedGitHubBearerTokens.set(scopedKey, cleanToken(token ?? undefined));
239
320
  }
240
- function readPrivateRemoteSessionToken(projectRoot) {
321
+ function readRemoteAuthState(projectRoot) {
241
322
  const path = resolve2(projectRoot, ".rig", "state", "github-auth.json");
242
323
  if (!existsSync2(path))
243
324
  return null;
244
325
  try {
245
326
  const parsed = JSON.parse(readFileSync2(path, "utf8"));
246
- return cleanToken(typeof parsed.apiSessionToken === "string" ? parsed.apiSessionToken : typeof parsed.sessionToken === "string" ? parsed.sessionToken : undefined);
327
+ return parsed && typeof parsed === "object" && !Array.isArray(parsed) ? parsed : null;
247
328
  } catch {
248
329
  return null;
249
330
  }
250
331
  }
332
+ function readPrivateRemoteSessionToken(projectRoot) {
333
+ const parsed = readRemoteAuthState(projectRoot);
334
+ return parsed ? cleanToken(typeof parsed.apiSessionToken === "string" ? parsed.apiSessionToken : typeof parsed.sessionToken === "string" ? parsed.sessionToken : undefined) : null;
335
+ }
251
336
  function readGitHubBearerTokenForRemote(projectRoot) {
252
337
  const scopedKey = resolve2(projectRoot);
253
338
  if (scopedGitHubBearerTokens.has(scopedKey))
@@ -258,15 +343,25 @@ function readGitHubBearerTokenForRemote(projectRoot) {
258
343
  return cleanToken(process.env.RIG_SERVER_AUTH_TOKEN) ?? cleanToken(process.env.RIG_REMOTE_AUTH_TOKEN);
259
344
  }
260
345
  function readStoredGitHubAuthToken(projectRoot) {
261
- const path = resolve2(projectRoot, ".rig", "state", "github-auth.json");
262
- if (!existsSync2(path))
346
+ const parsed = readRemoteAuthState(projectRoot);
347
+ return parsed ? cleanToken(typeof parsed.token === "string" ? parsed.token : undefined) : null;
348
+ }
349
+ function inferRemoteServerProjectRootFromAuthState(projectRoot) {
350
+ const repo = readRepoConnection(projectRoot);
351
+ const slug = repo?.project?.trim();
352
+ if (!slug || !/^[^/]+\/[^/]+$/.test(slug))
263
353
  return null;
264
- try {
265
- const parsed = JSON.parse(readFileSync2(path, "utf8"));
266
- return cleanToken(typeof parsed.token === "string" ? parsed.token : undefined);
267
- } catch {
354
+ const auth = readRemoteAuthState(projectRoot);
355
+ const authUpdatedAt = typeof auth?.updatedAt === "string" ? Date.parse(auth.updatedAt) : Number.NaN;
356
+ const repoLinkedAt = typeof repo?.linkedAt === "string" ? Date.parse(repo.linkedAt) : Number.NaN;
357
+ if (Number.isFinite(authUpdatedAt) && Number.isFinite(repoLinkedAt) && authUpdatedAt + 1000 < repoLinkedAt)
268
358
  return null;
269
- }
359
+ const checkoutBaseDir = typeof auth?.checkoutBaseDir === "string" && auth.checkoutBaseDir.trim() ? auth.checkoutBaseDir.trim() : null;
360
+ if (!checkoutBaseDir)
361
+ return null;
362
+ const inferred = `${checkoutBaseDir.replace(/\/+$/, "")}/${slug}`;
363
+ writeRepoServerProjectRoot(projectRoot, inferred);
364
+ return inferred;
270
365
  }
271
366
  function readLocalConnectionFallbackToken(projectRoot) {
272
367
  return readGitHubBearerTokenForRemote(projectRoot) ?? cleanToken(process.env.RIG_GITHUB_TOKEN) ?? readStoredGitHubAuthToken(projectRoot);
@@ -277,7 +372,7 @@ async function ensureServerForCli(projectRoot) {
277
372
  if (selected?.connection.kind === "remote") {
278
373
  reportServerPhase(`Connecting to ${selected.alias}\u2026`);
279
374
  const authToken = readGitHubBearerTokenForRemote(projectRoot);
280
- const serverProjectRoot = selected.serverProjectRoot ?? await backfillRemoteServerProjectRoot(projectRoot, selected.connection.baseUrl, authToken);
375
+ const serverProjectRoot = selected.serverProjectRoot ?? inferRemoteServerProjectRootFromAuthState(projectRoot) ?? await backfillRemoteServerProjectRoot(projectRoot, selected.connection.baseUrl, authToken);
281
376
  return {
282
377
  baseUrl: selected.connection.baseUrl,
283
378
  authToken,
@@ -306,7 +401,10 @@ async function backfillRemoteServerProjectRoot(projectRoot, baseUrl, authToken)
306
401
  if (!slug)
307
402
  return null;
308
403
  try {
309
- const response = await fetch(`${baseUrl}/api/projects/${encodeURIComponent(slug)}`, {
404
+ const url = new URL(`${baseUrl}/api/projects/${encodeURIComponent(slug)}`);
405
+ if (authToken && queryAuthFallbackEnabled())
406
+ url.searchParams.set("rt", authToken);
407
+ const response = await fetch(url, {
310
408
  headers: mergeHeaders(undefined, authToken)
311
409
  });
312
410
  if (!response.ok)
@@ -333,10 +431,23 @@ function appendTaskFilterParams(url, filters) {
333
431
  if (filters.limit !== undefined)
334
432
  url.searchParams.set("limit", String(filters.limit));
335
433
  }
434
+ function mergeCookie(existing, name, value) {
435
+ const encoded = `${name}=${encodeURIComponent(value)}`;
436
+ if (!existing?.trim())
437
+ return encoded;
438
+ const parts = existing.split(";").map((part) => part.trim()).filter((part) => part && !part.startsWith(`${name}=`));
439
+ return [...parts, encoded].join("; ");
440
+ }
441
+ function queryAuthFallbackEnabled(env = process.env) {
442
+ return env.RIG_ENABLE_QUERY_AUTH_FALLBACK === "1" || env.RIG_QUERY_AUTH_FALLBACK === "1";
443
+ }
336
444
  function mergeHeaders(headers, authToken) {
337
445
  const merged = new Headers(headers);
338
446
  if (authToken) {
339
- merged.set("authorization", `Bearer ${authToken}`);
447
+ const bearer = `Bearer ${authToken}`;
448
+ merged.set("authorization", bearer);
449
+ merged.set("x-auth", bearer);
450
+ merged.set("cookie", mergeCookie(merged.get("cookie"), "rig_auth", authToken));
340
451
  }
341
452
  return merged;
342
453
  }
@@ -396,15 +507,34 @@ async function buildServerFailureContext(projectRoot, server) {
396
507
  hint: "Check the selected server with `rig server status`, or switch with `rig server use <alias|local>`."
397
508
  };
398
509
  }
510
+ function isLoopbackRemoteBaseUrl(baseUrl) {
511
+ try {
512
+ const host = new URL(baseUrl).hostname.toLowerCase();
513
+ return host === "localhost" || host === "127.0.0.1" || host === "::1" || host === "[::1]";
514
+ } catch {
515
+ return false;
516
+ }
517
+ }
518
+ function canUseRemoteWithoutProjectRoot(pathname) {
519
+ return pathname === "/health" || pathname === "/api/health" || pathname === "/api/server/status" || pathname === "/api/server/project-root" || pathname.startsWith("/api/github/auth/") || pathname === "/api/projects" || pathname.startsWith("/api/projects/");
520
+ }
399
521
  async function requestServerJson(context, pathname, init = {}) {
400
522
  const server = await ensureServerForCli(context.projectRoot);
523
+ const requestUrl = new URL(`${server.baseUrl}${pathname}`);
524
+ if (server.connectionKind === "remote" && !server.serverProjectRoot && !canUseRemoteWithoutProjectRoot(requestUrl.pathname) && (server.authToken || !isLoopbackRemoteBaseUrl(server.baseUrl))) {
525
+ const repo = readRepoConnection(context.projectRoot);
526
+ throw new CliError(`Remote server is selected for ${repo?.project ?? "this repo"}, but this checkout has no server-host project root link.`, 1, { hint: "Run `rig init --repair` or `rig init --yes --repo owner/repo --server remote` to repair the remote project link before task/run commands." });
527
+ }
401
528
  const headers = mergeHeaders(init.headers, server.authToken);
402
529
  if (server.serverProjectRoot)
403
530
  headers.set("x-rig-project-root", server.serverProjectRoot);
531
+ if (server.connectionKind === "remote" && server.authToken && queryAuthFallbackEnabled()) {
532
+ requestUrl.searchParams.set("rt", server.authToken);
533
+ }
404
534
  reportServerPhase(`${(init.method ?? "GET").toUpperCase()} ${pathname.split("?")[0]}\u2026`);
405
535
  let response;
406
536
  try {
407
- response = await fetch(`${server.baseUrl}${pathname}`, {
537
+ response = await fetch(requestUrl, {
408
538
  ...init,
409
539
  headers
410
540
  });
@@ -466,11 +596,26 @@ async function getGitHubAuthStatusViaServer(context) {
466
596
  return payload && typeof payload === "object" && !Array.isArray(payload) ? payload : {};
467
597
  }
468
598
  async function postGitHubTokenViaServer(context, token, options = {}) {
469
- const payload = await requestServerJson(context, "/api/github/auth/token", {
470
- method: "POST",
471
- headers: { "content-type": "application/json" },
472
- body: JSON.stringify({ token, selectedRepo: options.selectedRepo, projectRoot: options.projectRoot })
473
- });
599
+ const server = await ensureServerForCli(context.projectRoot);
600
+ const requestUrl = new URL(`${server.baseUrl}/api/github/auth/token`);
601
+ reportServerPhase("POST /api/github/auth/token\u2026");
602
+ let response;
603
+ try {
604
+ response = await fetch(requestUrl, {
605
+ method: "POST",
606
+ headers: { "content-type": "application/json" },
607
+ body: JSON.stringify({ token, selectedRepo: options.selectedRepo, projectRoot: options.projectRoot ?? server.serverProjectRoot ?? undefined })
608
+ });
609
+ } catch (error) {
610
+ const failure = await buildServerFailureContext(context.projectRoot, server);
611
+ throw new CliError(`Rig server auth bootstrap failed: ${error instanceof Error ? error.message : String(error)}
612
+ ${failure.contextLine}`, 1, { hint: failure.hint });
613
+ }
614
+ const payload = await response.json().catch(() => null);
615
+ if (!response.ok) {
616
+ const detail = payload && typeof payload === "object" && !Array.isArray(payload) ? String(payload.error ?? JSON.stringify(payload)) : `HTTP ${response.status}`;
617
+ throw new CliError(`Rig server auth bootstrap failed (${response.status}): ${detail}`, 1, { hint: "Re-run `rig github auth import-gh`, or check the selected server with `rig server status`." });
618
+ }
474
619
  return payload && typeof payload === "object" && !Array.isArray(payload) ? payload : {};
475
620
  }
476
621
  async function prepareRemoteCheckoutViaServer(context, input) {
@@ -978,18 +1123,8 @@ import {
978
1123
  readJsonlFile,
979
1124
  writeAuthorityRunRecord
980
1125
  } from "@rig/runtime/control-plane/authority-files";
981
- function normalizeRuntimeAdapter(value) {
982
- const normalized = value?.trim().toLowerCase();
983
- if (!normalized) {
984
- return "pi";
985
- }
986
- if (normalized === "codex" || normalized === "codex-cli" || normalized === "codex-app-server" || normalized === "gpt-codex") {
987
- return "codex";
988
- }
989
- if (normalized === "pi" || normalized === "rig-pi" || normalized === "@rig/pi") {
990
- return "pi";
991
- }
992
- return "claude-code";
1126
+ function normalizeRuntimeAdapter(_value) {
1127
+ return "pi";
993
1128
  }
994
1129
  var init__authority_runs = __esm(() => {
995
1130
  init__paths();
@@ -1107,7 +1242,8 @@ async function runFastTaskRunPreflight(context, options = {}) {
1107
1242
  }
1108
1243
  }
1109
1244
  const repo = readRepoConnection(context.projectRoot);
1110
- checks.push(repo ? preflightCheck("project-link", "project linked to Rig server", repo.project ? "pass" : "warn", `${repo.selected}${repo.project ? ` -> ${repo.project}` : ""}`, "Run `rig init --yes --repo owner/repo` to record the GitHub repo slug.") : preflightCheck("project-link", "project linked to Rig server", legacyServerCompatibility ? "warn" : "fail", "missing .rig/state/connection.json", "Run `rig init` or `rig server use <alias|local>`."));
1245
+ const remoteWithoutProjectLink = selectedServer?.connectionKind === "remote" && !repo?.project;
1246
+ checks.push(repo ? preflightCheck("project-link", "project linked to selected server", repo.project ? "pass" : remoteWithoutProjectLink ? "fail" : "warn", `${repo.selected}${repo.project ? ` -> ${repo.project}` : " (missing project link)"}`, remoteWithoutProjectLink ? "Run `rig init --repair` or `rig init --yes --repo owner/repo --server remote` to restore the remote project link." : "Run `rig init --yes --repo owner/repo` to record the GitHub repo slug.") : preflightCheck("project-link", "project linked to selected server", legacyServerCompatibility ? "warn" : "fail", "missing .rig/state/connection.json", "Run `rig init` or `rig server use <alias|local>`."));
1111
1247
  try {
1112
1248
  const auth = await request("/api/github/auth/status");
1113
1249
  checks.push(isAuthenticated(auth) ? preflightCheck("github-auth", "GitHub auth valid", "pass") : preflightCheck("github-auth", "GitHub auth valid", legacyServerCompatibility ? "warn" : "fail", "not authenticated", "Run `rig github auth import-gh` or `rig github auth token --token <token>`."));
@@ -1610,6 +1746,23 @@ function buildOperatorPiEnv(input) {
1610
1746
  ...input.serverProjectRoot ? { RIG_PROJECT_ROOT: input.serverProjectRoot } : {}
1611
1747
  };
1612
1748
  }
1749
+ function shouldRequireOperatorTranscript(status) {
1750
+ return typeof status === "string" && TERMINAL_RUN_STATUSES.has(status.trim().toLowerCase());
1751
+ }
1752
+ function missingOperatorTranscriptMessage(runId, status) {
1753
+ const shortRun = runId.trim().slice(0, 8) || "unknown";
1754
+ const statusText = typeof status === "string" && status.trim() ? status.trim() : "terminal";
1755
+ return `Run ${shortRun} is ${statusText}, but no worker Pi session transcript is recorded for this run. It likely failed before Pi started or predates full-session capture. Use \`rig run show ${runId}\` and \`rig run logs ${runId}\` for the lifecycle details.`;
1756
+ }
1757
+ function statusFromRunDetails(run) {
1758
+ if (!run || typeof run !== "object" || Array.isArray(run))
1759
+ return;
1760
+ const record = run;
1761
+ if (typeof record.status === "string")
1762
+ return record.status;
1763
+ const nested = record.run;
1764
+ return nested && typeof nested === "object" && !Array.isArray(nested) ? nested.status : undefined;
1765
+ }
1613
1766
  async function prepareOperatorConsole(context, runId, tempSessionDir) {
1614
1767
  const server = await ensureServerForCli(context.projectRoot);
1615
1768
  const localCwd = join(homedir2(), ".rig", "drones", runId.slice(0, 8));
@@ -1639,6 +1792,13 @@ async function prepareOperatorConsole(context, runId, tempSessionDir) {
1639
1792
  sessionFileArg = ["--session", localSessionPath];
1640
1793
  }
1641
1794
  } catch {}
1795
+ if (sessionFileArg.length === 0) {
1796
+ const run = await getRunDetailsViaServer(context, runId).catch(() => null);
1797
+ const status = statusFromRunDetails(run);
1798
+ if (shouldRequireOperatorTranscript(status)) {
1799
+ throw new OperatorTranscriptUnavailableError(missingOperatorTranscriptMessage(runId, status));
1800
+ }
1801
+ }
1642
1802
  return { server, sessionFileArg };
1643
1803
  }
1644
1804
  function trustDroneCwd(localCwd) {
@@ -1682,6 +1842,7 @@ async function attachRunBundledPiFrontend(context, input) {
1682
1842
  let detached = false;
1683
1843
  try {
1684
1844
  await runPiMain([
1845
+ "--offline",
1685
1846
  "--no-extensions",
1686
1847
  "--no-skills",
1687
1848
  "--no-prompt-templates",
@@ -1709,10 +1870,17 @@ async function attachRunBundledPiFrontend(context, input) {
1709
1870
  rendered: "stock Pi operator console with the pi-rig extension"
1710
1871
  };
1711
1872
  }
1712
- var RIG_PI_THEME;
1873
+ var TERMINAL_RUN_STATUSES, OperatorTranscriptUnavailableError, RIG_PI_THEME;
1713
1874
  var init__pi_frontend = __esm(() => {
1714
1875
  init__server_client();
1715
1876
  init__async_ui();
1877
+ TERMINAL_RUN_STATUSES = new Set(["completed", "failed", "stopped", "cancelled", "canceled", "closed", "merged", "needs_attention", "needs-attention"]);
1878
+ OperatorTranscriptUnavailableError = class OperatorTranscriptUnavailableError extends Error {
1879
+ constructor(message2) {
1880
+ super(message2);
1881
+ this.name = "OperatorTranscriptUnavailableError";
1882
+ }
1883
+ };
1716
1884
  RIG_PI_THEME = {
1717
1885
  $schema: "https://raw.githubusercontent.com/earendil-works/pi/main/packages/coding-agent/src/modes/interactive/theme/theme-schema.json",
1718
1886
  name: "rig",
@@ -1867,7 +2035,7 @@ async function attachRunOperatorView(context, input) {
1867
2035
  }
1868
2036
  const pollMs = Math.max(250, Math.trunc(input.pollMs ?? 2000));
1869
2037
  let timelineCursor = snapshot.timelineCursor;
1870
- while (!detached && !TERMINAL_RUN_STATUSES.has(runStatusFromPayload(snapshot.run))) {
2038
+ while (!detached && !TERMINAL_RUN_STATUSES2.has(runStatusFromPayload(snapshot.run))) {
1871
2039
  await Bun.sleep(pollMs);
1872
2040
  snapshot = await readOperatorSnapshot(context, input.runId, { timelineCursor });
1873
2041
  timelineCursor = snapshot.timelineCursor;
@@ -1879,13 +2047,13 @@ async function attachRunOperatorView(context, input) {
1879
2047
  }
1880
2048
  return { ...snapshot, steered, detached };
1881
2049
  }
1882
- var TERMINAL_RUN_STATUSES;
2050
+ var TERMINAL_RUN_STATUSES2;
1883
2051
  var init__operator_view = __esm(() => {
1884
2052
  init__server_client();
1885
2053
  init__operator_surface();
1886
2054
  init__pi_frontend();
1887
2055
  init__async_ui();
1888
- TERMINAL_RUN_STATUSES = new Set(["completed", "failed", "stopped", "cancelled", "canceled", "closed", "merged", "needs_attention", "needs-attention"]);
2056
+ TERMINAL_RUN_STATUSES2 = new Set(["completed", "failed", "stopped", "cancelled", "canceled", "closed", "merged", "needs_attention", "needs-attention"]);
1889
2057
  });
1890
2058
 
1891
2059
  // packages/cli/src/commands/_cli-format.ts
@@ -2415,30 +2583,62 @@ var init__help_catalog = __esm(() => {
2415
2583
  }
2416
2584
  ];
2417
2585
  ADVANCED_GROUPS = [
2418
- { name: "setup", summary: "Bootstrap/check local setup.", usage: ["rig setup <bootstrap|check|preflight>"], commands: [{ command: "bootstrap|check|preflight", description: "Setup helpers." }] },
2419
- { name: "profile", summary: "Runtime profile/model defaults.", usage: ["rig profile <show|set>"], commands: [{ command: "show", description: "Show active profile." }] },
2586
+ {
2587
+ name: "setup",
2588
+ summary: "Bootstrap/check local setup.",
2589
+ usage: ["rig setup <bootstrap|check|preflight>"],
2590
+ commands: [
2591
+ { command: "bootstrap", description: "Bootstrap local setup dependencies.", primary: true },
2592
+ { command: "check", description: "Check local setup state (toolchain, deps, config).", primary: true },
2593
+ { command: "preflight", description: "Run preflight checks before a run.", primary: true }
2594
+ ]
2595
+ },
2596
+ {
2597
+ name: "profile",
2598
+ summary: "Runtime profile/model defaults.",
2599
+ usage: ["rig profile <show|set>"],
2600
+ commands: [
2601
+ { command: "show", description: "Show the active execution profile.", primary: true },
2602
+ { command: "set [--model <model>] [--runtime <runtime>] [--plugin <plugin>]", description: "Set model/runtime/plugin profile defaults.", primary: true }
2603
+ ]
2604
+ },
2420
2605
  {
2421
2606
  name: "review",
2422
2607
  summary: "Inspect or change completion review gate policy.",
2423
2608
  usage: ["rig review <show|set>"],
2424
2609
  commands: [
2425
2610
  { command: "show", description: "Show current review gate settings." },
2426
- { command: "set <off|advisory|required> [--provider greptile]", description: "Change review strictness/provider." }
2611
+ { command: "set <off|advisory|required> [--provider <provider>]", description: "Change review strictness/provider (e.g. --provider greptile)." }
2427
2612
  ],
2428
2613
  examples: ["rig review show", "rig review set required --provider greptile"],
2429
2614
  next: ["Use `rig inbox approvals` for blocked run handoffs."]
2430
2615
  },
2431
2616
  {
2432
2617
  name: "browser",
2433
- summary: "Browser/app diagnostics for browser-required tasks.",
2434
- usage: ["rig browser <help|explain|demo|app|hp-next> [options]"],
2618
+ summary: "Browser workstation actions for browser-required tasks: launch, check, CDP probe, profile reset, and app-specific smokes.",
2619
+ usage: ["rig browser <help|explain|demo|hp-next|cdp-probe|profile-persistence|profile-lock-check|smoke-test> [options]"],
2435
2620
  commands: [
2436
- { command: "help", description: "Rich browser command help (canonical: `rig browser help`)." },
2437
- { command: "explain", description: "Explain the browser-required task contract." },
2438
- { command: "demo", description: "Run browser demo flows against a local page." },
2439
- { command: "app", description: "Launch the Rig Browser workstation app." },
2440
- { command: "hp-next <dev|check|e2e|reset>", description: "Drive the hp-next browser test harness." }
2441
- ]
2621
+ { command: "help", description: "Rich browser command help (canonical: `rig browser help`).", primary: true },
2622
+ { command: "explain", description: "Explain the browser-required task contract and runtime helper commands.", primary: true },
2623
+ { command: "demo [--port <n>] [--profile <name>] [--state-dir <path>] [--target-url <url>] [--keep-open] [--no-build]", description: "Run a guided real browser/agent demo flow.", primary: true },
2624
+ { command: "hp-next dev", description: "Start hp-next Rig Browser in dev/watch mode.", primary: true },
2625
+ { command: "hp-next start", description: "Build/start the hp-next browser workstation without watch mode.", primary: true },
2626
+ { command: "hp-next check", description: "Validate the hp-next CDP endpoint on the configured port.", primary: true },
2627
+ { command: "hp-next e2e", description: "Run hp-next browser e2e smoke checks through Rig Browser.", primary: true },
2628
+ { command: "hp-next reset", description: "Reset the hp-next browser profile when stale browser state is suspected.", primary: true },
2629
+ { command: "cdp-probe", description: "Run an isolated CDP attach/evaluate/layout probe." },
2630
+ { command: "profile-persistence", description: "Verify a named browser profile persists browser state." },
2631
+ { command: "profile-lock-check", description: "Verify concurrent launches reject the same profile." },
2632
+ { command: "smoke-test", description: "Run the browser package smoke test." }
2633
+ ],
2634
+ examples: [
2635
+ "rig browser help",
2636
+ "rig browser hp-next check",
2637
+ "rig browser hp-next e2e",
2638
+ "rig browser cdp-probe",
2639
+ "rig browser profile-lock-check"
2640
+ ],
2641
+ next: ["Inside a task run, use rig-browser-launch, rig-browser-check, and rig-browser-attach-info from the worker PATH."]
2442
2642
  },
2443
2643
  {
2444
2644
  name: "pi",
@@ -2453,12 +2653,60 @@ var init__help_catalog = __esm(() => {
2453
2653
  examples: ["rig pi search subagents", "rig pi add pi-subagents", "rig pi list"],
2454
2654
  next: ["Config-managed extensions: declare `runtime: { pi: { packages: [...] } }` in rig.config.ts \u2014 workers pick them up automatically."]
2455
2655
  },
2456
- { name: "queue", summary: "Run task queues locally.", usage: ["rig queue run [options]"], commands: [{ command: "run", description: "Process queue work." }] },
2457
- { name: "agent", summary: "Runtime agent workspace helpers.", usage: ["rig agent <list|prepare|run|cleanup>"], commands: [{ command: "list", description: "List prepared agents." }] },
2656
+ {
2657
+ name: "queue",
2658
+ summary: "Run task queues locally.",
2659
+ usage: ["rig queue run [--workers <n>] [--max-tasks <n>] [--action validate|verify|pipeline] [--isolation off|worktree] [--no-runtime-reuse] [--fail-fast] [--skip-project-sync]"],
2660
+ commands: [
2661
+ { command: "run [--workers <n>] [--max-tasks <n>] [--action validate|verify|pipeline] [--isolation off|worktree] [--no-runtime-reuse] [--fail-fast] [--skip-project-sync]", description: "Process queue work: drain matching tasks with N workers, optional per-task action and isolation.", primary: true }
2662
+ ]
2663
+ },
2664
+ {
2665
+ name: "agent",
2666
+ summary: "Runtime agent workspace helpers.",
2667
+ usage: ["rig agent <list|prepare|run|cleanup>"],
2668
+ commands: [
2669
+ { command: "list", description: "List prepared agent runtimes.", primary: true },
2670
+ { command: "prepare [--id <id>] [--mode <mode>] [--task <task>]", description: "Prepare an isolated agent runtime workspace.", primary: true },
2671
+ { command: "run [--id <id>] [--mode <mode>] [--task <task>] [--skip-project-sync]", description: "Prepare (if needed) and run an agent in its workspace.", primary: true },
2672
+ { command: "cleanup [--all] [--id <id>]", description: "Remove prepared agent workspaces.", primary: true }
2673
+ ]
2674
+ },
2458
2675
  { name: "inspector", summary: "Event stream and drift scanners.", usage: ["rig inspector <stream|scan-upstream-drift>"], commands: [{ command: "stream", description: "Stream events." }] },
2459
- { name: "dist", summary: "Build/install packaged Rig CLI.", usage: ["rig dist <build|install|doctor>"], commands: [{ command: "build", description: "Build distribution." }] },
2676
+ {
2677
+ name: "dist",
2678
+ summary: "Build/install packaged Rig CLI.",
2679
+ usage: ["rig dist <build|install|doctor|rebuild-agent>"],
2680
+ commands: [
2681
+ { command: "build [--output-dir <dir>]", description: "Build the distributable Rig CLI.", primary: true },
2682
+ { command: "install [--scope <scope>] [--path <path>]", description: "Install the built CLI to a scope/path.", primary: true },
2683
+ { command: "doctor", description: "Diagnose the dist toolchain and install state.", primary: true },
2684
+ { command: "rebuild-agent", description: "Rebuild the agent runtime image, pruning stale images.", primary: true }
2685
+ ]
2686
+ },
2460
2687
  { name: "workspace", summary: "Workspace topology/service helpers.", usage: ["rig workspace <summary|topology|remote-hosts>"], commands: [{ command: "summary", description: "Show workspace summary." }] },
2461
- { name: "remote", summary: "Compatibility remote orchestration controls.", usage: ["rig remote <status|watch|pause|resume|...>"], commands: [{ command: "status", description: "Show remote state." }] },
2688
+ {
2689
+ name: "remote",
2690
+ summary: "Compatibility remote orchestration controls.",
2691
+ usage: ["rig remote <status|tasks|watch|pause|resume|continue|stop|refresh|add-iterations|remove-iterations|endpoint ...>"],
2692
+ commands: [
2693
+ { command: "status [--remote <alias>]", description: "Show the remote orchestration state.", primary: true },
2694
+ { command: "tasks [--remote <alias>]", description: "List the remote's tracked tasks.", primary: true },
2695
+ { command: "watch [--remote <alias>] [--seconds <n>] [--event <type>]", description: "Stream remote orchestration events.", primary: true },
2696
+ { command: "pause [--remote <alias>]", description: "Pause the running remote orchestration.", primary: true },
2697
+ { command: "resume [--remote <alias>] [--max-workers <n>] [--max-iterations <n>] [--direct-merge]", description: "Resume the remote with optional tuning.", primary: true },
2698
+ { command: "continue [--remote <alias>]", description: "Continue a paused remote orchestration.", primary: true },
2699
+ { command: "stop [--remote <alias>]", description: "Stop the remote orchestration.", primary: true },
2700
+ { command: "refresh [--remote <alias>]", description: "Refresh remote state.", primary: true },
2701
+ { command: "add-iterations [--count <n>] [--remote <alias>]", description: "Grant the remote more iterations.", primary: true },
2702
+ { command: "remove-iterations [--count <n>] [--remote <alias>]", description: "Reduce the remote's iteration budget.", primary: true },
2703
+ { command: "endpoint list", description: "List configured remote endpoints.", primary: true },
2704
+ { command: "endpoint add --alias <alias> --host <host> --port <n> [--token <token>]", description: "Register a remote endpoint.", primary: true },
2705
+ { command: "endpoint remove --alias <alias>", description: "Remove a remote endpoint.", primary: true },
2706
+ { command: "endpoint test [--alias <alias>]", description: "Test connectivity to a remote endpoint.", primary: true },
2707
+ { command: "endpoint doctor", description: "Diagnose remote endpoint configuration.", primary: true }
2708
+ ]
2709
+ },
2462
2710
  { name: "git", summary: "Pass through to Rig git-flow helper.", usage: ["rig git <args...>"], commands: [{ command: "<args...>", description: "Advanced git flow operations." }] },
2463
2711
  { name: "harness", summary: "Pass through to runtime harness CLI.", usage: ["rig harness <args...>"], commands: [{ command: "<args...>", description: "Advanced harness operations." }] },
2464
2712
  { name: "test", summary: "Project test wrappers.", usage: ["rig test <unit|e2e|all>"], commands: [{ command: "all", description: "Run configured project tests." }] }
@@ -2990,116 +3238,794 @@ var init_task = __esm(() => {
2990
3238
  init__connection_state();
2991
3239
  });
2992
3240
 
2993
- // packages/cli/src/app-opentui/adapters/tasks.ts
2994
- import { randomUUID } from "crypto";
2995
-
2996
- // packages/cli/src/app-opentui/adapters/common.ts
2997
- function adapterContextFromRuntime(runtime, renderer) {
3241
+ // packages/cli/src/app-opentui/pi-pty-host.ts
3242
+ import { fileURLToPath } from "url";
3243
+ import { basename } from "path";
3244
+ import { RGBA, StyledText, TextAttributes } from "@opentui/core";
3245
+ import { Terminal as XtermTerminal } from "@xterm/headless";
3246
+ function clampCols(cols) {
3247
+ return Math.max(MIN_COLS, Math.trunc(cols || 100));
3248
+ }
3249
+ function clampRows(rows) {
3250
+ return Math.max(MIN_ROWS, Math.min(MAX_ROWS, Math.trunc(rows || 35)));
3251
+ }
3252
+ function rgbaFromXtermColor(mode, value) {
3253
+ if (mode === 1 || mode === 2 || mode === XTERM_COLOR_MODE_INDEXED_16 || mode === XTERM_COLOR_MODE_INDEXED_256) {
3254
+ return RGBA.fromIndex(value);
3255
+ }
3256
+ if (mode === 3 || mode === XTERM_COLOR_MODE_RGB) {
3257
+ return RGBA.fromInts(value >> 16 & 255, value >> 8 & 255, value & 255);
3258
+ }
3259
+ return;
3260
+ }
3261
+ function textAttributesFromCell(cell) {
3262
+ let attributes = TextAttributes.NONE;
3263
+ if (cell.isBold())
3264
+ attributes |= TextAttributes.BOLD;
3265
+ if (cell.isDim())
3266
+ attributes |= TextAttributes.DIM;
3267
+ if (cell.isItalic())
3268
+ attributes |= TextAttributes.ITALIC;
3269
+ if (cell.isUnderline())
3270
+ attributes |= TextAttributes.UNDERLINE;
3271
+ if (cell.isBlink())
3272
+ attributes |= TextAttributes.BLINK;
3273
+ if (cell.isInverse())
3274
+ attributes |= TextAttributes.INVERSE;
3275
+ if (cell.isInvisible())
3276
+ attributes |= TextAttributes.HIDDEN;
3277
+ if (cell.isStrikethrough())
3278
+ attributes |= TextAttributes.STRIKETHROUGH;
3279
+ return attributes;
3280
+ }
3281
+ function sameRgba(a, b) {
3282
+ if (!a && !b)
3283
+ return true;
3284
+ return Boolean(a && b && a.equals(b));
3285
+ }
3286
+ function sameStyle(a, b) {
3287
+ return sameRgba(a.fg, b.fg) && sameRgba(a.bg, b.bg) && (a.attributes ?? 0) === (b.attributes ?? 0);
3288
+ }
3289
+ function styleFromCell(cell) {
2998
3290
  return {
2999
- projectRoot: runtime.getState().projectRoot,
3000
- renderer,
3001
- ensureRuntime: () => runtime.getRunnerContext(),
3002
- emit: (event) => runtime.emit(event),
3003
- getState: () => runtime.getState()
3291
+ fg: rgbaFromXtermColor(cell.getFgColorMode(), cell.getFgColor()),
3292
+ bg: rgbaFromXtermColor(cell.getBgColorMode(), cell.getBgColor()),
3293
+ attributes: textAttributesFromCell(cell)
3004
3294
  };
3005
3295
  }
3006
- function projectRootOf(ctx) {
3007
- const root = ctx.rig?.projectRoot ?? ctx.projectRoot ?? ctx.getState().projectRoot;
3008
- if (typeof root === "string" && root.trim())
3009
- return root;
3010
- throw new Error("Rig app adapter requires a projectRoot.");
3011
- }
3012
- async function runtimeOf(ctx) {
3013
- if (ctx.rig && "runId" in ctx.rig && ctx.ensureRuntime === undefined) {
3014
- return ctx.rig;
3296
+ function lineToStyledText(line, cols) {
3297
+ if (!line)
3298
+ return new StyledText([{ __isChunk: true, text: "" }]);
3299
+ const chunks = [];
3300
+ let run = "";
3301
+ let runStyle = null;
3302
+ const flush = () => {
3303
+ if (!run)
3304
+ return;
3305
+ chunks.push({
3306
+ __isChunk: true,
3307
+ text: run,
3308
+ ...runStyle?.fg ? { fg: runStyle.fg } : {},
3309
+ ...runStyle?.bg ? { bg: runStyle.bg } : {},
3310
+ ...(runStyle?.attributes ?? 0) !== 0 ? { attributes: runStyle.attributes } : {}
3311
+ });
3312
+ run = "";
3313
+ };
3314
+ for (let index = 0;index < cols; index += 1) {
3315
+ const cell = line.getCell(index);
3316
+ if (!cell) {
3317
+ if (runStyle !== null)
3318
+ flush();
3319
+ runStyle = null;
3320
+ run += " ";
3321
+ continue;
3322
+ }
3323
+ if (cell.getWidth() === 0)
3324
+ continue;
3325
+ const style = styleFromCell(cell);
3326
+ if (!runStyle || !sameStyle(runStyle, style)) {
3327
+ flush();
3328
+ runStyle = style;
3329
+ }
3330
+ run += cell.getChars() || " ";
3015
3331
  }
3016
- if (ctx.ensureRuntime)
3017
- return ctx.ensureRuntime();
3018
- throw new Error("Rig app adapter requires ensureRuntime() before this action can run.");
3332
+ flush();
3333
+ return new StyledText(chunks.length > 0 ? chunks : [{ __isChunk: true, text: "" }]);
3334
+ }
3335
+ function childCommandPrefix() {
3336
+ const execName = basename(process.execPath).toLowerCase();
3337
+ const currentEntry = process.argv[1];
3338
+ if (execName === "bun" || execName === "bun.exe") {
3339
+ return currentEntry ? [process.execPath, currentEntry, "__opentui-pi-host"] : [process.execPath, fallbackChildScriptPath];
3340
+ }
3341
+ return [process.execPath, "__opentui-pi-host"];
3019
3342
  }
3020
- function normalizeAppError(error) {
3021
- const message = error instanceof Error ? error.message : String(error);
3022
- const rawHint = error instanceof Error ? error.hint : undefined;
3343
+ function withEnv(base) {
3344
+ const env = {};
3345
+ for (const [key, value] of Object.entries(base ?? process.env)) {
3346
+ if (key === "RIG_PLAIN" || key === "RIG_CLI_PLAIN_HELP" || key === "NO_COLOR")
3347
+ continue;
3348
+ if (typeof value === "string")
3349
+ env[key] = value;
3350
+ }
3023
3351
  return {
3024
- message,
3025
- ...typeof rawHint === "string" && rawHint.trim() ? { hint: rawHint.trim() } : {}
3352
+ ...env,
3353
+ TERM: "xterm-256color",
3354
+ COLORTERM: "truecolor",
3355
+ FORCE_COLOR: env.FORCE_COLOR ?? "1",
3356
+ RIG_OPENTUI_PI_HOST: "1"
3026
3357
  };
3027
3358
  }
3028
- function emitStarted(ctx, label, optimistic) {
3029
- ctx.emit({ type: "action.started", label, ...optimistic ? { optimistic } : {} });
3359
+ async function startPiPtyHost(options) {
3360
+ activeHost?.dispose("replace");
3361
+ const host = new PiPtyHost(options);
3362
+ activeHost = host;
3363
+ await host.start();
3364
+ return host;
3030
3365
  }
3031
- function emitProgress(ctx, label, detail, data) {
3032
- ctx.emit({ type: "action.progress", label, ...detail ? { detail } : {}, ...data ? { data } : {} });
3366
+
3367
+ class PiPtyHost {
3368
+ runId;
3369
+ projectRoot;
3370
+ onSnapshot;
3371
+ onExit;
3372
+ onError;
3373
+ terminal;
3374
+ disposables = [];
3375
+ decoder = new TextDecoder("utf-8");
3376
+ proc = null;
3377
+ pty = null;
3378
+ status = "starting";
3379
+ cols;
3380
+ rows;
3381
+ message = "starting bundled Pi";
3382
+ lastResizeError = null;
3383
+ exitCode;
3384
+ signal;
3385
+ notifyTimer = null;
3386
+ lastStreamKey = "";
3387
+ _disposed = false;
3388
+ constructor(options) {
3389
+ this.runId = options.runId;
3390
+ this.projectRoot = options.projectRoot;
3391
+ this.cols = clampCols(options.cols);
3392
+ this.rows = clampRows(options.rows);
3393
+ this.onSnapshot = options.onSnapshot;
3394
+ this.onExit = options.onExit;
3395
+ this.onError = options.onError;
3396
+ this.terminal = new XtermTerminal({
3397
+ allowProposedApi: true,
3398
+ cols: this.cols,
3399
+ rows: this.rows,
3400
+ scrollback: 1000
3401
+ });
3402
+ this.registerTerminalResponders();
3403
+ this.disposables.push(this.terminal.onWriteParsed(() => {
3404
+ if (this._disposed)
3405
+ return;
3406
+ const snapshot = this.createSnapshot();
3407
+ const key = snapshot.lines.join(`
3408
+ `);
3409
+ if (key === this.lastStreamKey)
3410
+ return;
3411
+ this.lastStreamKey = key;
3412
+ this.onSnapshot?.(snapshot);
3413
+ }));
3414
+ }
3415
+ get disposed() {
3416
+ return this._disposed;
3417
+ }
3418
+ get snapshot() {
3419
+ return this.createSnapshot();
3420
+ }
3421
+ async start() {
3422
+ if (this._disposed)
3423
+ throw new Error("Pi PTY host is disposed.");
3424
+ if (typeof Bun.Terminal !== "function") {
3425
+ throw new Error("Bun native PTY is unavailable in this runtime. Pi host requires Bun.Terminal.");
3426
+ }
3427
+ const spawnOptions = {
3428
+ cwd: this.projectRoot,
3429
+ env: withEnv(process.env),
3430
+ terminal: {
3431
+ cols: this.cols,
3432
+ rows: this.rows,
3433
+ name: "xterm-256color",
3434
+ data: (_terminal, data) => this.handlePtyData(data)
3435
+ }
3436
+ };
3437
+ const proc = Bun.spawn([
3438
+ ...childCommandPrefix(),
3439
+ "--run-id",
3440
+ this.runId,
3441
+ "--project-root",
3442
+ this.projectRoot
3443
+ ], spawnOptions);
3444
+ if (!proc.terminal)
3445
+ throw new Error("Bun did not attach a terminal to the bundled Pi child process.");
3446
+ this.proc = proc;
3447
+ this.pty = proc.terminal;
3448
+ this.status = "running";
3449
+ this.message = "bundled Pi running inside this app";
3450
+ this.emitSnapshotSoon(0);
3451
+ proc.exited.then((exitCode) => {
3452
+ if (this._disposed)
3453
+ return;
3454
+ this.status = exitCode === 0 ? "exited" : "failed";
3455
+ this.exitCode = exitCode;
3456
+ this.signal = null;
3457
+ this.message = exitCode === 0 ? "bundled Pi exited" : `bundled Pi exited with code ${exitCode}`;
3458
+ const snapshot = this.createSnapshot();
3459
+ this.onSnapshot?.(snapshot);
3460
+ this.onExit?.(snapshot);
3461
+ if (activeHost === this)
3462
+ activeHost = null;
3463
+ this.dispose("exit", { kill: false, notify: false });
3464
+ }).catch((error) => {
3465
+ if (this._disposed)
3466
+ return;
3467
+ this.status = "failed";
3468
+ this.message = error instanceof Error ? error.message : String(error);
3469
+ const snapshot = this.createSnapshot();
3470
+ this.onSnapshot?.(snapshot);
3471
+ this.onError?.(error, snapshot);
3472
+ if (activeHost === this)
3473
+ activeHost = null;
3474
+ this.dispose("error", { kill: false, notify: false });
3475
+ });
3476
+ }
3477
+ write(data) {
3478
+ if (this._disposed || !this.pty)
3479
+ return;
3480
+ try {
3481
+ this.pty.write(data);
3482
+ } catch (error) {
3483
+ this.status = "failed";
3484
+ this.message = error instanceof Error ? error.message : String(error);
3485
+ const snapshot = this.createSnapshot();
3486
+ this.onSnapshot?.(snapshot);
3487
+ this.onError?.(error, snapshot);
3488
+ }
3489
+ }
3490
+ resize(cols, rows) {
3491
+ const nextCols = clampCols(cols);
3492
+ const nextRows = clampRows(rows);
3493
+ if (nextCols === this.cols && nextRows === this.rows)
3494
+ return;
3495
+ this.cols = nextCols;
3496
+ this.rows = nextRows;
3497
+ this.terminal.resize(nextCols, nextRows);
3498
+ try {
3499
+ this.pty?.resize(nextCols, nextRows);
3500
+ this.lastResizeError = null;
3501
+ } catch (error) {
3502
+ this.lastResizeError = error instanceof Error ? error.message : String(error);
3503
+ }
3504
+ this.emitSnapshotSoon(0);
3505
+ }
3506
+ detach() {
3507
+ this.dispose("detach");
3508
+ return this.createSnapshot("detached from bundled Pi");
3509
+ }
3510
+ dispose(reason = "dispose", options = {}) {
3511
+ if (this._disposed)
3512
+ return;
3513
+ this._disposed = true;
3514
+ if (this.notifyTimer)
3515
+ clearTimeout(this.notifyTimer);
3516
+ this.notifyTimer = null;
3517
+ for (const disposable of this.disposables.splice(0)) {
3518
+ try {
3519
+ disposable.dispose();
3520
+ } catch {}
3521
+ }
3522
+ if (options.kill !== false) {
3523
+ try {
3524
+ this.proc?.kill("SIGTERM");
3525
+ } catch {}
3526
+ }
3527
+ try {
3528
+ this.pty?.close();
3529
+ } catch {}
3530
+ try {
3531
+ this.terminal.dispose();
3532
+ } catch {}
3533
+ if (activeHost === this)
3534
+ activeHost = null;
3535
+ if (options.notify) {
3536
+ this.message = reason;
3537
+ this.onSnapshot?.(this.createSnapshot(reason));
3538
+ }
3539
+ }
3540
+ handlePtyData(data) {
3541
+ if (this._disposed)
3542
+ return;
3543
+ const text = this.decoder.decode(data, { stream: true });
3544
+ this.respondToRawTerminalQueries(text);
3545
+ this.terminal.write(data);
3546
+ }
3547
+ emitSnapshotSoon(delayMs = SNAPSHOT_DELAY_MS) {
3548
+ if (this._disposed || this.notifyTimer)
3549
+ return;
3550
+ this.notifyTimer = setTimeout(() => {
3551
+ this.notifyTimer = null;
3552
+ if (this._disposed)
3553
+ return;
3554
+ this.onSnapshot?.(this.createSnapshot());
3555
+ }, delayMs);
3556
+ }
3557
+ createSnapshot(message2 = this.message) {
3558
+ const buffer = this.terminal.buffer.active;
3559
+ const end = buffer.length;
3560
+ const start = Math.max(0, end - MAX_SNAPSHOT_LINES);
3561
+ const styledStart = Math.max(start, end - this.rows - STYLED_SNAPSHOT_MARGIN);
3562
+ const lines = [];
3563
+ const styledLines = [];
3564
+ for (let row = start;row < end; row += 1) {
3565
+ const line = buffer.getLine(row);
3566
+ lines.push((line?.translateToString(false) ?? "").slice(0, this.cols));
3567
+ if (row >= styledStart)
3568
+ styledLines[lines.length - 1] = lineToStyledText(line, this.cols);
3569
+ }
3570
+ while (lines.length < this.rows) {
3571
+ lines.push("");
3572
+ styledLines[lines.length - 1] = new StyledText([{ __isChunk: true, text: "" }]);
3573
+ }
3574
+ const resolvedMessage = this.lastResizeError ? `resize degraded: ${this.lastResizeError}` : message2;
3575
+ return {
3576
+ runId: this.runId,
3577
+ status: this.status,
3578
+ cols: this.cols,
3579
+ rows: this.rows,
3580
+ lines,
3581
+ styledLines,
3582
+ message: resolvedMessage,
3583
+ ...this.lastResizeError ? { resizeError: this.lastResizeError } : {},
3584
+ ...this.exitCode !== undefined ? { exitCode: this.exitCode } : {},
3585
+ ...this.signal !== undefined ? { signal: this.signal } : {}
3586
+ };
3587
+ }
3588
+ registerTerminalResponders() {
3589
+ this.disposables.push(this.terminal.parser.registerCsiHandler({ final: "c" }, (params) => {
3590
+ if (params.length === 0 || params[0] === 0)
3591
+ this.write("\x1B[?62;22c");
3592
+ return false;
3593
+ }));
3594
+ this.disposables.push(this.terminal.parser.registerCsiHandler({ prefix: ">", final: "c" }, (params) => {
3595
+ if (params.length === 0 || params[0] === 0)
3596
+ this.write("\x1B[>0;0;0c");
3597
+ return false;
3598
+ }));
3599
+ this.disposables.push(this.terminal.parser.registerCsiHandler({ final: "n" }, (params) => {
3600
+ if (params[0] === 5) {
3601
+ this.write("\x1B[0n");
3602
+ } else if (params[0] === 6) {
3603
+ const row = this.terminal.buffer.active.cursorY + 1;
3604
+ const col = this.terminal.buffer.active.cursorX + 1;
3605
+ this.write(`\x1B[${row};${col}R`);
3606
+ }
3607
+ return false;
3608
+ }));
3609
+ }
3610
+ respondToRawTerminalQueries(text) {
3611
+ if (!text)
3612
+ return;
3613
+ const decrqm = /\x1b\[\?(\d+)\$p/g;
3614
+ let match;
3615
+ while ((match = decrqm.exec(text)) !== null) {
3616
+ this.write(`\x1B[?${match[1]};2$y`);
3617
+ }
3618
+ }
3033
3619
  }
3034
- function emitCompleted(ctx, label, data) {
3035
- ctx.emit({ type: "action.completed", label, ...data ? { data } : {} });
3620
+ var MIN_COLS = 40, MIN_ROWS = 12, MAX_ROWS = 300, MAX_SNAPSHOT_LINES = 360, STYLED_SNAPSHOT_MARGIN = 6, SNAPSHOT_DELAY_MS = 120, fallbackChildScriptPath, activeHost = null, XTERM_COLOR_MODE_INDEXED_16 = 16777216, XTERM_COLOR_MODE_INDEXED_256 = 33554432, XTERM_COLOR_MODE_RGB = 50331648;
3621
+ var init_pi_pty_host = __esm(() => {
3622
+ fallbackChildScriptPath = fileURLToPath(new URL("./pi-host-child.ts", import.meta.url));
3623
+ });
3624
+
3625
+ // packages/cli/src/app-opentui/adapters/fleet.ts
3626
+ function normalizeRunRecord(record) {
3627
+ const runId = stringField(record, ["runId", "id"]);
3628
+ if (!runId)
3629
+ return null;
3630
+ const title = stringField(record, ["title", "taskTitle", "taskId"], "untitled run");
3631
+ const taskId3 = stringField(record, ["taskId"]);
3632
+ const updatedAt = stringField(record, ["updatedAt", "createdAt"]);
3633
+ return {
3634
+ runId,
3635
+ status: stringField(record, ["status", "state"], "unknown"),
3636
+ title,
3637
+ raw: record,
3638
+ ...taskId3 ? { taskId: taskId3 } : {},
3639
+ ...updatedAt ? { updatedAt } : {}
3640
+ };
3036
3641
  }
3037
- function emitFailed(ctx, label, error, data) {
3038
- if (data)
3039
- patchData(ctx, data);
3040
- const normalized = normalizeAppError(error);
3041
- ctx.emit({
3042
- type: "action.failed",
3043
- label,
3044
- message: normalized.message,
3045
- ...normalized.hint ? { hint: normalized.hint } : {},
3046
- cause: error
3047
- });
3642
+ async function refreshFleet(ctx, options = {}) {
3643
+ const label = "Refreshing fleet";
3644
+ emitStarted(ctx, label);
3645
+ try {
3646
+ const { listRunsViaServer: listRunsViaServer2, resolveServerConnectionLabel: resolveServerConnectionLabel2 } = await Promise.resolve().then(() => (init__server_client(), exports__server_client));
3647
+ const projectRoot = projectRootOf(ctx);
3648
+ emitProgress(ctx, label, "contacting selected server");
3649
+ const [runs, serverLabel] = await Promise.all([
3650
+ listRunsViaServer2({ projectRoot }, { limit: options.limit ?? 50 }),
3651
+ resolveServerConnectionLabel2(projectRoot)
3652
+ ]);
3653
+ const normalized = runs.flatMap((run) => {
3654
+ const entry = normalizeRunRecord(run);
3655
+ return entry ? [entry] : [];
3656
+ });
3657
+ patchData(ctx, { fleet: { runs: normalized, refreshedAt: new Date().toISOString() }, lastRefreshError: undefined });
3658
+ patchFooter(ctx, { server: serverLabel, runs: String(normalized.length) });
3659
+ emitCompleted(ctx, label, { count: normalized.length });
3660
+ return normalized;
3661
+ } catch (error) {
3662
+ emitFailed(ctx, label, error);
3663
+ throw error;
3664
+ }
3048
3665
  }
3049
- function patchData(ctx, data) {
3050
- ctx.emit({ type: "data.patch", data });
3666
+ var init_fleet = () => {};
3667
+
3668
+ // packages/cli/src/app-opentui/adapters/inspect.ts
3669
+ var exports_inspect = {};
3670
+ __export(exports_inspect, {
3671
+ loadInspectRuns: () => loadInspectRuns,
3672
+ loadInspect: () => loadInspect,
3673
+ INSPECT_VIEWS: () => INSPECT_VIEWS
3674
+ });
3675
+ import { execFile } from "child_process";
3676
+ import { existsSync as existsSync4, readFileSync as readFileSync5, readdirSync, statSync } from "fs";
3677
+ import { resolve as resolve4 } from "path";
3678
+ import { promisify } from "util";
3679
+ import { resolveTaskArtifactDirs } from "@rig/runtime/control-plane/authority-files";
3680
+ import { readTaskArtifactPreview } from "@rig/runtime/control-plane/native/workspace-ops";
3681
+ import { resolveHarnessPaths, resolveMonorepoRoot as resolveMonorepoRoot2, runCapture } from "@rig/runtime/control-plane/native/utils";
3682
+ function isFailureEntry(entry) {
3683
+ const status = `${entry.status ?? entry.level ?? entry.type ?? ""}`.toLowerCase();
3684
+ return status.includes("fail") || status.includes("error");
3685
+ }
3686
+ async function gitDiff(projectRoot) {
3687
+ try {
3688
+ const { stdout } = await execFileAsync("git", ["diff", "--no-color"], { cwd: projectRoot, maxBuffer: 8 * 1024 * 1024 });
3689
+ return { diff: stdout };
3690
+ } catch (error) {
3691
+ return { diff: "", error: normalizeAppError(error).message };
3692
+ }
3051
3693
  }
3052
- function patchFooter(ctx, footer) {
3053
- ctx.emit({ type: "footer.patch", footer });
3694
+ function listArtifacts(projectRoot, taskId3) {
3695
+ if (!taskId3)
3696
+ return { artifacts: [], error: "run has no task id \u2014 artifacts are resolved per task" };
3697
+ try {
3698
+ const dirs = resolveTaskArtifactDirs(projectRoot, taskId3).filter((dir) => existsSync4(dir));
3699
+ const out = [];
3700
+ const seen = new Set;
3701
+ for (const dir of dirs) {
3702
+ for (const name of readdirSync(dir)) {
3703
+ if (seen.has(name))
3704
+ continue;
3705
+ seen.add(name);
3706
+ const path = resolve4(dir, name);
3707
+ try {
3708
+ const stat = statSync(path);
3709
+ out.push({ name, path, sizeBytes: stat.size, isDir: stat.isDirectory() });
3710
+ } catch {
3711
+ out.push({ name, path, sizeBytes: 0, isDir: false });
3712
+ }
3713
+ }
3714
+ }
3715
+ out.sort((left, right) => left.name.localeCompare(right.name));
3716
+ return { artifacts: out };
3717
+ } catch (error) {
3718
+ return { artifacts: [], error: normalizeAppError(error).message };
3719
+ }
3054
3720
  }
3055
- function stringField(record, keys, fallback = "") {
3056
- for (const key of keys) {
3057
- const value = record[key];
3058
- if (typeof value === "string" && value.trim())
3059
- return value.trim();
3060
- if (typeof value === "number" && Number.isFinite(value))
3061
- return String(value);
3721
+ function readArtifact(projectRoot, taskId3, fileName) {
3722
+ if (!taskId3)
3723
+ return;
3724
+ try {
3725
+ const preview = readTaskArtifactPreview(projectRoot, taskId3, fileName);
3726
+ return { name: fileName, contents: preview.contents, truncated: preview.truncated, sizeBytes: preview.sizeBytes };
3727
+ } catch (error) {
3728
+ return { name: fileName, contents: `Failed to read artifact: ${normalizeAppError(error).message}`, truncated: false, sizeBytes: 0 };
3062
3729
  }
3063
- return fallback;
3064
3730
  }
3065
- function compactRecord(record, keys) {
3066
- const out = {};
3067
- for (const key of keys) {
3068
- const value = record[key];
3069
- if (value !== undefined && value !== null && value !== "")
3070
- out[key] = value;
3731
+ function loadGraph(projectRoot) {
3732
+ try {
3733
+ const result = runCapture(["br", "--no-db", "list", "--pretty"], resolveMonorepoRoot2(projectRoot));
3734
+ if (result.exitCode !== 0)
3735
+ return { graph: "", error: result.stderr || result.stdout || "failed to inspect graph" };
3736
+ return { graph: result.stdout };
3737
+ } catch (error) {
3738
+ return { graph: "", error: normalizeAppError(error).message };
3071
3739
  }
3072
- return out;
3073
3740
  }
3074
- function payloadString(intent, key) {
3075
- const value = intent.action.payload?.[key];
3076
- return typeof value === "string" && value.trim() ? value.trim() : undefined;
3741
+ function loadAudit(projectRoot) {
3742
+ try {
3743
+ const auditPath = resolve4(resolveHarnessPaths(projectRoot).logsDir, "audit.jsonl");
3744
+ if (!existsSync4(auditPath))
3745
+ return [];
3746
+ return readFileSync5(auditPath, "utf-8").split(/\r?\n/).filter(Boolean).slice(-50).flatMap((raw) => {
3747
+ try {
3748
+ const parsed = JSON.parse(raw);
3749
+ return parsed && typeof parsed === "object" && !Array.isArray(parsed) ? [parsed] : [{ detail: raw }];
3750
+ } catch {
3751
+ return [{ detail: raw }];
3752
+ }
3753
+ });
3754
+ } catch {
3755
+ return [];
3756
+ }
3077
3757
  }
3758
+ async function loadInspectRuns(ctx, options = {}) {
3759
+ const label = "Loading runs to inspect";
3760
+ emitStarted(ctx, label);
3761
+ try {
3762
+ const projectRoot = projectRootOf(ctx);
3763
+ const { listRunsViaServer: listRunsViaServer2 } = await Promise.resolve().then(() => (init__server_client(), exports__server_client));
3764
+ emitProgress(ctx, label, "listing recent runs");
3765
+ const runs = await listRunsViaServer2({ projectRoot }, { limit: options.limit ?? 50 });
3766
+ const options_ = runs.flatMap((record) => {
3767
+ const runId = stringField(record, ["runId", "id"]);
3768
+ if (!runId)
3769
+ return [];
3770
+ const taskId3 = stringField(record, ["taskId"]);
3771
+ const updatedAt = stringField(record, ["updatedAt", "createdAt"]);
3772
+ return [{
3773
+ runId,
3774
+ status: stringField(record, ["status", "state"], "unknown"),
3775
+ title: stringField(record, ["title", "taskTitle", "taskId"], "untitled run"),
3776
+ ...taskId3 ? { taskId: taskId3 } : {},
3777
+ ...updatedAt ? { updatedAt } : {}
3778
+ }];
3779
+ });
3780
+ patchData(ctx, { inspectRuns: options_, lastRefreshError: undefined });
3781
+ emitCompleted(ctx, label, { count: options_.length });
3782
+ return options_;
3783
+ } catch (error) {
3784
+ emitFailed(ctx, label, error);
3785
+ throw error;
3786
+ }
3787
+ }
3788
+ async function loadInspect(ctx, runId) {
3789
+ const cleanRunId = runId.trim();
3790
+ const label = `Inspecting run ${cleanRunId.slice(0, 8)}`;
3791
+ if (!cleanRunId) {
3792
+ await loadInspectRuns(ctx).catch(() => []);
3793
+ const error = new Error("Inspect requires a run id.");
3794
+ throw error;
3795
+ }
3796
+ emitStarted(ctx, label);
3797
+ try {
3798
+ const projectRoot = projectRootOf(ctx);
3799
+ const { getRunDetailsViaServer: getRunDetailsViaServer2, getRunLogsViaServer: getRunLogsViaServer2, getRunTimelineViaServer: getRunTimelineViaServer2 } = await Promise.resolve().then(() => (init__server_client(), exports__server_client));
3800
+ const context = { projectRoot };
3801
+ emitProgress(ctx, label, "loading logs, timeline, diff, artifacts, graph, and audit");
3802
+ const [runPayload, logsPayload, timelinePayload, diffResult] = await Promise.all([
3803
+ getRunDetailsViaServer2(context, cleanRunId).catch((error) => ({ error: normalizeAppError(error).message })),
3804
+ getRunLogsViaServer2(context, cleanRunId, { limit: 500 }).catch(() => []),
3805
+ getRunTimelineViaServer2(context, cleanRunId, { limit: 500 }).catch(() => []),
3806
+ gitDiff(projectRoot)
3807
+ ]);
3808
+ const run = runPayload && typeof runPayload === "object" && "run" in runPayload && runPayload.run && typeof runPayload.run === "object" ? runPayload.run : runPayload;
3809
+ const logs = arrayFromPayload(logsPayload);
3810
+ const timeline = arrayFromPayload(timelinePayload);
3811
+ const taskId3 = stringField(run, ["taskId"]) || undefined;
3812
+ const { artifacts, error: artifactsError } = listArtifacts(projectRoot, taskId3);
3813
+ const { graph, error: graphError } = loadGraph(projectRoot);
3814
+ const audit = loadAudit(projectRoot);
3815
+ const state = ctx.getState();
3816
+ const selectedArtifact = typeof state.data?.inspectArtifact === "string" ? state.data.inspectArtifact : undefined;
3817
+ const artifactView = selectedArtifact ? readArtifact(projectRoot, taskId3, selectedArtifact) : undefined;
3818
+ const snapshot = {
3819
+ runId: cleanRunId,
3820
+ status: stringField(run, ["status", "state"], "unknown"),
3821
+ title: stringField(run, ["title", "taskId", "id"], "untitled run"),
3822
+ logs,
3823
+ timeline,
3824
+ diff: diffResult.diff,
3825
+ failures: [...logs, ...timeline].filter(isFailureEntry),
3826
+ artifacts,
3827
+ graph,
3828
+ audit,
3829
+ refreshedAt: Date.now(),
3830
+ ...taskId3 ? { taskId: taskId3 } : {},
3831
+ ...artifactView ? { artifactView } : {},
3832
+ ...diffResult.error ? { diffError: diffResult.error } : {},
3833
+ ...graphError ? { graphError } : {},
3834
+ ...artifactsError ? { artifactsError } : {}
3835
+ };
3836
+ patchData(ctx, { inspect: snapshot, lastRefreshError: undefined });
3837
+ emitCompleted(ctx, label, {
3838
+ runId: cleanRunId,
3839
+ logs: logs.length,
3840
+ timeline: timeline.length,
3841
+ diffBytes: snapshot.diff.length,
3842
+ artifacts: artifacts.length,
3843
+ audit: audit.length
3844
+ });
3845
+ return snapshot;
3846
+ } catch (error) {
3847
+ emitFailed(ctx, label, error, { runId: cleanRunId });
3848
+ throw error;
3849
+ }
3850
+ }
3851
+ var execFileAsync, INSPECT_VIEWS;
3852
+ var init_inspect = __esm(() => {
3853
+ execFileAsync = promisify(execFile);
3854
+ INSPECT_VIEWS = [
3855
+ "logs",
3856
+ "timeline",
3857
+ "artifacts",
3858
+ "artifact",
3859
+ "diff",
3860
+ "failures",
3861
+ "graph",
3862
+ "audit"
3863
+ ];
3864
+ });
3078
3865
 
3079
- // packages/cli/src/app-opentui/adapters/tasks.ts
3080
- function createTasksAdapter() {
3081
- return {
3082
- id: "tasks",
3083
- async handleIntent(runtime, intent) {
3084
- if (intent.scene !== "tasks" && intent.action.kind !== "task-run-next" && intent.action.kind !== "task-run-id")
3085
- return false;
3086
- const ctx = adapterContextFromRuntime(runtime);
3087
- if (intent.action.kind === "task-run-next") {
3088
- await submitNextTaskRun(ctx);
3089
- return true;
3090
- }
3091
- if (intent.action.kind === "task-run-id") {
3092
- await submitTaskRun(ctx, { taskId: payloadString(intent, "task") });
3093
- return true;
3866
+ // packages/cli/src/app-opentui/adapters/pi-attach.ts
3867
+ var exports_pi_attach = {};
3868
+ __export(exports_pi_attach, {
3869
+ preparePiAttachHandoff: () => preparePiAttachHandoff,
3870
+ attachRunWithBundledPi: () => attachRunWithBundledPi
3871
+ });
3872
+ function currentAttachState(ctx, runId) {
3873
+ const value = ctx.getState().data?.piAttach;
3874
+ if (!value || typeof value !== "object" || Array.isArray(value))
3875
+ return null;
3876
+ const state = value;
3877
+ return state.runId === runId ? state : null;
3878
+ }
3879
+ function recordStep(ctx, runId, label, emitLabel) {
3880
+ const now = Date.now();
3881
+ const previous = currentAttachState(ctx, runId);
3882
+ const priorSteps = previous?.steps ?? [];
3883
+ const settled = priorSteps.map((step) => step.status === "running" ? { ...step, status: "done", endedAtMs: step.endedAtMs ?? now } : step);
3884
+ const steps = [...settled, { label, startedAtMs: now, status: "running" }];
3885
+ patchData(ctx, {
3886
+ piAttach: { ...previous ?? { runId, status: "preparing", message: label }, runId, steps }
3887
+ });
3888
+ emitProgress(ctx, emitLabel, label);
3889
+ }
3890
+ function settleSteps(ctx, runId) {
3891
+ const now = Date.now();
3892
+ const previous = currentAttachState(ctx, runId);
3893
+ if (!previous?.steps)
3894
+ return;
3895
+ const steps = previous.steps.map((step) => step.status === "running" ? { ...step, status: "done", endedAtMs: step.endedAtMs ?? now } : step);
3896
+ patchData(ctx, { piAttach: { ...previous, steps } });
3897
+ }
3898
+ async function preparePiAttachHandoff(ctx, runId) {
3899
+ const cleanRunId = runId.trim();
3900
+ const label = `Preparing Pi ${cleanRunId.slice(0, 8)}`;
3901
+ emitStarted(ctx, label, { piAttach: { runId: cleanRunId, status: "preparing" } });
3902
+ try {
3903
+ const { getRunDetailsViaServer: getRunDetailsViaServer2, getRunPiStatusViaServer: getRunPiStatusViaServer2, resolveServerConnectionLabel: resolveServerConnectionLabel2 } = await Promise.resolve().then(() => (init__server_client(), exports__server_client));
3904
+ const projectRoot = projectRootOf(ctx);
3905
+ recordStep(ctx, cleanRunId, "connecting to server \xB7 checking run and Pi session readiness", label);
3906
+ const [run, piStatus, server] = await Promise.all([
3907
+ getRunDetailsViaServer2({ projectRoot }, cleanRunId).catch((error) => ({ error: error instanceof Error ? error.message : String(error) })),
3908
+ getRunPiStatusViaServer2({ projectRoot }, cleanRunId).catch((error) => ({ error: error instanceof Error ? error.message : String(error) })),
3909
+ resolveServerConnectionLabel2(projectRoot)
3910
+ ]);
3911
+ const state = {
3912
+ runId: cleanRunId,
3913
+ status: "preparing",
3914
+ message: "ready to enter worker Pi",
3915
+ result: { run, piStatus, server },
3916
+ ready: true,
3917
+ ...currentAttachState(ctx, cleanRunId)?.steps ? { steps: currentAttachState(ctx, cleanRunId).steps } : {}
3918
+ };
3919
+ patchData(ctx, { piAttach: state, lastRefreshError: undefined });
3920
+ emitCompleted(ctx, label, { runId: cleanRunId, server });
3921
+ return state;
3922
+ } catch (error) {
3923
+ const reason = error instanceof Error ? error.message : String(error);
3924
+ const state = {
3925
+ runId: cleanRunId,
3926
+ status: "failed",
3927
+ message: reason,
3928
+ failureReason: reason,
3929
+ ...currentAttachState(ctx, cleanRunId)?.steps ? { steps: currentAttachState(ctx, cleanRunId).steps } : {}
3930
+ };
3931
+ patchData(ctx, { piAttach: state });
3932
+ emitFailed(ctx, label, error, { runId: cleanRunId });
3933
+ throw error;
3934
+ }
3935
+ }
3936
+ async function attachRunWithBundledPi(ctx, runId) {
3937
+ const cleanRunId = runId.trim();
3938
+ const label = `Entering Pi ${cleanRunId.slice(0, 8)}`;
3939
+ if (!cleanRunId) {
3940
+ const error = new Error("Attach requires a run id.");
3941
+ emitFailed(ctx, label, error);
3942
+ throw error;
3943
+ }
3944
+ emitStarted(ctx, label, { piAttach: { runId: cleanRunId, status: "entering-pi" } });
3945
+ patchData(ctx, { piAttach: { runId: cleanRunId, status: "entering-pi", message: "starting bundled Pi", steps: [] } });
3946
+ try {
3947
+ recordStep(ctx, cleanRunId, "connecting to server and fetching run transcript", label);
3948
+ await preparePiAttachHandoff(ctx, cleanRunId).catch((error) => {
3949
+ patchData(ctx, { lastRefreshError: normalizeAppError(error).message });
3950
+ return null;
3951
+ });
3952
+ recordStep(ctx, cleanRunId, "spawning Bun PTY host for bundled Pi", label);
3953
+ const projectRoot = projectRootOf(ctx);
3954
+ const cols = typeof process.stdout.columns === "number" ? process.stdout.columns : 100;
3955
+ const rows = Math.max(1, (typeof process.stdout.rows === "number" ? process.stdout.rows : 35) - 1);
3956
+ const patchTerminal = (snapshot) => {
3957
+ const previousSteps = currentAttachState(ctx, cleanRunId)?.steps;
3958
+ const failed = snapshot.status === "failed";
3959
+ const reason = failed ? snapshot.message ?? `bundled Pi exited with code ${snapshot.exitCode ?? "unknown"}` : undefined;
3960
+ patchData(ctx, {
3961
+ piTerminal: snapshot,
3962
+ piAttach: {
3963
+ runId: cleanRunId,
3964
+ status: failed ? "failed" : snapshot.status === "exited" ? "returned" : "entering-pi",
3965
+ message: snapshot.message ?? "bundled Pi running",
3966
+ result: { runId: cleanRunId, status: snapshot.status, exitCode: snapshot.exitCode },
3967
+ ...previousSteps ? { steps: previousSteps } : {},
3968
+ ...reason ? { failureReason: reason } : {}
3969
+ }
3970
+ });
3971
+ };
3972
+ recordStep(ctx, cleanRunId, "starting bundled Pi", label);
3973
+ await startPiPtyHost({
3974
+ runId: cleanRunId,
3975
+ projectRoot,
3976
+ cols,
3977
+ rows,
3978
+ onSnapshot: (snapshot) => {
3979
+ if (snapshot.status === "running" && snapshot.lines.some((entry) => entry.trim().length > 0)) {
3980
+ settleSteps(ctx, cleanRunId);
3981
+ }
3982
+ patchTerminal(snapshot);
3983
+ },
3984
+ onExit: (snapshot) => {
3985
+ patchTerminal(snapshot);
3986
+ if (snapshot.status === "failed") {
3987
+ Promise.resolve().then(() => (init_inspect(), exports_inspect)).then(({ loadInspect: loadInspect2 }) => loadInspect2(ctx, cleanRunId)).catch(() => null);
3988
+ emitFailed(ctx, label, new Error(snapshot.message ?? `bundled Pi exited with code ${snapshot.exitCode ?? "unknown"}`), { runId: cleanRunId, result: { status: snapshot.status, exitCode: snapshot.exitCode } });
3989
+ return;
3990
+ }
3991
+ emitCompleted(ctx, label, { runId: cleanRunId, result: { status: snapshot.status, exitCode: snapshot.exitCode } });
3992
+ ctx.emit({ type: "scene.change", scene: "fleet", intent: { scene: "fleet", argv: ["runs"], action: { kind: "refresh", label: "Opening runs" } } });
3993
+ refreshFleet(ctx).catch((error) => emitFailed(ctx, "Refresh runs", error));
3994
+ },
3995
+ onError: (error, snapshot) => {
3996
+ patchTerminal(snapshot);
3997
+ emitFailed(ctx, label, error, { runId: cleanRunId });
3094
3998
  }
3095
- if (intent.action.kind === "refresh" || intent.action.kind === "navigate") {
3096
- await refreshTasks(ctx, { limit: 20 });
3097
- return true;
3999
+ });
4000
+ const record = {
4001
+ runId: cleanRunId,
4002
+ status: "running",
4003
+ rendered: "bundled Pi embedded via Bun.Terminal + @xterm/headless"
4004
+ };
4005
+ emitProgress(ctx, label, "bundled Pi is running", { piAttach: { runId: cleanRunId, status: "entering-pi", message: "bundled Pi running", result: record } });
4006
+ return record;
4007
+ } catch (error) {
4008
+ const reason = error instanceof Error ? error.message : String(error);
4009
+ patchData(ctx, {
4010
+ piAttach: {
4011
+ runId: cleanRunId,
4012
+ status: "failed",
4013
+ message: reason,
4014
+ failureReason: reason,
4015
+ ...currentAttachState(ctx, cleanRunId)?.steps ? { steps: currentAttachState(ctx, cleanRunId).steps } : {}
3098
4016
  }
3099
- return false;
3100
- }
3101
- };
4017
+ });
4018
+ emitFailed(ctx, label, error, { runId: cleanRunId });
4019
+ throw error;
4020
+ }
3102
4021
  }
4022
+ var init_pi_attach = __esm(() => {
4023
+ init_pi_pty_host();
4024
+ init_fleet();
4025
+ });
4026
+
4027
+ // packages/cli/src/app-opentui/adapters/tasks.ts
4028
+ import { randomUUID } from "crypto";
3103
4029
  function rawRecord(record, key) {
3104
4030
  const direct = record[key];
3105
4031
  if (direct !== undefined && direct !== null)
@@ -3150,6 +4076,30 @@ function labelsFor(record) {
3150
4076
  function assigneesFor(record) {
3151
4077
  return stringList(rawRecord(record, "assignees"));
3152
4078
  }
4079
+ function activeRunForRecord(record) {
4080
+ const value = rawRecord(record, "activeRun");
4081
+ if (!value || typeof value !== "object" || Array.isArray(value))
4082
+ return;
4083
+ const activeRun = value;
4084
+ const runId = optionalString(activeRun, ["runId", "id"]);
4085
+ if (!runId)
4086
+ return;
4087
+ const status = optionalString(activeRun, ["status", "state"]);
4088
+ const stage = optionalString(activeRun, ["stage", "phase"]);
4089
+ return {
4090
+ runId,
4091
+ ...status ? { status } : {},
4092
+ ...stage ? { stage } : {}
4093
+ };
4094
+ }
4095
+ function activeRunIdFromTaskLike(task) {
4096
+ return activeRunForRecord(task)?.runId;
4097
+ }
4098
+ function activeRunIdFromTaskRunError(error) {
4099
+ const message2 = error instanceof Error ? error.message : String(error);
4100
+ const match = message2.match(/already has an active (?:Rig )?run:\s*([^\s"'}]+)/i);
4101
+ return match?.[1]?.replace(/[),.;]+$/g, "") ?? null;
4102
+ }
3153
4103
  function normalizeTaskRecord(record) {
3154
4104
  const id = stringField(record, ["id", "taskId", "number"]);
3155
4105
  if (!id)
@@ -3157,6 +4107,7 @@ function normalizeTaskRecord(record) {
3157
4107
  const priority = optionalNumber(record, ["priority", "rank", "score"]);
3158
4108
  const updatedAt = optionalString(record, ["updatedAt", "updated_at", "modifiedAt"]);
3159
4109
  const createdAt = optionalString(record, ["createdAt", "created_at"]);
4110
+ const activeRun = activeRunForRecord(record);
3160
4111
  return {
3161
4112
  id,
3162
4113
  status: stringField(record, ["status", "state"], "unknown"),
@@ -3166,6 +4117,7 @@ function normalizeTaskRecord(record) {
3166
4117
  ...createdAt ? { createdAt } : {},
3167
4118
  labels: labelsFor(record),
3168
4119
  assignees: assigneesFor(record),
4120
+ ...activeRun ? { activeRun } : {},
3169
4121
  raw: record
3170
4122
  };
3171
4123
  }
@@ -3393,7 +4345,7 @@ async function submitTaskRun(ctx, input = {}) {
3393
4345
  try {
3394
4346
  const { submitTaskRunViaServer: submitTaskRunViaServer2 } = await Promise.resolve().then(() => (init__server_client(), exports__server_client));
3395
4347
  const defaults = await loadRunDefaults(projectRoot);
3396
- emitProgress(ctx, label, "submitting run to selected Rig server", { runId, taskId: taskId3 ?? null });
4348
+ emitProgress(ctx, label, "submitting run to selected server", { runId, taskId: taskId3 ?? null });
3397
4349
  const submitted = await submitTaskRunViaServer2({ projectRoot }, {
3398
4350
  runId,
3399
4351
  taskId: taskId3 || undefined,
@@ -3413,14 +4365,41 @@ async function submitTaskRun(ctx, input = {}) {
3413
4365
  latestSubmittedRun: { runId: submitted.runId, taskId: taskId3 ?? null },
3414
4366
  runDetail: { runId: submitted.runId, status: "submitted", title: optimisticTitle, run: submitted, logs: [], timeline: [], timelineCursor: null }
3415
4367
  });
3416
- patchFooter(ctx, { run: `${submitted.runId.slice(0, 8)} submitted` });
4368
+ patchFooter(ctx, { run: `${submitted.runId.slice(0, 8)} ${input.detach ? "submitted" : "attaching"}` });
4369
+ if (!input.detach) {
4370
+ ctx.emit({
4371
+ type: "scene.change",
4372
+ scene: "handoff",
4373
+ intent: { scene: "handoff", argv: ["attach", submitted.runId], action: { kind: "run-attach", payload: { runId: submitted.runId }, label: `Attach Pi ${submitted.runId.slice(0, 8)}` } }
4374
+ });
4375
+ const { attachRunWithBundledPi: attachRunWithBundledPi2 } = await Promise.resolve().then(() => (init_pi_attach(), exports_pi_attach));
4376
+ await attachRunWithBundledPi2(ctx, submitted.runId);
4377
+ }
3417
4378
  return submitted;
3418
4379
  } catch (error) {
4380
+ const activeRunId = activeRunIdFromTaskRunError(error);
4381
+ if (activeRunId) {
4382
+ emitProgress(ctx, label, `task already has active run ${activeRunId.slice(0, 8)}; attaching Pi`, {
4383
+ selectedRunId: activeRunId,
4384
+ dispatchingRun: null,
4385
+ latestSubmittedRun: { runId: activeRunId, taskId: taskId3 ?? null }
4386
+ });
4387
+ patchData(ctx, { selectedRunId: activeRunId, dispatchingRun: null });
4388
+ patchFooter(ctx, { run: `${activeRunId.slice(0, 8)} active` });
4389
+ ctx.emit({
4390
+ type: "scene.change",
4391
+ scene: "handoff",
4392
+ intent: { scene: "handoff", argv: ["attach", activeRunId], action: { kind: "run-attach", payload: { runId: activeRunId }, label: `Attach Pi ${activeRunId.slice(0, 8)}` } }
4393
+ });
4394
+ const { attachRunWithBundledPi: attachRunWithBundledPi2 } = await Promise.resolve().then(() => (init_pi_attach(), exports_pi_attach));
4395
+ await attachRunWithBundledPi2(ctx, activeRunId);
4396
+ return { runId: activeRunId };
4397
+ }
3419
4398
  emitFailed(ctx, label, error, { runId, taskId: taskId3 ?? null, revert: { dispatchingRun: null } });
3420
4399
  throw error;
3421
4400
  }
3422
4401
  }
3423
- async function submitNextTaskRun(ctx, filters = {}) {
4402
+ async function submitNextTaskRun(ctx, filters = {}, options = {}) {
3424
4403
  const label = "Selecting next task";
3425
4404
  emitStarted(ctx, label, { dispatch: { status: "selecting-next" } });
3426
4405
  try {
@@ -3436,7 +4415,7 @@ async function submitNextTaskRun(ctx, filters = {}) {
3436
4415
  throw error;
3437
4416
  }
3438
4417
  emitCompleted(ctx, label, { taskId: task.id, count: selected.count });
3439
- const submitted = await submitTaskRun(ctx, { taskId: task.id, title: task.title });
4418
+ const submitted = await submitTaskRun(ctx, { taskId: task.id, title: task.title, detach: options.detach });
3440
4419
  return { ...submitted, task };
3441
4420
  } catch (error) {
3442
4421
  emitFailed(ctx, label, error);
@@ -3450,7 +4429,7 @@ async function loadTaskPreview(ctx, taskId3) {
3450
4429
  const { getWorkspaceTaskViaServer: getWorkspaceTaskViaServer2 } = await Promise.resolve().then(() => (init__server_client(), exports__server_client));
3451
4430
  const task = await getWorkspaceTaskViaServer2({ projectRoot: projectRootOf(ctx) }, taskId3);
3452
4431
  const normalized = task ? normalizeTaskRecord(task) : null;
3453
- patchData(ctx, { selectedTaskId: taskId3, taskPreview: normalized, taskPreviewRaw: task ? compactRecord(task, ["id", "title", "status", "body", "url"]) : null });
4432
+ patchData(ctx, { selectedTaskId: taskId3, taskDetailId: taskId3, taskPreview: normalized, taskPreviewRaw: task ? compactRecord(task, ["id", "title", "status", "body", "url"]) : null });
3454
4433
  emitCompleted(ctx, label, { taskId: taskId3, found: Boolean(normalized) });
3455
4434
  return normalized;
3456
4435
  } catch (error) {
@@ -3468,5 +4447,6 @@ export {
3468
4447
  normalizeTaskRecord,
3469
4448
  loadTaskPreview,
3470
4449
  ensureTaskRuntime,
3471
- createTasksAdapter
4450
+ activeRunIdFromTaskRunError,
4451
+ activeRunIdFromTaskLike
3472
4452
  };