@h-rig/cli 0.0.6-alpha.9 → 0.0.6-alpha.90

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 (114) hide show
  1. package/README.md +1 -1
  2. package/dist/bin/build-rig-binaries.d.ts +2 -0
  3. package/dist/bin/rig.d.ts +2 -0
  4. package/dist/bin/rig.js +11052 -6668
  5. package/dist/src/app/board.d.ts +23 -0
  6. package/dist/src/app/board.js +1786 -0
  7. package/dist/src/app/drone-ui.d.ts +37 -0
  8. package/dist/src/app/drone-ui.js +294 -0
  9. package/dist/src/app/theme.d.ts +47 -0
  10. package/dist/src/app/theme.js +150 -0
  11. package/dist/src/commands/_async-ui.d.ts +13 -0
  12. package/dist/src/commands/_async-ui.js +223 -0
  13. package/dist/src/commands/_authority-runs.d.ts +22 -0
  14. package/dist/src/commands/_authority-runs.js +2 -3
  15. package/dist/src/commands/_cli-format.d.ts +49 -0
  16. package/dist/src/commands/_cli-format.js +447 -0
  17. package/dist/src/commands/_connection-state.d.ts +44 -0
  18. package/dist/src/commands/_connection-state.js +30 -11
  19. package/dist/src/commands/_doctor-checks.d.ts +52 -0
  20. package/dist/src/commands/_doctor-checks.js +180 -44
  21. package/dist/src/commands/_help-catalog.d.ts +51 -0
  22. package/dist/src/commands/_help-catalog.js +442 -0
  23. package/dist/src/commands/_json-output.d.ts +11 -0
  24. package/dist/src/commands/_json-output.js +56 -0
  25. package/dist/src/commands/_operator-surface.d.ts +34 -0
  26. package/dist/src/commands/_operator-surface.js +220 -0
  27. package/dist/src/commands/_operator-view.d.ts +30 -0
  28. package/dist/src/commands/_operator-view.js +803 -73
  29. package/dist/src/commands/_parsers.d.ts +15 -0
  30. package/dist/src/commands/_parsers.js +18 -11
  31. package/dist/src/commands/_paths.d.ts +11 -0
  32. package/dist/src/commands/_pi-frontend.d.ts +27 -0
  33. package/dist/src/commands/_pi-frontend.js +742 -0
  34. package/dist/src/commands/_pi-install.d.ts +42 -0
  35. package/dist/src/commands/_pi-install.js +5 -4
  36. package/dist/src/commands/_policy.d.ts +8 -0
  37. package/dist/src/commands/_policy.js +12 -5
  38. package/dist/src/commands/_preflight.d.ts +22 -0
  39. package/dist/src/commands/_preflight.js +190 -128
  40. package/dist/src/commands/_probes.d.ts +1 -0
  41. package/dist/src/commands/_run-driver-helpers.d.ts +99 -0
  42. package/dist/src/commands/_run-driver-helpers.js +75 -22
  43. package/dist/src/commands/_run-replay.d.ts +24 -0
  44. package/dist/src/commands/_run-replay.js +142 -0
  45. package/dist/src/commands/_server-client.d.ts +186 -0
  46. package/dist/src/commands/_server-client.js +360 -61
  47. package/dist/src/commands/_snapshot-upload.d.ts +39 -0
  48. package/dist/src/commands/_snapshot-upload.js +163 -39
  49. package/dist/src/commands/_spinner.d.ts +25 -0
  50. package/dist/src/commands/_spinner.js +65 -0
  51. package/dist/src/commands/_task-picker.d.ts +9 -0
  52. package/dist/src/commands/_task-picker.js +172 -19
  53. package/dist/src/commands/agent.d.ts +3 -0
  54. package/dist/src/commands/agent.js +39 -20
  55. package/dist/src/commands/browser.d.ts +65 -0
  56. package/dist/src/commands/browser.js +334 -51
  57. package/dist/src/commands/connect.d.ts +7 -0
  58. package/dist/src/commands/connect.js +272 -33
  59. package/dist/src/commands/dist.d.ts +28 -0
  60. package/dist/src/commands/dist.js +19 -12
  61. package/dist/src/commands/doctor.d.ts +3 -0
  62. package/dist/src/commands/doctor.js +378 -45
  63. package/dist/src/commands/github.d.ts +3 -0
  64. package/dist/src/commands/github.js +375 -53
  65. package/dist/src/commands/inbox.d.ts +30 -0
  66. package/dist/src/commands/inbox.js +760 -70
  67. package/dist/src/commands/init.d.ts +74 -0
  68. package/dist/src/commands/init.js +963 -119
  69. package/dist/src/commands/inspect.d.ts +3 -0
  70. package/dist/src/commands/inspect.js +588 -32
  71. package/dist/src/commands/inspector.d.ts +3 -0
  72. package/dist/src/commands/inspector.js +20 -13
  73. package/dist/src/commands/pi.d.ts +3 -0
  74. package/dist/src/commands/pi.js +177 -0
  75. package/dist/src/commands/plugin.d.ts +16 -0
  76. package/dist/src/commands/plugin.js +95 -27
  77. package/dist/src/commands/profile-and-review.d.ts +4 -0
  78. package/dist/src/commands/profile-and-review.js +26 -19
  79. package/dist/src/commands/queue.d.ts +3 -0
  80. package/dist/src/commands/queue.js +32 -12
  81. package/dist/src/commands/remote.d.ts +3 -0
  82. package/dist/src/commands/remote.js +43 -36
  83. package/dist/src/commands/repo-git-harness.d.ts +5 -0
  84. package/dist/src/commands/repo-git-harness.js +22 -15
  85. package/dist/src/commands/run.d.ts +3 -0
  86. package/dist/src/commands/run.js +1379 -159
  87. package/dist/src/commands/server.d.ts +7 -0
  88. package/dist/src/commands/server.js +502 -57
  89. package/dist/src/commands/setup.d.ts +16 -0
  90. package/dist/src/commands/setup.js +390 -63
  91. package/dist/src/commands/stats.d.ts +12 -0
  92. package/dist/src/commands/stats.js +1051 -0
  93. package/dist/src/commands/task-report-bug.d.ts +19 -0
  94. package/dist/src/commands/task-report-bug.js +260 -62
  95. package/dist/src/commands/task-run-driver.d.ts +132 -0
  96. package/dist/src/commands/task-run-driver.js +886 -130
  97. package/dist/src/commands/task.d.ts +14 -0
  98. package/dist/src/commands/task.js +1644 -314
  99. package/dist/src/commands/test.d.ts +3 -0
  100. package/dist/src/commands/test.js +15 -8
  101. package/dist/src/commands/workspace.d.ts +3 -0
  102. package/dist/src/commands/workspace.js +18 -11
  103. package/dist/src/commands.d.ts +29 -0
  104. package/dist/src/commands.js +10967 -6637
  105. package/dist/src/index.d.ts +4 -0
  106. package/dist/src/index.js +10168 -5787
  107. package/dist/src/launcher.d.ts +61 -0
  108. package/dist/src/launcher.js +77 -13
  109. package/dist/src/report-bug.d.ts +44 -0
  110. package/dist/src/report-bug.js +3 -3
  111. package/dist/src/runner.d.ts +47 -0
  112. package/dist/src/runner.js +16 -22
  113. package/dist/src/withMutedConsole.d.ts +2 -0
  114. package/package.json +13 -6
@@ -1,15 +1,22 @@
1
1
  // @bun
2
2
  // packages/cli/src/commands/github.ts
3
- import { spawnSync as spawnSync2 } from "child_process";
3
+ import { spawnSync } from "child_process";
4
4
 
5
5
  // packages/cli/src/runner.ts
6
6
  import { EventBus } from "@rig/runtime/control-plane/runtime/events";
7
- import { CliError } from "@rig/runtime/control-plane/errors";
7
+ import { CliError as RuntimeCliError } from "@rig/runtime/control-plane/errors";
8
8
  import { evaluate, loadPolicy, resolveAction } from "@rig/runtime/control-plane/runtime/guard";
9
- import { PluginManager } from "@rig/runtime/control-plane/runtime/plugins";
10
- import { loadRuntimeContextFromEnv } from "@rig/runtime/control-plane/runtime/context";
11
9
  import { buildBinary } from "@rig/runtime/control-plane/runtime/isolation";
12
- import { CliError as CliError2 } from "@rig/runtime/control-plane/errors";
10
+
11
+ class CliError extends RuntimeCliError {
12
+ hint;
13
+ constructor(message, exitCode = 1, options = {}) {
14
+ super(message, exitCode);
15
+ if (options.hint?.trim()) {
16
+ this.hint = options.hint.trim();
17
+ }
18
+ }
19
+ }
13
20
  function takeOption(args, option) {
14
21
  const rest = [];
15
22
  let value;
@@ -18,7 +25,7 @@ function takeOption(args, option) {
18
25
  if (current === option) {
19
26
  const next = args[index + 1];
20
27
  if (!next || next.startsWith("-")) {
21
- throw new CliError(`Missing value for ${option}`);
28
+ throw new CliError(`Missing value for ${option}`, 1, { hint: `Provide a value after ${option}, e.g. \`${option} <value>\`.` });
22
29
  }
23
30
  value = next;
24
31
  index += 1;
@@ -32,7 +39,6 @@ function takeOption(args, option) {
32
39
  }
33
40
 
34
41
  // packages/cli/src/commands/_server-client.ts
35
- import { spawnSync } from "child_process";
36
42
  import { existsSync as existsSync2, readFileSync as readFileSync2 } from "fs";
37
43
  import { resolve as resolve2 } from "path";
38
44
  import { ensureLocalRigServerConnection } from "@rig/runtime/local-server";
@@ -59,9 +65,14 @@ function readJsonFile(path) {
59
65
  try {
60
66
  return JSON.parse(readFileSync(path, "utf8"));
61
67
  } catch (error) {
62
- throw new CliError2(`Invalid Rig connection state at ${path}: ${error instanceof Error ? error.message : String(error)}`, 1);
68
+ throw new CliError(`Invalid Rig connection state at ${path}: ${error instanceof Error ? error.message : String(error)}`, 1, { hint: "Fix or delete that file, then re-select a server with `rig server use <alias|local>`." });
63
69
  }
64
70
  }
71
+ function writeJsonFile(path, value) {
72
+ mkdirSync(dirname(path), { recursive: true });
73
+ writeFileSync(path, `${JSON.stringify(value, null, 2)}
74
+ `, "utf8");
75
+ }
65
76
  function normalizeConnection(value) {
66
77
  if (!value || typeof value !== "object" || Array.isArray(value))
67
78
  return null;
@@ -102,25 +113,44 @@ function readRepoConnection(projectRoot) {
102
113
  return {
103
114
  selected,
104
115
  project: typeof record.project === "string" ? record.project : undefined,
105
- linkedAt: typeof record.linkedAt === "string" ? record.linkedAt : undefined
116
+ linkedAt: typeof record.linkedAt === "string" ? record.linkedAt : undefined,
117
+ serverProjectRoot: typeof record.serverProjectRoot === "string" && record.serverProjectRoot.trim() ? record.serverProjectRoot.trim() : undefined
106
118
  };
107
119
  }
120
+ function writeRepoConnection(projectRoot, state) {
121
+ writeJsonFile(resolveRepoConnectionPath(projectRoot), state);
122
+ }
108
123
  function resolveSelectedConnection(projectRoot, options = {}) {
109
124
  const repo = readRepoConnection(projectRoot);
110
125
  if (!repo)
111
126
  return null;
112
127
  if (repo.selected === "local")
113
- return { alias: "local", connection: { kind: "local", mode: "auto" } };
128
+ return { alias: "local", connection: { kind: "local", mode: "auto" }, serverProjectRoot: repo.serverProjectRoot };
114
129
  const global = readGlobalConnections(options);
115
130
  const connection = global.connections[repo.selected];
116
131
  if (!connection) {
117
- throw new CliError2(`Selected Rig connection "${repo.selected}" was not found. Run \`rig connect list\` or \`rig connect use local\`.`, 1);
132
+ throw new CliError(`Selected Rig server "${repo.selected}" was not found. Run \`rig server list\` or \`rig server use local\`.`, 1);
118
133
  }
119
- return { alias: repo.selected, connection };
134
+ return { alias: repo.selected, connection, serverProjectRoot: repo.serverProjectRoot };
135
+ }
136
+ function writeRepoServerProjectRoot(projectRoot, serverProjectRoot) {
137
+ const repo = readRepoConnection(projectRoot);
138
+ if (!repo)
139
+ return;
140
+ writeRepoConnection(projectRoot, { ...repo, serverProjectRoot });
120
141
  }
121
142
 
122
143
  // packages/cli/src/commands/_server-client.ts
123
- var cachedGitHubBearerToken;
144
+ var scopedGitHubBearerTokens = new Map;
145
+ var serverPhaseListener = null;
146
+ function setServerPhaseListener(listener) {
147
+ const previous = serverPhaseListener;
148
+ serverPhaseListener = listener;
149
+ return previous;
150
+ }
151
+ function reportServerPhase(label) {
152
+ serverPhaseListener?.(label);
153
+ }
124
154
  function cleanToken(value) {
125
155
  const trimmed = value?.trim();
126
156
  return trimmed ? trimmed : null;
@@ -137,49 +167,80 @@ function readPrivateRemoteSessionToken(projectRoot) {
137
167
  }
138
168
  }
139
169
  function readGitHubBearerTokenForRemote(projectRoot) {
140
- if (cachedGitHubBearerToken !== undefined)
141
- return cachedGitHubBearerToken;
170
+ const scopedKey = resolve2(projectRoot);
171
+ if (scopedGitHubBearerTokens.has(scopedKey))
172
+ return scopedGitHubBearerTokens.get(scopedKey) ?? null;
142
173
  const privateSession = readPrivateRemoteSessionToken(projectRoot);
143
- if (privateSession) {
144
- cachedGitHubBearerToken = privateSession;
145
- return cachedGitHubBearerToken;
146
- }
147
- const envToken = cleanToken(process.env.RIG_GITHUB_TOKEN) ?? cleanToken(process.env.GITHUB_TOKEN) ?? cleanToken(process.env.GH_TOKEN);
148
- if (envToken) {
149
- cachedGitHubBearerToken = envToken;
150
- return cachedGitHubBearerToken;
174
+ if (privateSession)
175
+ return privateSession;
176
+ return cleanToken(process.env.RIG_SERVER_AUTH_TOKEN) ?? cleanToken(process.env.RIG_REMOTE_AUTH_TOKEN);
177
+ }
178
+ function readStoredGitHubAuthToken(projectRoot) {
179
+ const path = resolve2(projectRoot, ".rig", "state", "github-auth.json");
180
+ if (!existsSync2(path))
181
+ return null;
182
+ try {
183
+ const parsed = JSON.parse(readFileSync2(path, "utf8"));
184
+ return cleanToken(typeof parsed.token === "string" ? parsed.token : undefined);
185
+ } catch {
186
+ return null;
151
187
  }
152
- const result = spawnSync("gh", ["auth", "token"], {
153
- encoding: "utf8",
154
- timeout: 5000,
155
- stdio: ["ignore", "pipe", "ignore"]
156
- });
157
- cachedGitHubBearerToken = result.status === 0 ? cleanToken(result.stdout) : null;
158
- return cachedGitHubBearerToken;
188
+ }
189
+ function readLocalConnectionFallbackToken(projectRoot) {
190
+ return readGitHubBearerTokenForRemote(projectRoot) ?? cleanToken(process.env.RIG_GITHUB_TOKEN) ?? readStoredGitHubAuthToken(projectRoot);
159
191
  }
160
192
  async function ensureServerForCli(projectRoot) {
161
193
  try {
162
194
  const selected = resolveSelectedConnection(projectRoot);
163
195
  if (selected?.connection.kind === "remote") {
196
+ reportServerPhase(`Connecting to ${selected.alias}\u2026`);
197
+ const authToken = readGitHubBearerTokenForRemote(projectRoot);
198
+ const serverProjectRoot = selected.serverProjectRoot ?? await backfillRemoteServerProjectRoot(projectRoot, selected.connection.baseUrl, authToken);
164
199
  return {
165
200
  baseUrl: selected.connection.baseUrl,
166
- authToken: readGitHubBearerTokenForRemote(projectRoot),
167
- connectionKind: "remote"
201
+ authToken,
202
+ connectionKind: "remote",
203
+ serverProjectRoot
168
204
  };
169
205
  }
206
+ reportServerPhase("Starting local Rig server\u2026");
170
207
  const connection = await ensureLocalRigServerConnection(projectRoot);
171
208
  return {
172
209
  baseUrl: connection.baseUrl,
173
- authToken: connection.authToken,
174
- connectionKind: "local"
210
+ authToken: connection.authToken ?? readLocalConnectionFallbackToken(projectRoot),
211
+ connectionKind: "local",
212
+ serverProjectRoot: resolve2(projectRoot)
175
213
  };
176
214
  } catch (error) {
177
215
  if (error instanceof Error) {
178
- throw new CliError2(error.message, 1);
216
+ throw new CliError(error.message, 1);
179
217
  }
180
218
  throw error;
181
219
  }
182
220
  }
221
+ async function backfillRemoteServerProjectRoot(projectRoot, baseUrl, authToken) {
222
+ const repo = readRepoConnection(projectRoot);
223
+ const slug = repo?.project?.trim();
224
+ if (!slug)
225
+ return null;
226
+ try {
227
+ const response = await fetch(`${baseUrl}/api/projects/${encodeURIComponent(slug)}`, {
228
+ headers: mergeHeaders(undefined, authToken)
229
+ });
230
+ if (!response.ok)
231
+ return null;
232
+ const payload = await response.json();
233
+ const project = payload.project && typeof payload.project === "object" && !Array.isArray(payload.project) ? payload.project : null;
234
+ const checkouts = Array.isArray(project?.checkouts) ? project.checkouts : [];
235
+ const latestCheckout = [...checkouts].reverse().find((entry) => Boolean(entry && typeof entry === "object" && !Array.isArray(entry) && typeof entry.path === "string"));
236
+ const path = typeof latestCheckout?.path === "string" && latestCheckout.path.trim() ? latestCheckout.path.trim() : null;
237
+ if (path)
238
+ writeRepoServerProjectRoot(projectRoot, path);
239
+ return path;
240
+ } catch {
241
+ return null;
242
+ }
243
+ }
183
244
  function mergeHeaders(headers, authToken) {
184
245
  const merged = new Headers(headers);
185
246
  if (authToken) {
@@ -202,12 +263,65 @@ function diagnosticMessage(payload) {
202
263
  });
203
264
  return messages.length > 0 ? messages.join("; ") : null;
204
265
  }
266
+ var serverReachabilityCache = new Map;
267
+ async function probeServerReachability(baseUrl, authToken) {
268
+ try {
269
+ const response = await fetch(`${baseUrl.replace(/\/+$/, "")}/api/server/status`, {
270
+ headers: mergeHeaders(undefined, authToken),
271
+ signal: AbortSignal.timeout(1500)
272
+ });
273
+ return response.ok;
274
+ } catch {
275
+ return false;
276
+ }
277
+ }
278
+ function cachedServerReachability(projectRoot, baseUrl, authToken) {
279
+ const key = resolve2(projectRoot);
280
+ const cached = serverReachabilityCache.get(key);
281
+ if (cached)
282
+ return cached;
283
+ const probe = probeServerReachability(baseUrl, authToken);
284
+ serverReachabilityCache.set(key, probe);
285
+ return probe;
286
+ }
287
+ function describeSelectedServer(projectRoot, server) {
288
+ try {
289
+ const selected = resolveSelectedConnection(projectRoot);
290
+ if (selected) {
291
+ return {
292
+ alias: selected.alias,
293
+ target: selected.connection.kind === "remote" ? selected.connection.baseUrl : server.baseUrl
294
+ };
295
+ }
296
+ } catch {}
297
+ return { alias: server.connectionKind === "remote" ? "remote" : "local", target: server.baseUrl };
298
+ }
299
+ async function buildServerFailureContext(projectRoot, server) {
300
+ const { alias, target } = describeSelectedServer(projectRoot, server);
301
+ const reachable = await cachedServerReachability(projectRoot, server.baseUrl, server.authToken);
302
+ const reachability = reachable ? "server is reachable" : "server is unreachable";
303
+ return {
304
+ contextLine: `Currently connected to: ${alias} at ${target} (${reachability}).`,
305
+ hint: "Check the selected server with `rig server status`, or switch with `rig server use <alias|local>`."
306
+ };
307
+ }
205
308
  async function requestServerJson(context, pathname, init = {}) {
206
309
  const server = await ensureServerForCli(context.projectRoot);
207
- const response = await fetch(`${server.baseUrl}${pathname}`, {
208
- ...init,
209
- headers: mergeHeaders(init.headers, server.authToken)
210
- });
310
+ const headers = mergeHeaders(init.headers, server.authToken);
311
+ if (server.serverProjectRoot)
312
+ headers.set("x-rig-project-root", server.serverProjectRoot);
313
+ reportServerPhase(`${(init.method ?? "GET").toUpperCase()} ${pathname.split("?")[0]}\u2026`);
314
+ let response;
315
+ try {
316
+ response = await fetch(`${server.baseUrl}${pathname}`, {
317
+ ...init,
318
+ headers
319
+ });
320
+ } catch (error) {
321
+ const failure = await buildServerFailureContext(context.projectRoot, server);
322
+ throw new CliError(`Rig server request failed: ${error instanceof Error ? error.message : String(error)}
323
+ ${failure.contextLine}`, 1, { hint: failure.hint });
324
+ }
211
325
  const text = await response.text();
212
326
  const payload = text.trim().length > 0 ? (() => {
213
327
  try {
@@ -218,8 +332,12 @@ async function requestServerJson(context, pathname, init = {}) {
218
332
  })() : null;
219
333
  if (!response.ok) {
220
334
  const diagnostics = diagnosticMessage(payload);
221
- const detail = diagnostics ?? (text || response.statusText);
222
- throw new CliError2(`Rig server request failed (${response.status}): ${detail}`, 1);
335
+ const rawDetail = diagnostics ?? (text || response.statusText);
336
+ const detail = diagnostics ? rawDetail : rawDetail.split(`
337
+ `).map((line) => line.trim()).find((line) => line.length > 0)?.slice(0, 200) ?? response.statusText;
338
+ const failure = await buildServerFailureContext(context.projectRoot, server);
339
+ throw new CliError(`Rig server request failed (${response.status}): ${detail}
340
+ ${failure.contextLine}`, 1, { hint: failure.hint });
223
341
  }
224
342
  return payload;
225
343
  }
@@ -235,6 +353,209 @@ async function postGitHubTokenViaServer(context, token, options = {}) {
235
353
  });
236
354
  return payload && typeof payload === "object" && !Array.isArray(payload) ? payload : {};
237
355
  }
356
+ var RESUMABLE_RUN_STATUSES = new Set([
357
+ "created",
358
+ "preparing",
359
+ "running",
360
+ "validating",
361
+ "reviewing",
362
+ "stopped",
363
+ "failed",
364
+ "needs-attention",
365
+ "needs_attention"
366
+ ]);
367
+
368
+ // packages/cli/src/commands/_async-ui.ts
369
+ import pc from "picocolors";
370
+
371
+ // packages/cli/src/commands/_spinner.ts
372
+ var SPINNER_FRAMES = ["\u280B", "\u2819", "\u2839", "\u2838", "\u283C", "\u2834", "\u2826", "\u2827", "\u2807", "\u280F"];
373
+ function createTtySpinner(input) {
374
+ const output = input.output ?? process.stdout;
375
+ const isTty = output.isTTY === true;
376
+ const frames = input.frames && input.frames.length > 0 ? input.frames : SPINNER_FRAMES;
377
+ let label = input.label;
378
+ let frame = 0;
379
+ let paused = false;
380
+ let stopped = false;
381
+ let lastPrintedLabel = "";
382
+ const render = () => {
383
+ if (stopped || paused)
384
+ return;
385
+ if (!isTty) {
386
+ if (label !== lastPrintedLabel) {
387
+ output.write(`${label}
388
+ `);
389
+ lastPrintedLabel = label;
390
+ }
391
+ return;
392
+ }
393
+ frame = (frame + 1) % frames.length;
394
+ const glyph = frames[frame] ?? frames[0] ?? "";
395
+ output.write(`\r\x1B[2K${input.styleFrame ? input.styleFrame(glyph) : glyph} ${label}`);
396
+ };
397
+ const clearLine = () => {
398
+ if (isTty)
399
+ output.write("\r\x1B[2K");
400
+ };
401
+ render();
402
+ const timer = isTty ? setInterval(render, input.intervalMs ?? 120) : null;
403
+ return {
404
+ setLabel(next) {
405
+ label = next;
406
+ render();
407
+ },
408
+ pause() {
409
+ paused = true;
410
+ clearLine();
411
+ },
412
+ resume() {
413
+ if (stopped)
414
+ return;
415
+ paused = false;
416
+ render();
417
+ },
418
+ stop(finalLine) {
419
+ if (stopped)
420
+ return;
421
+ stopped = true;
422
+ if (timer)
423
+ clearInterval(timer);
424
+ clearLine();
425
+ if (finalLine)
426
+ output.write(`${finalLine}
427
+ `);
428
+ }
429
+ };
430
+ }
431
+
432
+ // packages/cli/src/app/theme.ts
433
+ var RIG_PALETTE = {
434
+ ink: "#f2f3f6",
435
+ ink2: "#aeb0ba",
436
+ ink3: "#6c6e79",
437
+ ink4: "#44464f",
438
+ accent: "#ccff4d",
439
+ accentDim: "#a9d63f",
440
+ cyan: "#56d8ff",
441
+ red: "#ff5d5d",
442
+ yellow: "#ffd24d"
443
+ };
444
+ function hexToRgb(hex) {
445
+ const value = hex.replace("#", "");
446
+ return [
447
+ Number.parseInt(value.slice(0, 2), 16),
448
+ Number.parseInt(value.slice(2, 4), 16),
449
+ Number.parseInt(value.slice(4, 6), 16)
450
+ ];
451
+ }
452
+ function fg(hex) {
453
+ const [r, g, b] = hexToRgb(hex);
454
+ return (text) => `\x1B[38;2;${r};${g};${b}m${text}\x1B[39m`;
455
+ }
456
+ var ink = fg(RIG_PALETTE.ink);
457
+ var ink2 = fg(RIG_PALETTE.ink2);
458
+ var ink3 = fg(RIG_PALETTE.ink3);
459
+ var ink4 = fg(RIG_PALETTE.ink4);
460
+ var accent = fg(RIG_PALETTE.accent);
461
+ var accentDim = fg(RIG_PALETTE.accentDim);
462
+ var cyan = fg(RIG_PALETTE.cyan);
463
+ var red = fg(RIG_PALETTE.red);
464
+ var yellow = fg(RIG_PALETTE.yellow);
465
+ function bold(text) {
466
+ return `\x1B[1m${text}\x1B[22m`;
467
+ }
468
+ var DRONE_ART = [
469
+ " .-=-. .-=-. ",
470
+ " ( !!! ) ( !!! ) ",
471
+ " '-=-'._ _.'-=-' ",
472
+ " '._ _.' ",
473
+ " '=$$$$$$$=.' ",
474
+ " =$$$$$$$$$$$= ",
475
+ " $$$@@@@@@@@@@$$$ ",
476
+ " $$$@@ @@$$$ ",
477
+ " $$@ ? @$$$ ",
478
+ " $$$@ '-' @$$$ ",
479
+ " $$$@@ @@$$$ ",
480
+ " $$$@@@@@@@@@@$$$ ",
481
+ " =$$$$$$$$$$$= ",
482
+ " '=$$$$$$$=.' ",
483
+ " _.' '._ ",
484
+ " .-=-.' '.-=-. ",
485
+ " ( !!! ) ( !!! ) ",
486
+ " '-=-' '-=-' "
487
+ ];
488
+ var EYE_FRAMES = ["@", "o", "."];
489
+ var DRONE_WIDTH = DRONE_ART[0].length;
490
+ var DRONE_HEIGHT = DRONE_ART.length;
491
+ var MICRO_BLADES = ["---", "\\\\\\", "|||", "///"];
492
+ function microDroneFrame(tick) {
493
+ const blade = MICRO_BLADES[tick % MICRO_BLADES.length];
494
+ const eye = EYE_FRAMES[Math.floor(tick / 2) % EYE_FRAMES.length];
495
+ return `(${blade})${eye}(${blade})`;
496
+ }
497
+ var MICRO_DRONE_FRAMES = Array.from({ length: 12 }, (_, index) => microDroneFrame(index));
498
+ function renderMicroDroneFrame(tick) {
499
+ const blade = MICRO_BLADES[tick % MICRO_BLADES.length];
500
+ const eye = EYE_FRAMES[Math.floor(tick / 2) % EYE_FRAMES.length];
501
+ return `${ink4("(")}${cyan(blade)}${ink4(")")}${bold(accent(eye))}${ink4("(")}${cyan(blade)}${ink4(")")}`;
502
+ }
503
+
504
+ // packages/cli/src/commands/_async-ui.ts
505
+ var DONE_SYMBOL = pc.green("\u25C7");
506
+ var FAIL_SYMBOL = pc.red("\u25A0");
507
+ var activeUpdate = null;
508
+ async function withSpinner(label, work, options = {}) {
509
+ if (options.outputMode === "json") {
510
+ return work(() => {});
511
+ }
512
+ if (activeUpdate) {
513
+ const outer = activeUpdate;
514
+ outer(label);
515
+ return work(outer);
516
+ }
517
+ const output = options.output ?? process.stderr;
518
+ const isTty = output.isTTY === true;
519
+ let lastLabel = label;
520
+ if (!isTty) {
521
+ output.write(`${label}
522
+ `);
523
+ const update2 = (next) => {
524
+ lastLabel = next;
525
+ };
526
+ activeUpdate = update2;
527
+ const previousListener2 = setServerPhaseListener(update2);
528
+ try {
529
+ return await work(update2);
530
+ } finally {
531
+ activeUpdate = null;
532
+ setServerPhaseListener(previousListener2);
533
+ }
534
+ }
535
+ const spinner = createTtySpinner({
536
+ label,
537
+ output,
538
+ frames: MICRO_DRONE_FRAMES,
539
+ styleFrame: (frame) => renderMicroDroneFrame(Math.max(0, MICRO_DRONE_FRAMES.indexOf(frame)))
540
+ });
541
+ const update = (next) => {
542
+ lastLabel = next;
543
+ spinner.setLabel(next);
544
+ };
545
+ activeUpdate = update;
546
+ const previousListener = setServerPhaseListener(update);
547
+ try {
548
+ const result = await work(update);
549
+ spinner.stop(options.doneLabel ? `${DONE_SYMBOL} ${options.doneLabel}` : undefined);
550
+ return result;
551
+ } catch (error) {
552
+ spinner.stop(`${FAIL_SYMBOL} ${lastLabel}`);
553
+ throw error;
554
+ } finally {
555
+ activeUpdate = null;
556
+ setServerPhaseListener(previousListener);
557
+ }
558
+ }
238
559
 
239
560
  // packages/cli/src/commands/github.ts
240
561
  function printPayload(context, payload, fallback) {
@@ -244,49 +565,50 @@ function printPayload(context, payload, fallback) {
244
565
  console.log(fallback);
245
566
  }
246
567
  function readGhToken() {
247
- const result = spawnSync2("gh", ["auth", "token"], { encoding: "utf8" });
568
+ const result = spawnSync("gh", ["auth", "token"], { encoding: "utf8" });
248
569
  if (result.status !== 0) {
249
570
  const detail = result.stderr?.trim() || result.stdout?.trim() || "gh auth token failed";
250
- throw new CliError2(`Could not import GitHub token from gh: ${detail}`, 1);
571
+ throw new CliError(`Could not import GitHub token from gh: ${detail}`, 1, { hint: "Sign in first with `gh auth login`, or store a token directly: `rig github auth token --token <token>`." });
251
572
  }
252
573
  const token = result.stdout.trim();
253
574
  if (!token)
254
- throw new CliError2("gh auth token returned an empty token.", 1);
575
+ throw new CliError("gh auth token returned an empty token.", 1, { hint: "Sign in with `gh auth login`, then re-run `rig github auth import-gh`." });
255
576
  return token;
256
577
  }
257
578
  async function executeGithub(context, args) {
258
579
  const [group, command, ...rest] = args;
259
580
  if (group !== "auth") {
260
- throw new CliError2("Usage: rig github auth <status|import-gh|token>", 1);
581
+ throw new CliError("Usage: rig github auth <status|import-gh|token>", 1);
261
582
  }
262
583
  switch (command) {
263
584
  case "status": {
264
585
  if (rest.length > 0)
265
- throw new CliError2("Usage: rig github auth status", 1);
266
- const status = await getGitHubAuthStatusViaServer(context);
586
+ throw new CliError("Usage: rig github auth status", 1);
587
+ const status = await withSpinner("Checking GitHub auth on the server\u2026", () => getGitHubAuthStatusViaServer(context), { outputMode: context.outputMode });
267
588
  printPayload(context, status, `GitHub auth: ${status.authenticated ? "authenticated" : "unauthenticated"}`);
268
589
  return { ok: true, group: "github", command: "auth status", details: status };
269
590
  }
270
591
  case "token": {
271
592
  const parsed = takeOption(rest, "--token");
272
593
  if (parsed.rest.length > 0)
273
- throw new CliError2("Usage: rig github auth token --token <token>", 1);
594
+ throw new CliError("Usage: rig github auth token --token <token>", 1);
274
595
  const token = parsed.value?.trim();
275
596
  if (!token)
276
- throw new CliError2("Missing --token value.", 1);
277
- const result = await postGitHubTokenViaServer(context, token);
597
+ throw new CliError("Missing --token value.", 1, { hint: "Re-run as `rig github auth token --token <token>`." });
598
+ const result = await withSpinner("Storing GitHub token on the server\u2026", () => postGitHubTokenViaServer(context, token), { outputMode: context.outputMode });
278
599
  printPayload(context, result, "GitHub token stored on the selected Rig server.");
279
600
  return { ok: true, group: "github", command: "auth token", details: result };
280
601
  }
281
602
  case "import-gh": {
282
603
  if (rest.length > 0)
283
- throw new CliError2("Usage: rig github auth import-gh", 1);
284
- const result = await postGitHubTokenViaServer(context, readGhToken());
604
+ throw new CliError("Usage: rig github auth import-gh", 1);
605
+ const importedToken = readGhToken();
606
+ const result = await withSpinner("Storing GitHub token on the server\u2026", () => postGitHubTokenViaServer(context, importedToken), { outputMode: context.outputMode });
285
607
  printPayload(context, result, "GitHub token imported from gh and stored on the selected Rig server.");
286
608
  return { ok: true, group: "github", command: "auth import-gh", details: result };
287
609
  }
288
610
  default:
289
- throw new CliError2("Usage: rig github auth <status|import-gh|token>", 1);
611
+ throw new CliError("Usage: rig github auth <status|import-gh|token>", 1);
290
612
  }
291
613
  }
292
614
  export {
@@ -0,0 +1,30 @@
1
+ import { type RunnerContext } from "../runner";
2
+ import type { CommandOutcome } from "@rig/runtime/control-plane/runtime/types";
3
+ export type InboxKind = "approvals" | "inputs";
4
+ export type InboxRecord = {
5
+ runId?: string;
6
+ taskId?: string | null;
7
+ requestId?: string | null;
8
+ status?: string | null;
9
+ record?: Record<string, unknown>;
10
+ };
11
+ /** Exported for the board's inbox view — same records `rig inbox` renders. */
12
+ export declare function listInboxRecords(context: Pick<RunnerContext, "projectRoot">, kind: InboxKind, filters: {
13
+ run?: string;
14
+ task?: string;
15
+ pendingOnly?: boolean;
16
+ }): Promise<InboxRecord[]>;
17
+ /**
18
+ * Pending-gate counts for proactive surfacing. Returns null when the server
19
+ * is unreachable — footer callers must never break the primary command.
20
+ */
21
+ export declare function readPendingInboxCounts(context: Pick<RunnerContext, "projectRoot">): Promise<{
22
+ approvals: number;
23
+ inputs: number;
24
+ } | null>;
25
+ /**
26
+ * One-line warning under task/run command output when human gates are
27
+ * waiting. Silent when there is nothing pending or the server is down.
28
+ */
29
+ export declare function printPendingInboxFooter(context: RunnerContext): Promise<void>;
30
+ export declare function executeInbox(context: RunnerContext, args: string[]): Promise<CommandOutcome>;