@h-rig/cli 0.0.6-alpha.82 → 0.0.6-alpha.84
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/dist/bin/build-rig-binaries.js +40 -8
- package/dist/bin/rig.js +23402 -14577
- package/dist/src/app/board.js +217 -41
- package/dist/src/app-opentui/adapters/command.d.ts +2 -0
- package/dist/src/app-opentui/adapters/command.js +329 -0
- package/dist/src/app-opentui/adapters/common.js +2 -2
- package/dist/src/app-opentui/adapters/doctor.d.ts +0 -2
- package/dist/src/app-opentui/adapters/doctor.js +64 -39
- package/dist/src/app-opentui/adapters/family.d.ts +62 -0
- package/dist/src/app-opentui/adapters/family.js +14305 -0
- package/dist/src/app-opentui/adapters/fleet.d.ts +0 -2
- package/dist/src/app-opentui/adapters/fleet.js +90 -60
- package/dist/src/app-opentui/adapters/inbox.d.ts +12 -2
- package/dist/src/app-opentui/adapters/inbox.js +137 -78
- package/dist/src/app-opentui/adapters/init.d.ts +0 -2
- package/dist/src/app-opentui/adapters/init.js +85 -47
- package/dist/src/app-opentui/adapters/inspect.d.ts +52 -0
- package/dist/src/app-opentui/adapters/inspect.js +1024 -0
- package/dist/src/app-opentui/adapters/pi-attach.d.ts +15 -6
- package/dist/src/app-opentui/adapters/pi-attach.js +442 -125
- package/dist/src/app-opentui/adapters/pi.d.ts +23 -0
- package/dist/src/app-opentui/adapters/pi.js +363 -0
- package/dist/src/app-opentui/adapters/plugin.d.ts +84 -0
- package/dist/src/app-opentui/adapters/plugin.js +544 -0
- package/dist/src/app-opentui/adapters/repo.d.ts +37 -0
- package/dist/src/app-opentui/adapters/repo.js +186 -0
- package/dist/src/app-opentui/adapters/run-detail.d.ts +9 -2
- package/dist/src/app-opentui/adapters/run-detail.js +180 -63
- package/dist/src/app-opentui/adapters/server.d.ts +10 -2
- package/dist/src/app-opentui/adapters/server.js +191 -45
- package/dist/src/app-opentui/adapters/tasks.d.ts +11 -2
- package/dist/src/app-opentui/adapters/tasks.js +1123 -143
- package/dist/src/app-opentui/adapters/workspace.d.ts +49 -0
- package/dist/src/app-opentui/adapters/workspace.js +333 -0
- package/dist/src/app-opentui/autocomplete.d.ts +20 -0
- package/dist/src/app-opentui/autocomplete.js +576 -0
- package/dist/src/app-opentui/bootstrap.d.ts +1 -6
- package/dist/src/app-opentui/bootstrap.js +25252 -16474
- package/dist/src/app-opentui/command-palette.d.ts +3 -0
- package/dist/src/app-opentui/command-palette.js +1010 -0
- package/dist/src/app-opentui/command-pty-host.d.ts +62 -0
- package/dist/src/app-opentui/command-pty-host.js +248 -0
- package/dist/src/app-opentui/drone.js +8 -6
- package/dist/src/app-opentui/events.js +1 -1
- package/dist/src/app-opentui/fleet-stats.d.ts +32 -0
- package/dist/src/app-opentui/fleet-stats.js +114 -0
- package/dist/src/app-opentui/focus-manager.d.ts +14 -0
- package/dist/src/app-opentui/focus-manager.js +24 -0
- package/dist/src/app-opentui/index.js +5431 -2797
- package/dist/src/app-opentui/intent.js +179 -50
- package/dist/src/app-opentui/keymap.d.ts +21 -0
- package/dist/src/app-opentui/keymap.js +1748 -0
- package/dist/src/app-opentui/launch-routing.d.ts +16 -0
- package/dist/src/app-opentui/launch-routing.js +55 -0
- package/dist/src/app-opentui/layout.d.ts +7 -0
- package/dist/src/app-opentui/layout.js +13 -6
- package/dist/src/app-opentui/list-search.d.ts +24 -0
- package/dist/src/app-opentui/list-search.js +88 -1
- package/dist/src/app-opentui/pi-host-child.js +99 -17
- package/dist/src/app-opentui/pi-pty-host.d.ts +14 -0
- package/dist/src/app-opentui/pi-pty-host.js +30 -14
- package/dist/src/app-opentui/react/App.d.ts +9 -0
- package/dist/src/app-opentui/react/App.js +5144 -0
- package/dist/src/app-opentui/react/Backdrop.d.ts +5 -0
- package/dist/src/app-opentui/react/Backdrop.js +1834 -0
- package/dist/src/app-opentui/react/ChromeHost.d.ts +5 -0
- package/dist/src/app-opentui/react/ChromeHost.js +2942 -0
- package/dist/src/app-opentui/react/SceneFrameView.d.ts +7 -0
- package/dist/src/app-opentui/react/SceneFrameView.js +529 -0
- package/dist/src/app-opentui/react/context.d.ts +17 -0
- package/dist/src/app-opentui/react/context.js +37 -0
- package/dist/src/app-opentui/react/launch.d.ts +2 -0
- package/dist/src/app-opentui/react/launch.js +5743 -0
- package/dist/src/app-opentui/react/nav.d.ts +18 -0
- package/dist/src/app-opentui/react/nav.js +54 -0
- package/dist/src/app-opentui/react/scroll.d.ts +12 -0
- package/dist/src/app-opentui/react/scroll.js +21 -0
- package/dist/src/app-opentui/react/syntax.d.ts +2 -0
- package/dist/src/app-opentui/react/syntax.js +64 -0
- package/dist/src/app-opentui/registry.js +20428 -4828
- package/dist/src/app-opentui/render/ascii-fleet.d.ts +15 -0
- package/dist/src/app-opentui/render/ascii-fleet.js +82 -0
- package/dist/src/app-opentui/render/constants.d.ts +31 -0
- package/dist/src/app-opentui/render/constants.js +66 -0
- package/dist/src/app-opentui/render/graphics.d.ts +2 -1
- package/dist/src/app-opentui/render/graphics.js +228 -46
- package/dist/src/app-opentui/render/image-visual-layer-worker.js +469 -930
- package/dist/src/app-opentui/render/image-visual-layer.d.ts +25 -10
- package/dist/src/app-opentui/render/image-visual-layer.js +448 -329
- package/dist/src/app-opentui/render/native-host.d.ts +37 -0
- package/dist/src/app-opentui/render/native-host.js +179 -0
- package/dist/src/app-opentui/render/panel-layout.d.ts +38 -0
- package/dist/src/app-opentui/render/panel-layout.js +48 -0
- package/dist/src/app-opentui/render/panels.d.ts +2 -0
- package/dist/src/app-opentui/render/panels.js +78 -38
- package/dist/src/app-opentui/render/preloader.d.ts +10 -0
- package/dist/src/app-opentui/render/preloader.js +165 -0
- package/dist/src/app-opentui/render/scene.d.ts +53 -1
- package/dist/src/app-opentui/render/scene.js +195 -6
- package/dist/src/app-opentui/render/text.d.ts +7 -1
- package/dist/src/app-opentui/render/text.js +15 -8
- package/dist/src/app-opentui/render/type-bar.d.ts +2 -1
- package/dist/src/app-opentui/render/type-bar.js +113 -39
- package/dist/src/app-opentui/runtime-resources.d.ts +16 -0
- package/dist/src/app-opentui/runtime-resources.js +62 -0
- package/dist/src/app-opentui/runtime.d.ts +44 -1
- package/dist/src/app-opentui/runtime.js +5415 -2738
- package/dist/src/app-opentui/scenes/command.d.ts +3 -0
- package/dist/src/app-opentui/scenes/command.js +117 -0
- package/dist/src/app-opentui/scenes/doctor.d.ts +2 -1
- package/dist/src/app-opentui/scenes/doctor.js +221 -17
- package/dist/src/app-opentui/scenes/error.js +60 -20
- package/dist/src/app-opentui/scenes/family-domains/agent.d.ts +2 -0
- package/dist/src/app-opentui/scenes/family-domains/agent.js +348 -0
- package/dist/src/app-opentui/scenes/family-domains/browser.d.ts +2 -0
- package/dist/src/app-opentui/scenes/family-domains/browser.js +195 -0
- package/dist/src/app-opentui/scenes/family-domains/dist.d.ts +2 -0
- package/dist/src/app-opentui/scenes/family-domains/dist.js +243 -0
- package/dist/src/app-opentui/scenes/family-domains/git.d.ts +2 -0
- package/dist/src/app-opentui/scenes/family-domains/git.js +195 -0
- package/dist/src/app-opentui/scenes/family-domains/github.d.ts +2 -0
- package/dist/src/app-opentui/scenes/family-domains/github.js +274 -0
- package/dist/src/app-opentui/scenes/family-domains/harness.d.ts +2 -0
- package/dist/src/app-opentui/scenes/family-domains/harness.js +152 -0
- package/dist/src/app-opentui/scenes/family-domains/index.d.ts +4 -0
- package/dist/src/app-opentui/scenes/family-domains/index.js +1679 -0
- package/dist/src/app-opentui/scenes/family-domains/kit.d.ts +76 -0
- package/dist/src/app-opentui/scenes/family-domains/kit.js +305 -0
- package/dist/src/app-opentui/scenes/family-domains/profile.d.ts +2 -0
- package/dist/src/app-opentui/scenes/family-domains/profile.js +212 -0
- package/dist/src/app-opentui/scenes/family-domains/queue.d.ts +2 -0
- package/dist/src/app-opentui/scenes/family-domains/queue.js +146 -0
- package/dist/src/app-opentui/scenes/family-domains/remote.d.ts +2 -0
- package/dist/src/app-opentui/scenes/family-domains/remote.js +518 -0
- package/dist/src/app-opentui/scenes/family-domains/review.d.ts +2 -0
- package/dist/src/app-opentui/scenes/family-domains/review.js +280 -0
- package/dist/src/app-opentui/scenes/family-domains/setup.d.ts +2 -0
- package/dist/src/app-opentui/scenes/family-domains/setup.js +267 -0
- package/dist/src/app-opentui/scenes/family-domains/stats.d.ts +2 -0
- package/dist/src/app-opentui/scenes/family-domains/stats.js +370 -0
- package/dist/src/app-opentui/scenes/family.d.ts +3 -0
- package/dist/src/app-opentui/scenes/family.js +2144 -0
- package/dist/src/app-opentui/scenes/fleet.js +552 -43
- package/dist/src/app-opentui/scenes/handoff.js +342 -35
- package/dist/src/app-opentui/scenes/help.js +640 -56
- package/dist/src/app-opentui/scenes/inbox.d.ts +2 -1
- package/dist/src/app-opentui/scenes/inbox.js +329 -21
- package/dist/src/app-opentui/scenes/init.d.ts +2 -1
- package/dist/src/app-opentui/scenes/init.js +120 -34
- package/dist/src/app-opentui/scenes/inspect.d.ts +3 -0
- package/dist/src/app-opentui/scenes/inspect.js +793 -0
- package/dist/src/app-opentui/scenes/main.d.ts +2 -1
- package/dist/src/app-opentui/scenes/main.js +264 -29
- package/dist/src/app-opentui/scenes/pi.d.ts +3 -0
- package/dist/src/app-opentui/scenes/pi.js +508 -0
- package/dist/src/app-opentui/scenes/plugin.d.ts +3 -0
- package/dist/src/app-opentui/scenes/plugin.js +486 -0
- package/dist/src/app-opentui/scenes/repo.d.ts +3 -0
- package/dist/src/app-opentui/scenes/repo.js +424 -0
- package/dist/src/app-opentui/scenes/run-detail.d.ts +2 -1
- package/dist/src/app-opentui/scenes/run-detail.js +362 -35
- package/dist/src/app-opentui/scenes/server.d.ts +2 -1
- package/dist/src/app-opentui/scenes/server.js +243 -26
- package/dist/src/app-opentui/scenes/tasks.js +518 -41
- package/dist/src/app-opentui/scenes/workspace.d.ts +3 -0
- package/dist/src/app-opentui/scenes/workspace.js +426 -0
- package/dist/src/app-opentui/selectable.d.ts +19 -0
- package/dist/src/app-opentui/selectable.js +79 -0
- package/dist/src/app-opentui/state.js +129 -36
- package/dist/src/app-opentui/surface-catalog.d.ts +20 -0
- package/dist/src/app-opentui/surface-catalog.js +540 -0
- package/dist/src/app-opentui/terminal-capabilities.d.ts +7 -0
- package/dist/src/app-opentui/terminal-capabilities.js +15 -0
- package/dist/src/app-opentui/theme.d.ts +28 -6
- package/dist/src/app-opentui/theme.js +61 -8
- package/dist/src/app-opentui/types.d.ts +130 -5
- package/dist/src/commands/_authority-runs.d.ts +1 -1
- package/dist/src/commands/_authority-runs.js +2 -12
- package/dist/src/commands/_doctor-checks.js +62 -13
- package/dist/src/commands/_help-catalog.js +95 -15
- package/dist/src/commands/_operator-view.js +97 -15
- package/dist/src/commands/_pi-frontend.d.ts +2 -0
- package/dist/src/commands/_pi-frontend.js +97 -13
- package/dist/src/commands/_preflight.js +64 -14
- package/dist/src/commands/_server-client.js +82 -18
- package/dist/src/commands/_server-events.d.ts +26 -0
- package/dist/src/commands/_server-events.js +310 -0
- package/dist/src/commands/_snapshot-upload.js +62 -13
- package/dist/src/commands/agent.js +2 -12
- package/dist/src/commands/connect.js +7 -1
- package/dist/src/commands/doctor.js +62 -13
- package/dist/src/commands/github.js +144 -23
- package/dist/src/commands/inbox.js +62 -13
- package/dist/src/commands/init.js +82 -18
- package/dist/src/commands/inspect.js +62 -13
- package/dist/src/commands/run.js +100 -15
- package/dist/src/commands/server.js +71 -26
- package/dist/src/commands/setup.js +62 -13
- package/dist/src/commands/stats.js +157 -28
- package/dist/src/commands/task-run-driver.js +64 -25
- package/dist/src/commands/task.js +196 -43
- package/dist/src/commands.js +419 -123
- package/dist/src/index.js +426 -130
- package/package.json +11 -10
- package/dist/src/app-opentui/render/image-visual-layer-native-canvas.d.ts +0 -2
- package/dist/src/app-opentui/render/image-visual-layer-native-canvas.js +0 -1484
|
@@ -392,7 +392,13 @@ async function executeConnectionCommand(context, args, options) {
|
|
|
392
392
|
if (!state.connections[alias])
|
|
393
393
|
throw new CliError(`Unknown Rig server: ${alias}`, 1, { hint: "Run `rig server list` to see saved aliases, or save one with `rig server add <alias> <url>`." });
|
|
394
394
|
}
|
|
395
|
-
const
|
|
395
|
+
const previousRepo = readRepoConnection(context.projectRoot);
|
|
396
|
+
const repoState = {
|
|
397
|
+
selected: alias,
|
|
398
|
+
...previousRepo?.project ? { project: previousRepo.project } : {},
|
|
399
|
+
linkedAt: new Date().toISOString(),
|
|
400
|
+
...previousRepo?.serverProjectRoot && previousRepo.selected === alias ? { serverProjectRoot: previousRepo.serverProjectRoot } : {}
|
|
401
|
+
};
|
|
396
402
|
writeRepoConnection(context.projectRoot, repoState);
|
|
397
403
|
printJsonOrText(context, repoState, formatSuccessCard("Rig server selected", [
|
|
398
404
|
["selected", alias],
|
|
@@ -143,17 +143,21 @@ function cleanToken(value) {
|
|
|
143
143
|
const trimmed = value?.trim();
|
|
144
144
|
return trimmed ? trimmed : null;
|
|
145
145
|
}
|
|
146
|
-
function
|
|
146
|
+
function readRemoteAuthState(projectRoot) {
|
|
147
147
|
const path = resolve2(projectRoot, ".rig", "state", "github-auth.json");
|
|
148
148
|
if (!existsSync2(path))
|
|
149
149
|
return null;
|
|
150
150
|
try {
|
|
151
151
|
const parsed = JSON.parse(readFileSync2(path, "utf8"));
|
|
152
|
-
return
|
|
152
|
+
return parsed && typeof parsed === "object" && !Array.isArray(parsed) ? parsed : null;
|
|
153
153
|
} catch {
|
|
154
154
|
return null;
|
|
155
155
|
}
|
|
156
156
|
}
|
|
157
|
+
function readPrivateRemoteSessionToken(projectRoot) {
|
|
158
|
+
const parsed = readRemoteAuthState(projectRoot);
|
|
159
|
+
return parsed ? cleanToken(typeof parsed.apiSessionToken === "string" ? parsed.apiSessionToken : typeof parsed.sessionToken === "string" ? parsed.sessionToken : undefined) : null;
|
|
160
|
+
}
|
|
157
161
|
function readGitHubBearerTokenForRemote(projectRoot) {
|
|
158
162
|
const scopedKey = resolve2(projectRoot);
|
|
159
163
|
if (scopedGitHubBearerTokens.has(scopedKey))
|
|
@@ -164,15 +168,25 @@ function readGitHubBearerTokenForRemote(projectRoot) {
|
|
|
164
168
|
return cleanToken(process.env.RIG_SERVER_AUTH_TOKEN) ?? cleanToken(process.env.RIG_REMOTE_AUTH_TOKEN);
|
|
165
169
|
}
|
|
166
170
|
function readStoredGitHubAuthToken(projectRoot) {
|
|
167
|
-
const
|
|
168
|
-
|
|
171
|
+
const parsed = readRemoteAuthState(projectRoot);
|
|
172
|
+
return parsed ? cleanToken(typeof parsed.token === "string" ? parsed.token : undefined) : null;
|
|
173
|
+
}
|
|
174
|
+
function inferRemoteServerProjectRootFromAuthState(projectRoot) {
|
|
175
|
+
const repo = readRepoConnection(projectRoot);
|
|
176
|
+
const slug = repo?.project?.trim();
|
|
177
|
+
if (!slug || !/^[^/]+\/[^/]+$/.test(slug))
|
|
169
178
|
return null;
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
179
|
+
const auth = readRemoteAuthState(projectRoot);
|
|
180
|
+
const authUpdatedAt = typeof auth?.updatedAt === "string" ? Date.parse(auth.updatedAt) : Number.NaN;
|
|
181
|
+
const repoLinkedAt = typeof repo?.linkedAt === "string" ? Date.parse(repo.linkedAt) : Number.NaN;
|
|
182
|
+
if (Number.isFinite(authUpdatedAt) && Number.isFinite(repoLinkedAt) && authUpdatedAt + 1000 < repoLinkedAt)
|
|
174
183
|
return null;
|
|
175
|
-
|
|
184
|
+
const checkoutBaseDir = typeof auth?.checkoutBaseDir === "string" && auth.checkoutBaseDir.trim() ? auth.checkoutBaseDir.trim() : null;
|
|
185
|
+
if (!checkoutBaseDir)
|
|
186
|
+
return null;
|
|
187
|
+
const inferred = `${checkoutBaseDir.replace(/\/+$/, "")}/${slug}`;
|
|
188
|
+
writeRepoServerProjectRoot(projectRoot, inferred);
|
|
189
|
+
return inferred;
|
|
176
190
|
}
|
|
177
191
|
function readLocalConnectionFallbackToken(projectRoot) {
|
|
178
192
|
return readGitHubBearerTokenForRemote(projectRoot) ?? cleanToken(process.env.RIG_GITHUB_TOKEN) ?? readStoredGitHubAuthToken(projectRoot);
|
|
@@ -183,7 +197,7 @@ async function ensureServerForCli(projectRoot) {
|
|
|
183
197
|
if (selected?.connection.kind === "remote") {
|
|
184
198
|
reportServerPhase(`Connecting to ${selected.alias}\u2026`);
|
|
185
199
|
const authToken = readGitHubBearerTokenForRemote(projectRoot);
|
|
186
|
-
const serverProjectRoot = selected.serverProjectRoot ?? await backfillRemoteServerProjectRoot(projectRoot, selected.connection.baseUrl, authToken);
|
|
200
|
+
const serverProjectRoot = selected.serverProjectRoot ?? inferRemoteServerProjectRootFromAuthState(projectRoot) ?? await backfillRemoteServerProjectRoot(projectRoot, selected.connection.baseUrl, authToken);
|
|
187
201
|
return {
|
|
188
202
|
baseUrl: selected.connection.baseUrl,
|
|
189
203
|
authToken,
|
|
@@ -212,7 +226,10 @@ async function backfillRemoteServerProjectRoot(projectRoot, baseUrl, authToken)
|
|
|
212
226
|
if (!slug)
|
|
213
227
|
return null;
|
|
214
228
|
try {
|
|
215
|
-
const
|
|
229
|
+
const url = new URL(`${baseUrl}/api/projects/${encodeURIComponent(slug)}`);
|
|
230
|
+
if (authToken && queryAuthFallbackEnabled())
|
|
231
|
+
url.searchParams.set("rt", authToken);
|
|
232
|
+
const response = await fetch(url, {
|
|
216
233
|
headers: mergeHeaders(undefined, authToken)
|
|
217
234
|
});
|
|
218
235
|
if (!response.ok)
|
|
@@ -229,10 +246,23 @@ async function backfillRemoteServerProjectRoot(projectRoot, baseUrl, authToken)
|
|
|
229
246
|
return null;
|
|
230
247
|
}
|
|
231
248
|
}
|
|
249
|
+
function mergeCookie(existing, name, value) {
|
|
250
|
+
const encoded = `${name}=${encodeURIComponent(value)}`;
|
|
251
|
+
if (!existing?.trim())
|
|
252
|
+
return encoded;
|
|
253
|
+
const parts = existing.split(";").map((part) => part.trim()).filter((part) => part && !part.startsWith(`${name}=`));
|
|
254
|
+
return [...parts, encoded].join("; ");
|
|
255
|
+
}
|
|
256
|
+
function queryAuthFallbackEnabled(env = process.env) {
|
|
257
|
+
return env.RIG_ENABLE_QUERY_AUTH_FALLBACK === "1" || env.RIG_QUERY_AUTH_FALLBACK === "1";
|
|
258
|
+
}
|
|
232
259
|
function mergeHeaders(headers, authToken) {
|
|
233
260
|
const merged = new Headers(headers);
|
|
234
261
|
if (authToken) {
|
|
235
|
-
|
|
262
|
+
const bearer = `Bearer ${authToken}`;
|
|
263
|
+
merged.set("authorization", bearer);
|
|
264
|
+
merged.set("x-auth", bearer);
|
|
265
|
+
merged.set("cookie", mergeCookie(merged.get("cookie"), "rig_auth", authToken));
|
|
236
266
|
}
|
|
237
267
|
return merged;
|
|
238
268
|
}
|
|
@@ -293,15 +323,34 @@ async function buildServerFailureContext(projectRoot, server) {
|
|
|
293
323
|
hint: "Check the selected server with `rig server status`, or switch with `rig server use <alias|local>`."
|
|
294
324
|
};
|
|
295
325
|
}
|
|
326
|
+
function isLoopbackRemoteBaseUrl(baseUrl) {
|
|
327
|
+
try {
|
|
328
|
+
const host = new URL(baseUrl).hostname.toLowerCase();
|
|
329
|
+
return host === "localhost" || host === "127.0.0.1" || host === "::1" || host === "[::1]";
|
|
330
|
+
} catch {
|
|
331
|
+
return false;
|
|
332
|
+
}
|
|
333
|
+
}
|
|
334
|
+
function canUseRemoteWithoutProjectRoot(pathname) {
|
|
335
|
+
return pathname === "/health" || pathname === "/api/health" || pathname === "/api/server/status" || pathname === "/api/server/project-root" || pathname.startsWith("/api/github/auth/") || pathname === "/api/projects" || pathname.startsWith("/api/projects/");
|
|
336
|
+
}
|
|
296
337
|
async function requestServerJson(context, pathname, init = {}) {
|
|
297
338
|
const server = await ensureServerForCli(context.projectRoot);
|
|
339
|
+
const requestUrl = new URL(`${server.baseUrl}${pathname}`);
|
|
340
|
+
if (server.connectionKind === "remote" && !server.serverProjectRoot && !canUseRemoteWithoutProjectRoot(requestUrl.pathname) && (server.authToken || !isLoopbackRemoteBaseUrl(server.baseUrl))) {
|
|
341
|
+
const repo = readRepoConnection(context.projectRoot);
|
|
342
|
+
throw new CliError(`Remote server is selected for ${repo?.project ?? "this repo"}, but this checkout has no server-host project root link.`, 1, { hint: "Run `rig init --repair` or `rig init --yes --repo owner/repo --server remote` to repair the remote project link before task/run commands." });
|
|
343
|
+
}
|
|
298
344
|
const headers = mergeHeaders(init.headers, server.authToken);
|
|
299
345
|
if (server.serverProjectRoot)
|
|
300
346
|
headers.set("x-rig-project-root", server.serverProjectRoot);
|
|
347
|
+
if (server.connectionKind === "remote" && server.authToken && queryAuthFallbackEnabled()) {
|
|
348
|
+
requestUrl.searchParams.set("rt", server.authToken);
|
|
349
|
+
}
|
|
301
350
|
reportServerPhase(`${(init.method ?? "GET").toUpperCase()} ${pathname.split("?")[0]}\u2026`);
|
|
302
351
|
let response;
|
|
303
352
|
try {
|
|
304
|
-
response = await fetch(
|
|
353
|
+
response = await fetch(requestUrl, {
|
|
305
354
|
...init,
|
|
306
355
|
headers
|
|
307
356
|
});
|
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
// @bun
|
|
2
2
|
// packages/cli/src/commands/github.ts
|
|
3
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";
|
|
@@ -155,17 +157,25 @@ function cleanToken(value) {
|
|
|
155
157
|
const trimmed = value?.trim();
|
|
156
158
|
return trimmed ? trimmed : null;
|
|
157
159
|
}
|
|
158
|
-
function
|
|
160
|
+
function setGitHubBearerTokenForCurrentProcess(token, projectRoot) {
|
|
161
|
+
const scopedKey = resolve2(projectRoot ?? process.cwd());
|
|
162
|
+
scopedGitHubBearerTokens.set(scopedKey, cleanToken(token ?? undefined));
|
|
163
|
+
}
|
|
164
|
+
function readRemoteAuthState(projectRoot) {
|
|
159
165
|
const path = resolve2(projectRoot, ".rig", "state", "github-auth.json");
|
|
160
166
|
if (!existsSync2(path))
|
|
161
167
|
return null;
|
|
162
168
|
try {
|
|
163
169
|
const parsed = JSON.parse(readFileSync2(path, "utf8"));
|
|
164
|
-
return
|
|
170
|
+
return parsed && typeof parsed === "object" && !Array.isArray(parsed) ? parsed : null;
|
|
165
171
|
} catch {
|
|
166
172
|
return null;
|
|
167
173
|
}
|
|
168
174
|
}
|
|
175
|
+
function readPrivateRemoteSessionToken(projectRoot) {
|
|
176
|
+
const parsed = readRemoteAuthState(projectRoot);
|
|
177
|
+
return parsed ? cleanToken(typeof parsed.apiSessionToken === "string" ? parsed.apiSessionToken : typeof parsed.sessionToken === "string" ? parsed.sessionToken : undefined) : null;
|
|
178
|
+
}
|
|
169
179
|
function readGitHubBearerTokenForRemote(projectRoot) {
|
|
170
180
|
const scopedKey = resolve2(projectRoot);
|
|
171
181
|
if (scopedGitHubBearerTokens.has(scopedKey))
|
|
@@ -176,15 +186,25 @@ function readGitHubBearerTokenForRemote(projectRoot) {
|
|
|
176
186
|
return cleanToken(process.env.RIG_SERVER_AUTH_TOKEN) ?? cleanToken(process.env.RIG_REMOTE_AUTH_TOKEN);
|
|
177
187
|
}
|
|
178
188
|
function readStoredGitHubAuthToken(projectRoot) {
|
|
179
|
-
const
|
|
180
|
-
|
|
189
|
+
const parsed = readRemoteAuthState(projectRoot);
|
|
190
|
+
return parsed ? cleanToken(typeof parsed.token === "string" ? parsed.token : undefined) : null;
|
|
191
|
+
}
|
|
192
|
+
function inferRemoteServerProjectRootFromAuthState(projectRoot) {
|
|
193
|
+
const repo = readRepoConnection(projectRoot);
|
|
194
|
+
const slug = repo?.project?.trim();
|
|
195
|
+
if (!slug || !/^[^/]+\/[^/]+$/.test(slug))
|
|
181
196
|
return null;
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
197
|
+
const auth = readRemoteAuthState(projectRoot);
|
|
198
|
+
const authUpdatedAt = typeof auth?.updatedAt === "string" ? Date.parse(auth.updatedAt) : Number.NaN;
|
|
199
|
+
const repoLinkedAt = typeof repo?.linkedAt === "string" ? Date.parse(repo.linkedAt) : Number.NaN;
|
|
200
|
+
if (Number.isFinite(authUpdatedAt) && Number.isFinite(repoLinkedAt) && authUpdatedAt + 1000 < repoLinkedAt)
|
|
186
201
|
return null;
|
|
187
|
-
|
|
202
|
+
const checkoutBaseDir = typeof auth?.checkoutBaseDir === "string" && auth.checkoutBaseDir.trim() ? auth.checkoutBaseDir.trim() : null;
|
|
203
|
+
if (!checkoutBaseDir)
|
|
204
|
+
return null;
|
|
205
|
+
const inferred = `${checkoutBaseDir.replace(/\/+$/, "")}/${slug}`;
|
|
206
|
+
writeRepoServerProjectRoot(projectRoot, inferred);
|
|
207
|
+
return inferred;
|
|
188
208
|
}
|
|
189
209
|
function readLocalConnectionFallbackToken(projectRoot) {
|
|
190
210
|
return readGitHubBearerTokenForRemote(projectRoot) ?? cleanToken(process.env.RIG_GITHUB_TOKEN) ?? readStoredGitHubAuthToken(projectRoot);
|
|
@@ -195,7 +215,7 @@ async function ensureServerForCli(projectRoot) {
|
|
|
195
215
|
if (selected?.connection.kind === "remote") {
|
|
196
216
|
reportServerPhase(`Connecting to ${selected.alias}\u2026`);
|
|
197
217
|
const authToken = readGitHubBearerTokenForRemote(projectRoot);
|
|
198
|
-
const serverProjectRoot = selected.serverProjectRoot ?? await backfillRemoteServerProjectRoot(projectRoot, selected.connection.baseUrl, authToken);
|
|
218
|
+
const serverProjectRoot = selected.serverProjectRoot ?? inferRemoteServerProjectRootFromAuthState(projectRoot) ?? await backfillRemoteServerProjectRoot(projectRoot, selected.connection.baseUrl, authToken);
|
|
199
219
|
return {
|
|
200
220
|
baseUrl: selected.connection.baseUrl,
|
|
201
221
|
authToken,
|
|
@@ -224,7 +244,10 @@ async function backfillRemoteServerProjectRoot(projectRoot, baseUrl, authToken)
|
|
|
224
244
|
if (!slug)
|
|
225
245
|
return null;
|
|
226
246
|
try {
|
|
227
|
-
const
|
|
247
|
+
const url = new URL(`${baseUrl}/api/projects/${encodeURIComponent(slug)}`);
|
|
248
|
+
if (authToken && queryAuthFallbackEnabled())
|
|
249
|
+
url.searchParams.set("rt", authToken);
|
|
250
|
+
const response = await fetch(url, {
|
|
228
251
|
headers: mergeHeaders(undefined, authToken)
|
|
229
252
|
});
|
|
230
253
|
if (!response.ok)
|
|
@@ -241,10 +264,23 @@ async function backfillRemoteServerProjectRoot(projectRoot, baseUrl, authToken)
|
|
|
241
264
|
return null;
|
|
242
265
|
}
|
|
243
266
|
}
|
|
267
|
+
function mergeCookie(existing, name, value) {
|
|
268
|
+
const encoded = `${name}=${encodeURIComponent(value)}`;
|
|
269
|
+
if (!existing?.trim())
|
|
270
|
+
return encoded;
|
|
271
|
+
const parts = existing.split(";").map((part) => part.trim()).filter((part) => part && !part.startsWith(`${name}=`));
|
|
272
|
+
return [...parts, encoded].join("; ");
|
|
273
|
+
}
|
|
274
|
+
function queryAuthFallbackEnabled(env = process.env) {
|
|
275
|
+
return env.RIG_ENABLE_QUERY_AUTH_FALLBACK === "1" || env.RIG_QUERY_AUTH_FALLBACK === "1";
|
|
276
|
+
}
|
|
244
277
|
function mergeHeaders(headers, authToken) {
|
|
245
278
|
const merged = new Headers(headers);
|
|
246
279
|
if (authToken) {
|
|
247
|
-
|
|
280
|
+
const bearer = `Bearer ${authToken}`;
|
|
281
|
+
merged.set("authorization", bearer);
|
|
282
|
+
merged.set("x-auth", bearer);
|
|
283
|
+
merged.set("cookie", mergeCookie(merged.get("cookie"), "rig_auth", authToken));
|
|
248
284
|
}
|
|
249
285
|
return merged;
|
|
250
286
|
}
|
|
@@ -305,15 +341,34 @@ async function buildServerFailureContext(projectRoot, server) {
|
|
|
305
341
|
hint: "Check the selected server with `rig server status`, or switch with `rig server use <alias|local>`."
|
|
306
342
|
};
|
|
307
343
|
}
|
|
344
|
+
function isLoopbackRemoteBaseUrl(baseUrl) {
|
|
345
|
+
try {
|
|
346
|
+
const host = new URL(baseUrl).hostname.toLowerCase();
|
|
347
|
+
return host === "localhost" || host === "127.0.0.1" || host === "::1" || host === "[::1]";
|
|
348
|
+
} catch {
|
|
349
|
+
return false;
|
|
350
|
+
}
|
|
351
|
+
}
|
|
352
|
+
function canUseRemoteWithoutProjectRoot(pathname) {
|
|
353
|
+
return pathname === "/health" || pathname === "/api/health" || pathname === "/api/server/status" || pathname === "/api/server/project-root" || pathname.startsWith("/api/github/auth/") || pathname === "/api/projects" || pathname.startsWith("/api/projects/");
|
|
354
|
+
}
|
|
308
355
|
async function requestServerJson(context, pathname, init = {}) {
|
|
309
356
|
const server = await ensureServerForCli(context.projectRoot);
|
|
357
|
+
const requestUrl = new URL(`${server.baseUrl}${pathname}`);
|
|
358
|
+
if (server.connectionKind === "remote" && !server.serverProjectRoot && !canUseRemoteWithoutProjectRoot(requestUrl.pathname) && (server.authToken || !isLoopbackRemoteBaseUrl(server.baseUrl))) {
|
|
359
|
+
const repo = readRepoConnection(context.projectRoot);
|
|
360
|
+
throw new CliError(`Remote server is selected for ${repo?.project ?? "this repo"}, but this checkout has no server-host project root link.`, 1, { hint: "Run `rig init --repair` or `rig init --yes --repo owner/repo --server remote` to repair the remote project link before task/run commands." });
|
|
361
|
+
}
|
|
310
362
|
const headers = mergeHeaders(init.headers, server.authToken);
|
|
311
363
|
if (server.serverProjectRoot)
|
|
312
364
|
headers.set("x-rig-project-root", server.serverProjectRoot);
|
|
365
|
+
if (server.connectionKind === "remote" && server.authToken && queryAuthFallbackEnabled()) {
|
|
366
|
+
requestUrl.searchParams.set("rt", server.authToken);
|
|
367
|
+
}
|
|
313
368
|
reportServerPhase(`${(init.method ?? "GET").toUpperCase()} ${pathname.split("?")[0]}\u2026`);
|
|
314
369
|
let response;
|
|
315
370
|
try {
|
|
316
|
-
response = await fetch(
|
|
371
|
+
response = await fetch(requestUrl, {
|
|
317
372
|
...init,
|
|
318
373
|
headers
|
|
319
374
|
});
|
|
@@ -346,11 +401,26 @@ async function getGitHubAuthStatusViaServer(context) {
|
|
|
346
401
|
return payload && typeof payload === "object" && !Array.isArray(payload) ? payload : {};
|
|
347
402
|
}
|
|
348
403
|
async function postGitHubTokenViaServer(context, token, options = {}) {
|
|
349
|
-
const
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
404
|
+
const server = await ensureServerForCli(context.projectRoot);
|
|
405
|
+
const requestUrl = new URL(`${server.baseUrl}/api/github/auth/token`);
|
|
406
|
+
reportServerPhase("POST /api/github/auth/token\u2026");
|
|
407
|
+
let response;
|
|
408
|
+
try {
|
|
409
|
+
response = await fetch(requestUrl, {
|
|
410
|
+
method: "POST",
|
|
411
|
+
headers: { "content-type": "application/json" },
|
|
412
|
+
body: JSON.stringify({ token, selectedRepo: options.selectedRepo, projectRoot: options.projectRoot ?? server.serverProjectRoot ?? undefined })
|
|
413
|
+
});
|
|
414
|
+
} catch (error) {
|
|
415
|
+
const failure = await buildServerFailureContext(context.projectRoot, server);
|
|
416
|
+
throw new CliError(`Rig server auth bootstrap failed: ${error instanceof Error ? error.message : String(error)}
|
|
417
|
+
${failure.contextLine}`, 1, { hint: failure.hint });
|
|
418
|
+
}
|
|
419
|
+
const payload = await response.json().catch(() => null);
|
|
420
|
+
if (!response.ok) {
|
|
421
|
+
const detail = payload && typeof payload === "object" && !Array.isArray(payload) ? String(payload.error ?? JSON.stringify(payload)) : `HTTP ${response.status}`;
|
|
422
|
+
throw new CliError(`Rig server auth bootstrap failed (${response.status}): ${detail}`, 1, { hint: "Re-run `rig github auth import-gh`, or check the selected server with `rig server status`." });
|
|
423
|
+
}
|
|
354
424
|
return payload && typeof payload === "object" && !Array.isArray(payload) ? payload : {};
|
|
355
425
|
}
|
|
356
426
|
var RESUMABLE_RUN_STATUSES = new Set([
|
|
@@ -564,6 +634,53 @@ function printPayload(context, payload, fallback) {
|
|
|
564
634
|
else
|
|
565
635
|
console.log(fallback);
|
|
566
636
|
}
|
|
637
|
+
function apiSessionTokenFrom(payload) {
|
|
638
|
+
if (!payload || typeof payload !== "object" || Array.isArray(payload))
|
|
639
|
+
return null;
|
|
640
|
+
const token = payload.apiSessionToken;
|
|
641
|
+
return typeof token === "string" && token.trim() ? token.trim() : null;
|
|
642
|
+
}
|
|
643
|
+
function payloadString(payload, key) {
|
|
644
|
+
const value = payload[key];
|
|
645
|
+
return typeof value === "string" && value.trim() ? value.trim() : null;
|
|
646
|
+
}
|
|
647
|
+
function payloadRecord(payload, key) {
|
|
648
|
+
const value = payload[key];
|
|
649
|
+
return value && typeof value === "object" && !Array.isArray(value) ? value : null;
|
|
650
|
+
}
|
|
651
|
+
function remoteNamespaceMetadata(result) {
|
|
652
|
+
const namespace = payloadRecord(result, "userNamespace");
|
|
653
|
+
return {
|
|
654
|
+
...payloadString(result, "login") ? { login: payloadString(result, "login") } : {},
|
|
655
|
+
...payloadString(result, "userId") ? { userId: payloadString(result, "userId") } : {},
|
|
656
|
+
...namespace && payloadString(namespace, "key") ? { userNamespaceKey: payloadString(namespace, "key") } : {},
|
|
657
|
+
...namespace && payloadString(namespace, "root") ? { userNamespaceRoot: payloadString(namespace, "root") } : {},
|
|
658
|
+
...namespace && payloadString(namespace, "checkoutBaseDir") ? { checkoutBaseDir: payloadString(namespace, "checkoutBaseDir") } : {},
|
|
659
|
+
...namespace && payloadString(namespace, "snapshotBaseDir") ? { snapshotBaseDir: payloadString(namespace, "snapshotBaseDir") } : {}
|
|
660
|
+
};
|
|
661
|
+
}
|
|
662
|
+
function persistRemoteAuthSession(context, source, result, fallbackToken) {
|
|
663
|
+
const apiSessionToken = apiSessionTokenFrom(result);
|
|
664
|
+
setGitHubBearerTokenForCurrentProcess(apiSessionToken ?? fallbackToken, context.projectRoot);
|
|
665
|
+
if (!apiSessionToken)
|
|
666
|
+
return;
|
|
667
|
+
const repo = readRepoConnection(context.projectRoot);
|
|
668
|
+
const path = resolve3(context.projectRoot, ".rig", "state", "github-auth.json");
|
|
669
|
+
mkdirSync2(dirname2(path), { recursive: true });
|
|
670
|
+
writeFileSync2(path, `${JSON.stringify({
|
|
671
|
+
authenticated: true,
|
|
672
|
+
source,
|
|
673
|
+
storedOnServer: true,
|
|
674
|
+
...repo?.project ? { selectedRepo: repo.project } : {},
|
|
675
|
+
...remoteNamespaceMetadata(result),
|
|
676
|
+
apiSessionToken,
|
|
677
|
+
updatedAt: new Date().toISOString()
|
|
678
|
+
}, null, 2)}
|
|
679
|
+
`, "utf8");
|
|
680
|
+
}
|
|
681
|
+
function isSignedIn(status) {
|
|
682
|
+
return status.signedIn === true || status.authenticated === true;
|
|
683
|
+
}
|
|
567
684
|
function readGhToken() {
|
|
568
685
|
const result = spawnSync("gh", ["auth", "token"], { encoding: "utf8" });
|
|
569
686
|
if (result.status !== 0) {
|
|
@@ -585,7 +702,7 @@ async function executeGithub(context, args) {
|
|
|
585
702
|
if (rest.length > 0)
|
|
586
703
|
throw new CliError("Usage: rig github auth status", 1);
|
|
587
704
|
const status = await withSpinner("Checking GitHub auth on the server\u2026", () => getGitHubAuthStatusViaServer(context), { outputMode: context.outputMode });
|
|
588
|
-
printPayload(context, status, `GitHub auth: ${status
|
|
705
|
+
printPayload(context, status, `GitHub auth: ${isSignedIn(status) ? "authenticated" : "unauthenticated"}`);
|
|
589
706
|
return { ok: true, group: "github", command: "auth status", details: status };
|
|
590
707
|
}
|
|
591
708
|
case "token": {
|
|
@@ -595,16 +712,20 @@ async function executeGithub(context, args) {
|
|
|
595
712
|
const token = parsed.value?.trim();
|
|
596
713
|
if (!token)
|
|
597
714
|
throw new CliError("Missing --token value.", 1, { hint: "Re-run as `rig github auth token --token <token>`." });
|
|
598
|
-
const
|
|
599
|
-
|
|
715
|
+
const repo = readRepoConnection(context.projectRoot);
|
|
716
|
+
const result = await withSpinner("Storing GitHub token on the server\u2026", () => postGitHubTokenViaServer(context, token, repo?.project ? { selectedRepo: repo.project } : {}), { outputMode: context.outputMode });
|
|
717
|
+
persistRemoteAuthSession(context, "token", result, token);
|
|
718
|
+
printPayload(context, result, "GitHub token stored on the selected server.");
|
|
600
719
|
return { ok: true, group: "github", command: "auth token", details: result };
|
|
601
720
|
}
|
|
602
721
|
case "import-gh": {
|
|
603
722
|
if (rest.length > 0)
|
|
604
723
|
throw new CliError("Usage: rig github auth import-gh", 1);
|
|
605
724
|
const importedToken = readGhToken();
|
|
606
|
-
const
|
|
607
|
-
|
|
725
|
+
const repo = readRepoConnection(context.projectRoot);
|
|
726
|
+
const result = await withSpinner("Storing GitHub token on the server\u2026", () => postGitHubTokenViaServer(context, importedToken, repo?.project ? { selectedRepo: repo.project } : {}), { outputMode: context.outputMode });
|
|
727
|
+
persistRemoteAuthSession(context, "gh", result, importedToken);
|
|
728
|
+
printPayload(context, result, "GitHub token imported from gh and stored on the selected server.");
|
|
608
729
|
return { ok: true, group: "github", command: "auth import-gh", details: result };
|
|
609
730
|
}
|
|
610
731
|
default:
|
|
@@ -384,17 +384,21 @@ function cleanToken(value) {
|
|
|
384
384
|
const trimmed = value?.trim();
|
|
385
385
|
return trimmed ? trimmed : null;
|
|
386
386
|
}
|
|
387
|
-
function
|
|
387
|
+
function readRemoteAuthState(projectRoot) {
|
|
388
388
|
const path = resolve2(projectRoot, ".rig", "state", "github-auth.json");
|
|
389
389
|
if (!existsSync2(path))
|
|
390
390
|
return null;
|
|
391
391
|
try {
|
|
392
392
|
const parsed = JSON.parse(readFileSync2(path, "utf8"));
|
|
393
|
-
return
|
|
393
|
+
return parsed && typeof parsed === "object" && !Array.isArray(parsed) ? parsed : null;
|
|
394
394
|
} catch {
|
|
395
395
|
return null;
|
|
396
396
|
}
|
|
397
397
|
}
|
|
398
|
+
function readPrivateRemoteSessionToken(projectRoot) {
|
|
399
|
+
const parsed = readRemoteAuthState(projectRoot);
|
|
400
|
+
return parsed ? cleanToken(typeof parsed.apiSessionToken === "string" ? parsed.apiSessionToken : typeof parsed.sessionToken === "string" ? parsed.sessionToken : undefined) : null;
|
|
401
|
+
}
|
|
398
402
|
function readGitHubBearerTokenForRemote(projectRoot) {
|
|
399
403
|
const scopedKey = resolve2(projectRoot);
|
|
400
404
|
if (scopedGitHubBearerTokens.has(scopedKey))
|
|
@@ -405,15 +409,25 @@ function readGitHubBearerTokenForRemote(projectRoot) {
|
|
|
405
409
|
return cleanToken(process.env.RIG_SERVER_AUTH_TOKEN) ?? cleanToken(process.env.RIG_REMOTE_AUTH_TOKEN);
|
|
406
410
|
}
|
|
407
411
|
function readStoredGitHubAuthToken(projectRoot) {
|
|
408
|
-
const
|
|
409
|
-
|
|
412
|
+
const parsed = readRemoteAuthState(projectRoot);
|
|
413
|
+
return parsed ? cleanToken(typeof parsed.token === "string" ? parsed.token : undefined) : null;
|
|
414
|
+
}
|
|
415
|
+
function inferRemoteServerProjectRootFromAuthState(projectRoot) {
|
|
416
|
+
const repo = readRepoConnection(projectRoot);
|
|
417
|
+
const slug = repo?.project?.trim();
|
|
418
|
+
if (!slug || !/^[^/]+\/[^/]+$/.test(slug))
|
|
410
419
|
return null;
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
420
|
+
const auth = readRemoteAuthState(projectRoot);
|
|
421
|
+
const authUpdatedAt = typeof auth?.updatedAt === "string" ? Date.parse(auth.updatedAt) : Number.NaN;
|
|
422
|
+
const repoLinkedAt = typeof repo?.linkedAt === "string" ? Date.parse(repo.linkedAt) : Number.NaN;
|
|
423
|
+
if (Number.isFinite(authUpdatedAt) && Number.isFinite(repoLinkedAt) && authUpdatedAt + 1000 < repoLinkedAt)
|
|
415
424
|
return null;
|
|
416
|
-
|
|
425
|
+
const checkoutBaseDir = typeof auth?.checkoutBaseDir === "string" && auth.checkoutBaseDir.trim() ? auth.checkoutBaseDir.trim() : null;
|
|
426
|
+
if (!checkoutBaseDir)
|
|
427
|
+
return null;
|
|
428
|
+
const inferred = `${checkoutBaseDir.replace(/\/+$/, "")}/${slug}`;
|
|
429
|
+
writeRepoServerProjectRoot(projectRoot, inferred);
|
|
430
|
+
return inferred;
|
|
417
431
|
}
|
|
418
432
|
function readLocalConnectionFallbackToken(projectRoot) {
|
|
419
433
|
return readGitHubBearerTokenForRemote(projectRoot) ?? cleanToken(process.env.RIG_GITHUB_TOKEN) ?? readStoredGitHubAuthToken(projectRoot);
|
|
@@ -424,7 +438,7 @@ async function ensureServerForCli(projectRoot) {
|
|
|
424
438
|
if (selected?.connection.kind === "remote") {
|
|
425
439
|
reportServerPhase(`Connecting to ${selected.alias}\u2026`);
|
|
426
440
|
const authToken = readGitHubBearerTokenForRemote(projectRoot);
|
|
427
|
-
const serverProjectRoot = selected.serverProjectRoot ?? await backfillRemoteServerProjectRoot(projectRoot, selected.connection.baseUrl, authToken);
|
|
441
|
+
const serverProjectRoot = selected.serverProjectRoot ?? inferRemoteServerProjectRootFromAuthState(projectRoot) ?? await backfillRemoteServerProjectRoot(projectRoot, selected.connection.baseUrl, authToken);
|
|
428
442
|
return {
|
|
429
443
|
baseUrl: selected.connection.baseUrl,
|
|
430
444
|
authToken,
|
|
@@ -453,7 +467,10 @@ async function backfillRemoteServerProjectRoot(projectRoot, baseUrl, authToken)
|
|
|
453
467
|
if (!slug)
|
|
454
468
|
return null;
|
|
455
469
|
try {
|
|
456
|
-
const
|
|
470
|
+
const url = new URL(`${baseUrl}/api/projects/${encodeURIComponent(slug)}`);
|
|
471
|
+
if (authToken && queryAuthFallbackEnabled())
|
|
472
|
+
url.searchParams.set("rt", authToken);
|
|
473
|
+
const response = await fetch(url, {
|
|
457
474
|
headers: mergeHeaders(undefined, authToken)
|
|
458
475
|
});
|
|
459
476
|
if (!response.ok)
|
|
@@ -470,10 +487,23 @@ async function backfillRemoteServerProjectRoot(projectRoot, baseUrl, authToken)
|
|
|
470
487
|
return null;
|
|
471
488
|
}
|
|
472
489
|
}
|
|
490
|
+
function mergeCookie(existing, name, value) {
|
|
491
|
+
const encoded = `${name}=${encodeURIComponent(value)}`;
|
|
492
|
+
if (!existing?.trim())
|
|
493
|
+
return encoded;
|
|
494
|
+
const parts = existing.split(";").map((part) => part.trim()).filter((part) => part && !part.startsWith(`${name}=`));
|
|
495
|
+
return [...parts, encoded].join("; ");
|
|
496
|
+
}
|
|
497
|
+
function queryAuthFallbackEnabled(env = process.env) {
|
|
498
|
+
return env.RIG_ENABLE_QUERY_AUTH_FALLBACK === "1" || env.RIG_QUERY_AUTH_FALLBACK === "1";
|
|
499
|
+
}
|
|
473
500
|
function mergeHeaders(headers, authToken) {
|
|
474
501
|
const merged = new Headers(headers);
|
|
475
502
|
if (authToken) {
|
|
476
|
-
|
|
503
|
+
const bearer = `Bearer ${authToken}`;
|
|
504
|
+
merged.set("authorization", bearer);
|
|
505
|
+
merged.set("x-auth", bearer);
|
|
506
|
+
merged.set("cookie", mergeCookie(merged.get("cookie"), "rig_auth", authToken));
|
|
477
507
|
}
|
|
478
508
|
return merged;
|
|
479
509
|
}
|
|
@@ -534,15 +564,34 @@ async function buildServerFailureContext(projectRoot, server) {
|
|
|
534
564
|
hint: "Check the selected server with `rig server status`, or switch with `rig server use <alias|local>`."
|
|
535
565
|
};
|
|
536
566
|
}
|
|
567
|
+
function isLoopbackRemoteBaseUrl(baseUrl) {
|
|
568
|
+
try {
|
|
569
|
+
const host = new URL(baseUrl).hostname.toLowerCase();
|
|
570
|
+
return host === "localhost" || host === "127.0.0.1" || host === "::1" || host === "[::1]";
|
|
571
|
+
} catch {
|
|
572
|
+
return false;
|
|
573
|
+
}
|
|
574
|
+
}
|
|
575
|
+
function canUseRemoteWithoutProjectRoot(pathname) {
|
|
576
|
+
return pathname === "/health" || pathname === "/api/health" || pathname === "/api/server/status" || pathname === "/api/server/project-root" || pathname.startsWith("/api/github/auth/") || pathname === "/api/projects" || pathname.startsWith("/api/projects/");
|
|
577
|
+
}
|
|
537
578
|
async function requestServerJson(context, pathname, init = {}) {
|
|
538
579
|
const server = await ensureServerForCli(context.projectRoot);
|
|
580
|
+
const requestUrl = new URL(`${server.baseUrl}${pathname}`);
|
|
581
|
+
if (server.connectionKind === "remote" && !server.serverProjectRoot && !canUseRemoteWithoutProjectRoot(requestUrl.pathname) && (server.authToken || !isLoopbackRemoteBaseUrl(server.baseUrl))) {
|
|
582
|
+
const repo = readRepoConnection(context.projectRoot);
|
|
583
|
+
throw new CliError(`Remote server is selected for ${repo?.project ?? "this repo"}, but this checkout has no server-host project root link.`, 1, { hint: "Run `rig init --repair` or `rig init --yes --repo owner/repo --server remote` to repair the remote project link before task/run commands." });
|
|
584
|
+
}
|
|
539
585
|
const headers = mergeHeaders(init.headers, server.authToken);
|
|
540
586
|
if (server.serverProjectRoot)
|
|
541
587
|
headers.set("x-rig-project-root", server.serverProjectRoot);
|
|
588
|
+
if (server.connectionKind === "remote" && server.authToken && queryAuthFallbackEnabled()) {
|
|
589
|
+
requestUrl.searchParams.set("rt", server.authToken);
|
|
590
|
+
}
|
|
542
591
|
reportServerPhase(`${(init.method ?? "GET").toUpperCase()} ${pathname.split("?")[0]}\u2026`);
|
|
543
592
|
let response;
|
|
544
593
|
try {
|
|
545
|
-
response = await fetch(
|
|
594
|
+
response = await fetch(requestUrl, {
|
|
546
595
|
...init,
|
|
547
596
|
headers
|
|
548
597
|
});
|