@h-rig/cli 0.0.6-alpha.9 → 0.0.6-alpha.91
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +18 -19
- package/dist/bin/build-rig-binaries.d.ts +2 -0
- package/dist/bin/build-rig-binaries.js +22 -10
- package/dist/bin/rig.d.ts +72 -0
- package/dist/bin/rig.js +15637 -7344
- package/dist/config/rig-default-config.yml +5 -0
- package/dist/src/app/drone-ui.d.ts +34 -0
- package/dist/src/app/drone-ui.js +278 -0
- package/dist/src/commands/_async-ui.d.ts +10 -0
- package/dist/src/commands/_async-ui.js +121 -0
- package/dist/src/commands/_cli-format.d.ts +56 -0
- package/dist/src/commands/_cli-format.js +319 -0
- package/dist/src/commands/_connection-state.d.ts +54 -0
- package/dist/src/commands/_connection-state.js +75 -11
- package/dist/src/commands/_doctor-checks.d.ts +46 -0
- package/dist/src/commands/_doctor-checks.js +100 -267
- package/dist/src/commands/_help-catalog.d.ts +51 -0
- package/dist/src/commands/_help-catalog.js +502 -0
- package/dist/src/commands/_inprocess-services.d.ts +33 -0
- package/dist/src/commands/_inprocess-services.js +102 -0
- package/dist/src/commands/_json-output.d.ts +11 -0
- package/dist/src/commands/_json-output.js +60 -0
- package/dist/src/commands/_lazy-reconcile.d.ts +34 -0
- package/dist/src/commands/_lazy-reconcile.js +102 -0
- 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/_paths.js +1 -1
- package/dist/src/commands/_pi-frontend.d.ts +35 -0
- package/dist/src/commands/_pi-frontend.js +64 -0
- package/dist/src/commands/_pi-install.d.ts +42 -0
- package/dist/src/commands/_pi-install.js +19 -36
- package/dist/src/commands/_policy.d.ts +8 -0
- package/dist/src/commands/_policy.js +67 -19
- package/dist/src/commands/_probes.d.ts +1 -0
- package/dist/src/commands/_run-bridge.d.ts +114 -0
- package/dist/src/commands/_run-bridge.js +387 -0
- package/dist/src/commands/_run-diagnostics.d.ts +9 -0
- package/dist/src/commands/_run-diagnostics.js +51 -0
- package/dist/src/commands/_run-driver-helpers.d.ts +26 -0
- package/dist/src/commands/_run-driver-helpers.js +79 -230
- package/dist/src/commands/_run-projection.d.ts +50 -0
- package/dist/src/commands/_run-projection.js +349 -0
- package/dist/src/commands/_run-subcommands.d.ts +3 -0
- package/dist/src/commands/_run-subcommands.js +31 -0
- package/dist/src/commands/_spinner.d.ts +25 -0
- package/dist/src/commands/_spinner.js +65 -0
- package/dist/src/commands/agent.d.ts +3 -0
- package/dist/src/commands/agent.js +8575 -236
- package/dist/src/commands/dist.d.ts +28 -0
- package/dist/src/commands/dist.js +44 -29
- package/dist/src/commands/doctor.d.ts +3 -0
- package/dist/src/commands/doctor.js +219 -268
- package/dist/src/commands/github.d.ts +3 -0
- package/dist/src/commands/github.js +236 -188
- package/dist/src/commands/inbox.d.ts +28 -0
- package/dist/src/commands/inbox.js +530 -111
- package/dist/src/commands/init.d.ts +64 -0
- package/dist/src/commands/init.js +912 -705
- package/dist/src/commands/inspect.d.ts +20 -0
- package/dist/src/commands/inspect.js +709 -119
- 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 +576 -29
- package/dist/src/commands/profile-and-review.d.ts +4 -0
- package/dist/src/commands/profile-and-review.js +112 -67
- package/dist/src/commands/queue.d.ts +3 -0
- package/dist/src/commands/queue.js +20 -20
- package/dist/src/commands/remote.d.ts +3 -0
- package/dist/src/commands/remote.js +880 -50
- package/dist/src/commands/repo-git-harness.d.ts +5 -0
- package/dist/src/commands/repo-git-harness.js +78 -28
- package/dist/src/commands/run.d.ts +21 -0
- package/dist/src/commands/run.js +17701 -661
- package/dist/src/commands/server.d.ts +3 -0
- package/dist/src/commands/server.js +166 -303
- package/dist/src/commands/setup.d.ts +16 -0
- package/dist/src/commands/setup.js +244 -299
- package/dist/src/commands/stats.d.ts +15 -0
- package/dist/src/commands/stats.js +979 -0
- package/dist/src/commands/task-run-driver.d.ts +94 -0
- package/dist/src/commands/task-run-driver.js +116 -1961
- package/dist/src/commands/task.d.ts +35 -0
- package/dist/src/commands/task.js +676 -1201
- package/dist/src/commands/test.d.ts +3 -0
- package/dist/src/commands/test.js +15 -8
- package/dist/src/commands/triage.d.ts +11 -0
- package/dist/src/commands/triage.js +227 -0
- package/dist/src/commands/workspace.d.ts +3 -0
- package/dist/src/commands/workspace.js +18 -11
- package/dist/src/commands.d.ts +13 -0
- package/dist/src/commands.js +16792 -8055
- package/dist/src/index.d.ts +4 -0
- package/dist/src/index.js +16862 -8450
- package/dist/src/launcher.d.ts +61 -0
- package/dist/src/launcher.js +81 -13
- package/dist/src/operator-cli.d.ts +2 -0
- package/dist/src/operator-cli.js +17837 -0
- package/dist/src/operator-entry.d.ts +1 -0
- package/dist/src/operator-entry.js +3 -0
- 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 +21 -8
- package/dist/src/commands/_authority-runs.js +0 -111
- package/dist/src/commands/_operator-view.js +0 -340
- package/dist/src/commands/_preflight.js +0 -478
- package/dist/src/commands/_server-client.js +0 -382
- package/dist/src/commands/_snapshot-upload.js +0 -331
- package/dist/src/commands/_task-picker.js +0 -48
- package/dist/src/commands/browser.js +0 -890
- package/dist/src/commands/connect.js +0 -180
- package/dist/src/commands/inspector.js +0 -256
- package/dist/src/commands/task-report-bug.js +0 -1083
- package/dist/src/report-bug.js +0 -260
|
@@ -1,15 +1,24 @@
|
|
|
1
1
|
// @bun
|
|
2
2
|
// packages/cli/src/commands/github.ts
|
|
3
|
-
import { spawnSync
|
|
3
|
+
import { spawnSync } from "child_process";
|
|
4
|
+
import { mkdirSync as mkdirSync2, writeFileSync as writeFileSync2 } from "fs";
|
|
5
|
+
import { dirname as dirname2, resolve as resolve3 } from "path";
|
|
4
6
|
|
|
5
7
|
// packages/cli/src/runner.ts
|
|
6
8
|
import { EventBus } from "@rig/runtime/control-plane/runtime/events";
|
|
7
|
-
import { CliError } from "@rig/runtime/control-plane/errors";
|
|
9
|
+
import { CliError as RuntimeCliError } from "@rig/runtime/control-plane/errors";
|
|
8
10
|
import { evaluate, loadPolicy, resolveAction } from "@rig/runtime/control-plane/runtime/guard";
|
|
9
|
-
import { PluginManager } from "@rig/runtime/control-plane/runtime/plugins";
|
|
10
|
-
import { loadRuntimeContextFromEnv } from "@rig/runtime/control-plane/runtime/context";
|
|
11
11
|
import { buildBinary } from "@rig/runtime/control-plane/runtime/isolation";
|
|
12
|
-
|
|
12
|
+
|
|
13
|
+
class CliError extends RuntimeCliError {
|
|
14
|
+
hint;
|
|
15
|
+
constructor(message, exitCode = 1, options = {}) {
|
|
16
|
+
super(message, exitCode);
|
|
17
|
+
if (options.hint?.trim()) {
|
|
18
|
+
this.hint = options.hint.trim();
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
}
|
|
13
22
|
function takeOption(args, option) {
|
|
14
23
|
const rest = [];
|
|
15
24
|
let value;
|
|
@@ -18,7 +27,7 @@ function takeOption(args, option) {
|
|
|
18
27
|
if (current === option) {
|
|
19
28
|
const next = args[index + 1];
|
|
20
29
|
if (!next || next.startsWith("-")) {
|
|
21
|
-
throw new CliError(`Missing value for ${option}`);
|
|
30
|
+
throw new CliError(`Missing value for ${option}`, 1, { hint: `Provide a value after ${option}, e.g. \`${option} <value>\`.` });
|
|
22
31
|
}
|
|
23
32
|
value = next;
|
|
24
33
|
index += 1;
|
|
@@ -31,27 +40,53 @@ function takeOption(args, option) {
|
|
|
31
40
|
return { value, rest };
|
|
32
41
|
}
|
|
33
42
|
|
|
34
|
-
// packages/cli/src/commands/
|
|
35
|
-
import {
|
|
36
|
-
import {
|
|
37
|
-
|
|
38
|
-
|
|
43
|
+
// packages/cli/src/commands/_inprocess-services.ts
|
|
44
|
+
import { resolve } from "path";
|
|
45
|
+
import {
|
|
46
|
+
beginGitHubDeviceFlow,
|
|
47
|
+
checkGitHubRepoPermissions,
|
|
48
|
+
createGitHubAuthStore,
|
|
49
|
+
listGitHubProjects,
|
|
50
|
+
pollGitHubDeviceFlow,
|
|
51
|
+
resolveGitHubAuthStatus,
|
|
52
|
+
resolveProjectStatusField,
|
|
53
|
+
saveGitHubTokenForProject
|
|
54
|
+
} from "@rig/runtime/control-plane/github/index";
|
|
55
|
+
var scopedGitHubBearerTokens = new Map;
|
|
56
|
+
function cleanToken(value) {
|
|
57
|
+
const trimmed = value?.trim();
|
|
58
|
+
return trimmed ? trimmed : null;
|
|
59
|
+
}
|
|
60
|
+
function oauthClientId() {
|
|
61
|
+
return cleanToken(process.env.RIG_GITHUB_OAUTH_CLIENT_ID);
|
|
62
|
+
}
|
|
63
|
+
function setGitHubBearerTokenForCurrentProcess(token, projectRoot) {
|
|
64
|
+
scopedGitHubBearerTokens.set(resolve(projectRoot ?? process.cwd()), cleanToken(token));
|
|
65
|
+
}
|
|
66
|
+
async function getGitHubAuthStatusInProcess(context) {
|
|
67
|
+
return { ok: true, ...resolveGitHubAuthStatus({ projectRoot: context.projectRoot, oauthConfigured: Boolean(oauthClientId()) }) };
|
|
68
|
+
}
|
|
69
|
+
async function postGitHubTokenInProcess(context, token, options = {}) {
|
|
70
|
+
const targetRoot = options.projectRoot?.trim() || context.projectRoot;
|
|
71
|
+
const result = await saveGitHubTokenForProject({
|
|
72
|
+
projectRoot: targetRoot,
|
|
73
|
+
token,
|
|
74
|
+
tokenSource: "manual-token",
|
|
75
|
+
selectedRepo: options.selectedRepo ?? null
|
|
76
|
+
});
|
|
77
|
+
const store = createGitHubAuthStore(targetRoot);
|
|
78
|
+
const session = store.createApiSession();
|
|
79
|
+
if (targetRoot !== context.projectRoot) {
|
|
80
|
+
store.copyToLocalProjectRoot(context.projectRoot);
|
|
81
|
+
}
|
|
82
|
+
return { ...result, authenticated: result.signedIn, apiSessionToken: session.token };
|
|
83
|
+
}
|
|
39
84
|
|
|
40
85
|
// packages/cli/src/commands/_connection-state.ts
|
|
41
86
|
import { existsSync, mkdirSync, readFileSync, writeFileSync } from "fs";
|
|
42
|
-
import {
|
|
43
|
-
import { dirname, resolve } from "path";
|
|
44
|
-
function resolveGlobalConnectionsPath(env = process.env) {
|
|
45
|
-
const explicit = env.RIG_CONNECTIONS_FILE?.trim();
|
|
46
|
-
if (explicit)
|
|
47
|
-
return resolve(explicit);
|
|
48
|
-
const stateDir = env.RIG_GLOBAL_STATE_DIR?.trim();
|
|
49
|
-
if (stateDir)
|
|
50
|
-
return resolve(stateDir, "connections.json");
|
|
51
|
-
return resolve(homedir(), ".rig", "connections.json");
|
|
52
|
-
}
|
|
87
|
+
import { dirname, resolve as resolve2 } from "path";
|
|
53
88
|
function resolveRepoConnectionPath(projectRoot) {
|
|
54
|
-
return
|
|
89
|
+
return resolve2(projectRoot, ".rig", "state", "connection.json");
|
|
55
90
|
}
|
|
56
91
|
function readJsonFile(path) {
|
|
57
92
|
if (!existsSync(path))
|
|
@@ -59,37 +94,8 @@ function readJsonFile(path) {
|
|
|
59
94
|
try {
|
|
60
95
|
return JSON.parse(readFileSync(path, "utf8"));
|
|
61
96
|
} catch (error) {
|
|
62
|
-
throw new
|
|
63
|
-
}
|
|
64
|
-
}
|
|
65
|
-
function normalizeConnection(value) {
|
|
66
|
-
if (!value || typeof value !== "object" || Array.isArray(value))
|
|
67
|
-
return null;
|
|
68
|
-
const record = value;
|
|
69
|
-
if (record.kind === "local")
|
|
70
|
-
return { kind: "local", mode: "auto" };
|
|
71
|
-
if (record.kind === "remote" && typeof record.baseUrl === "string" && record.baseUrl.trim()) {
|
|
72
|
-
const baseUrl = record.baseUrl.trim().replace(/\/+$/, "");
|
|
73
|
-
return { kind: "remote", baseUrl };
|
|
74
|
-
}
|
|
75
|
-
return null;
|
|
76
|
-
}
|
|
77
|
-
function readGlobalConnections(options = {}) {
|
|
78
|
-
const path = resolveGlobalConnectionsPath(options.env ?? process.env);
|
|
79
|
-
const payload = readJsonFile(path);
|
|
80
|
-
if (!payload || typeof payload !== "object" || Array.isArray(payload)) {
|
|
81
|
-
return { connections: {} };
|
|
82
|
-
}
|
|
83
|
-
const rawConnections = payload.connections;
|
|
84
|
-
const connections = {};
|
|
85
|
-
if (rawConnections && typeof rawConnections === "object" && !Array.isArray(rawConnections)) {
|
|
86
|
-
for (const [alias, raw] of Object.entries(rawConnections)) {
|
|
87
|
-
const connection = normalizeConnection(raw);
|
|
88
|
-
if (connection)
|
|
89
|
-
connections[alias] = connection;
|
|
90
|
-
}
|
|
97
|
+
throw new CliError(`Invalid Rig connection state at ${path}: ${error instanceof Error ? error.message : String(error)}`, 1, { hint: "Fix or delete that file, then re-select a server with `rig server use <alias|local>`." });
|
|
91
98
|
}
|
|
92
|
-
return { connections };
|
|
93
99
|
}
|
|
94
100
|
function readRepoConnection(projectRoot) {
|
|
95
101
|
const payload = readJsonFile(resolveRepoConnectionPath(projectRoot));
|
|
@@ -102,139 +108,130 @@ function readRepoConnection(projectRoot) {
|
|
|
102
108
|
return {
|
|
103
109
|
selected,
|
|
104
110
|
project: typeof record.project === "string" ? record.project : undefined,
|
|
105
|
-
linkedAt: typeof record.linkedAt === "string" ? record.linkedAt : undefined
|
|
111
|
+
linkedAt: typeof record.linkedAt === "string" ? record.linkedAt : undefined,
|
|
112
|
+
serverProjectRoot: typeof record.serverProjectRoot === "string" && record.serverProjectRoot.trim() ? record.serverProjectRoot.trim() : undefined,
|
|
113
|
+
serverProjectRootAlias: typeof record.serverProjectRootAlias === "string" && record.serverProjectRootAlias.trim() ? record.serverProjectRootAlias.trim() : undefined,
|
|
114
|
+
serverProjectRootBaseUrl: typeof record.serverProjectRootBaseUrl === "string" && record.serverProjectRootBaseUrl.trim() ? record.serverProjectRootBaseUrl.trim().replace(/\/+$/, "") : undefined
|
|
106
115
|
};
|
|
107
116
|
}
|
|
108
|
-
function resolveSelectedConnection(projectRoot, options = {}) {
|
|
109
|
-
const repo = readRepoConnection(projectRoot);
|
|
110
|
-
if (!repo)
|
|
111
|
-
return null;
|
|
112
|
-
if (repo.selected === "local")
|
|
113
|
-
return { alias: "local", connection: { kind: "local", mode: "auto" } };
|
|
114
|
-
const global = readGlobalConnections(options);
|
|
115
|
-
const connection = global.connections[repo.selected];
|
|
116
|
-
if (!connection) {
|
|
117
|
-
throw new CliError2(`Selected Rig connection "${repo.selected}" was not found. Run \`rig connect list\` or \`rig connect use local\`.`, 1);
|
|
118
|
-
}
|
|
119
|
-
return { alias: repo.selected, connection };
|
|
120
|
-
}
|
|
121
117
|
|
|
122
|
-
// packages/cli/src/commands/
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
118
|
+
// packages/cli/src/commands/_async-ui.ts
|
|
119
|
+
import pc from "picocolors";
|
|
120
|
+
|
|
121
|
+
// packages/cli/src/commands/_spinner.ts
|
|
122
|
+
var SPINNER_FRAMES = ["\u280B", "\u2819", "\u2839", "\u2838", "\u283C", "\u2834", "\u2826", "\u2827", "\u2807", "\u280F"];
|
|
123
|
+
function createTtySpinner(input) {
|
|
124
|
+
const output = input.output ?? process.stdout;
|
|
125
|
+
const isTty = output.isTTY === true;
|
|
126
|
+
const frames = input.frames && input.frames.length > 0 ? input.frames : SPINNER_FRAMES;
|
|
127
|
+
let label = input.label;
|
|
128
|
+
let frame = 0;
|
|
129
|
+
let paused = false;
|
|
130
|
+
let stopped = false;
|
|
131
|
+
let lastPrintedLabel = "";
|
|
132
|
+
const render = () => {
|
|
133
|
+
if (stopped || paused)
|
|
134
|
+
return;
|
|
135
|
+
if (!isTty) {
|
|
136
|
+
if (label !== lastPrintedLabel) {
|
|
137
|
+
output.write(`${label}
|
|
138
|
+
`);
|
|
139
|
+
lastPrintedLabel = label;
|
|
140
|
+
}
|
|
141
|
+
return;
|
|
142
|
+
}
|
|
143
|
+
frame = (frame + 1) % frames.length;
|
|
144
|
+
const glyph = frames[frame] ?? frames[0] ?? "";
|
|
145
|
+
output.write(`\r\x1B[2K${input.styleFrame ? input.styleFrame(glyph) : glyph} ${label}`);
|
|
146
|
+
};
|
|
147
|
+
const clearLine = () => {
|
|
148
|
+
if (isTty)
|
|
149
|
+
output.write("\r\x1B[2K");
|
|
150
|
+
};
|
|
151
|
+
render();
|
|
152
|
+
const timer = isTty ? setInterval(render, input.intervalMs ?? 16) : null;
|
|
153
|
+
return {
|
|
154
|
+
setLabel(next) {
|
|
155
|
+
label = next;
|
|
156
|
+
render();
|
|
157
|
+
},
|
|
158
|
+
pause() {
|
|
159
|
+
paused = true;
|
|
160
|
+
clearLine();
|
|
161
|
+
},
|
|
162
|
+
resume() {
|
|
163
|
+
if (stopped)
|
|
164
|
+
return;
|
|
165
|
+
paused = false;
|
|
166
|
+
render();
|
|
167
|
+
},
|
|
168
|
+
stop(finalLine) {
|
|
169
|
+
if (stopped)
|
|
170
|
+
return;
|
|
171
|
+
stopped = true;
|
|
172
|
+
if (timer)
|
|
173
|
+
clearInterval(timer);
|
|
174
|
+
clearLine();
|
|
175
|
+
if (finalLine)
|
|
176
|
+
output.write(`${finalLine}
|
|
177
|
+
`);
|
|
178
|
+
}
|
|
179
|
+
};
|
|
127
180
|
}
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
return
|
|
181
|
+
|
|
182
|
+
// packages/cli/src/commands/_async-ui.ts
|
|
183
|
+
var FRAMES = ["\u25D0", "\u25D3", "\u25D1", "\u25D2"];
|
|
184
|
+
var DONE_SYMBOL = pc.green("\u25C7");
|
|
185
|
+
var FAIL_SYMBOL = pc.red("\u25A0");
|
|
186
|
+
var activeUpdate = null;
|
|
187
|
+
async function withSpinner(label, work, options = {}) {
|
|
188
|
+
if (options.outputMode === "json") {
|
|
189
|
+
return work(() => {
|
|
190
|
+
return;
|
|
191
|
+
});
|
|
137
192
|
}
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
return cachedGitHubBearerToken;
|
|
142
|
-
const privateSession = readPrivateRemoteSessionToken(projectRoot);
|
|
143
|
-
if (privateSession) {
|
|
144
|
-
cachedGitHubBearerToken = privateSession;
|
|
145
|
-
return cachedGitHubBearerToken;
|
|
193
|
+
if (activeUpdate) {
|
|
194
|
+
activeUpdate(label);
|
|
195
|
+
return work(activeUpdate);
|
|
146
196
|
}
|
|
147
|
-
const
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
197
|
+
const output = options.output ?? process.stderr;
|
|
198
|
+
const isTty = output.isTTY === true;
|
|
199
|
+
let lastLabel = label;
|
|
200
|
+
if (!isTty) {
|
|
201
|
+
output.write(`${label}
|
|
202
|
+
`);
|
|
203
|
+
const update2 = (next) => {
|
|
204
|
+
lastLabel = next;
|
|
205
|
+
};
|
|
206
|
+
activeUpdate = update2;
|
|
207
|
+
try {
|
|
208
|
+
return await work(update2);
|
|
209
|
+
} finally {
|
|
210
|
+
activeUpdate = null;
|
|
211
|
+
}
|
|
151
212
|
}
|
|
152
|
-
const
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
213
|
+
const spinner = createTtySpinner({
|
|
214
|
+
label,
|
|
215
|
+
output,
|
|
216
|
+
frames: FRAMES,
|
|
217
|
+
styleFrame: (frame) => pc.cyan(frame)
|
|
156
218
|
});
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
219
|
+
const update = (next) => {
|
|
220
|
+
lastLabel = next;
|
|
221
|
+
spinner.setLabel(next);
|
|
222
|
+
};
|
|
223
|
+
activeUpdate = update;
|
|
161
224
|
try {
|
|
162
|
-
const
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
baseUrl: selected.connection.baseUrl,
|
|
166
|
-
authToken: readGitHubBearerTokenForRemote(projectRoot),
|
|
167
|
-
connectionKind: "remote"
|
|
168
|
-
};
|
|
169
|
-
}
|
|
170
|
-
const connection = await ensureLocalRigServerConnection(projectRoot);
|
|
171
|
-
return {
|
|
172
|
-
baseUrl: connection.baseUrl,
|
|
173
|
-
authToken: connection.authToken,
|
|
174
|
-
connectionKind: "local"
|
|
175
|
-
};
|
|
225
|
+
const result = await work(update);
|
|
226
|
+
spinner.stop(options.doneLabel ? `${DONE_SYMBOL} ${options.doneLabel}` : undefined);
|
|
227
|
+
return result;
|
|
176
228
|
} catch (error) {
|
|
177
|
-
|
|
178
|
-
throw new CliError2(error.message, 1);
|
|
179
|
-
}
|
|
229
|
+
spinner.stop(`${FAIL_SYMBOL} ${lastLabel}`);
|
|
180
230
|
throw error;
|
|
231
|
+
} finally {
|
|
232
|
+
activeUpdate = null;
|
|
181
233
|
}
|
|
182
234
|
}
|
|
183
|
-
function mergeHeaders(headers, authToken) {
|
|
184
|
-
const merged = new Headers(headers);
|
|
185
|
-
if (authToken) {
|
|
186
|
-
merged.set("authorization", `Bearer ${authToken}`);
|
|
187
|
-
}
|
|
188
|
-
return merged;
|
|
189
|
-
}
|
|
190
|
-
function diagnosticMessage(payload) {
|
|
191
|
-
if (!payload || typeof payload !== "object" || Array.isArray(payload))
|
|
192
|
-
return null;
|
|
193
|
-
const record = payload;
|
|
194
|
-
const diagnostics = Array.isArray(record.diagnostics) ? record.diagnostics : [];
|
|
195
|
-
const messages = diagnostics.flatMap((entry) => {
|
|
196
|
-
if (!entry || typeof entry !== "object" || Array.isArray(entry))
|
|
197
|
-
return [];
|
|
198
|
-
const diagnostic = entry;
|
|
199
|
-
const kind = typeof diagnostic.kind === "string" ? diagnostic.kind : "task-source";
|
|
200
|
-
const message = typeof diagnostic.message === "string" ? diagnostic.message : null;
|
|
201
|
-
return message ? [`${kind}: ${message}`] : [];
|
|
202
|
-
});
|
|
203
|
-
return messages.length > 0 ? messages.join("; ") : null;
|
|
204
|
-
}
|
|
205
|
-
async function requestServerJson(context, pathname, init = {}) {
|
|
206
|
-
const server = await ensureServerForCli(context.projectRoot);
|
|
207
|
-
const response = await fetch(`${server.baseUrl}${pathname}`, {
|
|
208
|
-
...init,
|
|
209
|
-
headers: mergeHeaders(init.headers, server.authToken)
|
|
210
|
-
});
|
|
211
|
-
const text = await response.text();
|
|
212
|
-
const payload = text.trim().length > 0 ? (() => {
|
|
213
|
-
try {
|
|
214
|
-
return JSON.parse(text);
|
|
215
|
-
} catch {
|
|
216
|
-
return null;
|
|
217
|
-
}
|
|
218
|
-
})() : null;
|
|
219
|
-
if (!response.ok) {
|
|
220
|
-
const diagnostics = diagnosticMessage(payload);
|
|
221
|
-
const detail = diagnostics ?? (text || response.statusText);
|
|
222
|
-
throw new CliError2(`Rig server request failed (${response.status}): ${detail}`, 1);
|
|
223
|
-
}
|
|
224
|
-
return payload;
|
|
225
|
-
}
|
|
226
|
-
async function getGitHubAuthStatusViaServer(context) {
|
|
227
|
-
const payload = await requestServerJson(context, "/api/github/auth/status");
|
|
228
|
-
return payload && typeof payload === "object" && !Array.isArray(payload) ? payload : {};
|
|
229
|
-
}
|
|
230
|
-
async function postGitHubTokenViaServer(context, token, options = {}) {
|
|
231
|
-
const payload = await requestServerJson(context, "/api/github/auth/token", {
|
|
232
|
-
method: "POST",
|
|
233
|
-
headers: { "content-type": "application/json" },
|
|
234
|
-
body: JSON.stringify({ token, selectedRepo: options.selectedRepo, projectRoot: options.projectRoot })
|
|
235
|
-
});
|
|
236
|
-
return payload && typeof payload === "object" && !Array.isArray(payload) ? payload : {};
|
|
237
|
-
}
|
|
238
235
|
|
|
239
236
|
// packages/cli/src/commands/github.ts
|
|
240
237
|
function printPayload(context, payload, fallback) {
|
|
@@ -243,50 +240,101 @@ function printPayload(context, payload, fallback) {
|
|
|
243
240
|
else
|
|
244
241
|
console.log(fallback);
|
|
245
242
|
}
|
|
243
|
+
function apiSessionTokenFrom(payload) {
|
|
244
|
+
if (!payload || typeof payload !== "object" || Array.isArray(payload))
|
|
245
|
+
return null;
|
|
246
|
+
return payloadString(payload, "apiSessionToken");
|
|
247
|
+
}
|
|
248
|
+
function payloadString(payload, key) {
|
|
249
|
+
const value = Reflect.get(payload, key);
|
|
250
|
+
return typeof value === "string" && value.trim() ? value.trim() : null;
|
|
251
|
+
}
|
|
252
|
+
function payloadRecord(payload, key) {
|
|
253
|
+
const value = Reflect.get(payload, key);
|
|
254
|
+
return value && typeof value === "object" && !Array.isArray(value) ? value : null;
|
|
255
|
+
}
|
|
256
|
+
function remoteNamespaceMetadata(result) {
|
|
257
|
+
const namespace = payloadRecord(result, "userNamespace");
|
|
258
|
+
return {
|
|
259
|
+
...payloadString(result, "login") ? { login: payloadString(result, "login") } : {},
|
|
260
|
+
...payloadString(result, "userId") ? { userId: payloadString(result, "userId") } : {},
|
|
261
|
+
...namespace && payloadString(namespace, "key") ? { userNamespaceKey: payloadString(namespace, "key") } : {},
|
|
262
|
+
...namespace && payloadString(namespace, "root") ? { userNamespaceRoot: payloadString(namespace, "root") } : {},
|
|
263
|
+
...namespace && payloadString(namespace, "checkoutBaseDir") ? { checkoutBaseDir: payloadString(namespace, "checkoutBaseDir") } : {},
|
|
264
|
+
...namespace && payloadString(namespace, "snapshotBaseDir") ? { snapshotBaseDir: payloadString(namespace, "snapshotBaseDir") } : {}
|
|
265
|
+
};
|
|
266
|
+
}
|
|
267
|
+
function persistRemoteAuthSession(context, source, result, fallbackToken) {
|
|
268
|
+
const apiSessionToken = result.apiSessionToken || apiSessionTokenFrom(result);
|
|
269
|
+
setGitHubBearerTokenForCurrentProcess(apiSessionToken ?? fallbackToken, context.projectRoot);
|
|
270
|
+
if (!apiSessionToken)
|
|
271
|
+
return;
|
|
272
|
+
const repo = readRepoConnection(context.projectRoot);
|
|
273
|
+
const path = resolve3(context.projectRoot, ".rig", "state", "github-auth.json");
|
|
274
|
+
mkdirSync2(dirname2(path), { recursive: true });
|
|
275
|
+
writeFileSync2(path, `${JSON.stringify({
|
|
276
|
+
authenticated: true,
|
|
277
|
+
source,
|
|
278
|
+
storedOnServer: true,
|
|
279
|
+
...repo?.project ? { selectedRepo: repo.project } : {},
|
|
280
|
+
...remoteNamespaceMetadata(result),
|
|
281
|
+
apiSessionToken,
|
|
282
|
+
updatedAt: new Date().toISOString()
|
|
283
|
+
}, null, 2)}
|
|
284
|
+
`, "utf8");
|
|
285
|
+
}
|
|
286
|
+
function isSignedIn(status) {
|
|
287
|
+
return status.signedIn === true;
|
|
288
|
+
}
|
|
246
289
|
function readGhToken() {
|
|
247
|
-
const result =
|
|
290
|
+
const result = spawnSync("gh", ["auth", "token"], { encoding: "utf8" });
|
|
248
291
|
if (result.status !== 0) {
|
|
249
292
|
const detail = result.stderr?.trim() || result.stdout?.trim() || "gh auth token failed";
|
|
250
|
-
throw new
|
|
293
|
+
throw new CliError(`Could not import GitHub token from gh: ${detail}`, 1, { hint: "Sign in first with `gh auth login`, or store a token directly: `rig github auth token --token <token>`." });
|
|
251
294
|
}
|
|
252
295
|
const token = result.stdout.trim();
|
|
253
296
|
if (!token)
|
|
254
|
-
throw new
|
|
297
|
+
throw new CliError("gh auth token returned an empty token.", 1, { hint: "Sign in with `gh auth login`, then re-run `rig github auth import-gh`." });
|
|
255
298
|
return token;
|
|
256
299
|
}
|
|
257
300
|
async function executeGithub(context, args) {
|
|
258
301
|
const [group, command, ...rest] = args;
|
|
259
302
|
if (group !== "auth") {
|
|
260
|
-
throw new
|
|
303
|
+
throw new CliError("Usage: rig github auth <status|import-gh|token>", 1);
|
|
261
304
|
}
|
|
262
305
|
switch (command) {
|
|
263
306
|
case "status": {
|
|
264
307
|
if (rest.length > 0)
|
|
265
|
-
throw new
|
|
266
|
-
const status = await
|
|
267
|
-
printPayload(context, status, `GitHub auth: ${status
|
|
308
|
+
throw new CliError("Usage: rig github auth status", 1);
|
|
309
|
+
const status = await withSpinner("Checking GitHub auth on the server\u2026", () => getGitHubAuthStatusInProcess(context), { outputMode: context.outputMode });
|
|
310
|
+
printPayload(context, status, `GitHub auth: ${isSignedIn(status) ? "authenticated" : "unauthenticated"}`);
|
|
268
311
|
return { ok: true, group: "github", command: "auth status", details: status };
|
|
269
312
|
}
|
|
270
313
|
case "token": {
|
|
271
314
|
const parsed = takeOption(rest, "--token");
|
|
272
315
|
if (parsed.rest.length > 0)
|
|
273
|
-
throw new
|
|
316
|
+
throw new CliError("Usage: rig github auth token --token <token>", 1);
|
|
274
317
|
const token = parsed.value?.trim();
|
|
275
318
|
if (!token)
|
|
276
|
-
throw new
|
|
277
|
-
const
|
|
278
|
-
|
|
319
|
+
throw new CliError("Missing --token value.", 1, { hint: "Re-run as `rig github auth token --token <token>`." });
|
|
320
|
+
const repo = readRepoConnection(context.projectRoot);
|
|
321
|
+
const result = await withSpinner("Storing GitHub token on the server\u2026", () => postGitHubTokenInProcess(context, token, repo?.project ? { selectedRepo: repo.project } : {}), { outputMode: context.outputMode });
|
|
322
|
+
persistRemoteAuthSession(context, "token", result, token);
|
|
323
|
+
printPayload(context, result, "GitHub token stored on the selected server.");
|
|
279
324
|
return { ok: true, group: "github", command: "auth token", details: result };
|
|
280
325
|
}
|
|
281
326
|
case "import-gh": {
|
|
282
327
|
if (rest.length > 0)
|
|
283
|
-
throw new
|
|
284
|
-
const
|
|
285
|
-
|
|
328
|
+
throw new CliError("Usage: rig github auth import-gh", 1);
|
|
329
|
+
const importedToken = readGhToken();
|
|
330
|
+
const repo = readRepoConnection(context.projectRoot);
|
|
331
|
+
const result = await withSpinner("Storing GitHub token on the server\u2026", () => postGitHubTokenInProcess(context, importedToken, repo?.project ? { selectedRepo: repo.project } : {}), { outputMode: context.outputMode });
|
|
332
|
+
persistRemoteAuthSession(context, "gh", result, importedToken);
|
|
333
|
+
printPayload(context, result, "GitHub token imported from gh and stored on the selected server.");
|
|
286
334
|
return { ok: true, group: "github", command: "auth import-gh", details: result };
|
|
287
335
|
}
|
|
288
336
|
default:
|
|
289
|
-
throw new
|
|
337
|
+
throw new CliError("Usage: rig github auth <status|import-gh|token>", 1);
|
|
290
338
|
}
|
|
291
339
|
}
|
|
292
340
|
export {
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { type RunnerContext } from "../runner";
|
|
2
|
+
import type { CommandOutcome } from "@rig/runtime";
|
|
3
|
+
import { type RunProjectionRecord } from "./_run-projection";
|
|
4
|
+
export type InboxKind = "approvals" | "inputs";
|
|
5
|
+
export interface InboxFilters {
|
|
6
|
+
readonly run?: string;
|
|
7
|
+
readonly task?: string;
|
|
8
|
+
}
|
|
9
|
+
export interface InboxRecord {
|
|
10
|
+
readonly runId: string;
|
|
11
|
+
readonly taskId: string | null;
|
|
12
|
+
readonly requestId: string;
|
|
13
|
+
readonly status: "pending";
|
|
14
|
+
readonly prompt: string;
|
|
15
|
+
readonly requestedAt: string;
|
|
16
|
+
readonly payload: unknown;
|
|
17
|
+
}
|
|
18
|
+
/** Every pending inbox request across discoverable runs, optionally filtered by run/task. */
|
|
19
|
+
export interface InboxDeps {
|
|
20
|
+
readonly listRuns?: (projectRoot: string) => Promise<RunProjectionRecord[]>;
|
|
21
|
+
}
|
|
22
|
+
export declare function listInboxRecords(context: Pick<RunnerContext, "projectRoot">, kind: InboxKind, filters?: InboxFilters, deps?: InboxDeps): Promise<InboxRecord[]>;
|
|
23
|
+
/** Pending-gate counts for proactive surfacing; null-safe for footer callers. */
|
|
24
|
+
export declare function readPendingInboxCounts(context: Pick<RunnerContext, "projectRoot">): Promise<{
|
|
25
|
+
approvals: number;
|
|
26
|
+
inputs: number;
|
|
27
|
+
} | null>;
|
|
28
|
+
export declare function executeInbox(context: RunnerContext, args: string[], deps?: InboxDeps): Promise<CommandOutcome>;
|