@h-rig/cli 0.0.6-alpha.10 → 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 +15683 -7361
  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 +100 -267
  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 -236
  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 +219 -268
  58. package/dist/src/commands/github.d.ts +3 -0
  59. package/dist/src/commands/github.js +236 -188
  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 +912 -705
  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 +17731 -661
  80. package/dist/src/commands/server.d.ts +3 -0
  81. package/dist/src/commands/server.js +166 -303
  82. package/dist/src/commands/setup.d.ts +16 -0
  83. package/dist/src/commands/setup.js +244 -299
  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 +116 -1961
  88. package/dist/src/commands/task.d.ts +35 -0
  89. package/dist/src/commands/task.js +676 -1201
  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 +16812 -8045
  98. package/dist/src/index.d.ts +4 -0
  99. package/dist/src/index.js +16817 -8377
  100. package/dist/src/launcher.d.ts +61 -0
  101. package/dist/src/launcher.js +77 -11
  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 +16 -22
  108. package/dist/src/withMutedConsole.d.ts +2 -0
  109. package/package.json +21 -8
  110. package/dist/src/commands/_authority-runs.js +0 -111
  111. package/dist/src/commands/_operator-view.js +0 -340
  112. package/dist/src/commands/_preflight.js +0 -478
  113. package/dist/src/commands/_server-client.js +0 -382
  114. package/dist/src/commands/_snapshot-upload.js +0 -331
  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
@@ -0,0 +1,319 @@
1
+ // @bun
2
+ // packages/cli/src/commands/_cli-format.ts
3
+ import pc from "picocolors";
4
+ var dim = pc.dim;
5
+ var faintBar = pc.dim("\u2502");
6
+ var accent = pc.cyan;
7
+ function numberField(record, key) {
8
+ const value = record[key];
9
+ return typeof value === "number" && Number.isFinite(value) ? value : null;
10
+ }
11
+ function arrayField(record, key) {
12
+ const value = record[key];
13
+ return Array.isArray(value) ? value.filter((entry) => typeof entry === "string" && entry.trim().length > 0) : [];
14
+ }
15
+ function rawObject(record) {
16
+ const value = record.raw;
17
+ return value && typeof value === "object" && !Array.isArray(value) ? value : record;
18
+ }
19
+ function truncate(value, width) {
20
+ return value.length <= width ? value : `${value.slice(0, Math.max(0, width - 1))}\u2026`;
21
+ }
22
+ function pad(value, width) {
23
+ return value.length >= width ? value : `${value}${" ".repeat(width - value.length)}`;
24
+ }
25
+ function statusColor(status) {
26
+ const normalized = status.trim().toLowerCase();
27
+ if (["done", "completed", "ready", "healthy", "open", "approved"].includes(normalized))
28
+ return pc.green;
29
+ if (["failed", "error", "blocked", "rejected", "stopped"].includes(normalized))
30
+ return pc.red;
31
+ if (["running", "in_progress", "in-progress", "active", "booting"].includes(normalized))
32
+ return pc.cyan;
33
+ if (["pending", "queued", "created", "local"].includes(normalized))
34
+ return pc.yellow;
35
+ return pc.dim;
36
+ }
37
+ function compactDate(value) {
38
+ const parsed = Date.parse(value);
39
+ if (!Number.isFinite(parsed))
40
+ return value;
41
+ return new Date(parsed).toISOString().replace("T", " ").replace(/\.\d{3}Z$/, "Z");
42
+ }
43
+ function compactValue(value) {
44
+ if (value === null || value === undefined)
45
+ return "\u2014";
46
+ if (typeof value === "string")
47
+ return value;
48
+ if (typeof value === "number" || typeof value === "boolean")
49
+ return String(value);
50
+ return JSON.stringify(value);
51
+ }
52
+ function firstString(record, keys, fallback = "") {
53
+ for (const key of keys) {
54
+ const value = record[key];
55
+ if (typeof value === "string" && value.trim())
56
+ return value;
57
+ }
58
+ return fallback;
59
+ }
60
+ function runIdOf(run) {
61
+ return firstString(run, ["runId", "id"], "(unknown-run)");
62
+ }
63
+ function taskIdOf(run) {
64
+ return firstString(run, ["taskId", "task", "task_id"]);
65
+ }
66
+ function runTitleOf(run) {
67
+ return firstString(run, ["title", "summary", "name"], taskIdOf(run) || "(untitled)");
68
+ }
69
+ function requestIdOf(entry) {
70
+ return firstString(entry, ["requestId", "id", "approvalId", "inputId"], "(unknown-request)");
71
+ }
72
+ function printFormattedOutput(message) {
73
+ console.log(message);
74
+ }
75
+ function formatStatusPill(status) {
76
+ const label = status || "unknown";
77
+ return statusColor(label)(`\u25CF ${label}`);
78
+ }
79
+ function formatSection(title, subtitle) {
80
+ return `${pc.bold(accent("\u25C6"))} ${pc.bold(title)}${subtitle ? dim(` \u2014 ${subtitle}`) : ""}`;
81
+ }
82
+ function formatSuccessCard(title, rows = []) {
83
+ const body = rows.filter(([, value]) => value !== undefined && value !== null && String(value).length > 0).map(([key, value]) => `${faintBar} ${dim(key.padEnd(12))} ${value}`);
84
+ return [formatSection(title), ...body].join(`
85
+ `);
86
+ }
87
+ function formatNextSteps(steps) {
88
+ if (steps.length === 0)
89
+ return [];
90
+ return [pc.bold("Next"), ...steps.map((step) => `${accent("\u203A")} ${step}`)];
91
+ }
92
+ function formatLegacyAutomationSurface() {
93
+ return [];
94
+ }
95
+ function formatTaskList(tasks, options = {}) {
96
+ if (tasks.length === 0) {
97
+ return [formatSection("Tasks", "empty"), dim("No tasks available."), "", ...formatNextSteps(["Refresh: `rig task list`", "Pick next: `rig task next`"])].join(`
98
+ `);
99
+ }
100
+ if (options.raw) {
101
+ return JSON.stringify(tasks, null, 2);
102
+ }
103
+ const rows = tasks.map((task) => summarizeTaskRow(task));
104
+ const idWidth = Math.max(2, ...rows.map((row) => row.id.length));
105
+ const statusWidth = Math.max(6, ...rows.map((row) => row.status.length));
106
+ const body = rows.map((row) => {
107
+ const labels = row.labels.length > 0 ? dim(` ${row.labels.slice(0, 4).map((label) => `#${label}`).join(" ")}`) : "";
108
+ const source = row.source ? dim(` ${row.source}`) : "";
109
+ return [
110
+ pc.bold(pad(truncate(row.id, idWidth), idWidth)),
111
+ statusColor(row.status)(pad(truncate(row.status, statusWidth), statusWidth)),
112
+ `${row.title}${labels}${source}`
113
+ ].join(" ");
114
+ });
115
+ return [formatSection("Tasks", `${tasks.length}`), ...body].join(`
116
+ `);
117
+ }
118
+ function summarizeTaskRow(task) {
119
+ const raw = rawObject(task);
120
+ return {
121
+ id: firstString(task, ["id", "taskId"], "(unknown-task)"),
122
+ title: firstString(task, ["title", "summary", "name"], "(untitled)"),
123
+ status: firstString(task, ["status"], "unknown"),
124
+ labels: arrayField(task, "labels").length > 0 ? arrayField(task, "labels") : arrayField(raw, "labels"),
125
+ source: firstString(task, ["source"], "")
126
+ };
127
+ }
128
+ function formatTaskCard(task, options = {}) {
129
+ const raw = rawObject(task);
130
+ const lines = [formatSection(options.title ?? (options.selected ? "Selected task" : "Task"))];
131
+ for (const [label, value] of [
132
+ ["id", firstString(task, ["id", "taskId"], "(unknown-task)")],
133
+ ["title", firstString(task, ["title", "summary", "name"], "(untitled)")],
134
+ ["status", firstString(task, ["status"], "unknown")],
135
+ ["source", firstString(task, ["source"], "")],
136
+ ["url", firstString(raw, ["url"], "")]
137
+ ]) {
138
+ if (value)
139
+ lines.push(`${faintBar} ${dim(label.padEnd(12))} ${value}`);
140
+ }
141
+ const labels = arrayField(task, "labels");
142
+ if (labels.length > 0)
143
+ lines.push(`${faintBar} ${dim("labels".padEnd(12))} ${labels.map((label) => `#${label}`).join(" ")}`);
144
+ return lines.join(`
145
+ `);
146
+ }
147
+ function formatTaskDetails(task) {
148
+ return formatTaskCard(task, { title: "Task details" });
149
+ }
150
+ function formatRunList(runs, options = {}) {
151
+ if (runs.length === 0) {
152
+ return [
153
+ formatSection("Runs", "none recorded"),
154
+ options.source === "server" ? dim("No runs recorded on the selected server.") : dim("No runs recorded in local state."),
155
+ "",
156
+ ...formatLegacyAutomationSurface()
157
+ ].join(`
158
+ `);
159
+ }
160
+ const body = runs.map((run) => {
161
+ const row = run;
162
+ const runId = runIdOf(row);
163
+ const status = firstString(row, ["status"], "unknown");
164
+ const runtime = firstString(row, ["runtime", "runtimeAdapter"], "");
165
+ return [
166
+ pc.bold(runId),
167
+ statusColor(status)(pad(truncate(status, 12), 12)),
168
+ `${runTitleOf(row)}${runtime ? dim(` ${runtime}`) : ""}`
169
+ ].join(" ");
170
+ });
171
+ return [formatSection("Runs", options.source === "server" ? "selected server" : "local state"), ...body, "", ...formatLegacyAutomationSurface()].join(`
172
+ `);
173
+ }
174
+ function formatSubmittedRun(input) {
175
+ return formatSuccessCard("Run submitted", [
176
+ ["run", input.runId],
177
+ ["task", input.taskId ?? undefined],
178
+ ["title", input.title ?? undefined],
179
+ ["queue", input.queuePosition ?? undefined]
180
+ ]);
181
+ }
182
+ function formatRunCard(run, options = {}) {
183
+ const record = run;
184
+ const lines = [formatSection(options.title ?? "Run")];
185
+ for (const [label, value] of [
186
+ ["run", runIdOf(record)],
187
+ ["task", taskIdOf(record)],
188
+ ["title", runTitleOf(record)],
189
+ ["status", firstString(record, ["status"], "unknown")],
190
+ ["runtime", firstString(record, ["runtime", "runtimeAdapter"], "")],
191
+ ["created", firstString(record, ["createdAt"], "")],
192
+ ["updated", firstString(record, ["updatedAt"], "")]
193
+ ]) {
194
+ if (value)
195
+ lines.push(`${faintBar} ${dim(label.padEnd(12))} ${label === "created" || label === "updated" ? compactDate(value) : value}`);
196
+ }
197
+ const errorSummary = firstString(record, ["errorSummary"]);
198
+ if (errorSummary)
199
+ lines.push(`${faintBar} Error: ${errorSummary}`);
200
+ return lines.join(`
201
+ `);
202
+ }
203
+ function formatRunStatus(summary, options = {}) {
204
+ const activeRuns = summary.activeRuns ?? [];
205
+ const recentRuns = summary.recentRuns ?? [];
206
+ const lines = [formatSection("Run status", options.source === "server" ? "selected server" : "local state")];
207
+ lines.push(`Active runs (${activeRuns.length})`);
208
+ if (activeRuns.length === 0)
209
+ lines.push(dim("No active runs."));
210
+ else
211
+ for (const run of activeRuns)
212
+ lines.push(formatRunSummaryLine(run));
213
+ lines.push("", `Recent runs (${recentRuns.length})`);
214
+ if (recentRuns.length === 0)
215
+ lines.push(dim("No recent terminal runs."));
216
+ else
217
+ for (const run of recentRuns.slice(0, 10))
218
+ lines.push(formatRunSummaryLine(run));
219
+ lines.push("", ...formatLegacyAutomationSurface());
220
+ return lines.join(`
221
+ `);
222
+ }
223
+ function formatRunSummaryLine(run) {
224
+ const record = run;
225
+ const runId = runIdOf(record);
226
+ const taskId = taskIdOf(record);
227
+ const title = runTitleOf(record);
228
+ const status = firstString(record, ["status"], "unknown");
229
+ const runtime = firstString(record, ["runtime", "runtimeAdapter"], "");
230
+ const descriptor = [taskId, title].filter(Boolean).join(" \xB7 ");
231
+ return `${faintBar} ${pc.bold(runId)} ${formatStatusPill(status)} ${descriptor}${runtime ? dim(` ${runtime}`) : ""}`;
232
+ }
233
+ function formatInboxList(kind, entries) {
234
+ const title = kind === "approvals" ? "Pending approvals" : "Pending user input";
235
+ if (entries.length === 0) {
236
+ return [
237
+ formatSection(title, "empty"),
238
+ dim(kind === "approvals" ? "No pending approvals." : "No pending user-input requests."),
239
+ "",
240
+ ...formatLegacyAutomationSurface()
241
+ ].join(`
242
+ `);
243
+ }
244
+ const lines = [formatSection(title, `${entries.length}`)];
245
+ for (const record of entries) {
246
+ const runId = firstString(record, ["runId"], "(unknown-run)");
247
+ const taskId = firstString(record, ["taskId"], "");
248
+ const status = firstString(record, ["status"], "pending");
249
+ const prompt = firstString(record, ["prompt", "message", "reason", "title", "summary"], kind === "approvals" ? "Approval requested" : "Input requested");
250
+ lines.push(`${faintBar} ${pc.bold(requestIdOf(record))} ${formatStatusPill(status)} ${prompt}`);
251
+ lines.push(`${faintBar} ${dim("run".padEnd(12))} ${runId}${taskId ? dim(` task ${taskId}`) : ""}`);
252
+ }
253
+ 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 ...`"]));
254
+ return lines.join(`
255
+ `);
256
+ }
257
+ function formatConnectionList(connections) {
258
+ const names = Object.keys(connections).sort();
259
+ if (names.length === 0) {
260
+ return [formatSection("Rig servers", "empty"), dim("No saved server connections.")].join(`
261
+ `);
262
+ }
263
+ return [formatSection("Rig servers", `${names.length}`), ...names.map((name) => {
264
+ const connection = connections[name];
265
+ const target = connection.kind === "remote" ? connection.baseUrl ?? "(missing url)" : connection.mode ?? "local";
266
+ return `${faintBar} ${pc.bold(name)} ${dim(connection.kind)} ${target}`;
267
+ })].join(`
268
+ `);
269
+ }
270
+ function formatConnectionStatus(selected, connections, repo, remoteProjectLink) {
271
+ const selectedConnection = connections[selected];
272
+ const lines = [formatSection("Rig server", "selected for this repo")];
273
+ lines.push(`${faintBar} ${dim("selected".padEnd(12))} ${pc.bold(selected)}`);
274
+ if (selectedConnection) {
275
+ lines.push(`${faintBar} ${dim("kind".padEnd(12))} ${selectedConnection.kind}`);
276
+ if (selectedConnection.baseUrl)
277
+ lines.push(`${faintBar} ${dim("base url".padEnd(12))} ${selectedConnection.baseUrl}`);
278
+ if (selectedConnection.mode)
279
+ lines.push(`${faintBar} ${dim("mode".padEnd(12))} ${selectedConnection.mode}`);
280
+ }
281
+ if (repo?.project)
282
+ lines.push(`${faintBar} ${dim("project".padEnd(12))} ${repo.project}`);
283
+ if (repo?.serverProjectRoot)
284
+ lines.push(`${faintBar} ${dim("server root".padEnd(12))} ${repo.serverProjectRoot}`);
285
+ if (remoteProjectLink?.status)
286
+ lines.push(`${faintBar} ${dim("link".padEnd(12))} ${remoteProjectLink.status}`);
287
+ if (remoteProjectLink?.message)
288
+ lines.push(`${faintBar} ${dim("message".padEnd(12))} ${remoteProjectLink.message}`);
289
+ if (remoteProjectLink?.hint)
290
+ lines.push(`${faintBar} ${dim("hint".padEnd(12))} ${remoteProjectLink.hint}`);
291
+ return lines.join(`
292
+ `);
293
+ }
294
+ function formatStatsTable(rows) {
295
+ return rows.map(([label, value]) => `${faintBar} ${dim(label.padEnd(20))} ${compactValue(value)}`).join(`
296
+ `);
297
+ }
298
+ function readOptionalCount(record, key) {
299
+ return numberField(record, key);
300
+ }
301
+ export {
302
+ readOptionalCount,
303
+ printFormattedOutput,
304
+ formatTaskList,
305
+ formatTaskDetails,
306
+ formatTaskCard,
307
+ formatSuccessCard,
308
+ formatSubmittedRun,
309
+ formatStatusPill,
310
+ formatStatsTable,
311
+ formatSection,
312
+ formatRunStatus,
313
+ formatRunList,
314
+ formatRunCard,
315
+ formatNextSteps,
316
+ formatInboxList,
317
+ formatConnectionStatus,
318
+ formatConnectionList
319
+ };
@@ -0,0 +1,18 @@
1
+ import type { RigConfig } from "@rig/contracts";
2
+ /**
3
+ * Bridge `rig.config.ts` → `process.env`, FILL-IF-UNSET, establishing the global
4
+ * resolution order for every env-driven / computed value:
5
+ *
6
+ * ambient process env at run time (top winner — `RIG_X=… rig …`)
7
+ * → rig.config.ts (typed sections, then the generic `env` map)
8
+ * → computed default / derived value (under the hood, in the resolvers)
9
+ *
10
+ * Because we only set a key when it is currently absent, an ambient env var always
11
+ * wins; the config only fills gaps; the resolvers' own defaults fill what neither
12
+ * set. Typed sections are applied before the generic `env` map so a specific
13
+ * `runtime.server.relayUrl` beats a generic `env: { RIG_COLLAB_RELAY }`.
14
+ *
15
+ * Call this ONCE at CLI startup, before any resolver runs and before launching OMP
16
+ * or spawning run subprocesses (which inherit the populated env).
17
+ */
18
+ export declare function applyRigConfigEnv(config: Pick<RigConfig, "env" | "runtime">, env?: NodeJS.ProcessEnv): void;
@@ -0,0 +1,22 @@
1
+ // @bun
2
+ // packages/cli/src/commands/_config-env.ts
3
+ function applyRigConfigEnv(config, env = process.env) {
4
+ const fill = (key, value) => {
5
+ if (value && !env[key])
6
+ env[key] = value;
7
+ };
8
+ const server = config.runtime?.server;
9
+ if (server) {
10
+ const host = server.host?.trim();
11
+ fill("RIG_COLLAB_RELAY", server.relayUrl ?? (host ? `wss://${host}` : undefined));
12
+ fill("RIG_REGISTRY_URL", server.registryUrl ?? (host ? `https://${host}/registry` : undefined));
13
+ fill("RIG_REGISTRY_SHARED_SECRET", server.registrySecret);
14
+ fill("RIG_SSH_TARGET", server.sshTarget);
15
+ fill("RIG_REMOTE_CHECKOUT", server.checkout);
16
+ }
17
+ for (const [key, value] of Object.entries(config.env ?? {}))
18
+ fill(key, value);
19
+ }
20
+ export {
21
+ applyRigConfigEnv
22
+ };
@@ -0,0 +1,54 @@
1
+ export type RigConnection = {
2
+ kind: "local";
3
+ mode: "auto";
4
+ } | {
5
+ kind: "remote";
6
+ baseUrl: string;
7
+ };
8
+ export type GlobalConnectionState = {
9
+ connections: Record<string, RigConnection>;
10
+ };
11
+ export type RepoConnectionState = {
12
+ selected: string;
13
+ project?: string;
14
+ linkedAt?: string;
15
+ /**
16
+ * The server-host project root this repo scopes to (multi-root serving).
17
+ * Sent as the x-rig-project-root header on every remote request.
18
+ */
19
+ serverProjectRoot?: string;
20
+ /** The selected alias that produced serverProjectRoot. Prevents cross-alias reuse. */
21
+ serverProjectRootAlias?: string;
22
+ /** The remote base URL that produced serverProjectRoot. Prevents same-alias retarget leaks. */
23
+ serverProjectRootBaseUrl?: string;
24
+ };
25
+ export declare function resolveGlobalConnectionsPath(env?: NodeJS.ProcessEnv): string;
26
+ export declare function resolveRepoConnectionPath(projectRoot: string): string;
27
+ export declare function readGlobalConnections(options?: {
28
+ env?: NodeJS.ProcessEnv;
29
+ }): GlobalConnectionState;
30
+ export declare function writeGlobalConnections(state: GlobalConnectionState, options?: {
31
+ env?: NodeJS.ProcessEnv;
32
+ }): void;
33
+ export declare function upsertGlobalConnection(alias: string, connection: RigConnection, options?: {
34
+ env?: NodeJS.ProcessEnv;
35
+ }): GlobalConnectionState;
36
+ export declare function readRepoConnection(projectRoot: string): RepoConnectionState | null;
37
+ export declare function writeRepoConnection(projectRoot: string, state: RepoConnectionState): void;
38
+ export declare function resolveSelectedConnection(projectRoot: string, options?: {
39
+ env?: NodeJS.ProcessEnv;
40
+ }): {
41
+ alias: string;
42
+ connection: RigConnection;
43
+ serverProjectRoot?: string;
44
+ } | null;
45
+ /** Persist the server-host project root this repo scopes to (multi-root serving). */
46
+ export declare function writeRepoServerProjectRoot(projectRoot: string, serverProjectRoot: string, metadata?: {
47
+ alias?: string;
48
+ baseUrl?: string;
49
+ project?: string;
50
+ }): void;
51
+ /** Remove a stale server-host root while preserving selected server/repo state. */
52
+ export declare function clearRepoServerProjectRoot(projectRoot: string): void;
53
+ /** Whether this repo's selected connection targets a remote Rig server. */
54
+ export declare function isRemoteConnectionSelected(projectRoot: string): boolean;
@@ -6,12 +6,19 @@ import { dirname, resolve } from "path";
6
6
 
7
7
  // packages/cli/src/runner.ts
8
8
  import { EventBus } from "@rig/runtime/control-plane/runtime/events";
9
- import { CliError } from "@rig/runtime/control-plane/errors";
9
+ import { CliError as RuntimeCliError } from "@rig/runtime/control-plane/errors";
10
10
  import { evaluate, loadPolicy, resolveAction } from "@rig/runtime/control-plane/runtime/guard";
11
- import { PluginManager } from "@rig/runtime/control-plane/runtime/plugins";
12
- import { loadRuntimeContextFromEnv } from "@rig/runtime/control-plane/runtime/context";
13
11
  import { buildBinary } from "@rig/runtime/control-plane/runtime/isolation";
14
- 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
+ }
15
22
 
16
23
  // packages/cli/src/commands/_connection-state.ts
17
24
  function resolveGlobalConnectionsPath(env = process.env) {
@@ -32,7 +39,7 @@ function readJsonFile(path) {
32
39
  try {
33
40
  return JSON.parse(readFileSync(path, "utf8"));
34
41
  } catch (error) {
35
- throw new CliError2(`Invalid Rig connection state at ${path}: ${error instanceof Error ? error.message : String(error)}`, 1);
42
+ 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>`." });
36
43
  }
37
44
  }
38
45
  function writeJsonFile(path, value) {
@@ -75,7 +82,7 @@ function writeGlobalConnections(state, options = {}) {
75
82
  function upsertGlobalConnection(alias, connection, options = {}) {
76
83
  const cleanAlias = alias.trim();
77
84
  if (!cleanAlias)
78
- throw new CliError2("Connection alias is required.", 1);
85
+ throw new CliError("Connection alias is required.", 1, { hint: "Save a server with `rig server add <alias> <url>`." });
79
86
  const state = readGlobalConnections(options);
80
87
  state.connections[cleanAlias] = connection;
81
88
  writeGlobalConnections(state, options);
@@ -92,26 +99,81 @@ function readRepoConnection(projectRoot) {
92
99
  return {
93
100
  selected,
94
101
  project: typeof record.project === "string" ? record.project : undefined,
95
- linkedAt: typeof record.linkedAt === "string" ? record.linkedAt : undefined
102
+ linkedAt: typeof record.linkedAt === "string" ? record.linkedAt : undefined,
103
+ serverProjectRoot: typeof record.serverProjectRoot === "string" && record.serverProjectRoot.trim() ? record.serverProjectRoot.trim() : undefined,
104
+ serverProjectRootAlias: typeof record.serverProjectRootAlias === "string" && record.serverProjectRootAlias.trim() ? record.serverProjectRootAlias.trim() : undefined,
105
+ serverProjectRootBaseUrl: typeof record.serverProjectRootBaseUrl === "string" && record.serverProjectRootBaseUrl.trim() ? record.serverProjectRootBaseUrl.trim().replace(/\/+$/, "") : undefined
96
106
  };
97
107
  }
98
108
  function writeRepoConnection(projectRoot, state) {
99
109
  writeJsonFile(resolveRepoConnectionPath(projectRoot), state);
100
110
  }
111
+ function rootAllowedForSelection(repo, connection) {
112
+ const root = repo.serverProjectRoot?.trim();
113
+ if (!root)
114
+ return;
115
+ if (connection.kind === "remote") {
116
+ if (repo.serverProjectRootAlias !== repo.selected)
117
+ return;
118
+ if (repo.serverProjectRootBaseUrl !== connection.baseUrl)
119
+ return;
120
+ } else if (repo.serverProjectRootAlias && repo.serverProjectRootAlias !== repo.selected) {
121
+ return;
122
+ }
123
+ return root;
124
+ }
101
125
  function resolveSelectedConnection(projectRoot, options = {}) {
102
126
  const repo = readRepoConnection(projectRoot);
103
127
  if (!repo)
104
128
  return null;
105
129
  if (repo.selected === "local")
106
- return { alias: "local", connection: { kind: "local", mode: "auto" } };
130
+ return { alias: "local", connection: { kind: "local", mode: "auto" }, serverProjectRoot: repo.serverProjectRoot };
107
131
  const global = readGlobalConnections(options);
108
132
  const connection = global.connections[repo.selected];
109
133
  if (!connection) {
110
- throw new CliError2(`Selected Rig connection "${repo.selected}" was not found. Run \`rig connect list\` or \`rig connect use local\`.`, 1);
134
+ throw new CliError(`Selected Rig server "${repo.selected}" was not found. Run \`rig server list\` or \`rig server use local\`.`, 1);
135
+ }
136
+ return { alias: repo.selected, connection, serverProjectRoot: rootAllowedForSelection(repo, connection) };
137
+ }
138
+ function writeRepoServerProjectRoot(projectRoot, serverProjectRoot, metadata = {}) {
139
+ const repo = readRepoConnection(projectRoot);
140
+ if (!repo)
141
+ return;
142
+ let inferred = metadata;
143
+ if (!inferred.alias || !inferred.baseUrl) {
144
+ try {
145
+ const selected = resolveSelectedConnection(projectRoot);
146
+ if (selected?.connection.kind === "remote") {
147
+ inferred = {
148
+ alias: inferred.alias ?? selected.alias,
149
+ baseUrl: inferred.baseUrl ?? selected.connection.baseUrl
150
+ };
151
+ }
152
+ } catch {}
111
153
  }
112
- return { alias: repo.selected, connection };
154
+ writeRepoConnection(projectRoot, {
155
+ ...repo,
156
+ ...metadata.project ? { project: metadata.project } : {},
157
+ serverProjectRoot,
158
+ ...inferred.alias ? { serverProjectRootAlias: inferred.alias } : {},
159
+ ...inferred.baseUrl ? { serverProjectRootBaseUrl: inferred.baseUrl.replace(/\/+$/, "") } : {}
160
+ });
161
+ }
162
+ function clearRepoServerProjectRoot(projectRoot) {
163
+ const repo = readRepoConnection(projectRoot);
164
+ if (!repo)
165
+ return;
166
+ writeRepoConnection(projectRoot, {
167
+ selected: repo.selected,
168
+ ...repo.project ? { project: repo.project } : {},
169
+ ...repo.linkedAt ? { linkedAt: repo.linkedAt } : {}
170
+ });
171
+ }
172
+ function isRemoteConnectionSelected(projectRoot) {
173
+ return resolveSelectedConnection(projectRoot)?.connection.kind === "remote";
113
174
  }
114
175
  export {
176
+ writeRepoServerProjectRoot,
115
177
  writeRepoConnection,
116
178
  writeGlobalConnections,
117
179
  upsertGlobalConnection,
@@ -119,5 +181,7 @@ export {
119
181
  resolveRepoConnectionPath,
120
182
  resolveGlobalConnectionsPath,
121
183
  readRepoConnection,
122
- readGlobalConnections
184
+ readGlobalConnections,
185
+ isRemoteConnectionSelected,
186
+ clearRepoServerProjectRoot
123
187
  };
@@ -0,0 +1,46 @@
1
+ type DoctorConfig = {
2
+ readonly project?: Record<string, unknown>;
3
+ readonly taskSource?: {
4
+ readonly kind?: string;
5
+ readonly owner?: string;
6
+ readonly repo?: string;
7
+ };
8
+ readonly github?: {
9
+ readonly projects?: {
10
+ readonly enabled?: boolean;
11
+ readonly projectId?: string;
12
+ readonly statusFieldId?: string;
13
+ };
14
+ };
15
+ readonly pr?: {
16
+ readonly mode?: string;
17
+ };
18
+ readonly merge?: {
19
+ readonly mode?: string;
20
+ readonly method?: string;
21
+ };
22
+ };
23
+ import { type PiInstallCheck } from "./_pi-install";
24
+ export type DoctorStatus = "pass" | "warn" | "fail";
25
+ export type RigDoctorCheck = {
26
+ readonly id: string;
27
+ readonly label: string;
28
+ readonly status: DoctorStatus;
29
+ readonly detail?: string;
30
+ readonly remediation?: string;
31
+ };
32
+ export type RunRigDoctorChecksOptions = {
33
+ readonly projectRoot: string;
34
+ readonly requestJson?: (pathname: string, init?: RequestInit) => Promise<unknown>;
35
+ readonly piChecks?: () => Promise<readonly PiInstallCheck[]>;
36
+ readonly which?: (binary: string) => string | null;
37
+ readonly bunVersion?: string;
38
+ readonly loadConfig?: (projectRoot: string) => Promise<DoctorConfig | null>;
39
+ /** Per-check status narration ("Checking GitHub auth…") for wait spinners. */
40
+ readonly onProgress?: (label: string) => void;
41
+ };
42
+ export declare function runRigDoctorChecks(options: RunRigDoctorChecksOptions): Promise<RigDoctorCheck[]>;
43
+ export declare function formatDoctorChecks(checks: readonly RigDoctorCheck[]): string;
44
+ export declare function countDoctorFailures(checks: readonly RigDoctorCheck[]): number;
45
+ export declare function throwIfDoctorFailed(checks: readonly RigDoctorCheck[]): void;
46
+ export {};