@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
|
@@ -1,15 +1,22 @@
|
|
|
1
1
|
// @bun
|
|
2
2
|
// packages/cli/src/commands/github.ts
|
|
3
|
-
import { spawnSync
|
|
3
|
+
import { spawnSync } from "child_process";
|
|
4
4
|
|
|
5
5
|
// packages/cli/src/runner.ts
|
|
6
6
|
import { EventBus } from "@rig/runtime/control-plane/runtime/events";
|
|
7
|
-
import { CliError } from "@rig/runtime/control-plane/errors";
|
|
7
|
+
import { CliError as RuntimeCliError } from "@rig/runtime/control-plane/errors";
|
|
8
8
|
import { evaluate, loadPolicy, resolveAction } from "@rig/runtime/control-plane/runtime/guard";
|
|
9
|
-
import { PluginManager } from "@rig/runtime/control-plane/runtime/plugins";
|
|
10
|
-
import { loadRuntimeContextFromEnv } from "@rig/runtime/control-plane/runtime/context";
|
|
11
9
|
import { buildBinary } from "@rig/runtime/control-plane/runtime/isolation";
|
|
12
|
-
|
|
10
|
+
|
|
11
|
+
class CliError extends RuntimeCliError {
|
|
12
|
+
hint;
|
|
13
|
+
constructor(message, exitCode = 1, options = {}) {
|
|
14
|
+
super(message, exitCode);
|
|
15
|
+
if (options.hint?.trim()) {
|
|
16
|
+
this.hint = options.hint.trim();
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
}
|
|
13
20
|
function takeOption(args, option) {
|
|
14
21
|
const rest = [];
|
|
15
22
|
let value;
|
|
@@ -18,7 +25,7 @@ function takeOption(args, option) {
|
|
|
18
25
|
if (current === option) {
|
|
19
26
|
const next = args[index + 1];
|
|
20
27
|
if (!next || next.startsWith("-")) {
|
|
21
|
-
throw new CliError(`Missing value for ${option}`);
|
|
28
|
+
throw new CliError(`Missing value for ${option}`, 1, { hint: `Provide a value after ${option}, e.g. \`${option} <value>\`.` });
|
|
22
29
|
}
|
|
23
30
|
value = next;
|
|
24
31
|
index += 1;
|
|
@@ -32,7 +39,6 @@ function takeOption(args, option) {
|
|
|
32
39
|
}
|
|
33
40
|
|
|
34
41
|
// packages/cli/src/commands/_server-client.ts
|
|
35
|
-
import { spawnSync } from "child_process";
|
|
36
42
|
import { existsSync as existsSync2, readFileSync as readFileSync2 } from "fs";
|
|
37
43
|
import { resolve as resolve2 } from "path";
|
|
38
44
|
import { ensureLocalRigServerConnection } from "@rig/runtime/local-server";
|
|
@@ -59,9 +65,14 @@ function readJsonFile(path) {
|
|
|
59
65
|
try {
|
|
60
66
|
return JSON.parse(readFileSync(path, "utf8"));
|
|
61
67
|
} catch (error) {
|
|
62
|
-
throw new
|
|
68
|
+
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>`." });
|
|
63
69
|
}
|
|
64
70
|
}
|
|
71
|
+
function writeJsonFile(path, value) {
|
|
72
|
+
mkdirSync(dirname(path), { recursive: true });
|
|
73
|
+
writeFileSync(path, `${JSON.stringify(value, null, 2)}
|
|
74
|
+
`, "utf8");
|
|
75
|
+
}
|
|
65
76
|
function normalizeConnection(value) {
|
|
66
77
|
if (!value || typeof value !== "object" || Array.isArray(value))
|
|
67
78
|
return null;
|
|
@@ -102,25 +113,44 @@ function readRepoConnection(projectRoot) {
|
|
|
102
113
|
return {
|
|
103
114
|
selected,
|
|
104
115
|
project: typeof record.project === "string" ? record.project : undefined,
|
|
105
|
-
linkedAt: typeof record.linkedAt === "string" ? record.linkedAt : undefined
|
|
116
|
+
linkedAt: typeof record.linkedAt === "string" ? record.linkedAt : undefined,
|
|
117
|
+
serverProjectRoot: typeof record.serverProjectRoot === "string" && record.serverProjectRoot.trim() ? record.serverProjectRoot.trim() : undefined
|
|
106
118
|
};
|
|
107
119
|
}
|
|
120
|
+
function writeRepoConnection(projectRoot, state) {
|
|
121
|
+
writeJsonFile(resolveRepoConnectionPath(projectRoot), state);
|
|
122
|
+
}
|
|
108
123
|
function resolveSelectedConnection(projectRoot, options = {}) {
|
|
109
124
|
const repo = readRepoConnection(projectRoot);
|
|
110
125
|
if (!repo)
|
|
111
126
|
return null;
|
|
112
127
|
if (repo.selected === "local")
|
|
113
|
-
return { alias: "local", connection: { kind: "local", mode: "auto" } };
|
|
128
|
+
return { alias: "local", connection: { kind: "local", mode: "auto" }, serverProjectRoot: repo.serverProjectRoot };
|
|
114
129
|
const global = readGlobalConnections(options);
|
|
115
130
|
const connection = global.connections[repo.selected];
|
|
116
131
|
if (!connection) {
|
|
117
|
-
throw new
|
|
132
|
+
throw new CliError(`Selected Rig server "${repo.selected}" was not found. Run \`rig server list\` or \`rig server use local\`.`, 1);
|
|
118
133
|
}
|
|
119
|
-
return { alias: repo.selected, connection };
|
|
134
|
+
return { alias: repo.selected, connection, serverProjectRoot: repo.serverProjectRoot };
|
|
135
|
+
}
|
|
136
|
+
function writeRepoServerProjectRoot(projectRoot, serverProjectRoot) {
|
|
137
|
+
const repo = readRepoConnection(projectRoot);
|
|
138
|
+
if (!repo)
|
|
139
|
+
return;
|
|
140
|
+
writeRepoConnection(projectRoot, { ...repo, serverProjectRoot });
|
|
120
141
|
}
|
|
121
142
|
|
|
122
143
|
// packages/cli/src/commands/_server-client.ts
|
|
123
|
-
var
|
|
144
|
+
var scopedGitHubBearerTokens = new Map;
|
|
145
|
+
var serverPhaseListener = null;
|
|
146
|
+
function setServerPhaseListener(listener) {
|
|
147
|
+
const previous = serverPhaseListener;
|
|
148
|
+
serverPhaseListener = listener;
|
|
149
|
+
return previous;
|
|
150
|
+
}
|
|
151
|
+
function reportServerPhase(label) {
|
|
152
|
+
serverPhaseListener?.(label);
|
|
153
|
+
}
|
|
124
154
|
function cleanToken(value) {
|
|
125
155
|
const trimmed = value?.trim();
|
|
126
156
|
return trimmed ? trimmed : null;
|
|
@@ -137,49 +167,80 @@ function readPrivateRemoteSessionToken(projectRoot) {
|
|
|
137
167
|
}
|
|
138
168
|
}
|
|
139
169
|
function readGitHubBearerTokenForRemote(projectRoot) {
|
|
140
|
-
|
|
141
|
-
|
|
170
|
+
const scopedKey = resolve2(projectRoot);
|
|
171
|
+
if (scopedGitHubBearerTokens.has(scopedKey))
|
|
172
|
+
return scopedGitHubBearerTokens.get(scopedKey) ?? null;
|
|
142
173
|
const privateSession = readPrivateRemoteSessionToken(projectRoot);
|
|
143
|
-
if (privateSession)
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
return
|
|
174
|
+
if (privateSession)
|
|
175
|
+
return privateSession;
|
|
176
|
+
return cleanToken(process.env.RIG_SERVER_AUTH_TOKEN) ?? cleanToken(process.env.RIG_REMOTE_AUTH_TOKEN);
|
|
177
|
+
}
|
|
178
|
+
function readStoredGitHubAuthToken(projectRoot) {
|
|
179
|
+
const path = resolve2(projectRoot, ".rig", "state", "github-auth.json");
|
|
180
|
+
if (!existsSync2(path))
|
|
181
|
+
return null;
|
|
182
|
+
try {
|
|
183
|
+
const parsed = JSON.parse(readFileSync2(path, "utf8"));
|
|
184
|
+
return cleanToken(typeof parsed.token === "string" ? parsed.token : undefined);
|
|
185
|
+
} catch {
|
|
186
|
+
return null;
|
|
151
187
|
}
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
stdio: ["ignore", "pipe", "ignore"]
|
|
156
|
-
});
|
|
157
|
-
cachedGitHubBearerToken = result.status === 0 ? cleanToken(result.stdout) : null;
|
|
158
|
-
return cachedGitHubBearerToken;
|
|
188
|
+
}
|
|
189
|
+
function readLocalConnectionFallbackToken(projectRoot) {
|
|
190
|
+
return readGitHubBearerTokenForRemote(projectRoot) ?? cleanToken(process.env.RIG_GITHUB_TOKEN) ?? readStoredGitHubAuthToken(projectRoot);
|
|
159
191
|
}
|
|
160
192
|
async function ensureServerForCli(projectRoot) {
|
|
161
193
|
try {
|
|
162
194
|
const selected = resolveSelectedConnection(projectRoot);
|
|
163
195
|
if (selected?.connection.kind === "remote") {
|
|
196
|
+
reportServerPhase(`Connecting to ${selected.alias}\u2026`);
|
|
197
|
+
const authToken = readGitHubBearerTokenForRemote(projectRoot);
|
|
198
|
+
const serverProjectRoot = selected.serverProjectRoot ?? await backfillRemoteServerProjectRoot(projectRoot, selected.connection.baseUrl, authToken);
|
|
164
199
|
return {
|
|
165
200
|
baseUrl: selected.connection.baseUrl,
|
|
166
|
-
authToken
|
|
167
|
-
connectionKind: "remote"
|
|
201
|
+
authToken,
|
|
202
|
+
connectionKind: "remote",
|
|
203
|
+
serverProjectRoot
|
|
168
204
|
};
|
|
169
205
|
}
|
|
206
|
+
reportServerPhase("Starting local Rig server\u2026");
|
|
170
207
|
const connection = await ensureLocalRigServerConnection(projectRoot);
|
|
171
208
|
return {
|
|
172
209
|
baseUrl: connection.baseUrl,
|
|
173
|
-
authToken: connection.authToken,
|
|
174
|
-
connectionKind: "local"
|
|
210
|
+
authToken: connection.authToken ?? readLocalConnectionFallbackToken(projectRoot),
|
|
211
|
+
connectionKind: "local",
|
|
212
|
+
serverProjectRoot: resolve2(projectRoot)
|
|
175
213
|
};
|
|
176
214
|
} catch (error) {
|
|
177
215
|
if (error instanceof Error) {
|
|
178
|
-
throw new
|
|
216
|
+
throw new CliError(error.message, 1);
|
|
179
217
|
}
|
|
180
218
|
throw error;
|
|
181
219
|
}
|
|
182
220
|
}
|
|
221
|
+
async function backfillRemoteServerProjectRoot(projectRoot, baseUrl, authToken) {
|
|
222
|
+
const repo = readRepoConnection(projectRoot);
|
|
223
|
+
const slug = repo?.project?.trim();
|
|
224
|
+
if (!slug)
|
|
225
|
+
return null;
|
|
226
|
+
try {
|
|
227
|
+
const response = await fetch(`${baseUrl}/api/projects/${encodeURIComponent(slug)}`, {
|
|
228
|
+
headers: mergeHeaders(undefined, authToken)
|
|
229
|
+
});
|
|
230
|
+
if (!response.ok)
|
|
231
|
+
return null;
|
|
232
|
+
const payload = await response.json();
|
|
233
|
+
const project = payload.project && typeof payload.project === "object" && !Array.isArray(payload.project) ? payload.project : null;
|
|
234
|
+
const checkouts = Array.isArray(project?.checkouts) ? project.checkouts : [];
|
|
235
|
+
const latestCheckout = [...checkouts].reverse().find((entry) => Boolean(entry && typeof entry === "object" && !Array.isArray(entry) && typeof entry.path === "string"));
|
|
236
|
+
const path = typeof latestCheckout?.path === "string" && latestCheckout.path.trim() ? latestCheckout.path.trim() : null;
|
|
237
|
+
if (path)
|
|
238
|
+
writeRepoServerProjectRoot(projectRoot, path);
|
|
239
|
+
return path;
|
|
240
|
+
} catch {
|
|
241
|
+
return null;
|
|
242
|
+
}
|
|
243
|
+
}
|
|
183
244
|
function mergeHeaders(headers, authToken) {
|
|
184
245
|
const merged = new Headers(headers);
|
|
185
246
|
if (authToken) {
|
|
@@ -202,12 +263,65 @@ function diagnosticMessage(payload) {
|
|
|
202
263
|
});
|
|
203
264
|
return messages.length > 0 ? messages.join("; ") : null;
|
|
204
265
|
}
|
|
266
|
+
var serverReachabilityCache = new Map;
|
|
267
|
+
async function probeServerReachability(baseUrl, authToken) {
|
|
268
|
+
try {
|
|
269
|
+
const response = await fetch(`${baseUrl.replace(/\/+$/, "")}/api/server/status`, {
|
|
270
|
+
headers: mergeHeaders(undefined, authToken),
|
|
271
|
+
signal: AbortSignal.timeout(1500)
|
|
272
|
+
});
|
|
273
|
+
return response.ok;
|
|
274
|
+
} catch {
|
|
275
|
+
return false;
|
|
276
|
+
}
|
|
277
|
+
}
|
|
278
|
+
function cachedServerReachability(projectRoot, baseUrl, authToken) {
|
|
279
|
+
const key = resolve2(projectRoot);
|
|
280
|
+
const cached = serverReachabilityCache.get(key);
|
|
281
|
+
if (cached)
|
|
282
|
+
return cached;
|
|
283
|
+
const probe = probeServerReachability(baseUrl, authToken);
|
|
284
|
+
serverReachabilityCache.set(key, probe);
|
|
285
|
+
return probe;
|
|
286
|
+
}
|
|
287
|
+
function describeSelectedServer(projectRoot, server) {
|
|
288
|
+
try {
|
|
289
|
+
const selected = resolveSelectedConnection(projectRoot);
|
|
290
|
+
if (selected) {
|
|
291
|
+
return {
|
|
292
|
+
alias: selected.alias,
|
|
293
|
+
target: selected.connection.kind === "remote" ? selected.connection.baseUrl : server.baseUrl
|
|
294
|
+
};
|
|
295
|
+
}
|
|
296
|
+
} catch {}
|
|
297
|
+
return { alias: server.connectionKind === "remote" ? "remote" : "local", target: server.baseUrl };
|
|
298
|
+
}
|
|
299
|
+
async function buildServerFailureContext(projectRoot, server) {
|
|
300
|
+
const { alias, target } = describeSelectedServer(projectRoot, server);
|
|
301
|
+
const reachable = await cachedServerReachability(projectRoot, server.baseUrl, server.authToken);
|
|
302
|
+
const reachability = reachable ? "server is reachable" : "server is unreachable";
|
|
303
|
+
return {
|
|
304
|
+
contextLine: `Currently connected to: ${alias} at ${target} (${reachability}).`,
|
|
305
|
+
hint: "Check the selected server with `rig server status`, or switch with `rig server use <alias|local>`."
|
|
306
|
+
};
|
|
307
|
+
}
|
|
205
308
|
async function requestServerJson(context, pathname, init = {}) {
|
|
206
309
|
const server = await ensureServerForCli(context.projectRoot);
|
|
207
|
-
const
|
|
208
|
-
|
|
209
|
-
headers
|
|
210
|
-
});
|
|
310
|
+
const headers = mergeHeaders(init.headers, server.authToken);
|
|
311
|
+
if (server.serverProjectRoot)
|
|
312
|
+
headers.set("x-rig-project-root", server.serverProjectRoot);
|
|
313
|
+
reportServerPhase(`${(init.method ?? "GET").toUpperCase()} ${pathname.split("?")[0]}\u2026`);
|
|
314
|
+
let response;
|
|
315
|
+
try {
|
|
316
|
+
response = await fetch(`${server.baseUrl}${pathname}`, {
|
|
317
|
+
...init,
|
|
318
|
+
headers
|
|
319
|
+
});
|
|
320
|
+
} catch (error) {
|
|
321
|
+
const failure = await buildServerFailureContext(context.projectRoot, server);
|
|
322
|
+
throw new CliError(`Rig server request failed: ${error instanceof Error ? error.message : String(error)}
|
|
323
|
+
${failure.contextLine}`, 1, { hint: failure.hint });
|
|
324
|
+
}
|
|
211
325
|
const text = await response.text();
|
|
212
326
|
const payload = text.trim().length > 0 ? (() => {
|
|
213
327
|
try {
|
|
@@ -218,8 +332,12 @@ async function requestServerJson(context, pathname, init = {}) {
|
|
|
218
332
|
})() : null;
|
|
219
333
|
if (!response.ok) {
|
|
220
334
|
const diagnostics = diagnosticMessage(payload);
|
|
221
|
-
const
|
|
222
|
-
|
|
335
|
+
const rawDetail = diagnostics ?? (text || response.statusText);
|
|
336
|
+
const detail = diagnostics ? rawDetail : rawDetail.split(`
|
|
337
|
+
`).map((line) => line.trim()).find((line) => line.length > 0)?.slice(0, 200) ?? response.statusText;
|
|
338
|
+
const failure = await buildServerFailureContext(context.projectRoot, server);
|
|
339
|
+
throw new CliError(`Rig server request failed (${response.status}): ${detail}
|
|
340
|
+
${failure.contextLine}`, 1, { hint: failure.hint });
|
|
223
341
|
}
|
|
224
342
|
return payload;
|
|
225
343
|
}
|
|
@@ -235,6 +353,209 @@ async function postGitHubTokenViaServer(context, token, options = {}) {
|
|
|
235
353
|
});
|
|
236
354
|
return payload && typeof payload === "object" && !Array.isArray(payload) ? payload : {};
|
|
237
355
|
}
|
|
356
|
+
var RESUMABLE_RUN_STATUSES = new Set([
|
|
357
|
+
"created",
|
|
358
|
+
"preparing",
|
|
359
|
+
"running",
|
|
360
|
+
"validating",
|
|
361
|
+
"reviewing",
|
|
362
|
+
"stopped",
|
|
363
|
+
"failed",
|
|
364
|
+
"needs-attention",
|
|
365
|
+
"needs_attention"
|
|
366
|
+
]);
|
|
367
|
+
|
|
368
|
+
// packages/cli/src/commands/_async-ui.ts
|
|
369
|
+
import pc from "picocolors";
|
|
370
|
+
|
|
371
|
+
// packages/cli/src/commands/_spinner.ts
|
|
372
|
+
var SPINNER_FRAMES = ["\u280B", "\u2819", "\u2839", "\u2838", "\u283C", "\u2834", "\u2826", "\u2827", "\u2807", "\u280F"];
|
|
373
|
+
function createTtySpinner(input) {
|
|
374
|
+
const output = input.output ?? process.stdout;
|
|
375
|
+
const isTty = output.isTTY === true;
|
|
376
|
+
const frames = input.frames && input.frames.length > 0 ? input.frames : SPINNER_FRAMES;
|
|
377
|
+
let label = input.label;
|
|
378
|
+
let frame = 0;
|
|
379
|
+
let paused = false;
|
|
380
|
+
let stopped = false;
|
|
381
|
+
let lastPrintedLabel = "";
|
|
382
|
+
const render = () => {
|
|
383
|
+
if (stopped || paused)
|
|
384
|
+
return;
|
|
385
|
+
if (!isTty) {
|
|
386
|
+
if (label !== lastPrintedLabel) {
|
|
387
|
+
output.write(`${label}
|
|
388
|
+
`);
|
|
389
|
+
lastPrintedLabel = label;
|
|
390
|
+
}
|
|
391
|
+
return;
|
|
392
|
+
}
|
|
393
|
+
frame = (frame + 1) % frames.length;
|
|
394
|
+
const glyph = frames[frame] ?? frames[0] ?? "";
|
|
395
|
+
output.write(`\r\x1B[2K${input.styleFrame ? input.styleFrame(glyph) : glyph} ${label}`);
|
|
396
|
+
};
|
|
397
|
+
const clearLine = () => {
|
|
398
|
+
if (isTty)
|
|
399
|
+
output.write("\r\x1B[2K");
|
|
400
|
+
};
|
|
401
|
+
render();
|
|
402
|
+
const timer = isTty ? setInterval(render, input.intervalMs ?? 120) : null;
|
|
403
|
+
return {
|
|
404
|
+
setLabel(next) {
|
|
405
|
+
label = next;
|
|
406
|
+
render();
|
|
407
|
+
},
|
|
408
|
+
pause() {
|
|
409
|
+
paused = true;
|
|
410
|
+
clearLine();
|
|
411
|
+
},
|
|
412
|
+
resume() {
|
|
413
|
+
if (stopped)
|
|
414
|
+
return;
|
|
415
|
+
paused = false;
|
|
416
|
+
render();
|
|
417
|
+
},
|
|
418
|
+
stop(finalLine) {
|
|
419
|
+
if (stopped)
|
|
420
|
+
return;
|
|
421
|
+
stopped = true;
|
|
422
|
+
if (timer)
|
|
423
|
+
clearInterval(timer);
|
|
424
|
+
clearLine();
|
|
425
|
+
if (finalLine)
|
|
426
|
+
output.write(`${finalLine}
|
|
427
|
+
`);
|
|
428
|
+
}
|
|
429
|
+
};
|
|
430
|
+
}
|
|
431
|
+
|
|
432
|
+
// packages/cli/src/app/theme.ts
|
|
433
|
+
var RIG_PALETTE = {
|
|
434
|
+
ink: "#f2f3f6",
|
|
435
|
+
ink2: "#aeb0ba",
|
|
436
|
+
ink3: "#6c6e79",
|
|
437
|
+
ink4: "#44464f",
|
|
438
|
+
accent: "#ccff4d",
|
|
439
|
+
accentDim: "#a9d63f",
|
|
440
|
+
cyan: "#56d8ff",
|
|
441
|
+
red: "#ff5d5d",
|
|
442
|
+
yellow: "#ffd24d"
|
|
443
|
+
};
|
|
444
|
+
function hexToRgb(hex) {
|
|
445
|
+
const value = hex.replace("#", "");
|
|
446
|
+
return [
|
|
447
|
+
Number.parseInt(value.slice(0, 2), 16),
|
|
448
|
+
Number.parseInt(value.slice(2, 4), 16),
|
|
449
|
+
Number.parseInt(value.slice(4, 6), 16)
|
|
450
|
+
];
|
|
451
|
+
}
|
|
452
|
+
function fg(hex) {
|
|
453
|
+
const [r, g, b] = hexToRgb(hex);
|
|
454
|
+
return (text) => `\x1B[38;2;${r};${g};${b}m${text}\x1B[39m`;
|
|
455
|
+
}
|
|
456
|
+
var ink = fg(RIG_PALETTE.ink);
|
|
457
|
+
var ink2 = fg(RIG_PALETTE.ink2);
|
|
458
|
+
var ink3 = fg(RIG_PALETTE.ink3);
|
|
459
|
+
var ink4 = fg(RIG_PALETTE.ink4);
|
|
460
|
+
var accent = fg(RIG_PALETTE.accent);
|
|
461
|
+
var accentDim = fg(RIG_PALETTE.accentDim);
|
|
462
|
+
var cyan = fg(RIG_PALETTE.cyan);
|
|
463
|
+
var red = fg(RIG_PALETTE.red);
|
|
464
|
+
var yellow = fg(RIG_PALETTE.yellow);
|
|
465
|
+
function bold(text) {
|
|
466
|
+
return `\x1B[1m${text}\x1B[22m`;
|
|
467
|
+
}
|
|
468
|
+
var DRONE_ART = [
|
|
469
|
+
" .-=-. .-=-. ",
|
|
470
|
+
" ( !!! ) ( !!! ) ",
|
|
471
|
+
" '-=-'._ _.'-=-' ",
|
|
472
|
+
" '._ _.' ",
|
|
473
|
+
" '=$$$$$$$=.' ",
|
|
474
|
+
" =$$$$$$$$$$$= ",
|
|
475
|
+
" $$$@@@@@@@@@@$$$ ",
|
|
476
|
+
" $$$@@ @@$$$ ",
|
|
477
|
+
" $$@ ? @$$$ ",
|
|
478
|
+
" $$$@ '-' @$$$ ",
|
|
479
|
+
" $$$@@ @@$$$ ",
|
|
480
|
+
" $$$@@@@@@@@@@$$$ ",
|
|
481
|
+
" =$$$$$$$$$$$= ",
|
|
482
|
+
" '=$$$$$$$=.' ",
|
|
483
|
+
" _.' '._ ",
|
|
484
|
+
" .-=-.' '.-=-. ",
|
|
485
|
+
" ( !!! ) ( !!! ) ",
|
|
486
|
+
" '-=-' '-=-' "
|
|
487
|
+
];
|
|
488
|
+
var EYE_FRAMES = ["@", "o", "."];
|
|
489
|
+
var DRONE_WIDTH = DRONE_ART[0].length;
|
|
490
|
+
var DRONE_HEIGHT = DRONE_ART.length;
|
|
491
|
+
var MICRO_BLADES = ["---", "\\\\\\", "|||", "///"];
|
|
492
|
+
function microDroneFrame(tick) {
|
|
493
|
+
const blade = MICRO_BLADES[tick % MICRO_BLADES.length];
|
|
494
|
+
const eye = EYE_FRAMES[Math.floor(tick / 2) % EYE_FRAMES.length];
|
|
495
|
+
return `(${blade})${eye}(${blade})`;
|
|
496
|
+
}
|
|
497
|
+
var MICRO_DRONE_FRAMES = Array.from({ length: 12 }, (_, index) => microDroneFrame(index));
|
|
498
|
+
function renderMicroDroneFrame(tick) {
|
|
499
|
+
const blade = MICRO_BLADES[tick % MICRO_BLADES.length];
|
|
500
|
+
const eye = EYE_FRAMES[Math.floor(tick / 2) % EYE_FRAMES.length];
|
|
501
|
+
return `${ink4("(")}${cyan(blade)}${ink4(")")}${bold(accent(eye))}${ink4("(")}${cyan(blade)}${ink4(")")}`;
|
|
502
|
+
}
|
|
503
|
+
|
|
504
|
+
// packages/cli/src/commands/_async-ui.ts
|
|
505
|
+
var DONE_SYMBOL = pc.green("\u25C7");
|
|
506
|
+
var FAIL_SYMBOL = pc.red("\u25A0");
|
|
507
|
+
var activeUpdate = null;
|
|
508
|
+
async function withSpinner(label, work, options = {}) {
|
|
509
|
+
if (options.outputMode === "json") {
|
|
510
|
+
return work(() => {});
|
|
511
|
+
}
|
|
512
|
+
if (activeUpdate) {
|
|
513
|
+
const outer = activeUpdate;
|
|
514
|
+
outer(label);
|
|
515
|
+
return work(outer);
|
|
516
|
+
}
|
|
517
|
+
const output = options.output ?? process.stderr;
|
|
518
|
+
const isTty = output.isTTY === true;
|
|
519
|
+
let lastLabel = label;
|
|
520
|
+
if (!isTty) {
|
|
521
|
+
output.write(`${label}
|
|
522
|
+
`);
|
|
523
|
+
const update2 = (next) => {
|
|
524
|
+
lastLabel = next;
|
|
525
|
+
};
|
|
526
|
+
activeUpdate = update2;
|
|
527
|
+
const previousListener2 = setServerPhaseListener(update2);
|
|
528
|
+
try {
|
|
529
|
+
return await work(update2);
|
|
530
|
+
} finally {
|
|
531
|
+
activeUpdate = null;
|
|
532
|
+
setServerPhaseListener(previousListener2);
|
|
533
|
+
}
|
|
534
|
+
}
|
|
535
|
+
const spinner = createTtySpinner({
|
|
536
|
+
label,
|
|
537
|
+
output,
|
|
538
|
+
frames: MICRO_DRONE_FRAMES,
|
|
539
|
+
styleFrame: (frame) => renderMicroDroneFrame(Math.max(0, MICRO_DRONE_FRAMES.indexOf(frame)))
|
|
540
|
+
});
|
|
541
|
+
const update = (next) => {
|
|
542
|
+
lastLabel = next;
|
|
543
|
+
spinner.setLabel(next);
|
|
544
|
+
};
|
|
545
|
+
activeUpdate = update;
|
|
546
|
+
const previousListener = setServerPhaseListener(update);
|
|
547
|
+
try {
|
|
548
|
+
const result = await work(update);
|
|
549
|
+
spinner.stop(options.doneLabel ? `${DONE_SYMBOL} ${options.doneLabel}` : undefined);
|
|
550
|
+
return result;
|
|
551
|
+
} catch (error) {
|
|
552
|
+
spinner.stop(`${FAIL_SYMBOL} ${lastLabel}`);
|
|
553
|
+
throw error;
|
|
554
|
+
} finally {
|
|
555
|
+
activeUpdate = null;
|
|
556
|
+
setServerPhaseListener(previousListener);
|
|
557
|
+
}
|
|
558
|
+
}
|
|
238
559
|
|
|
239
560
|
// packages/cli/src/commands/github.ts
|
|
240
561
|
function printPayload(context, payload, fallback) {
|
|
@@ -244,49 +565,50 @@ function printPayload(context, payload, fallback) {
|
|
|
244
565
|
console.log(fallback);
|
|
245
566
|
}
|
|
246
567
|
function readGhToken() {
|
|
247
|
-
const result =
|
|
568
|
+
const result = spawnSync("gh", ["auth", "token"], { encoding: "utf8" });
|
|
248
569
|
if (result.status !== 0) {
|
|
249
570
|
const detail = result.stderr?.trim() || result.stdout?.trim() || "gh auth token failed";
|
|
250
|
-
throw new
|
|
571
|
+
throw new CliError(`Could not import GitHub token from gh: ${detail}`, 1, { hint: "Sign in first with `gh auth login`, or store a token directly: `rig github auth token --token <token>`." });
|
|
251
572
|
}
|
|
252
573
|
const token = result.stdout.trim();
|
|
253
574
|
if (!token)
|
|
254
|
-
throw new
|
|
575
|
+
throw new CliError("gh auth token returned an empty token.", 1, { hint: "Sign in with `gh auth login`, then re-run `rig github auth import-gh`." });
|
|
255
576
|
return token;
|
|
256
577
|
}
|
|
257
578
|
async function executeGithub(context, args) {
|
|
258
579
|
const [group, command, ...rest] = args;
|
|
259
580
|
if (group !== "auth") {
|
|
260
|
-
throw new
|
|
581
|
+
throw new CliError("Usage: rig github auth <status|import-gh|token>", 1);
|
|
261
582
|
}
|
|
262
583
|
switch (command) {
|
|
263
584
|
case "status": {
|
|
264
585
|
if (rest.length > 0)
|
|
265
|
-
throw new
|
|
266
|
-
const status = await getGitHubAuthStatusViaServer(context);
|
|
586
|
+
throw new CliError("Usage: rig github auth status", 1);
|
|
587
|
+
const status = await withSpinner("Checking GitHub auth on the server\u2026", () => getGitHubAuthStatusViaServer(context), { outputMode: context.outputMode });
|
|
267
588
|
printPayload(context, status, `GitHub auth: ${status.authenticated ? "authenticated" : "unauthenticated"}`);
|
|
268
589
|
return { ok: true, group: "github", command: "auth status", details: status };
|
|
269
590
|
}
|
|
270
591
|
case "token": {
|
|
271
592
|
const parsed = takeOption(rest, "--token");
|
|
272
593
|
if (parsed.rest.length > 0)
|
|
273
|
-
throw new
|
|
594
|
+
throw new CliError("Usage: rig github auth token --token <token>", 1);
|
|
274
595
|
const token = parsed.value?.trim();
|
|
275
596
|
if (!token)
|
|
276
|
-
throw new
|
|
277
|
-
const result = await postGitHubTokenViaServer(context, token);
|
|
597
|
+
throw new CliError("Missing --token value.", 1, { hint: "Re-run as `rig github auth token --token <token>`." });
|
|
598
|
+
const result = await withSpinner("Storing GitHub token on the server\u2026", () => postGitHubTokenViaServer(context, token), { outputMode: context.outputMode });
|
|
278
599
|
printPayload(context, result, "GitHub token stored on the selected Rig server.");
|
|
279
600
|
return { ok: true, group: "github", command: "auth token", details: result };
|
|
280
601
|
}
|
|
281
602
|
case "import-gh": {
|
|
282
603
|
if (rest.length > 0)
|
|
283
|
-
throw new
|
|
284
|
-
const
|
|
604
|
+
throw new CliError("Usage: rig github auth import-gh", 1);
|
|
605
|
+
const importedToken = readGhToken();
|
|
606
|
+
const result = await withSpinner("Storing GitHub token on the server\u2026", () => postGitHubTokenViaServer(context, importedToken), { outputMode: context.outputMode });
|
|
285
607
|
printPayload(context, result, "GitHub token imported from gh and stored on the selected Rig server.");
|
|
286
608
|
return { ok: true, group: "github", command: "auth import-gh", details: result };
|
|
287
609
|
}
|
|
288
610
|
default:
|
|
289
|
-
throw new
|
|
611
|
+
throw new CliError("Usage: rig github auth <status|import-gh|token>", 1);
|
|
290
612
|
}
|
|
291
613
|
}
|
|
292
614
|
export {
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import { type RunnerContext } from "../runner";
|
|
2
|
+
import type { CommandOutcome } from "@rig/runtime/control-plane/runtime/types";
|
|
3
|
+
export type InboxKind = "approvals" | "inputs";
|
|
4
|
+
export type InboxRecord = {
|
|
5
|
+
runId?: string;
|
|
6
|
+
taskId?: string | null;
|
|
7
|
+
requestId?: string | null;
|
|
8
|
+
status?: string | null;
|
|
9
|
+
record?: Record<string, unknown>;
|
|
10
|
+
};
|
|
11
|
+
/** Exported for the board's inbox view — same records `rig inbox` renders. */
|
|
12
|
+
export declare function listInboxRecords(context: Pick<RunnerContext, "projectRoot">, kind: InboxKind, filters: {
|
|
13
|
+
run?: string;
|
|
14
|
+
task?: string;
|
|
15
|
+
pendingOnly?: boolean;
|
|
16
|
+
}): Promise<InboxRecord[]>;
|
|
17
|
+
/**
|
|
18
|
+
* Pending-gate counts for proactive surfacing. Returns null when the server
|
|
19
|
+
* is unreachable — footer callers must never break the primary command.
|
|
20
|
+
*/
|
|
21
|
+
export declare function readPendingInboxCounts(context: Pick<RunnerContext, "projectRoot">): Promise<{
|
|
22
|
+
approvals: number;
|
|
23
|
+
inputs: number;
|
|
24
|
+
} | null>;
|
|
25
|
+
/**
|
|
26
|
+
* One-line warning under task/run command output when human gates are
|
|
27
|
+
* waiting. Silent when there is nothing pending or the server is down.
|
|
28
|
+
*/
|
|
29
|
+
export declare function printPendingInboxFooter(context: RunnerContext): Promise<void>;
|
|
30
|
+
export declare function executeInbox(context: RunnerContext, args: string[]): Promise<CommandOutcome>;
|