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

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 (116) 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 +15637 -7344
  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/_connection-state.d.ts +54 -0
  14. package/dist/src/commands/_connection-state.js +75 -11
  15. package/dist/src/commands/_doctor-checks.d.ts +46 -0
  16. package/dist/src/commands/_doctor-checks.js +100 -267
  17. package/dist/src/commands/_help-catalog.d.ts +51 -0
  18. package/dist/src/commands/_help-catalog.js +502 -0
  19. package/dist/src/commands/_inprocess-services.d.ts +33 -0
  20. package/dist/src/commands/_inprocess-services.js +102 -0
  21. package/dist/src/commands/_json-output.d.ts +11 -0
  22. package/dist/src/commands/_json-output.js +60 -0
  23. package/dist/src/commands/_lazy-reconcile.d.ts +34 -0
  24. package/dist/src/commands/_lazy-reconcile.js +102 -0
  25. package/dist/src/commands/_parsers.d.ts +15 -0
  26. package/dist/src/commands/_parsers.js +18 -11
  27. package/dist/src/commands/_paths.d.ts +11 -0
  28. package/dist/src/commands/_paths.js +1 -1
  29. package/dist/src/commands/_pi-frontend.d.ts +35 -0
  30. package/dist/src/commands/_pi-frontend.js +64 -0
  31. package/dist/src/commands/_pi-install.d.ts +42 -0
  32. package/dist/src/commands/_pi-install.js +19 -36
  33. package/dist/src/commands/_policy.d.ts +8 -0
  34. package/dist/src/commands/_policy.js +67 -19
  35. package/dist/src/commands/_probes.d.ts +1 -0
  36. package/dist/src/commands/_run-bridge.d.ts +114 -0
  37. package/dist/src/commands/_run-bridge.js +387 -0
  38. package/dist/src/commands/_run-diagnostics.d.ts +9 -0
  39. package/dist/src/commands/_run-diagnostics.js +51 -0
  40. package/dist/src/commands/_run-driver-helpers.d.ts +26 -0
  41. package/dist/src/commands/_run-driver-helpers.js +79 -230
  42. package/dist/src/commands/_run-projection.d.ts +50 -0
  43. package/dist/src/commands/_run-projection.js +349 -0
  44. package/dist/src/commands/_run-subcommands.d.ts +3 -0
  45. package/dist/src/commands/_run-subcommands.js +31 -0
  46. package/dist/src/commands/_spinner.d.ts +25 -0
  47. package/dist/src/commands/_spinner.js +65 -0
  48. package/dist/src/commands/agent.d.ts +3 -0
  49. package/dist/src/commands/agent.js +8575 -236
  50. package/dist/src/commands/dist.d.ts +28 -0
  51. package/dist/src/commands/dist.js +44 -29
  52. package/dist/src/commands/doctor.d.ts +3 -0
  53. package/dist/src/commands/doctor.js +219 -268
  54. package/dist/src/commands/github.d.ts +3 -0
  55. package/dist/src/commands/github.js +236 -188
  56. package/dist/src/commands/inbox.d.ts +28 -0
  57. package/dist/src/commands/inbox.js +530 -111
  58. package/dist/src/commands/init.d.ts +64 -0
  59. package/dist/src/commands/init.js +912 -705
  60. package/dist/src/commands/inspect.d.ts +20 -0
  61. package/dist/src/commands/inspect.js +709 -119
  62. package/dist/src/commands/pi.d.ts +3 -0
  63. package/dist/src/commands/pi.js +177 -0
  64. package/dist/src/commands/plugin.d.ts +16 -0
  65. package/dist/src/commands/plugin.js +576 -29
  66. package/dist/src/commands/profile-and-review.d.ts +4 -0
  67. package/dist/src/commands/profile-and-review.js +112 -67
  68. package/dist/src/commands/queue.d.ts +3 -0
  69. package/dist/src/commands/queue.js +20 -20
  70. package/dist/src/commands/remote.d.ts +3 -0
  71. package/dist/src/commands/remote.js +880 -50
  72. package/dist/src/commands/repo-git-harness.d.ts +5 -0
  73. package/dist/src/commands/repo-git-harness.js +78 -28
  74. package/dist/src/commands/run.d.ts +21 -0
  75. package/dist/src/commands/run.js +17701 -661
  76. package/dist/src/commands/server.d.ts +3 -0
  77. package/dist/src/commands/server.js +166 -303
  78. package/dist/src/commands/setup.d.ts +16 -0
  79. package/dist/src/commands/setup.js +244 -299
  80. package/dist/src/commands/stats.d.ts +15 -0
  81. package/dist/src/commands/stats.js +979 -0
  82. package/dist/src/commands/task-run-driver.d.ts +94 -0
  83. package/dist/src/commands/task-run-driver.js +116 -1961
  84. package/dist/src/commands/task.d.ts +35 -0
  85. package/dist/src/commands/task.js +676 -1201
  86. package/dist/src/commands/test.d.ts +3 -0
  87. package/dist/src/commands/test.js +15 -8
  88. package/dist/src/commands/triage.d.ts +11 -0
  89. package/dist/src/commands/triage.js +227 -0
  90. package/dist/src/commands/workspace.d.ts +3 -0
  91. package/dist/src/commands/workspace.js +18 -11
  92. package/dist/src/commands.d.ts +13 -0
  93. package/dist/src/commands.js +16792 -8055
  94. package/dist/src/index.d.ts +4 -0
  95. package/dist/src/index.js +16862 -8450
  96. package/dist/src/launcher.d.ts +61 -0
  97. package/dist/src/launcher.js +81 -13
  98. package/dist/src/operator-cli.d.ts +2 -0
  99. package/dist/src/operator-cli.js +17837 -0
  100. package/dist/src/operator-entry.d.ts +1 -0
  101. package/dist/src/operator-entry.js +3 -0
  102. package/dist/src/runner.d.ts +47 -0
  103. package/dist/src/runner.js +16 -22
  104. package/dist/src/withMutedConsole.d.ts +2 -0
  105. package/package.json +21 -8
  106. package/dist/src/commands/_authority-runs.js +0 -111
  107. package/dist/src/commands/_operator-view.js +0 -340
  108. package/dist/src/commands/_preflight.js +0 -478
  109. package/dist/src/commands/_server-client.js +0 -382
  110. package/dist/src/commands/_snapshot-upload.js +0 -331
  111. package/dist/src/commands/_task-picker.js +0 -48
  112. package/dist/src/commands/browser.js +0 -890
  113. package/dist/src/commands/connect.js +0 -180
  114. package/dist/src/commands/inspector.js +0 -256
  115. package/dist/src/commands/task-report-bug.js +0 -1083
  116. 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,27 +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 { existsSync as existsSync2, readFileSync as readFileSync2 } from "fs";
37
- import { resolve as resolve2 } from "path";
38
- 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
+ }
39
84
 
40
85
  // packages/cli/src/commands/_connection-state.ts
41
86
  import { existsSync, mkdirSync, readFileSync, writeFileSync } from "fs";
42
- import { homedir } from "os";
43
- import { dirname, resolve } from "path";
44
- function resolveGlobalConnectionsPath(env = process.env) {
45
- const explicit = env.RIG_CONNECTIONS_FILE?.trim();
46
- if (explicit)
47
- return resolve(explicit);
48
- const stateDir = env.RIG_GLOBAL_STATE_DIR?.trim();
49
- if (stateDir)
50
- return resolve(stateDir, "connections.json");
51
- return resolve(homedir(), ".rig", "connections.json");
52
- }
87
+ import { dirname, resolve as resolve2 } from "path";
53
88
  function resolveRepoConnectionPath(projectRoot) {
54
- return resolve(projectRoot, ".rig", "state", "connection.json");
89
+ return resolve2(projectRoot, ".rig", "state", "connection.json");
55
90
  }
56
91
  function readJsonFile(path) {
57
92
  if (!existsSync(path))
@@ -59,37 +94,8 @@ function readJsonFile(path) {
59
94
  try {
60
95
  return JSON.parse(readFileSync(path, "utf8"));
61
96
  } catch (error) {
62
- throw new CliError2(`Invalid Rig connection state at ${path}: ${error instanceof Error ? error.message : String(error)}`, 1);
63
- }
64
- }
65
- function normalizeConnection(value) {
66
- if (!value || typeof value !== "object" || Array.isArray(value))
67
- return null;
68
- const record = value;
69
- if (record.kind === "local")
70
- return { kind: "local", mode: "auto" };
71
- if (record.kind === "remote" && typeof record.baseUrl === "string" && record.baseUrl.trim()) {
72
- const baseUrl = record.baseUrl.trim().replace(/\/+$/, "");
73
- return { kind: "remote", baseUrl };
74
- }
75
- return null;
76
- }
77
- function readGlobalConnections(options = {}) {
78
- const path = resolveGlobalConnectionsPath(options.env ?? process.env);
79
- const payload = readJsonFile(path);
80
- if (!payload || typeof payload !== "object" || Array.isArray(payload)) {
81
- return { connections: {} };
82
- }
83
- const rawConnections = payload.connections;
84
- const connections = {};
85
- if (rawConnections && typeof rawConnections === "object" && !Array.isArray(rawConnections)) {
86
- for (const [alias, raw] of Object.entries(rawConnections)) {
87
- const connection = normalizeConnection(raw);
88
- if (connection)
89
- connections[alias] = connection;
90
- }
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>`." });
91
98
  }
92
- return { connections };
93
99
  }
94
100
  function readRepoConnection(projectRoot) {
95
101
  const payload = readJsonFile(resolveRepoConnectionPath(projectRoot));
@@ -102,139 +108,130 @@ function readRepoConnection(projectRoot) {
102
108
  return {
103
109
  selected,
104
110
  project: typeof record.project === "string" ? record.project : undefined,
105
- 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
106
115
  };
107
116
  }
108
- function resolveSelectedConnection(projectRoot, options = {}) {
109
- const repo = readRepoConnection(projectRoot);
110
- if (!repo)
111
- return null;
112
- if (repo.selected === "local")
113
- return { alias: "local", connection: { kind: "local", mode: "auto" } };
114
- const global = readGlobalConnections(options);
115
- const connection = global.connections[repo.selected];
116
- if (!connection) {
117
- throw new CliError2(`Selected Rig connection "${repo.selected}" was not found. Run \`rig connect list\` or \`rig connect use local\`.`, 1);
118
- }
119
- return { alias: repo.selected, connection };
120
- }
121
117
 
122
- // packages/cli/src/commands/_server-client.ts
123
- var cachedGitHubBearerToken;
124
- function cleanToken(value) {
125
- const trimmed = value?.trim();
126
- return trimmed ? trimmed : null;
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;
142
+ }
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
+ `);
178
+ }
179
+ };
127
180
  }
128
- function readPrivateRemoteSessionToken(projectRoot) {
129
- const path = resolve2(projectRoot, ".rig", "state", "github-auth.json");
130
- if (!existsSync2(path))
131
- return null;
132
- try {
133
- const parsed = JSON.parse(readFileSync2(path, "utf8"));
134
- return cleanToken(typeof parsed.apiSessionToken === "string" ? parsed.apiSessionToken : typeof parsed.sessionToken === "string" ? parsed.sessionToken : undefined);
135
- } catch {
136
- return null;
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
+ });
137
192
  }
138
- }
139
- function readGitHubBearerTokenForRemote(projectRoot) {
140
- if (cachedGitHubBearerToken !== undefined)
141
- return cachedGitHubBearerToken;
142
- const privateSession = readPrivateRemoteSessionToken(projectRoot);
143
- if (privateSession) {
144
- cachedGitHubBearerToken = privateSession;
145
- return cachedGitHubBearerToken;
193
+ if (activeUpdate) {
194
+ activeUpdate(label);
195
+ return work(activeUpdate);
146
196
  }
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;
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;
207
+ try {
208
+ return await work(update2);
209
+ } finally {
210
+ activeUpdate = null;
211
+ }
151
212
  }
152
- const result = spawnSync("gh", ["auth", "token"], {
153
- encoding: "utf8",
154
- timeout: 5000,
155
- stdio: ["ignore", "pipe", "ignore"]
213
+ const spinner = createTtySpinner({
214
+ label,
215
+ output,
216
+ frames: FRAMES,
217
+ styleFrame: (frame) => pc.cyan(frame)
156
218
  });
157
- cachedGitHubBearerToken = result.status === 0 ? cleanToken(result.stdout) : null;
158
- return cachedGitHubBearerToken;
159
- }
160
- async function ensureServerForCli(projectRoot) {
219
+ const update = (next) => {
220
+ lastLabel = next;
221
+ spinner.setLabel(next);
222
+ };
223
+ activeUpdate = update;
161
224
  try {
162
- const selected = resolveSelectedConnection(projectRoot);
163
- if (selected?.connection.kind === "remote") {
164
- return {
165
- baseUrl: selected.connection.baseUrl,
166
- authToken: readGitHubBearerTokenForRemote(projectRoot),
167
- connectionKind: "remote"
168
- };
169
- }
170
- const connection = await ensureLocalRigServerConnection(projectRoot);
171
- return {
172
- baseUrl: connection.baseUrl,
173
- authToken: connection.authToken,
174
- connectionKind: "local"
175
- };
225
+ const result = await work(update);
226
+ spinner.stop(options.doneLabel ? `${DONE_SYMBOL} ${options.doneLabel}` : undefined);
227
+ return result;
176
228
  } catch (error) {
177
- if (error instanceof Error) {
178
- throw new CliError2(error.message, 1);
179
- }
229
+ spinner.stop(`${FAIL_SYMBOL} ${lastLabel}`);
180
230
  throw error;
231
+ } finally {
232
+ activeUpdate = null;
181
233
  }
182
234
  }
183
- function mergeHeaders(headers, authToken) {
184
- const merged = new Headers(headers);
185
- if (authToken) {
186
- merged.set("authorization", `Bearer ${authToken}`);
187
- }
188
- return merged;
189
- }
190
- function diagnosticMessage(payload) {
191
- if (!payload || typeof payload !== "object" || Array.isArray(payload))
192
- return null;
193
- const record = payload;
194
- const diagnostics = Array.isArray(record.diagnostics) ? record.diagnostics : [];
195
- const messages = diagnostics.flatMap((entry) => {
196
- if (!entry || typeof entry !== "object" || Array.isArray(entry))
197
- return [];
198
- const diagnostic = entry;
199
- const kind = typeof diagnostic.kind === "string" ? diagnostic.kind : "task-source";
200
- const message = typeof diagnostic.message === "string" ? diagnostic.message : null;
201
- return message ? [`${kind}: ${message}`] : [];
202
- });
203
- return messages.length > 0 ? messages.join("; ") : null;
204
- }
205
- async function requestServerJson(context, pathname, init = {}) {
206
- const server = await ensureServerForCli(context.projectRoot);
207
- const response = await fetch(`${server.baseUrl}${pathname}`, {
208
- ...init,
209
- headers: mergeHeaders(init.headers, server.authToken)
210
- });
211
- const text = await response.text();
212
- const payload = text.trim().length > 0 ? (() => {
213
- try {
214
- return JSON.parse(text);
215
- } catch {
216
- return null;
217
- }
218
- })() : null;
219
- if (!response.ok) {
220
- const diagnostics = diagnosticMessage(payload);
221
- const detail = diagnostics ?? (text || response.statusText);
222
- throw new CliError2(`Rig server request failed (${response.status}): ${detail}`, 1);
223
- }
224
- return payload;
225
- }
226
- async function getGitHubAuthStatusViaServer(context) {
227
- const payload = await requestServerJson(context, "/api/github/auth/status");
228
- return payload && typeof payload === "object" && !Array.isArray(payload) ? payload : {};
229
- }
230
- async function postGitHubTokenViaServer(context, token, options = {}) {
231
- const payload = await requestServerJson(context, "/api/github/auth/token", {
232
- method: "POST",
233
- headers: { "content-type": "application/json" },
234
- body: JSON.stringify({ token, selectedRepo: options.selectedRepo, projectRoot: options.projectRoot })
235
- });
236
- return payload && typeof payload === "object" && !Array.isArray(payload) ? payload : {};
237
- }
238
235
 
239
236
  // packages/cli/src/commands/github.ts
240
237
  function printPayload(context, payload, fallback) {
@@ -243,50 +240,101 @@ function printPayload(context, payload, fallback) {
243
240
  else
244
241
  console.log(fallback);
245
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
+ }
246
289
  function readGhToken() {
247
- const result = spawnSync2("gh", ["auth", "token"], { encoding: "utf8" });
290
+ const result = spawnSync("gh", ["auth", "token"], { encoding: "utf8" });
248
291
  if (result.status !== 0) {
249
292
  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);
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>`." });
251
294
  }
252
295
  const token = result.stdout.trim();
253
296
  if (!token)
254
- 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`." });
255
298
  return token;
256
299
  }
257
300
  async function executeGithub(context, args) {
258
301
  const [group, command, ...rest] = args;
259
302
  if (group !== "auth") {
260
- 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);
261
304
  }
262
305
  switch (command) {
263
306
  case "status": {
264
307
  if (rest.length > 0)
265
- throw new CliError2("Usage: rig github auth status", 1);
266
- const status = await getGitHubAuthStatusViaServer(context);
267
- 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"}`);
268
311
  return { ok: true, group: "github", command: "auth status", details: status };
269
312
  }
270
313
  case "token": {
271
314
  const parsed = takeOption(rest, "--token");
272
315
  if (parsed.rest.length > 0)
273
- throw new CliError2("Usage: rig github auth token --token <token>", 1);
316
+ throw new CliError("Usage: rig github auth token --token <token>", 1);
274
317
  const token = parsed.value?.trim();
275
318
  if (!token)
276
- throw new CliError2("Missing --token value.", 1);
277
- const result = await postGitHubTokenViaServer(context, token);
278
- 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.");
279
324
  return { ok: true, group: "github", command: "auth token", details: result };
280
325
  }
281
326
  case "import-gh": {
282
327
  if (rest.length > 0)
283
- throw new CliError2("Usage: rig github auth import-gh", 1);
284
- const result = await postGitHubTokenViaServer(context, readGhToken());
285
- 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.");
286
334
  return { ok: true, group: "github", command: "auth import-gh", details: result };
287
335
  }
288
336
  default:
289
- 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);
290
338
  }
291
339
  }
292
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>;