@h-rig/cli 0.0.6-alpha.9 → 0.0.6-alpha.90
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -1
- package/dist/bin/build-rig-binaries.d.ts +2 -0
- package/dist/bin/rig.d.ts +2 -0
- package/dist/bin/rig.js +11052 -6668
- package/dist/src/app/board.d.ts +23 -0
- package/dist/src/app/board.js +1786 -0
- package/dist/src/app/drone-ui.d.ts +37 -0
- package/dist/src/app/drone-ui.js +294 -0
- package/dist/src/app/theme.d.ts +47 -0
- package/dist/src/app/theme.js +150 -0
- package/dist/src/commands/_async-ui.d.ts +13 -0
- package/dist/src/commands/_async-ui.js +223 -0
- package/dist/src/commands/_authority-runs.d.ts +22 -0
- package/dist/src/commands/_authority-runs.js +2 -3
- package/dist/src/commands/_cli-format.d.ts +49 -0
- package/dist/src/commands/_cli-format.js +447 -0
- package/dist/src/commands/_connection-state.d.ts +44 -0
- package/dist/src/commands/_connection-state.js +30 -11
- package/dist/src/commands/_doctor-checks.d.ts +52 -0
- package/dist/src/commands/_doctor-checks.js +180 -44
- package/dist/src/commands/_help-catalog.d.ts +51 -0
- package/dist/src/commands/_help-catalog.js +442 -0
- package/dist/src/commands/_json-output.d.ts +11 -0
- package/dist/src/commands/_json-output.js +56 -0
- package/dist/src/commands/_operator-surface.d.ts +34 -0
- package/dist/src/commands/_operator-surface.js +220 -0
- package/dist/src/commands/_operator-view.d.ts +30 -0
- package/dist/src/commands/_operator-view.js +803 -73
- package/dist/src/commands/_parsers.d.ts +15 -0
- package/dist/src/commands/_parsers.js +18 -11
- package/dist/src/commands/_paths.d.ts +11 -0
- package/dist/src/commands/_pi-frontend.d.ts +27 -0
- package/dist/src/commands/_pi-frontend.js +742 -0
- package/dist/src/commands/_pi-install.d.ts +42 -0
- package/dist/src/commands/_pi-install.js +5 -4
- package/dist/src/commands/_policy.d.ts +8 -0
- package/dist/src/commands/_policy.js +12 -5
- package/dist/src/commands/_preflight.d.ts +22 -0
- package/dist/src/commands/_preflight.js +190 -128
- package/dist/src/commands/_probes.d.ts +1 -0
- package/dist/src/commands/_run-driver-helpers.d.ts +99 -0
- package/dist/src/commands/_run-driver-helpers.js +75 -22
- package/dist/src/commands/_run-replay.d.ts +24 -0
- package/dist/src/commands/_run-replay.js +142 -0
- package/dist/src/commands/_server-client.d.ts +186 -0
- package/dist/src/commands/_server-client.js +360 -61
- package/dist/src/commands/_snapshot-upload.d.ts +39 -0
- package/dist/src/commands/_snapshot-upload.js +163 -39
- package/dist/src/commands/_spinner.d.ts +25 -0
- package/dist/src/commands/_spinner.js +65 -0
- package/dist/src/commands/_task-picker.d.ts +9 -0
- package/dist/src/commands/_task-picker.js +172 -19
- package/dist/src/commands/agent.d.ts +3 -0
- package/dist/src/commands/agent.js +39 -20
- package/dist/src/commands/browser.d.ts +65 -0
- package/dist/src/commands/browser.js +334 -51
- package/dist/src/commands/connect.d.ts +7 -0
- package/dist/src/commands/connect.js +272 -33
- package/dist/src/commands/dist.d.ts +28 -0
- package/dist/src/commands/dist.js +19 -12
- package/dist/src/commands/doctor.d.ts +3 -0
- package/dist/src/commands/doctor.js +378 -45
- package/dist/src/commands/github.d.ts +3 -0
- package/dist/src/commands/github.js +375 -53
- package/dist/src/commands/inbox.d.ts +30 -0
- package/dist/src/commands/inbox.js +760 -70
- package/dist/src/commands/init.d.ts +74 -0
- package/dist/src/commands/init.js +963 -119
- package/dist/src/commands/inspect.d.ts +3 -0
- package/dist/src/commands/inspect.js +588 -32
- package/dist/src/commands/inspector.d.ts +3 -0
- package/dist/src/commands/inspector.js +20 -13
- package/dist/src/commands/pi.d.ts +3 -0
- package/dist/src/commands/pi.js +177 -0
- package/dist/src/commands/plugin.d.ts +16 -0
- package/dist/src/commands/plugin.js +95 -27
- package/dist/src/commands/profile-and-review.d.ts +4 -0
- package/dist/src/commands/profile-and-review.js +26 -19
- package/dist/src/commands/queue.d.ts +3 -0
- package/dist/src/commands/queue.js +32 -12
- package/dist/src/commands/remote.d.ts +3 -0
- package/dist/src/commands/remote.js +43 -36
- package/dist/src/commands/repo-git-harness.d.ts +5 -0
- package/dist/src/commands/repo-git-harness.js +22 -15
- package/dist/src/commands/run.d.ts +3 -0
- package/dist/src/commands/run.js +1379 -159
- package/dist/src/commands/server.d.ts +7 -0
- package/dist/src/commands/server.js +502 -57
- package/dist/src/commands/setup.d.ts +16 -0
- package/dist/src/commands/setup.js +390 -63
- package/dist/src/commands/stats.d.ts +12 -0
- package/dist/src/commands/stats.js +1051 -0
- package/dist/src/commands/task-report-bug.d.ts +19 -0
- package/dist/src/commands/task-report-bug.js +260 -62
- package/dist/src/commands/task-run-driver.d.ts +132 -0
- package/dist/src/commands/task-run-driver.js +886 -130
- package/dist/src/commands/task.d.ts +14 -0
- package/dist/src/commands/task.js +1644 -314
- package/dist/src/commands/test.d.ts +3 -0
- package/dist/src/commands/test.js +15 -8
- package/dist/src/commands/workspace.d.ts +3 -0
- package/dist/src/commands/workspace.js +18 -11
- package/dist/src/commands.d.ts +29 -0
- package/dist/src/commands.js +10967 -6637
- package/dist/src/index.d.ts +4 -0
- package/dist/src/index.js +10168 -5787
- package/dist/src/launcher.d.ts +61 -0
- package/dist/src/launcher.js +77 -13
- package/dist/src/report-bug.d.ts +44 -0
- package/dist/src/report-bug.js +3 -3
- package/dist/src/runner.d.ts +47 -0
- package/dist/src/runner.js +16 -22
- package/dist/src/withMutedConsole.d.ts +2 -0
- package/package.json +13 -6
|
@@ -3,12 +3,19 @@ var __require = import.meta.require;
|
|
|
3
3
|
|
|
4
4
|
// packages/cli/src/runner.ts
|
|
5
5
|
import { EventBus } from "@rig/runtime/control-plane/runtime/events";
|
|
6
|
-
import { CliError } from "@rig/runtime/control-plane/errors";
|
|
6
|
+
import { CliError as RuntimeCliError } from "@rig/runtime/control-plane/errors";
|
|
7
7
|
import { evaluate, loadPolicy, resolveAction } from "@rig/runtime/control-plane/runtime/guard";
|
|
8
|
-
import { PluginManager } from "@rig/runtime/control-plane/runtime/plugins";
|
|
9
|
-
import { loadRuntimeContextFromEnv } from "@rig/runtime/control-plane/runtime/context";
|
|
10
8
|
import { buildBinary } from "@rig/runtime/control-plane/runtime/isolation";
|
|
11
|
-
|
|
9
|
+
|
|
10
|
+
class CliError extends RuntimeCliError {
|
|
11
|
+
hint;
|
|
12
|
+
constructor(message, exitCode = 1, options = {}) {
|
|
13
|
+
super(message, exitCode);
|
|
14
|
+
if (options.hint?.trim()) {
|
|
15
|
+
this.hint = options.hint.trim();
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
}
|
|
12
19
|
function requireNoExtraArgs(args, usage) {
|
|
13
20
|
if (args.length > 0) {
|
|
14
21
|
throw new CliError(`Unexpected arguments: ${args.join(" ")}
|
|
@@ -43,9 +50,14 @@ function readJsonFile(path) {
|
|
|
43
50
|
try {
|
|
44
51
|
return JSON.parse(readFileSync(path, "utf8"));
|
|
45
52
|
} catch (error) {
|
|
46
|
-
throw new
|
|
53
|
+
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>`." });
|
|
47
54
|
}
|
|
48
55
|
}
|
|
56
|
+
function writeJsonFile(path, value) {
|
|
57
|
+
mkdirSync(dirname(path), { recursive: true });
|
|
58
|
+
writeFileSync(path, `${JSON.stringify(value, null, 2)}
|
|
59
|
+
`, "utf8");
|
|
60
|
+
}
|
|
49
61
|
function normalizeConnection(value) {
|
|
50
62
|
if (!value || typeof value !== "object" || Array.isArray(value))
|
|
51
63
|
return null;
|
|
@@ -86,29 +98,47 @@ function readRepoConnection(projectRoot) {
|
|
|
86
98
|
return {
|
|
87
99
|
selected,
|
|
88
100
|
project: typeof record.project === "string" ? record.project : undefined,
|
|
89
|
-
linkedAt: typeof record.linkedAt === "string" ? record.linkedAt : undefined
|
|
101
|
+
linkedAt: typeof record.linkedAt === "string" ? record.linkedAt : undefined,
|
|
102
|
+
serverProjectRoot: typeof record.serverProjectRoot === "string" && record.serverProjectRoot.trim() ? record.serverProjectRoot.trim() : undefined
|
|
90
103
|
};
|
|
91
104
|
}
|
|
105
|
+
function writeRepoConnection(projectRoot, state) {
|
|
106
|
+
writeJsonFile(resolveRepoConnectionPath(projectRoot), state);
|
|
107
|
+
}
|
|
92
108
|
function resolveSelectedConnection(projectRoot, options = {}) {
|
|
93
109
|
const repo = readRepoConnection(projectRoot);
|
|
94
110
|
if (!repo)
|
|
95
111
|
return null;
|
|
96
112
|
if (repo.selected === "local")
|
|
97
|
-
return { alias: "local", connection: { kind: "local", mode: "auto" } };
|
|
113
|
+
return { alias: "local", connection: { kind: "local", mode: "auto" }, serverProjectRoot: repo.serverProjectRoot };
|
|
98
114
|
const global = readGlobalConnections(options);
|
|
99
115
|
const connection = global.connections[repo.selected];
|
|
100
116
|
if (!connection) {
|
|
101
|
-
throw new
|
|
117
|
+
throw new CliError(`Selected Rig server "${repo.selected}" was not found. Run \`rig server list\` or \`rig server use local\`.`, 1);
|
|
102
118
|
}
|
|
103
|
-
return { alias: repo.selected, connection };
|
|
119
|
+
return { alias: repo.selected, connection, serverProjectRoot: repo.serverProjectRoot };
|
|
120
|
+
}
|
|
121
|
+
function writeRepoServerProjectRoot(projectRoot, serverProjectRoot) {
|
|
122
|
+
const repo = readRepoConnection(projectRoot);
|
|
123
|
+
if (!repo)
|
|
124
|
+
return;
|
|
125
|
+
writeRepoConnection(projectRoot, { ...repo, serverProjectRoot });
|
|
104
126
|
}
|
|
105
127
|
|
|
106
128
|
// packages/cli/src/commands/_server-client.ts
|
|
107
|
-
import { spawnSync } from "child_process";
|
|
108
129
|
import { existsSync as existsSync2, readFileSync as readFileSync2 } from "fs";
|
|
109
130
|
import { resolve as resolve2 } from "path";
|
|
110
131
|
import { ensureLocalRigServerConnection } from "@rig/runtime/local-server";
|
|
111
|
-
var
|
|
132
|
+
var scopedGitHubBearerTokens = new Map;
|
|
133
|
+
var serverPhaseListener = null;
|
|
134
|
+
function setServerPhaseListener(listener) {
|
|
135
|
+
const previous = serverPhaseListener;
|
|
136
|
+
serverPhaseListener = listener;
|
|
137
|
+
return previous;
|
|
138
|
+
}
|
|
139
|
+
function reportServerPhase(label) {
|
|
140
|
+
serverPhaseListener?.(label);
|
|
141
|
+
}
|
|
112
142
|
function cleanToken(value) {
|
|
113
143
|
const trimmed = value?.trim();
|
|
114
144
|
return trimmed ? trimmed : null;
|
|
@@ -125,49 +155,80 @@ function readPrivateRemoteSessionToken(projectRoot) {
|
|
|
125
155
|
}
|
|
126
156
|
}
|
|
127
157
|
function readGitHubBearerTokenForRemote(projectRoot) {
|
|
128
|
-
|
|
129
|
-
|
|
158
|
+
const scopedKey = resolve2(projectRoot);
|
|
159
|
+
if (scopedGitHubBearerTokens.has(scopedKey))
|
|
160
|
+
return scopedGitHubBearerTokens.get(scopedKey) ?? null;
|
|
130
161
|
const privateSession = readPrivateRemoteSessionToken(projectRoot);
|
|
131
|
-
if (privateSession)
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
return
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
}
|
|
145
|
-
|
|
146
|
-
|
|
162
|
+
if (privateSession)
|
|
163
|
+
return privateSession;
|
|
164
|
+
return cleanToken(process.env.RIG_SERVER_AUTH_TOKEN) ?? cleanToken(process.env.RIG_REMOTE_AUTH_TOKEN);
|
|
165
|
+
}
|
|
166
|
+
function readStoredGitHubAuthToken(projectRoot) {
|
|
167
|
+
const path = resolve2(projectRoot, ".rig", "state", "github-auth.json");
|
|
168
|
+
if (!existsSync2(path))
|
|
169
|
+
return null;
|
|
170
|
+
try {
|
|
171
|
+
const parsed = JSON.parse(readFileSync2(path, "utf8"));
|
|
172
|
+
return cleanToken(typeof parsed.token === "string" ? parsed.token : undefined);
|
|
173
|
+
} catch {
|
|
174
|
+
return null;
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
function readLocalConnectionFallbackToken(projectRoot) {
|
|
178
|
+
return readGitHubBearerTokenForRemote(projectRoot) ?? cleanToken(process.env.RIG_GITHUB_TOKEN) ?? readStoredGitHubAuthToken(projectRoot);
|
|
147
179
|
}
|
|
148
180
|
async function ensureServerForCli(projectRoot) {
|
|
149
181
|
try {
|
|
150
182
|
const selected = resolveSelectedConnection(projectRoot);
|
|
151
183
|
if (selected?.connection.kind === "remote") {
|
|
184
|
+
reportServerPhase(`Connecting to ${selected.alias}\u2026`);
|
|
185
|
+
const authToken = readGitHubBearerTokenForRemote(projectRoot);
|
|
186
|
+
const serverProjectRoot = selected.serverProjectRoot ?? await backfillRemoteServerProjectRoot(projectRoot, selected.connection.baseUrl, authToken);
|
|
152
187
|
return {
|
|
153
188
|
baseUrl: selected.connection.baseUrl,
|
|
154
|
-
authToken
|
|
155
|
-
connectionKind: "remote"
|
|
189
|
+
authToken,
|
|
190
|
+
connectionKind: "remote",
|
|
191
|
+
serverProjectRoot
|
|
156
192
|
};
|
|
157
193
|
}
|
|
194
|
+
reportServerPhase("Starting local Rig server\u2026");
|
|
158
195
|
const connection = await ensureLocalRigServerConnection(projectRoot);
|
|
159
196
|
return {
|
|
160
197
|
baseUrl: connection.baseUrl,
|
|
161
|
-
authToken: connection.authToken,
|
|
162
|
-
connectionKind: "local"
|
|
198
|
+
authToken: connection.authToken ?? readLocalConnectionFallbackToken(projectRoot),
|
|
199
|
+
connectionKind: "local",
|
|
200
|
+
serverProjectRoot: resolve2(projectRoot)
|
|
163
201
|
};
|
|
164
202
|
} catch (error) {
|
|
165
203
|
if (error instanceof Error) {
|
|
166
|
-
throw new
|
|
204
|
+
throw new CliError(error.message, 1);
|
|
167
205
|
}
|
|
168
206
|
throw error;
|
|
169
207
|
}
|
|
170
208
|
}
|
|
209
|
+
async function backfillRemoteServerProjectRoot(projectRoot, baseUrl, authToken) {
|
|
210
|
+
const repo = readRepoConnection(projectRoot);
|
|
211
|
+
const slug = repo?.project?.trim();
|
|
212
|
+
if (!slug)
|
|
213
|
+
return null;
|
|
214
|
+
try {
|
|
215
|
+
const response = await fetch(`${baseUrl}/api/projects/${encodeURIComponent(slug)}`, {
|
|
216
|
+
headers: mergeHeaders(undefined, authToken)
|
|
217
|
+
});
|
|
218
|
+
if (!response.ok)
|
|
219
|
+
return null;
|
|
220
|
+
const payload = await response.json();
|
|
221
|
+
const project = payload.project && typeof payload.project === "object" && !Array.isArray(payload.project) ? payload.project : null;
|
|
222
|
+
const checkouts = Array.isArray(project?.checkouts) ? project.checkouts : [];
|
|
223
|
+
const latestCheckout = [...checkouts].reverse().find((entry) => Boolean(entry && typeof entry === "object" && !Array.isArray(entry) && typeof entry.path === "string"));
|
|
224
|
+
const path = typeof latestCheckout?.path === "string" && latestCheckout.path.trim() ? latestCheckout.path.trim() : null;
|
|
225
|
+
if (path)
|
|
226
|
+
writeRepoServerProjectRoot(projectRoot, path);
|
|
227
|
+
return path;
|
|
228
|
+
} catch {
|
|
229
|
+
return null;
|
|
230
|
+
}
|
|
231
|
+
}
|
|
171
232
|
function mergeHeaders(headers, authToken) {
|
|
172
233
|
const merged = new Headers(headers);
|
|
173
234
|
if (authToken) {
|
|
@@ -190,12 +251,65 @@ function diagnosticMessage(payload) {
|
|
|
190
251
|
});
|
|
191
252
|
return messages.length > 0 ? messages.join("; ") : null;
|
|
192
253
|
}
|
|
254
|
+
var serverReachabilityCache = new Map;
|
|
255
|
+
async function probeServerReachability(baseUrl, authToken) {
|
|
256
|
+
try {
|
|
257
|
+
const response = await fetch(`${baseUrl.replace(/\/+$/, "")}/api/server/status`, {
|
|
258
|
+
headers: mergeHeaders(undefined, authToken),
|
|
259
|
+
signal: AbortSignal.timeout(1500)
|
|
260
|
+
});
|
|
261
|
+
return response.ok;
|
|
262
|
+
} catch {
|
|
263
|
+
return false;
|
|
264
|
+
}
|
|
265
|
+
}
|
|
266
|
+
function cachedServerReachability(projectRoot, baseUrl, authToken) {
|
|
267
|
+
const key = resolve2(projectRoot);
|
|
268
|
+
const cached = serverReachabilityCache.get(key);
|
|
269
|
+
if (cached)
|
|
270
|
+
return cached;
|
|
271
|
+
const probe = probeServerReachability(baseUrl, authToken);
|
|
272
|
+
serverReachabilityCache.set(key, probe);
|
|
273
|
+
return probe;
|
|
274
|
+
}
|
|
275
|
+
function describeSelectedServer(projectRoot, server) {
|
|
276
|
+
try {
|
|
277
|
+
const selected = resolveSelectedConnection(projectRoot);
|
|
278
|
+
if (selected) {
|
|
279
|
+
return {
|
|
280
|
+
alias: selected.alias,
|
|
281
|
+
target: selected.connection.kind === "remote" ? selected.connection.baseUrl : server.baseUrl
|
|
282
|
+
};
|
|
283
|
+
}
|
|
284
|
+
} catch {}
|
|
285
|
+
return { alias: server.connectionKind === "remote" ? "remote" : "local", target: server.baseUrl };
|
|
286
|
+
}
|
|
287
|
+
async function buildServerFailureContext(projectRoot, server) {
|
|
288
|
+
const { alias, target } = describeSelectedServer(projectRoot, server);
|
|
289
|
+
const reachable = await cachedServerReachability(projectRoot, server.baseUrl, server.authToken);
|
|
290
|
+
const reachability = reachable ? "server is reachable" : "server is unreachable";
|
|
291
|
+
return {
|
|
292
|
+
contextLine: `Currently connected to: ${alias} at ${target} (${reachability}).`,
|
|
293
|
+
hint: "Check the selected server with `rig server status`, or switch with `rig server use <alias|local>`."
|
|
294
|
+
};
|
|
295
|
+
}
|
|
193
296
|
async function requestServerJson(context, pathname, init = {}) {
|
|
194
297
|
const server = await ensureServerForCli(context.projectRoot);
|
|
195
|
-
const
|
|
196
|
-
|
|
197
|
-
headers
|
|
198
|
-
});
|
|
298
|
+
const headers = mergeHeaders(init.headers, server.authToken);
|
|
299
|
+
if (server.serverProjectRoot)
|
|
300
|
+
headers.set("x-rig-project-root", server.serverProjectRoot);
|
|
301
|
+
reportServerPhase(`${(init.method ?? "GET").toUpperCase()} ${pathname.split("?")[0]}\u2026`);
|
|
302
|
+
let response;
|
|
303
|
+
try {
|
|
304
|
+
response = await fetch(`${server.baseUrl}${pathname}`, {
|
|
305
|
+
...init,
|
|
306
|
+
headers
|
|
307
|
+
});
|
|
308
|
+
} catch (error) {
|
|
309
|
+
const failure = await buildServerFailureContext(context.projectRoot, server);
|
|
310
|
+
throw new CliError(`Rig server request failed: ${error instanceof Error ? error.message : String(error)}
|
|
311
|
+
${failure.contextLine}`, 1, { hint: failure.hint });
|
|
312
|
+
}
|
|
199
313
|
const text = await response.text();
|
|
200
314
|
const payload = text.trim().length > 0 ? (() => {
|
|
201
315
|
try {
|
|
@@ -206,11 +320,26 @@ async function requestServerJson(context, pathname, init = {}) {
|
|
|
206
320
|
})() : null;
|
|
207
321
|
if (!response.ok) {
|
|
208
322
|
const diagnostics = diagnosticMessage(payload);
|
|
209
|
-
const
|
|
210
|
-
|
|
323
|
+
const rawDetail = diagnostics ?? (text || response.statusText);
|
|
324
|
+
const detail = diagnostics ? rawDetail : rawDetail.split(`
|
|
325
|
+
`).map((line) => line.trim()).find((line) => line.length > 0)?.slice(0, 200) ?? response.statusText;
|
|
326
|
+
const failure = await buildServerFailureContext(context.projectRoot, server);
|
|
327
|
+
throw new CliError(`Rig server request failed (${response.status}): ${detail}
|
|
328
|
+
${failure.contextLine}`, 1, { hint: failure.hint });
|
|
211
329
|
}
|
|
212
330
|
return payload;
|
|
213
331
|
}
|
|
332
|
+
var RESUMABLE_RUN_STATUSES = new Set([
|
|
333
|
+
"created",
|
|
334
|
+
"preparing",
|
|
335
|
+
"running",
|
|
336
|
+
"validating",
|
|
337
|
+
"reviewing",
|
|
338
|
+
"stopped",
|
|
339
|
+
"failed",
|
|
340
|
+
"needs-attention",
|
|
341
|
+
"needs_attention"
|
|
342
|
+
]);
|
|
214
343
|
|
|
215
344
|
// packages/cli/src/commands/_parsers.ts
|
|
216
345
|
async function loadRigConfigOrNull(projectRoot) {
|
|
@@ -226,7 +355,8 @@ async function loadRigConfigOrNull(projectRoot) {
|
|
|
226
355
|
import { existsSync as existsSync3, readFileSync as readFileSync3, rmSync } from "fs";
|
|
227
356
|
import { homedir as homedir2 } from "os";
|
|
228
357
|
import { resolve as resolve3 } from "path";
|
|
229
|
-
var PI_RIG_PACKAGE_NAME = "@rig/pi-rig";
|
|
358
|
+
var PI_RIG_PACKAGE_NAME = "@h-rig/pi-rig";
|
|
359
|
+
var LEGACY_PI_RIG_PACKAGE_NAME = "@rig/pi-rig";
|
|
230
360
|
async function defaultCommandRunner(command, options = {}) {
|
|
231
361
|
const proc = Bun.spawn(command, { cwd: options.cwd, stdout: "pipe", stderr: "pipe" });
|
|
232
362
|
const [stdout, stderr, exitCode] = await Promise.all([
|
|
@@ -245,7 +375,7 @@ function resolvePiHomeDir(inputHomeDir) {
|
|
|
245
375
|
function piListContainsPiRig(output) {
|
|
246
376
|
return output.split(/\r?\n/).some((line) => {
|
|
247
377
|
const normalized = line.trim();
|
|
248
|
-
return normalized.includes(PI_RIG_PACKAGE_NAME) || /(?:^|[\\/])packages[\\/]pi-rig(?:$|\s)/.test(normalized);
|
|
378
|
+
return normalized.includes(PI_RIG_PACKAGE_NAME) || normalized.includes(LEGACY_PI_RIG_PACKAGE_NAME) || /(?:^|[\\/])packages[\\/]pi-rig(?:$|\s)/.test(normalized);
|
|
249
379
|
});
|
|
250
380
|
}
|
|
251
381
|
async function safeRun(runner, command, options) {
|
|
@@ -413,7 +543,10 @@ async function runRigDoctorChecks(options) {
|
|
|
413
543
|
const bunVersion = options.bunVersion ?? Bun.version;
|
|
414
544
|
const request = options.requestJson ?? ((pathname, init) => requestServerJson({ projectRoot }, pathname, init));
|
|
415
545
|
const loadConfig = options.loadConfig ?? loadRigConfigOrNull;
|
|
546
|
+
const progress = options.onProgress ?? (() => {});
|
|
547
|
+
progress("Checking local toolchain\u2026");
|
|
416
548
|
checks.push(check("bun", `bun >= ${MIN_SUPPORTED_BUN_VERSION}`, isSupportedBunVersion(bunVersion) ? "pass" : "fail", `found ${bunVersion}`, `Install Bun ${MIN_SUPPORTED_BUN_VERSION} or newer.`), check("git", "git", which("git") ? "pass" : "fail", which("git") ?? undefined, "Install git and ensure it is on PATH."), check("jq", "jq", which("jq") ? "pass" : "warn", which("jq") ?? undefined, "Install jq (for example `brew install jq`)."));
|
|
549
|
+
progress("Loading rig.config\u2026");
|
|
417
550
|
const loadedConfig = await loadConfig(projectRoot).catch(() => null);
|
|
418
551
|
const config = loadedConfig ?? loadFallbackConfig(projectRoot);
|
|
419
552
|
const hasConfigFile = ["rig.config.ts", "rig.config.mts", "rig.config.json"].some((name) => existsSync4(resolve4(projectRoot, name)));
|
|
@@ -421,7 +554,7 @@ async function runRigDoctorChecks(options) {
|
|
|
421
554
|
const taskSourceKind = config?.taskSource?.kind;
|
|
422
555
|
checks.push(taskSourceKind ? check("task-source", "task source configured", "pass", taskSourceKind) : check("task-source", "task source configured", "fail", "missing taskSource", "Configure taskSource in rig.config.ts."));
|
|
423
556
|
const repo = readRepoConnection(projectRoot);
|
|
424
|
-
checks.push(repo ? check("project-link", "repo selected Rig
|
|
557
|
+
checks.push(repo ? check("project-link", "repo selected Rig server", repo.project ? "pass" : "warn", `${repo.selected}${repo.project ? ` -> ${repo.project}` : ""}`, "Run `rig init --yes --repo owner/repo` to link this checkout to a GitHub repo slug.") : check("project-link", "repo selected Rig server", "fail", "missing .rig/state/connection.json", "Run `rig init` or `rig server use <alias|local>`."));
|
|
425
558
|
const selected = (() => {
|
|
426
559
|
try {
|
|
427
560
|
return resolveSelectedConnection(projectRoot);
|
|
@@ -429,9 +562,10 @@ async function runRigDoctorChecks(options) {
|
|
|
429
562
|
return null;
|
|
430
563
|
}
|
|
431
564
|
})();
|
|
432
|
-
checks.push(selected ? check("connection", "selected server connection", "pass", selected.connection.kind === "remote" ? selected.connection.baseUrl : "local auto") : check("connection", "selected server
|
|
565
|
+
checks.push(selected ? check("connection", "selected server connection", "pass", selected.connection.kind === "remote" ? selected.connection.baseUrl : "local auto") : check("connection", "selected server", repo ? "fail" : "warn", repo ? "selected alias is missing" : "will auto-start local server", repo ? "Run `rig server list` and `rig server use <alias|local>`." : undefined));
|
|
433
566
|
let server = null;
|
|
434
567
|
try {
|
|
568
|
+
progress("Connecting to the selected Rig server\u2026");
|
|
435
569
|
server = await (options.resolveServer ?? ensureServerForCli)(projectRoot);
|
|
436
570
|
checks.push(check("server", "Rig server reachable", "pass", `${server.connectionKind} ${server.baseUrl}`));
|
|
437
571
|
} catch (error) {
|
|
@@ -439,18 +573,21 @@ async function runRigDoctorChecks(options) {
|
|
|
439
573
|
}
|
|
440
574
|
if (server || options.requestJson) {
|
|
441
575
|
try {
|
|
576
|
+
progress("Checking server status\u2026");
|
|
442
577
|
const status = await request("/api/server/status");
|
|
443
578
|
checks.push(check("server-status", "server project status", "pass", JSON.stringify(status).slice(0, 180)));
|
|
444
579
|
} catch (error) {
|
|
445
580
|
checks.push(check("server-status", "server project status", "fail", errorMessage(error), "Run `rig doctor` after the selected server is reachable."));
|
|
446
581
|
}
|
|
447
582
|
try {
|
|
583
|
+
progress("Checking GitHub auth\u2026");
|
|
448
584
|
const auth = await request("/api/github/auth/status");
|
|
449
585
|
checks.push(isAuthenticated(auth) ? check("github-auth", "GitHub auth", "pass") : check("github-auth", "GitHub auth", "fail", "not authenticated", "Run `rig github auth import-gh` or `rig github auth token --token <token>`."));
|
|
450
586
|
} catch (error) {
|
|
451
587
|
checks.push(check("github-auth", "GitHub auth", "fail", errorMessage(error), "Authenticate GitHub through Rig and ensure the server exposes auth status."));
|
|
452
588
|
}
|
|
453
589
|
try {
|
|
590
|
+
progress("Checking GitHub repo permissions\u2026");
|
|
454
591
|
const permissions = await request("/api/github/repo/permissions");
|
|
455
592
|
const allowed = permissionAllowsPr(permissions);
|
|
456
593
|
checks.push(allowed === true ? check("github-repo-permissions", "GitHub repo PR permissions", "pass", JSON.stringify(permissions).slice(0, 180)) : allowed === false ? check("github-repo-permissions", "GitHub repo PR permissions", "fail", JSON.stringify(permissions).slice(0, 180), "Grant the selected GitHub token permission to push branches, open PRs, and merge according to repo rules.") : check("github-repo-permissions", "GitHub repo PR permissions", "warn", JSON.stringify(permissions).slice(0, 180), "Confirm the selected token can push branches and open PRs."));
|
|
@@ -458,6 +595,7 @@ async function runRigDoctorChecks(options) {
|
|
|
458
595
|
checks.push(check("github-repo-permissions", "GitHub repo PR permissions", "warn", errorMessage(error), "Ensure the server exposes repo permission checks and the token can open PRs."));
|
|
459
596
|
}
|
|
460
597
|
try {
|
|
598
|
+
progress("Checking GitHub issue labels\u2026");
|
|
461
599
|
const labels = await request("/api/workspace/task-labels");
|
|
462
600
|
const ready = labelsReady(labels);
|
|
463
601
|
checks.push(ready === false ? check("task-labels", "GitHub issue labels", "fail", JSON.stringify(labels).slice(0, 180), "Let Rig create required labels or create the configured lifecycle labels manually.") : check("task-labels", "GitHub issue labels", ready === true ? "pass" : "warn", JSON.stringify(labels).slice(0, 180), "Confirm required Rig lifecycle labels exist."));
|
|
@@ -465,6 +603,7 @@ async function runRigDoctorChecks(options) {
|
|
|
465
603
|
checks.push(check("task-labels", "GitHub issue labels", "warn", errorMessage(error), "Run `rig init`/`rig doctor` after label setup is wired on the server."));
|
|
466
604
|
}
|
|
467
605
|
try {
|
|
606
|
+
progress("Checking task projection\u2026");
|
|
468
607
|
const projection = await request("/api/workspace/task-projection");
|
|
469
608
|
checks.push(check("task-projection", "task projection", "pass", JSON.stringify(projection).slice(0, 180)));
|
|
470
609
|
} catch (error) {
|
|
@@ -473,6 +612,7 @@ async function runRigDoctorChecks(options) {
|
|
|
473
612
|
const slug = projectStatusSlug(projectRoot, config);
|
|
474
613
|
if (slug) {
|
|
475
614
|
try {
|
|
615
|
+
progress("Checking server project checkout\u2026");
|
|
476
616
|
const project = await request(`/api/projects/${encodeURIComponent(slug)}`);
|
|
477
617
|
checks.push(check("remote-checkout", "server project checkout", "pass", JSON.stringify(project).slice(0, 180)));
|
|
478
618
|
} catch (error) {
|
|
@@ -487,6 +627,7 @@ async function runRigDoctorChecks(options) {
|
|
|
487
627
|
}
|
|
488
628
|
checks.push(githubProjectsCheck(config));
|
|
489
629
|
checks.push(prMergeCheck(config));
|
|
630
|
+
progress("Checking Pi installation\u2026");
|
|
490
631
|
const piChecks = await (options.piChecks ?? (() => buildPiSetupChecks()))().catch((error) => [{
|
|
491
632
|
ok: false,
|
|
492
633
|
label: "pi/pi-rig checks",
|
|
@@ -510,10 +651,202 @@ function countDoctorFailures(checks) {
|
|
|
510
651
|
return checks.filter((entry) => entry.status === "fail").length;
|
|
511
652
|
}
|
|
512
653
|
|
|
654
|
+
// packages/cli/src/commands/_async-ui.ts
|
|
655
|
+
import pc from "picocolors";
|
|
656
|
+
|
|
657
|
+
// packages/cli/src/commands/_spinner.ts
|
|
658
|
+
var SPINNER_FRAMES = ["\u280B", "\u2819", "\u2839", "\u2838", "\u283C", "\u2834", "\u2826", "\u2827", "\u2807", "\u280F"];
|
|
659
|
+
function createTtySpinner(input) {
|
|
660
|
+
const output = input.output ?? process.stdout;
|
|
661
|
+
const isTty = output.isTTY === true;
|
|
662
|
+
const frames = input.frames && input.frames.length > 0 ? input.frames : SPINNER_FRAMES;
|
|
663
|
+
let label = input.label;
|
|
664
|
+
let frame = 0;
|
|
665
|
+
let paused = false;
|
|
666
|
+
let stopped = false;
|
|
667
|
+
let lastPrintedLabel = "";
|
|
668
|
+
const render = () => {
|
|
669
|
+
if (stopped || paused)
|
|
670
|
+
return;
|
|
671
|
+
if (!isTty) {
|
|
672
|
+
if (label !== lastPrintedLabel) {
|
|
673
|
+
output.write(`${label}
|
|
674
|
+
`);
|
|
675
|
+
lastPrintedLabel = label;
|
|
676
|
+
}
|
|
677
|
+
return;
|
|
678
|
+
}
|
|
679
|
+
frame = (frame + 1) % frames.length;
|
|
680
|
+
const glyph = frames[frame] ?? frames[0] ?? "";
|
|
681
|
+
output.write(`\r\x1B[2K${input.styleFrame ? input.styleFrame(glyph) : glyph} ${label}`);
|
|
682
|
+
};
|
|
683
|
+
const clearLine = () => {
|
|
684
|
+
if (isTty)
|
|
685
|
+
output.write("\r\x1B[2K");
|
|
686
|
+
};
|
|
687
|
+
render();
|
|
688
|
+
const timer = isTty ? setInterval(render, input.intervalMs ?? 120) : null;
|
|
689
|
+
return {
|
|
690
|
+
setLabel(next) {
|
|
691
|
+
label = next;
|
|
692
|
+
render();
|
|
693
|
+
},
|
|
694
|
+
pause() {
|
|
695
|
+
paused = true;
|
|
696
|
+
clearLine();
|
|
697
|
+
},
|
|
698
|
+
resume() {
|
|
699
|
+
if (stopped)
|
|
700
|
+
return;
|
|
701
|
+
paused = false;
|
|
702
|
+
render();
|
|
703
|
+
},
|
|
704
|
+
stop(finalLine) {
|
|
705
|
+
if (stopped)
|
|
706
|
+
return;
|
|
707
|
+
stopped = true;
|
|
708
|
+
if (timer)
|
|
709
|
+
clearInterval(timer);
|
|
710
|
+
clearLine();
|
|
711
|
+
if (finalLine)
|
|
712
|
+
output.write(`${finalLine}
|
|
713
|
+
`);
|
|
714
|
+
}
|
|
715
|
+
};
|
|
716
|
+
}
|
|
717
|
+
|
|
718
|
+
// packages/cli/src/app/theme.ts
|
|
719
|
+
var RIG_PALETTE = {
|
|
720
|
+
ink: "#f2f3f6",
|
|
721
|
+
ink2: "#aeb0ba",
|
|
722
|
+
ink3: "#6c6e79",
|
|
723
|
+
ink4: "#44464f",
|
|
724
|
+
accent: "#ccff4d",
|
|
725
|
+
accentDim: "#a9d63f",
|
|
726
|
+
cyan: "#56d8ff",
|
|
727
|
+
red: "#ff5d5d",
|
|
728
|
+
yellow: "#ffd24d"
|
|
729
|
+
};
|
|
730
|
+
function hexToRgb(hex) {
|
|
731
|
+
const value = hex.replace("#", "");
|
|
732
|
+
return [
|
|
733
|
+
Number.parseInt(value.slice(0, 2), 16),
|
|
734
|
+
Number.parseInt(value.slice(2, 4), 16),
|
|
735
|
+
Number.parseInt(value.slice(4, 6), 16)
|
|
736
|
+
];
|
|
737
|
+
}
|
|
738
|
+
function fg(hex) {
|
|
739
|
+
const [r, g, b] = hexToRgb(hex);
|
|
740
|
+
return (text) => `\x1B[38;2;${r};${g};${b}m${text}\x1B[39m`;
|
|
741
|
+
}
|
|
742
|
+
var ink = fg(RIG_PALETTE.ink);
|
|
743
|
+
var ink2 = fg(RIG_PALETTE.ink2);
|
|
744
|
+
var ink3 = fg(RIG_PALETTE.ink3);
|
|
745
|
+
var ink4 = fg(RIG_PALETTE.ink4);
|
|
746
|
+
var accent = fg(RIG_PALETTE.accent);
|
|
747
|
+
var accentDim = fg(RIG_PALETTE.accentDim);
|
|
748
|
+
var cyan = fg(RIG_PALETTE.cyan);
|
|
749
|
+
var red = fg(RIG_PALETTE.red);
|
|
750
|
+
var yellow = fg(RIG_PALETTE.yellow);
|
|
751
|
+
function bold(text) {
|
|
752
|
+
return `\x1B[1m${text}\x1B[22m`;
|
|
753
|
+
}
|
|
754
|
+
var DRONE_ART = [
|
|
755
|
+
" .-=-. .-=-. ",
|
|
756
|
+
" ( !!! ) ( !!! ) ",
|
|
757
|
+
" '-=-'._ _.'-=-' ",
|
|
758
|
+
" '._ _.' ",
|
|
759
|
+
" '=$$$$$$$=.' ",
|
|
760
|
+
" =$$$$$$$$$$$= ",
|
|
761
|
+
" $$$@@@@@@@@@@$$$ ",
|
|
762
|
+
" $$$@@ @@$$$ ",
|
|
763
|
+
" $$@ ? @$$$ ",
|
|
764
|
+
" $$$@ '-' @$$$ ",
|
|
765
|
+
" $$$@@ @@$$$ ",
|
|
766
|
+
" $$$@@@@@@@@@@$$$ ",
|
|
767
|
+
" =$$$$$$$$$$$= ",
|
|
768
|
+
" '=$$$$$$$=.' ",
|
|
769
|
+
" _.' '._ ",
|
|
770
|
+
" .-=-.' '.-=-. ",
|
|
771
|
+
" ( !!! ) ( !!! ) ",
|
|
772
|
+
" '-=-' '-=-' "
|
|
773
|
+
];
|
|
774
|
+
var EYE_FRAMES = ["@", "o", "."];
|
|
775
|
+
var DRONE_WIDTH = DRONE_ART[0].length;
|
|
776
|
+
var DRONE_HEIGHT = DRONE_ART.length;
|
|
777
|
+
var MICRO_BLADES = ["---", "\\\\\\", "|||", "///"];
|
|
778
|
+
function microDroneFrame(tick) {
|
|
779
|
+
const blade = MICRO_BLADES[tick % MICRO_BLADES.length];
|
|
780
|
+
const eye = EYE_FRAMES[Math.floor(tick / 2) % EYE_FRAMES.length];
|
|
781
|
+
return `(${blade})${eye}(${blade})`;
|
|
782
|
+
}
|
|
783
|
+
var MICRO_DRONE_FRAMES = Array.from({ length: 12 }, (_, index) => microDroneFrame(index));
|
|
784
|
+
function renderMicroDroneFrame(tick) {
|
|
785
|
+
const blade = MICRO_BLADES[tick % MICRO_BLADES.length];
|
|
786
|
+
const eye = EYE_FRAMES[Math.floor(tick / 2) % EYE_FRAMES.length];
|
|
787
|
+
return `${ink4("(")}${cyan(blade)}${ink4(")")}${bold(accent(eye))}${ink4("(")}${cyan(blade)}${ink4(")")}`;
|
|
788
|
+
}
|
|
789
|
+
|
|
790
|
+
// packages/cli/src/commands/_async-ui.ts
|
|
791
|
+
var DONE_SYMBOL = pc.green("\u25C7");
|
|
792
|
+
var FAIL_SYMBOL = pc.red("\u25A0");
|
|
793
|
+
var activeUpdate = null;
|
|
794
|
+
async function withSpinner(label, work, options = {}) {
|
|
795
|
+
if (options.outputMode === "json") {
|
|
796
|
+
return work(() => {});
|
|
797
|
+
}
|
|
798
|
+
if (activeUpdate) {
|
|
799
|
+
const outer = activeUpdate;
|
|
800
|
+
outer(label);
|
|
801
|
+
return work(outer);
|
|
802
|
+
}
|
|
803
|
+
const output = options.output ?? process.stderr;
|
|
804
|
+
const isTty = output.isTTY === true;
|
|
805
|
+
let lastLabel = label;
|
|
806
|
+
if (!isTty) {
|
|
807
|
+
output.write(`${label}
|
|
808
|
+
`);
|
|
809
|
+
const update2 = (next) => {
|
|
810
|
+
lastLabel = next;
|
|
811
|
+
};
|
|
812
|
+
activeUpdate = update2;
|
|
813
|
+
const previousListener2 = setServerPhaseListener(update2);
|
|
814
|
+
try {
|
|
815
|
+
return await work(update2);
|
|
816
|
+
} finally {
|
|
817
|
+
activeUpdate = null;
|
|
818
|
+
setServerPhaseListener(previousListener2);
|
|
819
|
+
}
|
|
820
|
+
}
|
|
821
|
+
const spinner = createTtySpinner({
|
|
822
|
+
label,
|
|
823
|
+
output,
|
|
824
|
+
frames: MICRO_DRONE_FRAMES,
|
|
825
|
+
styleFrame: (frame) => renderMicroDroneFrame(Math.max(0, MICRO_DRONE_FRAMES.indexOf(frame)))
|
|
826
|
+
});
|
|
827
|
+
const update = (next) => {
|
|
828
|
+
lastLabel = next;
|
|
829
|
+
spinner.setLabel(next);
|
|
830
|
+
};
|
|
831
|
+
activeUpdate = update;
|
|
832
|
+
const previousListener = setServerPhaseListener(update);
|
|
833
|
+
try {
|
|
834
|
+
const result = await work(update);
|
|
835
|
+
spinner.stop(options.doneLabel ? `${DONE_SYMBOL} ${options.doneLabel}` : undefined);
|
|
836
|
+
return result;
|
|
837
|
+
} catch (error) {
|
|
838
|
+
spinner.stop(`${FAIL_SYMBOL} ${lastLabel}`);
|
|
839
|
+
throw error;
|
|
840
|
+
} finally {
|
|
841
|
+
activeUpdate = null;
|
|
842
|
+
setServerPhaseListener(previousListener);
|
|
843
|
+
}
|
|
844
|
+
}
|
|
845
|
+
|
|
513
846
|
// packages/cli/src/commands/doctor.ts
|
|
514
847
|
async function executeDoctor(context, args) {
|
|
515
848
|
requireNoExtraArgs(args, "rig doctor");
|
|
516
|
-
const checks = await runRigDoctorChecks({ projectRoot: context.projectRoot });
|
|
849
|
+
const checks = await withSpinner("Running doctor checks\u2026", (update) => runRigDoctorChecks({ projectRoot: context.projectRoot, onProgress: update }), { outputMode: context.outputMode });
|
|
517
850
|
if (context.outputMode === "text") {
|
|
518
851
|
console.log(formatDoctorChecks(checks));
|
|
519
852
|
}
|