@h-rig/cli-surface-plugin 0.0.6-alpha.146

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 (94) hide show
  1. package/README.md +1 -0
  2. package/dist/src/app/drone-ui.d.ts +34 -0
  3. package/dist/src/app/drone-ui.js +278 -0
  4. package/dist/src/commands/_async-ui.d.ts +10 -0
  5. package/dist/src/commands/_async-ui.js +121 -0
  6. package/dist/src/commands/_cli-format.d.ts +56 -0
  7. package/dist/src/commands/_cli-format.js +332 -0
  8. package/dist/src/commands/_connection-state.d.ts +54 -0
  9. package/dist/src/commands/_connection-state.js +187 -0
  10. package/dist/src/commands/_doctor-checks.d.ts +9 -0
  11. package/dist/src/commands/_doctor-checks.js +24 -0
  12. package/dist/src/commands/_help-catalog.d.ts +29 -0
  13. package/dist/src/commands/_help-catalog.js +157 -0
  14. package/dist/src/commands/_inprocess-services.d.ts +33 -0
  15. package/dist/src/commands/_inprocess-services.js +102 -0
  16. package/dist/src/commands/_json-output.d.ts +11 -0
  17. package/dist/src/commands/_json-output.js +54 -0
  18. package/dist/src/commands/_parsers.d.ts +15 -0
  19. package/dist/src/commands/_parsers.js +114 -0
  20. package/dist/src/commands/_paths.d.ts +11 -0
  21. package/dist/src/commands/_paths.js +50 -0
  22. package/dist/src/commands/_pi-frontend.d.ts +35 -0
  23. package/dist/src/commands/_pi-frontend.js +64 -0
  24. package/dist/src/commands/_pi-install.d.ts +42 -0
  25. package/dist/src/commands/_pi-install.js +167 -0
  26. package/dist/src/commands/_policy.d.ts +8 -0
  27. package/dist/src/commands/_policy.js +138 -0
  28. package/dist/src/commands/_probes.d.ts +1 -0
  29. package/dist/src/commands/_probes.js +13 -0
  30. package/dist/src/commands/_run-driver-helpers.d.ts +26 -0
  31. package/dist/src/commands/_run-driver-helpers.js +132 -0
  32. package/dist/src/commands/_run-subcommands.d.ts +3 -0
  33. package/dist/src/commands/_run-subcommands.js +31 -0
  34. package/dist/src/commands/_spinner.d.ts +25 -0
  35. package/dist/src/commands/_spinner.js +65 -0
  36. package/dist/src/commands/agent.d.ts +3 -0
  37. package/dist/src/commands/agent.js +322 -0
  38. package/dist/src/commands/config.d.ts +3 -0
  39. package/dist/src/commands/config.js +193 -0
  40. package/dist/src/commands/dist.d.ts +28 -0
  41. package/dist/src/commands/dist.js +435 -0
  42. package/dist/src/commands/doctor.d.ts +3 -0
  43. package/dist/src/commands/doctor.js +171 -0
  44. package/dist/src/commands/github.d.ts +3 -0
  45. package/dist/src/commands/github.js +342 -0
  46. package/dist/src/commands/inbox.d.ts +19 -0
  47. package/dist/src/commands/inbox.js +241 -0
  48. package/dist/src/commands/init.d.ts +64 -0
  49. package/dist/src/commands/init.js +1449 -0
  50. package/dist/src/commands/inspect.d.ts +20 -0
  51. package/dist/src/commands/inspect.js +337 -0
  52. package/dist/src/commands/pi.d.ts +3 -0
  53. package/dist/src/commands/pi.js +177 -0
  54. package/dist/src/commands/plugin.d.ts +20 -0
  55. package/dist/src/commands/plugin.js +238 -0
  56. package/dist/src/commands/profile-and-review.d.ts +4 -0
  57. package/dist/src/commands/profile-and-review.js +223 -0
  58. package/dist/src/commands/queue.d.ts +3 -0
  59. package/dist/src/commands/queue.js +197 -0
  60. package/dist/src/commands/remote.d.ts +3 -0
  61. package/dist/src/commands/remote.js +516 -0
  62. package/dist/src/commands/repo-git-harness.d.ts +5 -0
  63. package/dist/src/commands/repo-git-harness.js +282 -0
  64. package/dist/src/commands/run.d.ts +22 -0
  65. package/dist/src/commands/run.js +645 -0
  66. package/dist/src/commands/server.d.ts +3 -0
  67. package/dist/src/commands/server.js +155 -0
  68. package/dist/src/commands/setup.d.ts +16 -0
  69. package/dist/src/commands/setup.js +356 -0
  70. package/dist/src/commands/stats.d.ts +11 -0
  71. package/dist/src/commands/stats.js +219 -0
  72. package/dist/src/commands/task-run-driver.d.ts +93 -0
  73. package/dist/src/commands/task-run-driver.js +136 -0
  74. package/dist/src/commands/task.d.ts +46 -0
  75. package/dist/src/commands/task.js +555 -0
  76. package/dist/src/commands/test.d.ts +3 -0
  77. package/dist/src/commands/test.js +46 -0
  78. package/dist/src/commands/triage.d.ts +11 -0
  79. package/dist/src/commands/triage.js +224 -0
  80. package/dist/src/commands/workspace.d.ts +3 -0
  81. package/dist/src/commands/workspace.js +130 -0
  82. package/dist/src/kernel-dispatch.d.ts +15 -0
  83. package/dist/src/kernel-dispatch.js +16 -0
  84. package/dist/src/plugin.d.ts +3 -0
  85. package/dist/src/plugin.js +5440 -0
  86. package/dist/src/rig-config-package-deps.d.ts +10 -0
  87. package/dist/src/rig-config-package-deps.js +272 -0
  88. package/dist/src/runner.d.ts +47 -0
  89. package/dist/src/runner.js +267 -0
  90. package/dist/src/version.d.ts +8 -0
  91. package/dist/src/version.js +47 -0
  92. package/dist/src/withMutedConsole.d.ts +2 -0
  93. package/dist/src/withMutedConsole.js +42 -0
  94. package/package.json +34 -0
@@ -0,0 +1,342 @@
1
+ // @bun
2
+ // packages/cli-surface-plugin/src/commands/github.ts
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";
6
+
7
+ // packages/cli-surface-plugin/src/runner.ts
8
+ import { EventBus } from "@rig/runtime/control-plane/runtime/events";
9
+ import { CliError as RuntimeCliError } from "@rig/runtime/control-plane/errors";
10
+ import { evaluate, loadPolicy, resolveAction } from "@rig/runtime/control-plane/runtime/guard";
11
+ import { buildBinary } from "@rig/runtime/control-plane/runtime/isolation";
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
+ }
22
+ function takeOption(args, option) {
23
+ const rest = [];
24
+ let value;
25
+ for (let index = 0;index < args.length; index += 1) {
26
+ const current = args[index];
27
+ if (current === option) {
28
+ const next = args[index + 1];
29
+ if (!next || next.startsWith("-")) {
30
+ throw new CliError(`Missing value for ${option}`, 1, { hint: `Provide a value after ${option}, e.g. \`${option} <value>\`.` });
31
+ }
32
+ value = next;
33
+ index += 1;
34
+ continue;
35
+ }
36
+ if (current !== undefined) {
37
+ rest.push(current);
38
+ }
39
+ }
40
+ return { value, rest };
41
+ }
42
+
43
+ // packages/cli-surface-plugin/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
+ }
84
+
85
+ // packages/cli-surface-plugin/src/commands/_connection-state.ts
86
+ import { existsSync, mkdirSync, readFileSync, writeFileSync } from "fs";
87
+ import { dirname, resolve as resolve2 } from "path";
88
+ function resolveRepoConnectionPath(projectRoot) {
89
+ return resolve2(projectRoot, ".rig", "state", "connection.json");
90
+ }
91
+ function readJsonFile(path) {
92
+ if (!existsSync(path))
93
+ return null;
94
+ try {
95
+ return JSON.parse(readFileSync(path, "utf8"));
96
+ } catch (error) {
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>`." });
98
+ }
99
+ }
100
+ function readRepoConnection(projectRoot) {
101
+ const payload = readJsonFile(resolveRepoConnectionPath(projectRoot));
102
+ if (!payload || typeof payload !== "object" || Array.isArray(payload))
103
+ return null;
104
+ const record = payload;
105
+ const selected = typeof record.selected === "string" ? record.selected.trim() : "";
106
+ if (!selected)
107
+ return null;
108
+ return {
109
+ selected,
110
+ project: typeof record.project === "string" ? record.project : 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
115
+ };
116
+ }
117
+
118
+ // packages/cli-surface-plugin/src/commands/_async-ui.ts
119
+ import pc from "picocolors";
120
+
121
+ // packages/cli-surface-plugin/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
+ };
180
+ }
181
+
182
+ // packages/cli-surface-plugin/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
+ });
192
+ }
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;
207
+ try {
208
+ return await work(update2);
209
+ } finally {
210
+ activeUpdate = null;
211
+ }
212
+ }
213
+ const spinner = createTtySpinner({
214
+ label,
215
+ output,
216
+ frames: FRAMES,
217
+ styleFrame: (frame) => pc.cyan(frame)
218
+ });
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
+ }
234
+ }
235
+
236
+ // packages/cli-surface-plugin/src/commands/github.ts
237
+ function printPayload(context, _payload, fallback) {
238
+ if (context.outputMode === "text")
239
+ console.log(fallback);
240
+ }
241
+ function apiSessionTokenFrom(payload) {
242
+ if (!payload || typeof payload !== "object" || Array.isArray(payload))
243
+ return null;
244
+ return payloadString(payload, "apiSessionToken");
245
+ }
246
+ function payloadString(payload, key) {
247
+ const value = Reflect.get(payload, key);
248
+ return typeof value === "string" && value.trim() ? value.trim() : null;
249
+ }
250
+ function payloadRecord(payload, key) {
251
+ const value = Reflect.get(payload, key);
252
+ return value && typeof value === "object" && !Array.isArray(value) ? value : null;
253
+ }
254
+ function remoteNamespaceMetadata(result) {
255
+ const namespace = payloadRecord(result, "userNamespace");
256
+ const userId = payloadString(result, "userId");
257
+ const namespaceKey = (namespace ? payloadString(namespace, "key") : null) ?? (userId ? `gh:${userId}` : null);
258
+ return {
259
+ ...payloadString(result, "login") ? { login: payloadString(result, "login") } : {},
260
+ ...userId ? { userId } : {},
261
+ ...namespaceKey ? { userNamespaceKey: namespaceKey } : {},
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
+ }
289
+ function readGhToken() {
290
+ const result = spawnSync("gh", ["auth", "token"], { encoding: "utf8" });
291
+ if (result.status !== 0) {
292
+ const detail = result.stderr?.trim() || result.stdout?.trim() || "gh auth token failed";
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>`." });
294
+ }
295
+ const token = result.stdout.trim();
296
+ if (!token)
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`." });
298
+ return token;
299
+ }
300
+ async function executeGithub(context, args) {
301
+ const [group, command, ...rest] = args;
302
+ if (group !== "auth") {
303
+ throw new CliError("Usage: rig github auth <status|import-gh|token>", 1);
304
+ }
305
+ switch (command) {
306
+ case "status": {
307
+ if (rest.length > 0)
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"}`);
311
+ return { ok: true, group: "github", command: "auth status", details: status };
312
+ }
313
+ case "token": {
314
+ const parsed = takeOption(rest, "--token");
315
+ if (parsed.rest.length > 0)
316
+ throw new CliError("Usage: rig github auth token --token <token>", 1);
317
+ const token = parsed.value?.trim();
318
+ if (!token)
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.");
324
+ return { ok: true, group: "github", command: "auth token", details: result };
325
+ }
326
+ case "import-gh": {
327
+ if (rest.length > 0)
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.");
334
+ return { ok: true, group: "github", command: "auth import-gh", details: result };
335
+ }
336
+ default:
337
+ throw new CliError("Usage: rig github auth <status|import-gh|token>", 1);
338
+ }
339
+ }
340
+ export {
341
+ executeGithub
342
+ };
@@ -0,0 +1,19 @@
1
+ import type { CommandOutcome } from "@rig/runtime";
2
+ import type { InboxDeps as ClientInboxDeps, InboxKind, InboxRecord } from "@rig/client";
3
+ import { type RunnerContext } from "../runner";
4
+ export type { InboxKind, InboxRecord } from "@rig/client";
5
+ export interface InboxFilters {
6
+ readonly run?: string;
7
+ readonly task?: string;
8
+ }
9
+ export interface InboxDeps {
10
+ readonly listInbox?: (projectRoot: string, kind: InboxKind, filters: InboxFilters) => Promise<InboxRecord[]>;
11
+ readonly listRuns?: ClientInboxDeps["listRuns"];
12
+ }
13
+ export declare function listInboxRecords(context: Pick<RunnerContext, "projectRoot">, kind: InboxKind, filters?: InboxFilters, deps?: InboxDeps): Promise<InboxRecord[]>;
14
+ export declare function listInbox(context: Pick<RunnerContext, "projectRoot">, kind: InboxKind, filters?: InboxFilters, deps?: InboxDeps): Promise<InboxRecord[]>;
15
+ export declare function readPendingInboxCounts(context: Pick<RunnerContext, "projectRoot">): Promise<{
16
+ approvals: number;
17
+ inputs: number;
18
+ } | null>;
19
+ export declare function executeInbox(context: RunnerContext, args: string[], deps?: InboxDeps): Promise<CommandOutcome>;
@@ -0,0 +1,241 @@
1
+ // @bun
2
+ // packages/cli-surface-plugin/src/commands/inbox.ts
3
+ import { Duration, Effect, Stream } from "effect";
4
+ import { localRunChanges } from "@rig/runtime/control-plane/run-discovery-stream";
5
+ import { listInboxRecords as clientListInboxRecords, readInboxCounts, resolveInboxRequest } from "@rig/client";
6
+
7
+ // packages/cli-surface-plugin/src/runner.ts
8
+ import { EventBus } from "@rig/runtime/control-plane/runtime/events";
9
+ import { CliError as RuntimeCliError } from "@rig/runtime/control-plane/errors";
10
+ import { evaluate, loadPolicy, resolveAction } from "@rig/runtime/control-plane/runtime/guard";
11
+ import { buildBinary } from "@rig/runtime/control-plane/runtime/isolation";
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
+ }
22
+ function takeOption(args, option) {
23
+ const rest = [];
24
+ let value;
25
+ for (let index = 0;index < args.length; index += 1) {
26
+ const current = args[index];
27
+ if (current === option) {
28
+ const next = args[index + 1];
29
+ if (!next || next.startsWith("-")) {
30
+ throw new CliError(`Missing value for ${option}`, 1, { hint: `Provide a value after ${option}, e.g. \`${option} <value>\`.` });
31
+ }
32
+ value = next;
33
+ index += 1;
34
+ continue;
35
+ }
36
+ if (current !== undefined) {
37
+ rest.push(current);
38
+ }
39
+ }
40
+ return { value, rest };
41
+ }
42
+ function requireNoExtraArgs(args, usage) {
43
+ if (args.length > 0) {
44
+ throw new CliError(`Unexpected arguments: ${args.join(" ")}
45
+ Usage: ${usage}`);
46
+ }
47
+ }
48
+
49
+ // packages/cli-surface-plugin/src/commands/_cli-format.ts
50
+ import pc from "picocolors";
51
+ import { runStatusColorRole, runStatusText, statusColorRole } from "@rig/client";
52
+ var dim = pc.dim;
53
+ var faintBar = pc.dim("\u2502");
54
+ var accent = pc.cyan;
55
+ function colorForRole(role) {
56
+ switch (role) {
57
+ case "success":
58
+ return pc.green;
59
+ case "action-yellow":
60
+ return pc.yellow;
61
+ case "active-cyan":
62
+ return pc.cyan;
63
+ case "failure":
64
+ return pc.red;
65
+ case "muted":
66
+ case "neutral":
67
+ return pc.dim;
68
+ }
69
+ }
70
+ function statusColor(status) {
71
+ return colorForRole(statusColorRole(status));
72
+ }
73
+ function firstString(record, keys, fallback = "") {
74
+ for (const key of keys) {
75
+ const value = record[key];
76
+ if (typeof value === "string" && value.trim())
77
+ return value;
78
+ }
79
+ return fallback;
80
+ }
81
+ function requestIdOf(entry) {
82
+ return firstString(entry, ["requestId", "id", "approvalId", "inputId"], "(unknown-request)");
83
+ }
84
+ function printFormattedOutput(message) {
85
+ console.log(message);
86
+ }
87
+ function formatStatusPill(status) {
88
+ const label = status || "unknown";
89
+ return statusColor(label)(`\u25CF ${label}`);
90
+ }
91
+ function formatSection(title, subtitle) {
92
+ return `${pc.bold(accent("\u25C6"))} ${pc.bold(title)}${subtitle ? dim(` \u2014 ${subtitle}`) : ""}`;
93
+ }
94
+ function formatNextSteps(steps) {
95
+ if (steps.length === 0)
96
+ return [];
97
+ return [pc.bold("Next"), ...steps.map((step) => `${accent("\u203A")} ${step}`)];
98
+ }
99
+ function formatLegacyAutomationSurface() {
100
+ return [];
101
+ }
102
+ function formatInboxList(kind, entries) {
103
+ const title = kind === "approvals" ? "Pending approvals" : "Pending user input";
104
+ if (entries.length === 0) {
105
+ return [
106
+ formatSection(title, "empty"),
107
+ dim(kind === "approvals" ? "No pending approvals." : "No pending user-input requests."),
108
+ "",
109
+ ...formatLegacyAutomationSurface()
110
+ ].join(`
111
+ `);
112
+ }
113
+ const lines = [formatSection(title, `${entries.length}`)];
114
+ for (const record of entries) {
115
+ const runId = firstString(record, ["runId"], "(unknown-run)");
116
+ const taskId = firstString(record, ["taskId"], "");
117
+ const status = firstString(record, ["status"], "pending");
118
+ const prompt = firstString(record, ["prompt", "message", "reason", "title", "summary"], kind === "approvals" ? "Approval requested" : "Input requested");
119
+ lines.push(`${faintBar} ${pc.bold(requestIdOf(record))} ${formatStatusPill(status)} ${prompt}`);
120
+ lines.push(`${faintBar} ${dim("run".padEnd(12))} ${runId}${taskId ? dim(` task ${taskId}`) : ""}`);
121
+ }
122
+ lines.push("", ...formatLegacyAutomationSurface(), ...formatNextSteps(kind === "approvals" ? ["Resolve: `rig inbox approve --run <run-id> --request <request-id> --decision approve|reject`"] : ["Reply: `rig inbox answer --run <run-id> --request <request-id> --text ...`"]));
123
+ return lines.join(`
124
+ `);
125
+ }
126
+
127
+ // packages/cli-surface-plugin/src/commands/inbox.ts
128
+ async function listInboxRecords(context, kind, filters = {}, deps = {}) {
129
+ const clientDeps = deps.listRuns ? { listRuns: deps.listRuns } : {};
130
+ return deps.listInbox ? deps.listInbox(context.projectRoot, kind, filters) : clientListInboxRecords(context.projectRoot, kind, filters, clientDeps);
131
+ }
132
+ async function listInbox(context, kind, filters = {}, deps = {}) {
133
+ return listInboxRecords(context, kind, filters, deps);
134
+ }
135
+ async function readPendingInboxCounts(context) {
136
+ return readInboxCounts(context.projectRoot);
137
+ }
138
+ function resolutionAttachError(runId, verb, error) {
139
+ const target = runId ?? "<run-id>";
140
+ const detail = error instanceof Error ? error.message : String(error);
141
+ throw new CliError(`rig inbox ${verb} could not deliver a resolution from this one-shot command.`, 2, {
142
+ hint: `${detail}
143
+ Attach to resolve interactively: rig run attach ${target}`
144
+ });
145
+ }
146
+ async function readInboxSnapshot(context, filters, deps) {
147
+ const [approvals, inputs] = await Promise.all([
148
+ listInbox(context, "approvals", filters, deps),
149
+ listInbox(context, "inputs", filters, deps)
150
+ ]);
151
+ return { approvals, inputs };
152
+ }
153
+ function renderInboxSnapshot(snapshot) {
154
+ console.clear();
155
+ console.log(`rig inbox \u2014 watching (Ctrl-C to stop) \u2014 ${new Date().toLocaleTimeString()}`);
156
+ printFormattedOutput(formatInboxList("approvals", snapshot.approvals.map((entry) => ({ ...entry }))));
157
+ printFormattedOutput(formatInboxList("inputs", snapshot.inputs.map((entry) => ({ ...entry }))));
158
+ if (snapshot.approvals.length === 0 && snapshot.inputs.length === 0)
159
+ console.log("Nothing pending.");
160
+ }
161
+ async function watchInbox(context, filters, deps) {
162
+ const snapshot = await readInboxSnapshot(context, filters, deps);
163
+ if (context.outputMode !== "text")
164
+ return { ok: true, group: "inbox", command: "watch", details: snapshot };
165
+ renderInboxSnapshot(snapshot);
166
+ await Effect.runPromise(localRunChanges(context.projectRoot).pipe(Stream.debounce(Duration.millis(500)), Stream.runForEach(() => Effect.promise(async () => renderInboxSnapshot(await readInboxSnapshot(context, filters, deps))))));
167
+ return { ok: true, group: "inbox", command: "watch", details: snapshot };
168
+ }
169
+ async function executeInbox(context, args, deps = {}) {
170
+ const [command = "approvals", ...rest] = args;
171
+ const text = context.outputMode === "text";
172
+ switch (command) {
173
+ case "approvals":
174
+ case "inputs": {
175
+ const run = takeOption(rest, "--run");
176
+ const task = takeOption(run.rest, "--task");
177
+ requireNoExtraArgs(task.rest, `rig inbox ${command} [--run <id>] [--task <id>]`);
178
+ const kind = command === "approvals" ? "approvals" : "inputs";
179
+ const filters = { ...run.value ? { run: run.value } : {}, ...task.value ? { task: task.value } : {} };
180
+ const entries = await listInbox(context, kind, filters, deps);
181
+ if (text)
182
+ printFormattedOutput(formatInboxList(kind, entries.map((entry) => ({ ...entry }))));
183
+ return { ok: true, group: "inbox", command, details: { entries } };
184
+ }
185
+ case "approve": {
186
+ const run = takeOption(rest, "--run");
187
+ const request = takeOption(run.rest, "--request");
188
+ const decision = takeOption(request.rest, "--decision");
189
+ requireNoExtraArgs(decision.rest, "rig inbox approve --run <id> --request <id> --decision approve|reject");
190
+ if (!run.value || !request.value)
191
+ throw new CliError("rig inbox approve requires --run and --request.", 2);
192
+ const rawDecision = (decision.value ?? "approve").trim().toLowerCase();
193
+ if (rawDecision !== "approve" && rawDecision !== "approved" && rawDecision !== "reject" && rawDecision !== "rejected")
194
+ throw new CliError("--decision must be approve or reject.", 2);
195
+ try {
196
+ await resolveInboxRequest(context.projectRoot, run.value, request.value, { kind: "approval", decision: rawDecision });
197
+ } catch (error) {
198
+ resolutionAttachError(run.value, "approve", error);
199
+ }
200
+ if (text)
201
+ printFormattedOutput(`Resolved approval ${request.value} for run ${run.value}.`);
202
+ return { ok: true, group: "inbox", command, details: { runId: run.value, requestId: request.value, decision: rawDecision } };
203
+ }
204
+ case "respond":
205
+ case "answer": {
206
+ const run = takeOption(rest, "--run");
207
+ const request = takeOption(run.rest, "--request");
208
+ const textResult = takeOption(request.rest, "--text");
209
+ const answerResult = takeOption(textResult.rest, "--answer");
210
+ requireNoExtraArgs(answerResult.rest, `rig inbox ${command} --run <id> --request <id> (--text|--answer) <answer>`);
211
+ if (textResult.value && answerResult.value)
212
+ throw new CliError("Pass only one of --text or --answer.", 2);
213
+ const answer = textResult.value ?? answerResult.value;
214
+ if (!run.value || !request.value || !answer)
215
+ resolutionAttachError(run.value, "respond", new Error(`rig inbox ${command} requires --run, --request, and --text/--answer.`));
216
+ try {
217
+ await resolveInboxRequest(context.projectRoot, run.value, request.value, { kind: "input", answer });
218
+ } catch (error) {
219
+ resolutionAttachError(run.value, "respond", error);
220
+ }
221
+ if (text)
222
+ printFormattedOutput(`Sent response ${request.value} for run ${run.value}.`);
223
+ return { ok: true, group: "inbox", command, details: { runId: run.value, requestId: request.value, responded: true } };
224
+ }
225
+ case "watch": {
226
+ const run = takeOption(rest, "--run");
227
+ const task = takeOption(run.rest, "--task");
228
+ requireNoExtraArgs(task.rest, "rig inbox watch [--run <id>] [--task <id>]");
229
+ const filters = { ...run.value ? { run: run.value } : {}, ...task.value ? { task: task.value } : {} };
230
+ return watchInbox(context, filters, deps);
231
+ }
232
+ default:
233
+ throw new CliError(`Unknown inbox command: ${command}`, 1, { hint: "Run `rig inbox --help` to list inbox commands." });
234
+ }
235
+ }
236
+ export {
237
+ readPendingInboxCounts,
238
+ listInboxRecords,
239
+ listInbox,
240
+ executeInbox
241
+ };
@@ -0,0 +1,64 @@
1
+ import { type RunnerContext } from "../runner";
2
+ import { buildRigInitConfigSource, type RigInitConfigInput } from "@rig/core";
3
+ import type { CommandOutcome } from "@rig/runtime";
4
+ export { buildRigInitConfigSource };
5
+ export type { RigInitConfigInput };
6
+ type InitAuthMethod = "gh" | "token" | "device" | "skip";
7
+ type InitControlPlaneOptions = {
8
+ demo?: boolean;
9
+ repoSlug?: string;
10
+ githubToken?: string;
11
+ githubAuthMethod?: InitAuthMethod;
12
+ githubProject?: "off" | string;
13
+ githubProjectStatusField?: string;
14
+ githubProjectStatuses?: Record<string, string>;
15
+ yes?: boolean;
16
+ repair?: boolean;
17
+ privateStateOnly?: boolean;
18
+ server?: "local" | "remote";
19
+ remoteAlias?: string;
20
+ remoteHost?: string;
21
+ remotePort?: number;
22
+ remoteCheckout?: string;
23
+ };
24
+ type InitPromptAdapter = {
25
+ intro?: (message: string) => void;
26
+ outro?: (message: string) => void;
27
+ cancel?: (message: string) => void;
28
+ isCancel: (value: unknown) => boolean;
29
+ text: (options: {
30
+ message: string;
31
+ placeholder?: string;
32
+ initialValue?: string;
33
+ defaultValue?: string;
34
+ }) => Promise<unknown>;
35
+ select: (options: {
36
+ message: string;
37
+ options: Array<{
38
+ value: string;
39
+ label: string;
40
+ hint?: string;
41
+ }>;
42
+ initialValue?: string;
43
+ }) => Promise<unknown>;
44
+ confirm?: (options: {
45
+ message: string;
46
+ initialValue?: boolean;
47
+ }) => Promise<unknown>;
48
+ };
49
+ export declare const DEMO_TASKS_RELATIVE_DIR = ".rig/demo-tasks";
50
+ /**
51
+ * The three sample tasks `rig init --demo` seeds. Shapes match what the
52
+ * standard plugin's files task source reads: one JSON object per file with
53
+ * id/title/body/status/labels (see packages/standard-plugin/src/files-source.ts).
54
+ */
55
+ export declare const DEMO_TASKS: ReadonlyArray<Record<string, unknown>>;
56
+ /**
57
+ * `rig init --demo` — zero-GitHub, offline demo project: files task source
58
+ * pointing at .rig/demo-tasks/ with three seeded sample tasks, so an
59
+ * operator can see the full run lifecycle in minutes. Non-interactive and
60
+ * idempotent: an existing rig.config is left untouched unless --repair.
61
+ */
62
+ export declare function runDemoInit(context: RunnerContext, options: InitControlPlaneOptions): CommandOutcome;
63
+ export declare function runInteractiveControlPlaneInit(context: RunnerContext, prompts: InitPromptAdapter): Promise<CommandOutcome>;
64
+ export declare function executeInit(context: RunnerContext, args: string[]): Promise<CommandOutcome>;