@h-rig/cli 0.0.6-alpha.1 → 0.0.6-alpha.100

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 (120) hide show
  1. package/README.md +18 -19
  2. package/dist/bin/build-rig-binaries.d.ts +2 -0
  3. package/dist/bin/build-rig-binaries.js +22 -10
  4. package/dist/bin/rig.d.ts +72 -0
  5. package/dist/bin/rig.js +15742 -7203
  6. package/dist/config/rig-default-config.yml +5 -0
  7. package/dist/src/app/drone-ui.d.ts +34 -0
  8. package/dist/src/app/drone-ui.js +278 -0
  9. package/dist/src/commands/_async-ui.d.ts +10 -0
  10. package/dist/src/commands/_async-ui.js +121 -0
  11. package/dist/src/commands/_cli-format.d.ts +56 -0
  12. package/dist/src/commands/_cli-format.js +319 -0
  13. package/dist/src/commands/_config-env.d.ts +18 -0
  14. package/dist/src/commands/_config-env.js +22 -0
  15. package/dist/src/commands/_connection-state.d.ts +54 -0
  16. package/dist/src/commands/_connection-state.js +75 -11
  17. package/dist/src/commands/_doctor-checks.d.ts +46 -0
  18. package/dist/src/commands/_doctor-checks.js +95 -244
  19. package/dist/src/commands/_help-catalog.d.ts +29 -0
  20. package/dist/src/commands/_help-catalog.js +146 -0
  21. package/dist/src/commands/_inprocess-services.d.ts +33 -0
  22. package/dist/src/commands/_inprocess-services.js +102 -0
  23. package/dist/src/commands/_json-output.d.ts +11 -0
  24. package/dist/src/commands/_json-output.js +60 -0
  25. package/dist/src/commands/_lazy-reconcile.d.ts +34 -0
  26. package/dist/src/commands/_lazy-reconcile.js +102 -0
  27. package/dist/src/commands/_parsers.d.ts +15 -0
  28. package/dist/src/commands/_parsers.js +18 -11
  29. package/dist/src/commands/_paths.d.ts +11 -0
  30. package/dist/src/commands/_paths.js +1 -1
  31. package/dist/src/commands/_pi-frontend.d.ts +35 -0
  32. package/dist/src/commands/_pi-frontend.js +64 -0
  33. package/dist/src/commands/_pi-install.d.ts +42 -0
  34. package/dist/src/commands/_pi-install.js +19 -36
  35. package/dist/src/commands/_policy.d.ts +8 -0
  36. package/dist/src/commands/_policy.js +67 -19
  37. package/dist/src/commands/_probes.d.ts +1 -0
  38. package/dist/src/commands/_run-bridge.d.ts +114 -0
  39. package/dist/src/commands/_run-bridge.js +387 -0
  40. package/dist/src/commands/_run-diagnostics.d.ts +9 -0
  41. package/dist/src/commands/_run-diagnostics.js +51 -0
  42. package/dist/src/commands/_run-driver-helpers.d.ts +26 -0
  43. package/dist/src/commands/_run-driver-helpers.js +79 -230
  44. package/dist/src/commands/_run-projection.d.ts +50 -0
  45. package/dist/src/commands/_run-projection.js +349 -0
  46. package/dist/src/commands/_run-subcommands.d.ts +3 -0
  47. package/dist/src/commands/_run-subcommands.js +31 -0
  48. package/dist/src/commands/_spinner.d.ts +25 -0
  49. package/dist/src/commands/_spinner.js +65 -0
  50. package/dist/src/commands/agent.d.ts +3 -0
  51. package/dist/src/commands/agent.js +8690 -235
  52. package/dist/src/commands/config.d.ts +3 -0
  53. package/dist/src/commands/config.js +181 -0
  54. package/dist/src/commands/dist.d.ts +28 -0
  55. package/dist/src/commands/dist.js +44 -29
  56. package/dist/src/commands/doctor.d.ts +3 -0
  57. package/dist/src/commands/doctor.js +214 -245
  58. package/dist/src/commands/github.d.ts +3 -0
  59. package/dist/src/commands/github.js +237 -171
  60. package/dist/src/commands/inbox.d.ts +28 -0
  61. package/dist/src/commands/inbox.js +530 -111
  62. package/dist/src/commands/init.d.ts +64 -0
  63. package/dist/src/commands/init.js +941 -654
  64. package/dist/src/commands/inspect.d.ts +20 -0
  65. package/dist/src/commands/inspect.js +709 -119
  66. package/dist/src/commands/pi.d.ts +3 -0
  67. package/dist/src/commands/pi.js +177 -0
  68. package/dist/src/commands/plugin.d.ts +16 -0
  69. package/dist/src/commands/plugin.js +576 -29
  70. package/dist/src/commands/profile-and-review.d.ts +4 -0
  71. package/dist/src/commands/profile-and-review.js +112 -67
  72. package/dist/src/commands/queue.d.ts +3 -0
  73. package/dist/src/commands/queue.js +20 -20
  74. package/dist/src/commands/remote.d.ts +3 -0
  75. package/dist/src/commands/remote.js +883 -50
  76. package/dist/src/commands/repo-git-harness.d.ts +5 -0
  77. package/dist/src/commands/repo-git-harness.js +78 -28
  78. package/dist/src/commands/run.d.ts +21 -0
  79. package/dist/src/commands/run.js +17732 -618
  80. package/dist/src/commands/server.d.ts +3 -0
  81. package/dist/src/commands/server.js +166 -285
  82. package/dist/src/commands/setup.d.ts +16 -0
  83. package/dist/src/commands/setup.js +230 -267
  84. package/dist/src/commands/stats.d.ts +15 -0
  85. package/dist/src/commands/stats.js +632 -0
  86. package/dist/src/commands/task-run-driver.d.ts +94 -0
  87. package/dist/src/commands/task-run-driver.js +120 -1856
  88. package/dist/src/commands/task.d.ts +35 -0
  89. package/dist/src/commands/task.js +676 -1183
  90. package/dist/src/commands/test.d.ts +3 -0
  91. package/dist/src/commands/test.js +15 -8
  92. package/dist/src/commands/triage.d.ts +11 -0
  93. package/dist/src/commands/triage.js +227 -0
  94. package/dist/src/commands/workspace.d.ts +3 -0
  95. package/dist/src/commands/workspace.js +18 -11
  96. package/dist/src/commands.d.ts +13 -0
  97. package/dist/src/commands.js +16760 -7779
  98. package/dist/src/index.d.ts +4 -0
  99. package/dist/src/index.js +16880 -8223
  100. package/dist/src/launcher.d.ts +61 -0
  101. package/dist/src/launcher.js +81 -13
  102. package/dist/src/operator-cli.d.ts +2 -0
  103. package/dist/src/operator-cli.js +17867 -0
  104. package/dist/src/operator-entry.d.ts +1 -0
  105. package/dist/src/operator-entry.js +3 -0
  106. package/dist/src/runner.d.ts +47 -0
  107. package/dist/src/runner.js +19 -24
  108. package/dist/src/withMutedConsole.d.ts +2 -0
  109. package/package.json +21 -7
  110. package/dist/src/commands/_authority-runs.js +0 -110
  111. package/dist/src/commands/_operator-view.js +0 -322
  112. package/dist/src/commands/_preflight.js +0 -460
  113. package/dist/src/commands/_server-client.js +0 -364
  114. package/dist/src/commands/_snapshot-upload.js +0 -313
  115. package/dist/src/commands/_task-picker.js +0 -48
  116. package/dist/src/commands/browser.js +0 -890
  117. package/dist/src/commands/connect.js +0 -180
  118. package/dist/src/commands/inspector.js +0 -256
  119. package/dist/src/commands/task-report-bug.js +0 -1083
  120. package/dist/src/report-bug.js +0 -260
@@ -1,15 +1,24 @@
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
+ import { mkdirSync as mkdirSync2, writeFileSync as writeFileSync2 } from "fs";
5
+ import { dirname as dirname2, resolve as resolve3 } from "path";
4
6
 
5
7
  // packages/cli/src/runner.ts
6
8
  import { EventBus } from "@rig/runtime/control-plane/runtime/events";
7
- import { CliError } from "@rig/runtime/control-plane/errors";
9
+ import { CliError as RuntimeCliError } from "@rig/runtime/control-plane/errors";
8
10
  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
11
  import { buildBinary } from "@rig/runtime/control-plane/runtime/isolation";
12
- import { CliError as CliError2 } from "@rig/runtime/control-plane/errors";
12
+
13
+ class CliError extends RuntimeCliError {
14
+ hint;
15
+ constructor(message, exitCode = 1, options = {}) {
16
+ super(message, exitCode);
17
+ if (options.hint?.trim()) {
18
+ this.hint = options.hint.trim();
19
+ }
20
+ }
21
+ }
13
22
  function takeOption(args, option) {
14
23
  const rest = [];
15
24
  let value;
@@ -18,7 +27,7 @@ function takeOption(args, option) {
18
27
  if (current === option) {
19
28
  const next = args[index + 1];
20
29
  if (!next || next.startsWith("-")) {
21
- throw new CliError(`Missing value for ${option}`);
30
+ throw new CliError(`Missing value for ${option}`, 1, { hint: `Provide a value after ${option}, e.g. \`${option} <value>\`.` });
22
31
  }
23
32
  value = next;
24
33
  index += 1;
@@ -31,25 +40,53 @@ function takeOption(args, option) {
31
40
  return { value, rest };
32
41
  }
33
42
 
34
- // packages/cli/src/commands/_server-client.ts
35
- import { spawnSync } from "child_process";
36
- import { ensureLocalRigServerConnection } from "@rig/runtime/local-server";
43
+ // packages/cli/src/commands/_inprocess-services.ts
44
+ import { resolve } from "path";
45
+ import {
46
+ beginGitHubDeviceFlow,
47
+ checkGitHubRepoPermissions,
48
+ createGitHubAuthStore,
49
+ listGitHubProjects,
50
+ pollGitHubDeviceFlow,
51
+ resolveGitHubAuthStatus,
52
+ resolveProjectStatusField,
53
+ saveGitHubTokenForProject
54
+ } from "@rig/runtime/control-plane/github/index";
55
+ var scopedGitHubBearerTokens = new Map;
56
+ function cleanToken(value) {
57
+ const trimmed = value?.trim();
58
+ return trimmed ? trimmed : null;
59
+ }
60
+ function oauthClientId() {
61
+ return cleanToken(process.env.RIG_GITHUB_OAUTH_CLIENT_ID);
62
+ }
63
+ function setGitHubBearerTokenForCurrentProcess(token, projectRoot) {
64
+ scopedGitHubBearerTokens.set(resolve(projectRoot ?? process.cwd()), cleanToken(token));
65
+ }
66
+ async function getGitHubAuthStatusInProcess(context) {
67
+ return { ok: true, ...resolveGitHubAuthStatus({ projectRoot: context.projectRoot, oauthConfigured: Boolean(oauthClientId()) }) };
68
+ }
69
+ async function postGitHubTokenInProcess(context, token, options = {}) {
70
+ const targetRoot = options.projectRoot?.trim() || context.projectRoot;
71
+ const result = await saveGitHubTokenForProject({
72
+ projectRoot: targetRoot,
73
+ token,
74
+ tokenSource: "manual-token",
75
+ selectedRepo: options.selectedRepo ?? null
76
+ });
77
+ const store = createGitHubAuthStore(targetRoot);
78
+ const session = store.createApiSession();
79
+ if (targetRoot !== context.projectRoot) {
80
+ store.copyToLocalProjectRoot(context.projectRoot);
81
+ }
82
+ return { ...result, authenticated: result.signedIn, apiSessionToken: session.token };
83
+ }
37
84
 
38
85
  // packages/cli/src/commands/_connection-state.ts
39
86
  import { existsSync, mkdirSync, readFileSync, writeFileSync } from "fs";
40
- import { homedir } from "os";
41
- import { dirname, resolve } from "path";
42
- function resolveGlobalConnectionsPath(env = process.env) {
43
- const explicit = env.RIG_CONNECTIONS_FILE?.trim();
44
- if (explicit)
45
- return resolve(explicit);
46
- const stateDir = env.RIG_GLOBAL_STATE_DIR?.trim();
47
- if (stateDir)
48
- return resolve(stateDir, "connections.json");
49
- return resolve(homedir(), ".rig", "connections.json");
50
- }
87
+ import { dirname, resolve as resolve2 } from "path";
51
88
  function resolveRepoConnectionPath(projectRoot) {
52
- return resolve(projectRoot, ".rig", "state", "connection.json");
89
+ return resolve2(projectRoot, ".rig", "state", "connection.json");
53
90
  }
54
91
  function readJsonFile(path) {
55
92
  if (!existsSync(path))
@@ -57,37 +94,8 @@ function readJsonFile(path) {
57
94
  try {
58
95
  return JSON.parse(readFileSync(path, "utf8"));
59
96
  } catch (error) {
60
- throw new CliError2(`Invalid Rig connection state at ${path}: ${error instanceof Error ? error.message : String(error)}`, 1);
61
- }
62
- }
63
- function normalizeConnection(value) {
64
- if (!value || typeof value !== "object" || Array.isArray(value))
65
- return null;
66
- const record = value;
67
- if (record.kind === "local")
68
- return { kind: "local", mode: "auto" };
69
- if (record.kind === "remote" && typeof record.baseUrl === "string" && record.baseUrl.trim()) {
70
- const baseUrl = record.baseUrl.trim().replace(/\/+$/, "");
71
- return { kind: "remote", baseUrl };
97
+ 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>`." });
72
98
  }
73
- return null;
74
- }
75
- function readGlobalConnections(options = {}) {
76
- const path = resolveGlobalConnectionsPath(options.env ?? process.env);
77
- const payload = readJsonFile(path);
78
- if (!payload || typeof payload !== "object" || Array.isArray(payload)) {
79
- return { connections: {} };
80
- }
81
- const rawConnections = payload.connections;
82
- const connections = {};
83
- if (rawConnections && typeof rawConnections === "object" && !Array.isArray(rawConnections)) {
84
- for (const [alias, raw] of Object.entries(rawConnections)) {
85
- const connection = normalizeConnection(raw);
86
- if (connection)
87
- connections[alias] = connection;
88
- }
89
- }
90
- return { connections };
91
99
  }
92
100
  function readRepoConnection(projectRoot) {
93
101
  const payload = readJsonFile(resolveRepoConnectionPath(projectRoot));
@@ -100,122 +108,129 @@ function readRepoConnection(projectRoot) {
100
108
  return {
101
109
  selected,
102
110
  project: typeof record.project === "string" ? record.project : undefined,
103
- linkedAt: typeof record.linkedAt === "string" ? record.linkedAt : undefined
111
+ linkedAt: typeof record.linkedAt === "string" ? record.linkedAt : undefined,
112
+ serverProjectRoot: typeof record.serverProjectRoot === "string" && record.serverProjectRoot.trim() ? record.serverProjectRoot.trim() : undefined,
113
+ serverProjectRootAlias: typeof record.serverProjectRootAlias === "string" && record.serverProjectRootAlias.trim() ? record.serverProjectRootAlias.trim() : undefined,
114
+ serverProjectRootBaseUrl: typeof record.serverProjectRootBaseUrl === "string" && record.serverProjectRootBaseUrl.trim() ? record.serverProjectRootBaseUrl.trim().replace(/\/+$/, "") : undefined
104
115
  };
105
116
  }
106
- function resolveSelectedConnection(projectRoot, options = {}) {
107
- const repo = readRepoConnection(projectRoot);
108
- if (!repo)
109
- return null;
110
- if (repo.selected === "local")
111
- return { alias: "local", connection: { kind: "local", mode: "auto" } };
112
- const global = readGlobalConnections(options);
113
- const connection = global.connections[repo.selected];
114
- if (!connection) {
115
- throw new CliError2(`Selected Rig connection "${repo.selected}" was not found. Run \`rig connect list\` or \`rig connect use local\`.`, 1);
116
- }
117
- return { alias: repo.selected, connection };
118
- }
119
117
 
120
- // packages/cli/src/commands/_server-client.ts
121
- var cachedGitHubBearerToken;
122
- function cleanToken(value) {
123
- const trimmed = value?.trim();
124
- return trimmed ? trimmed : null;
125
- }
126
- function readGitHubBearerTokenForRemote() {
127
- if (cachedGitHubBearerToken !== undefined)
128
- return cachedGitHubBearerToken;
129
- const envToken = cleanToken(process.env.RIG_GITHUB_TOKEN) ?? cleanToken(process.env.GITHUB_TOKEN) ?? cleanToken(process.env.GH_TOKEN);
130
- if (envToken) {
131
- cachedGitHubBearerToken = envToken;
132
- return cachedGitHubBearerToken;
133
- }
134
- const result = spawnSync("gh", ["auth", "token"], {
135
- encoding: "utf8",
136
- timeout: 5000,
137
- stdio: ["ignore", "pipe", "ignore"]
138
- });
139
- cachedGitHubBearerToken = result.status === 0 ? cleanToken(result.stdout) : null;
140
- return cachedGitHubBearerToken;
141
- }
142
- async function ensureServerForCli(projectRoot) {
143
- try {
144
- const selected = resolveSelectedConnection(projectRoot);
145
- if (selected?.connection.kind === "remote") {
146
- return {
147
- baseUrl: selected.connection.baseUrl,
148
- authToken: readGitHubBearerTokenForRemote(),
149
- connectionKind: "remote"
150
- };
118
+ // packages/cli/src/commands/_async-ui.ts
119
+ import pc from "picocolors";
120
+
121
+ // packages/cli/src/commands/_spinner.ts
122
+ var SPINNER_FRAMES = ["\u280B", "\u2819", "\u2839", "\u2838", "\u283C", "\u2834", "\u2826", "\u2827", "\u2807", "\u280F"];
123
+ function createTtySpinner(input) {
124
+ const output = input.output ?? process.stdout;
125
+ const isTty = output.isTTY === true;
126
+ const frames = input.frames && input.frames.length > 0 ? input.frames : SPINNER_FRAMES;
127
+ let label = input.label;
128
+ let frame = 0;
129
+ let paused = false;
130
+ let stopped = false;
131
+ let lastPrintedLabel = "";
132
+ const render = () => {
133
+ if (stopped || paused)
134
+ return;
135
+ if (!isTty) {
136
+ if (label !== lastPrintedLabel) {
137
+ output.write(`${label}
138
+ `);
139
+ lastPrintedLabel = label;
140
+ }
141
+ return;
151
142
  }
152
- const connection = await ensureLocalRigServerConnection(projectRoot);
153
- return {
154
- baseUrl: connection.baseUrl,
155
- authToken: connection.authToken,
156
- connectionKind: "local"
157
- };
158
- } catch (error) {
159
- if (error instanceof Error) {
160
- throw new CliError2(error.message, 1);
143
+ frame = (frame + 1) % frames.length;
144
+ const glyph = frames[frame] ?? frames[0] ?? "";
145
+ output.write(`\r\x1B[2K${input.styleFrame ? input.styleFrame(glyph) : glyph} ${label}`);
146
+ };
147
+ const clearLine = () => {
148
+ if (isTty)
149
+ output.write("\r\x1B[2K");
150
+ };
151
+ render();
152
+ const timer = isTty ? setInterval(render, input.intervalMs ?? 16) : null;
153
+ return {
154
+ setLabel(next) {
155
+ label = next;
156
+ render();
157
+ },
158
+ pause() {
159
+ paused = true;
160
+ clearLine();
161
+ },
162
+ resume() {
163
+ if (stopped)
164
+ return;
165
+ paused = false;
166
+ render();
167
+ },
168
+ stop(finalLine) {
169
+ if (stopped)
170
+ return;
171
+ stopped = true;
172
+ if (timer)
173
+ clearInterval(timer);
174
+ clearLine();
175
+ if (finalLine)
176
+ output.write(`${finalLine}
177
+ `);
161
178
  }
162
- throw error;
163
- }
179
+ };
164
180
  }
165
- function mergeHeaders(headers, authToken) {
166
- const merged = new Headers(headers);
167
- if (authToken) {
168
- merged.set("authorization", `Bearer ${authToken}`);
181
+
182
+ // packages/cli/src/commands/_async-ui.ts
183
+ var FRAMES = ["\u25D0", "\u25D3", "\u25D1", "\u25D2"];
184
+ var DONE_SYMBOL = pc.green("\u25C7");
185
+ var FAIL_SYMBOL = pc.red("\u25A0");
186
+ var activeUpdate = null;
187
+ async function withSpinner(label, work, options = {}) {
188
+ if (options.outputMode === "json") {
189
+ return work(() => {
190
+ return;
191
+ });
169
192
  }
170
- return merged;
171
- }
172
- function diagnosticMessage(payload) {
173
- if (!payload || typeof payload !== "object" || Array.isArray(payload))
174
- return null;
175
- const record = payload;
176
- const diagnostics = Array.isArray(record.diagnostics) ? record.diagnostics : [];
177
- const messages = diagnostics.flatMap((entry) => {
178
- if (!entry || typeof entry !== "object" || Array.isArray(entry))
179
- return [];
180
- const diagnostic = entry;
181
- const kind = typeof diagnostic.kind === "string" ? diagnostic.kind : "task-source";
182
- const message = typeof diagnostic.message === "string" ? diagnostic.message : null;
183
- return message ? [`${kind}: ${message}`] : [];
184
- });
185
- return messages.length > 0 ? messages.join("; ") : null;
186
- }
187
- async function requestServerJson(context, pathname, init = {}) {
188
- const server = await ensureServerForCli(context.projectRoot);
189
- const response = await fetch(`${server.baseUrl}${pathname}`, {
190
- ...init,
191
- headers: mergeHeaders(init.headers, server.authToken)
192
- });
193
- const text = await response.text();
194
- const payload = text.trim().length > 0 ? (() => {
193
+ if (activeUpdate) {
194
+ activeUpdate(label);
195
+ return work(activeUpdate);
196
+ }
197
+ const output = options.output ?? process.stderr;
198
+ const isTty = output.isTTY === true;
199
+ let lastLabel = label;
200
+ if (!isTty) {
201
+ output.write(`${label}
202
+ `);
203
+ const update2 = (next) => {
204
+ lastLabel = next;
205
+ };
206
+ activeUpdate = update2;
195
207
  try {
196
- return JSON.parse(text);
197
- } catch {
198
- return null;
208
+ return await work(update2);
209
+ } finally {
210
+ activeUpdate = null;
199
211
  }
200
- })() : null;
201
- if (!response.ok) {
202
- const diagnostics = diagnosticMessage(payload);
203
- const detail = diagnostics ?? (text || response.statusText);
204
- throw new CliError2(`Rig server request failed (${response.status}): ${detail}`, 1);
205
212
  }
206
- return payload;
207
- }
208
- async function getGitHubAuthStatusViaServer(context) {
209
- const payload = await requestServerJson(context, "/api/github/auth/status");
210
- return payload && typeof payload === "object" && !Array.isArray(payload) ? payload : {};
211
- }
212
- async function postGitHubTokenViaServer(context, token, options = {}) {
213
- const payload = await requestServerJson(context, "/api/github/auth/token", {
214
- method: "POST",
215
- headers: { "content-type": "application/json" },
216
- body: JSON.stringify({ token, selectedRepo: options.selectedRepo })
213
+ const spinner = createTtySpinner({
214
+ label,
215
+ output,
216
+ frames: FRAMES,
217
+ styleFrame: (frame) => pc.cyan(frame)
217
218
  });
218
- return payload && typeof payload === "object" && !Array.isArray(payload) ? payload : {};
219
+ const update = (next) => {
220
+ lastLabel = next;
221
+ spinner.setLabel(next);
222
+ };
223
+ activeUpdate = update;
224
+ try {
225
+ const result = await work(update);
226
+ spinner.stop(options.doneLabel ? `${DONE_SYMBOL} ${options.doneLabel}` : undefined);
227
+ return result;
228
+ } catch (error) {
229
+ spinner.stop(`${FAIL_SYMBOL} ${lastLabel}`);
230
+ throw error;
231
+ } finally {
232
+ activeUpdate = null;
233
+ }
219
234
  }
220
235
 
221
236
  // packages/cli/src/commands/github.ts
@@ -225,50 +240,101 @@ function printPayload(context, payload, fallback) {
225
240
  else
226
241
  console.log(fallback);
227
242
  }
243
+ function apiSessionTokenFrom(payload) {
244
+ if (!payload || typeof payload !== "object" || Array.isArray(payload))
245
+ return null;
246
+ return payloadString(payload, "apiSessionToken");
247
+ }
248
+ function payloadString(payload, key) {
249
+ const value = Reflect.get(payload, key);
250
+ return typeof value === "string" && value.trim() ? value.trim() : null;
251
+ }
252
+ function payloadRecord(payload, key) {
253
+ const value = Reflect.get(payload, key);
254
+ return value && typeof value === "object" && !Array.isArray(value) ? value : null;
255
+ }
256
+ function remoteNamespaceMetadata(result) {
257
+ const namespace = payloadRecord(result, "userNamespace");
258
+ return {
259
+ ...payloadString(result, "login") ? { login: payloadString(result, "login") } : {},
260
+ ...payloadString(result, "userId") ? { userId: payloadString(result, "userId") } : {},
261
+ ...namespace && payloadString(namespace, "key") ? { userNamespaceKey: payloadString(namespace, "key") } : {},
262
+ ...namespace && payloadString(namespace, "root") ? { userNamespaceRoot: payloadString(namespace, "root") } : {},
263
+ ...namespace && payloadString(namespace, "checkoutBaseDir") ? { checkoutBaseDir: payloadString(namespace, "checkoutBaseDir") } : {},
264
+ ...namespace && payloadString(namespace, "snapshotBaseDir") ? { snapshotBaseDir: payloadString(namespace, "snapshotBaseDir") } : {}
265
+ };
266
+ }
267
+ function persistRemoteAuthSession(context, source, result, fallbackToken) {
268
+ const apiSessionToken = result.apiSessionToken || apiSessionTokenFrom(result);
269
+ setGitHubBearerTokenForCurrentProcess(apiSessionToken ?? fallbackToken, context.projectRoot);
270
+ if (!apiSessionToken)
271
+ return;
272
+ const repo = readRepoConnection(context.projectRoot);
273
+ const path = resolve3(context.projectRoot, ".rig", "state", "github-auth.json");
274
+ mkdirSync2(dirname2(path), { recursive: true });
275
+ writeFileSync2(path, `${JSON.stringify({
276
+ authenticated: true,
277
+ source,
278
+ storedOnServer: true,
279
+ ...repo?.project ? { selectedRepo: repo.project } : {},
280
+ ...remoteNamespaceMetadata(result),
281
+ apiSessionToken,
282
+ updatedAt: new Date().toISOString()
283
+ }, null, 2)}
284
+ `, "utf8");
285
+ }
286
+ function isSignedIn(status) {
287
+ return status.signedIn === true;
288
+ }
228
289
  function readGhToken() {
229
- const result = spawnSync2("gh", ["auth", "token"], { encoding: "utf8" });
290
+ const result = spawnSync("gh", ["auth", "token"], { encoding: "utf8" });
230
291
  if (result.status !== 0) {
231
292
  const detail = result.stderr?.trim() || result.stdout?.trim() || "gh auth token failed";
232
- throw new CliError2(`Could not import GitHub token from gh: ${detail}`, 1);
293
+ 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>`." });
233
294
  }
234
295
  const token = result.stdout.trim();
235
296
  if (!token)
236
- throw new CliError2("gh auth token returned an empty token.", 1);
297
+ 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`." });
237
298
  return token;
238
299
  }
239
300
  async function executeGithub(context, args) {
240
301
  const [group, command, ...rest] = args;
241
302
  if (group !== "auth") {
242
- throw new CliError2("Usage: rig github auth <status|import-gh|token>", 1);
303
+ throw new CliError("Usage: rig github auth <status|import-gh|token>", 1);
243
304
  }
244
305
  switch (command) {
245
306
  case "status": {
246
307
  if (rest.length > 0)
247
- throw new CliError2("Usage: rig github auth status", 1);
248
- const status = await getGitHubAuthStatusViaServer(context);
249
- printPayload(context, status, `GitHub auth: ${status.authenticated ? "authenticated" : "unauthenticated"}`);
308
+ throw new CliError("Usage: rig github auth status", 1);
309
+ const status = await withSpinner("Checking GitHub auth on the server\u2026", () => getGitHubAuthStatusInProcess(context), { outputMode: context.outputMode });
310
+ printPayload(context, status, `GitHub auth: ${isSignedIn(status) ? "authenticated" : "unauthenticated"}`);
250
311
  return { ok: true, group: "github", command: "auth status", details: status };
251
312
  }
252
313
  case "token": {
253
314
  const parsed = takeOption(rest, "--token");
254
315
  if (parsed.rest.length > 0)
255
- throw new CliError2("Usage: rig github auth token --token <token>", 1);
316
+ throw new CliError("Usage: rig github auth token --token <token>", 1);
256
317
  const token = parsed.value?.trim();
257
318
  if (!token)
258
- throw new CliError2("Missing --token value.", 1);
259
- const result = await postGitHubTokenViaServer(context, token);
260
- printPayload(context, result, "GitHub token stored on the selected Rig server.");
319
+ throw new CliError("Missing --token value.", 1, { hint: "Re-run as `rig github auth token --token <token>`." });
320
+ const repo = readRepoConnection(context.projectRoot);
321
+ const result = await withSpinner("Storing GitHub token on the server\u2026", () => postGitHubTokenInProcess(context, token, repo?.project ? { selectedRepo: repo.project } : {}), { outputMode: context.outputMode });
322
+ persistRemoteAuthSession(context, "token", result, token);
323
+ printPayload(context, result, "GitHub token stored on the selected server.");
261
324
  return { ok: true, group: "github", command: "auth token", details: result };
262
325
  }
263
326
  case "import-gh": {
264
327
  if (rest.length > 0)
265
- throw new CliError2("Usage: rig github auth import-gh", 1);
266
- const result = await postGitHubTokenViaServer(context, readGhToken());
267
- printPayload(context, result, "GitHub token imported from gh and stored on the selected Rig server.");
328
+ throw new CliError("Usage: rig github auth import-gh", 1);
329
+ const importedToken = readGhToken();
330
+ const repo = readRepoConnection(context.projectRoot);
331
+ const result = await withSpinner("Storing GitHub token on the server\u2026", () => postGitHubTokenInProcess(context, importedToken, repo?.project ? { selectedRepo: repo.project } : {}), { outputMode: context.outputMode });
332
+ persistRemoteAuthSession(context, "gh", result, importedToken);
333
+ printPayload(context, result, "GitHub token imported from gh and stored on the selected server.");
268
334
  return { ok: true, group: "github", command: "auth import-gh", details: result };
269
335
  }
270
336
  default:
271
- throw new CliError2("Usage: rig github auth <status|import-gh|token>", 1);
337
+ throw new CliError("Usage: rig github auth <status|import-gh|token>", 1);
272
338
  }
273
339
  }
274
340
  export {
@@ -0,0 +1,28 @@
1
+ import { type RunnerContext } from "../runner";
2
+ import type { CommandOutcome } from "@rig/runtime";
3
+ import { type RunProjectionRecord } from "./_run-projection";
4
+ export type InboxKind = "approvals" | "inputs";
5
+ export interface InboxFilters {
6
+ readonly run?: string;
7
+ readonly task?: string;
8
+ }
9
+ export interface InboxRecord {
10
+ readonly runId: string;
11
+ readonly taskId: string | null;
12
+ readonly requestId: string;
13
+ readonly status: "pending";
14
+ readonly prompt: string;
15
+ readonly requestedAt: string;
16
+ readonly payload: unknown;
17
+ }
18
+ /** Every pending inbox request across discoverable runs, optionally filtered by run/task. */
19
+ export interface InboxDeps {
20
+ readonly listRuns?: (projectRoot: string) => Promise<RunProjectionRecord[]>;
21
+ }
22
+ export declare function listInboxRecords(context: Pick<RunnerContext, "projectRoot">, kind: InboxKind, filters?: InboxFilters, deps?: InboxDeps): Promise<InboxRecord[]>;
23
+ /** Pending-gate counts for proactive surfacing; null-safe for footer callers. */
24
+ export declare function readPendingInboxCounts(context: Pick<RunnerContext, "projectRoot">): Promise<{
25
+ approvals: number;
26
+ inputs: number;
27
+ } | null>;
28
+ export declare function executeInbox(context: RunnerContext, args: string[], deps?: InboxDeps): Promise<CommandOutcome>;