@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
|
@@ -138,17 +138,21 @@ function setGitHubBearerTokenForCurrentProcess(token, projectRoot) {
|
|
|
138
138
|
const scopedKey = resolve2(projectRoot ?? process.cwd());
|
|
139
139
|
scopedGitHubBearerTokens.set(scopedKey, cleanToken(token ?? undefined));
|
|
140
140
|
}
|
|
141
|
-
function
|
|
141
|
+
function readRemoteAuthState(projectRoot) {
|
|
142
142
|
const path = resolve2(projectRoot, ".rig", "state", "github-auth.json");
|
|
143
143
|
if (!existsSync2(path))
|
|
144
144
|
return null;
|
|
145
145
|
try {
|
|
146
146
|
const parsed = JSON.parse(readFileSync2(path, "utf8"));
|
|
147
|
-
return
|
|
147
|
+
return parsed && typeof parsed === "object" && !Array.isArray(parsed) ? parsed : null;
|
|
148
148
|
} catch {
|
|
149
149
|
return null;
|
|
150
150
|
}
|
|
151
151
|
}
|
|
152
|
+
function readPrivateRemoteSessionToken(projectRoot) {
|
|
153
|
+
const parsed = readRemoteAuthState(projectRoot);
|
|
154
|
+
return parsed ? cleanToken(typeof parsed.apiSessionToken === "string" ? parsed.apiSessionToken : typeof parsed.sessionToken === "string" ? parsed.sessionToken : undefined) : null;
|
|
155
|
+
}
|
|
152
156
|
function readGitHubBearerTokenForRemote(projectRoot) {
|
|
153
157
|
const scopedKey = resolve2(projectRoot);
|
|
154
158
|
if (scopedGitHubBearerTokens.has(scopedKey))
|
|
@@ -159,15 +163,25 @@ function readGitHubBearerTokenForRemote(projectRoot) {
|
|
|
159
163
|
return cleanToken(process.env.RIG_SERVER_AUTH_TOKEN) ?? cleanToken(process.env.RIG_REMOTE_AUTH_TOKEN);
|
|
160
164
|
}
|
|
161
165
|
function readStoredGitHubAuthToken(projectRoot) {
|
|
162
|
-
const
|
|
163
|
-
|
|
166
|
+
const parsed = readRemoteAuthState(projectRoot);
|
|
167
|
+
return parsed ? cleanToken(typeof parsed.token === "string" ? parsed.token : undefined) : null;
|
|
168
|
+
}
|
|
169
|
+
function inferRemoteServerProjectRootFromAuthState(projectRoot) {
|
|
170
|
+
const repo = readRepoConnection(projectRoot);
|
|
171
|
+
const slug = repo?.project?.trim();
|
|
172
|
+
if (!slug || !/^[^/]+\/[^/]+$/.test(slug))
|
|
164
173
|
return null;
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
174
|
+
const auth = readRemoteAuthState(projectRoot);
|
|
175
|
+
const authUpdatedAt = typeof auth?.updatedAt === "string" ? Date.parse(auth.updatedAt) : Number.NaN;
|
|
176
|
+
const repoLinkedAt = typeof repo?.linkedAt === "string" ? Date.parse(repo.linkedAt) : Number.NaN;
|
|
177
|
+
if (Number.isFinite(authUpdatedAt) && Number.isFinite(repoLinkedAt) && authUpdatedAt + 1000 < repoLinkedAt)
|
|
169
178
|
return null;
|
|
170
|
-
|
|
179
|
+
const checkoutBaseDir = typeof auth?.checkoutBaseDir === "string" && auth.checkoutBaseDir.trim() ? auth.checkoutBaseDir.trim() : null;
|
|
180
|
+
if (!checkoutBaseDir)
|
|
181
|
+
return null;
|
|
182
|
+
const inferred = `${checkoutBaseDir.replace(/\/+$/, "")}/${slug}`;
|
|
183
|
+
writeRepoServerProjectRoot(projectRoot, inferred);
|
|
184
|
+
return inferred;
|
|
171
185
|
}
|
|
172
186
|
function readLocalConnectionFallbackToken(projectRoot) {
|
|
173
187
|
return readGitHubBearerTokenForRemote(projectRoot) ?? cleanToken(process.env.RIG_GITHUB_TOKEN) ?? readStoredGitHubAuthToken(projectRoot);
|
|
@@ -178,7 +192,7 @@ async function ensureServerForCli(projectRoot) {
|
|
|
178
192
|
if (selected?.connection.kind === "remote") {
|
|
179
193
|
reportServerPhase(`Connecting to ${selected.alias}\u2026`);
|
|
180
194
|
const authToken = readGitHubBearerTokenForRemote(projectRoot);
|
|
181
|
-
const serverProjectRoot = selected.serverProjectRoot ?? await backfillRemoteServerProjectRoot(projectRoot, selected.connection.baseUrl, authToken);
|
|
195
|
+
const serverProjectRoot = selected.serverProjectRoot ?? inferRemoteServerProjectRootFromAuthState(projectRoot) ?? await backfillRemoteServerProjectRoot(projectRoot, selected.connection.baseUrl, authToken);
|
|
182
196
|
return {
|
|
183
197
|
baseUrl: selected.connection.baseUrl,
|
|
184
198
|
authToken,
|
|
@@ -207,7 +221,10 @@ async function backfillRemoteServerProjectRoot(projectRoot, baseUrl, authToken)
|
|
|
207
221
|
if (!slug)
|
|
208
222
|
return null;
|
|
209
223
|
try {
|
|
210
|
-
const
|
|
224
|
+
const url = new URL(`${baseUrl}/api/projects/${encodeURIComponent(slug)}`);
|
|
225
|
+
if (authToken && queryAuthFallbackEnabled())
|
|
226
|
+
url.searchParams.set("rt", authToken);
|
|
227
|
+
const response = await fetch(url, {
|
|
211
228
|
headers: mergeHeaders(undefined, authToken)
|
|
212
229
|
});
|
|
213
230
|
if (!response.ok)
|
|
@@ -234,10 +251,23 @@ function appendTaskFilterParams(url, filters) {
|
|
|
234
251
|
if (filters.limit !== undefined)
|
|
235
252
|
url.searchParams.set("limit", String(filters.limit));
|
|
236
253
|
}
|
|
254
|
+
function mergeCookie(existing, name, value) {
|
|
255
|
+
const encoded = `${name}=${encodeURIComponent(value)}`;
|
|
256
|
+
if (!existing?.trim())
|
|
257
|
+
return encoded;
|
|
258
|
+
const parts = existing.split(";").map((part) => part.trim()).filter((part) => part && !part.startsWith(`${name}=`));
|
|
259
|
+
return [...parts, encoded].join("; ");
|
|
260
|
+
}
|
|
261
|
+
function queryAuthFallbackEnabled(env = process.env) {
|
|
262
|
+
return env.RIG_ENABLE_QUERY_AUTH_FALLBACK === "1" || env.RIG_QUERY_AUTH_FALLBACK === "1";
|
|
263
|
+
}
|
|
237
264
|
function mergeHeaders(headers, authToken) {
|
|
238
265
|
const merged = new Headers(headers);
|
|
239
266
|
if (authToken) {
|
|
240
|
-
|
|
267
|
+
const bearer = `Bearer ${authToken}`;
|
|
268
|
+
merged.set("authorization", bearer);
|
|
269
|
+
merged.set("x-auth", bearer);
|
|
270
|
+
merged.set("cookie", mergeCookie(merged.get("cookie"), "rig_auth", authToken));
|
|
241
271
|
}
|
|
242
272
|
return merged;
|
|
243
273
|
}
|
|
@@ -298,15 +328,34 @@ async function buildServerFailureContext(projectRoot, server) {
|
|
|
298
328
|
hint: "Check the selected server with `rig server status`, or switch with `rig server use <alias|local>`."
|
|
299
329
|
};
|
|
300
330
|
}
|
|
331
|
+
function isLoopbackRemoteBaseUrl(baseUrl) {
|
|
332
|
+
try {
|
|
333
|
+
const host = new URL(baseUrl).hostname.toLowerCase();
|
|
334
|
+
return host === "localhost" || host === "127.0.0.1" || host === "::1" || host === "[::1]";
|
|
335
|
+
} catch {
|
|
336
|
+
return false;
|
|
337
|
+
}
|
|
338
|
+
}
|
|
339
|
+
function canUseRemoteWithoutProjectRoot(pathname) {
|
|
340
|
+
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/");
|
|
341
|
+
}
|
|
301
342
|
async function requestServerJson(context, pathname, init = {}) {
|
|
302
343
|
const server = await ensureServerForCli(context.projectRoot);
|
|
344
|
+
const requestUrl = new URL(`${server.baseUrl}${pathname}`);
|
|
345
|
+
if (server.connectionKind === "remote" && !server.serverProjectRoot && !canUseRemoteWithoutProjectRoot(requestUrl.pathname) && (server.authToken || !isLoopbackRemoteBaseUrl(server.baseUrl))) {
|
|
346
|
+
const repo = readRepoConnection(context.projectRoot);
|
|
347
|
+
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." });
|
|
348
|
+
}
|
|
303
349
|
const headers = mergeHeaders(init.headers, server.authToken);
|
|
304
350
|
if (server.serverProjectRoot)
|
|
305
351
|
headers.set("x-rig-project-root", server.serverProjectRoot);
|
|
352
|
+
if (server.connectionKind === "remote" && server.authToken && queryAuthFallbackEnabled()) {
|
|
353
|
+
requestUrl.searchParams.set("rt", server.authToken);
|
|
354
|
+
}
|
|
306
355
|
reportServerPhase(`${(init.method ?? "GET").toUpperCase()} ${pathname.split("?")[0]}\u2026`);
|
|
307
356
|
let response;
|
|
308
357
|
try {
|
|
309
|
-
response = await fetch(
|
|
358
|
+
response = await fetch(requestUrl, {
|
|
310
359
|
...init,
|
|
311
360
|
headers
|
|
312
361
|
});
|
|
@@ -368,11 +417,26 @@ async function getGitHubAuthStatusViaServer(context) {
|
|
|
368
417
|
return payload && typeof payload === "object" && !Array.isArray(payload) ? payload : {};
|
|
369
418
|
}
|
|
370
419
|
async function postGitHubTokenViaServer(context, token, options = {}) {
|
|
371
|
-
const
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
420
|
+
const server = await ensureServerForCli(context.projectRoot);
|
|
421
|
+
const requestUrl = new URL(`${server.baseUrl}/api/github/auth/token`);
|
|
422
|
+
reportServerPhase("POST /api/github/auth/token\u2026");
|
|
423
|
+
let response;
|
|
424
|
+
try {
|
|
425
|
+
response = await fetch(requestUrl, {
|
|
426
|
+
method: "POST",
|
|
427
|
+
headers: { "content-type": "application/json" },
|
|
428
|
+
body: JSON.stringify({ token, selectedRepo: options.selectedRepo, projectRoot: options.projectRoot ?? server.serverProjectRoot ?? undefined })
|
|
429
|
+
});
|
|
430
|
+
} catch (error) {
|
|
431
|
+
const failure = await buildServerFailureContext(context.projectRoot, server);
|
|
432
|
+
throw new CliError(`Rig server auth bootstrap failed: ${error instanceof Error ? error.message : String(error)}
|
|
433
|
+
${failure.contextLine}`, 1, { hint: failure.hint });
|
|
434
|
+
}
|
|
435
|
+
const payload = await response.json().catch(() => null);
|
|
436
|
+
if (!response.ok) {
|
|
437
|
+
const detail = payload && typeof payload === "object" && !Array.isArray(payload) ? String(payload.error ?? JSON.stringify(payload)) : `HTTP ${response.status}`;
|
|
438
|
+
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`." });
|
|
439
|
+
}
|
|
376
440
|
return payload && typeof payload === "object" && !Array.isArray(payload) ? payload : {};
|
|
377
441
|
}
|
|
378
442
|
async function prepareRemoteCheckoutViaServer(context, input) {
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
export type RigLiveStatus = "connecting" | "connected" | "disconnected";
|
|
2
|
+
export type RigLiveHandlers = {
|
|
3
|
+
/** Any snapshot-affecting change (run/task/inbox state). The canonical
|
|
4
|
+
* "something changed, re-pull what you show" signal. */
|
|
5
|
+
readonly onSnapshotInvalidated?: (payload: unknown) => void;
|
|
6
|
+
/** A run appended a log/timeline line — for live run-detail tails. */
|
|
7
|
+
readonly onRunLogAppended?: (payload: unknown) => void;
|
|
8
|
+
/** Sequence-numbered engine domain events. */
|
|
9
|
+
readonly onEngineEvent?: (payload: unknown) => void;
|
|
10
|
+
/** Socket health transitions; callers gate their polling fallback on this. */
|
|
11
|
+
readonly onStatus?: (status: RigLiveStatus) => void;
|
|
12
|
+
};
|
|
13
|
+
export type RigLiveSubscription = {
|
|
14
|
+
/** True while the push socket is connected — gate HTTP fallback polling on it. */
|
|
15
|
+
readonly connected: () => boolean;
|
|
16
|
+
readonly close: () => void;
|
|
17
|
+
};
|
|
18
|
+
/** http(s):// → ws(s)://, with the auth token as the `token` query param the
|
|
19
|
+
* server's upgrade handler authenticates against (packages/server/src/
|
|
20
|
+
* websocket.ts). Mirrors pi-rig's buildRigWebSocketUrl so every surface speaks
|
|
21
|
+
* the same dialect. */
|
|
22
|
+
export declare function buildRigWebSocketUrl(baseUrl: string, authToken: string | null): string;
|
|
23
|
+
/** Open a reconnecting push subscription to the Rig server for a project root.
|
|
24
|
+
* Resolves the same connection (local/remote + auth) the HTTP `…ViaServer`
|
|
25
|
+
* calls use, so the socket targets the exact server the surface reads from. */
|
|
26
|
+
export declare function connectRigServerEvents(projectRoot: string, handlers: RigLiveHandlers): Promise<RigLiveSubscription>;
|
|
@@ -0,0 +1,310 @@
|
|
|
1
|
+
// @bun
|
|
2
|
+
// packages/cli/src/commands/_server-events.ts
|
|
3
|
+
import { WsTransport } from "@rig/client";
|
|
4
|
+
import { RIG_WS_CHANNELS } from "@rig/contracts";
|
|
5
|
+
|
|
6
|
+
// packages/cli/src/commands/_server-client.ts
|
|
7
|
+
import { existsSync as existsSync2, readFileSync as readFileSync2 } from "fs";
|
|
8
|
+
import { resolve as resolve2 } from "path";
|
|
9
|
+
|
|
10
|
+
// packages/cli/src/runner.ts
|
|
11
|
+
import { EventBus } from "@rig/runtime/control-plane/runtime/events";
|
|
12
|
+
import { CliError as RuntimeCliError } from "@rig/runtime/control-plane/errors";
|
|
13
|
+
import { evaluate, loadPolicy, resolveAction } from "@rig/runtime/control-plane/runtime/guard";
|
|
14
|
+
import { buildBinary } from "@rig/runtime/control-plane/runtime/isolation";
|
|
15
|
+
|
|
16
|
+
class CliError extends RuntimeCliError {
|
|
17
|
+
hint;
|
|
18
|
+
constructor(message, exitCode = 1, options = {}) {
|
|
19
|
+
super(message, exitCode);
|
|
20
|
+
if (options.hint?.trim()) {
|
|
21
|
+
this.hint = options.hint.trim();
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
// packages/cli/src/commands/_server-client.ts
|
|
27
|
+
import { ensureLocalRigServerConnection } from "@rig/runtime/local-server";
|
|
28
|
+
|
|
29
|
+
// packages/cli/src/commands/_connection-state.ts
|
|
30
|
+
import { existsSync, mkdirSync, readFileSync, writeFileSync } from "fs";
|
|
31
|
+
import { homedir } from "os";
|
|
32
|
+
import { dirname, resolve } from "path";
|
|
33
|
+
function resolveGlobalConnectionsPath(env = process.env) {
|
|
34
|
+
const explicit = env.RIG_CONNECTIONS_FILE?.trim();
|
|
35
|
+
if (explicit)
|
|
36
|
+
return resolve(explicit);
|
|
37
|
+
const stateDir = env.RIG_GLOBAL_STATE_DIR?.trim();
|
|
38
|
+
if (stateDir)
|
|
39
|
+
return resolve(stateDir, "connections.json");
|
|
40
|
+
return resolve(homedir(), ".rig", "connections.json");
|
|
41
|
+
}
|
|
42
|
+
function resolveRepoConnectionPath(projectRoot) {
|
|
43
|
+
return resolve(projectRoot, ".rig", "state", "connection.json");
|
|
44
|
+
}
|
|
45
|
+
function readJsonFile(path) {
|
|
46
|
+
if (!existsSync(path))
|
|
47
|
+
return null;
|
|
48
|
+
try {
|
|
49
|
+
return JSON.parse(readFileSync(path, "utf8"));
|
|
50
|
+
} catch (error) {
|
|
51
|
+
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>`." });
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
function writeJsonFile(path, value) {
|
|
55
|
+
mkdirSync(dirname(path), { recursive: true });
|
|
56
|
+
writeFileSync(path, `${JSON.stringify(value, null, 2)}
|
|
57
|
+
`, "utf8");
|
|
58
|
+
}
|
|
59
|
+
function normalizeConnection(value) {
|
|
60
|
+
if (!value || typeof value !== "object" || Array.isArray(value))
|
|
61
|
+
return null;
|
|
62
|
+
const record = value;
|
|
63
|
+
if (record.kind === "local")
|
|
64
|
+
return { kind: "local", mode: "auto" };
|
|
65
|
+
if (record.kind === "remote" && typeof record.baseUrl === "string" && record.baseUrl.trim()) {
|
|
66
|
+
const baseUrl = record.baseUrl.trim().replace(/\/+$/, "");
|
|
67
|
+
return { kind: "remote", baseUrl };
|
|
68
|
+
}
|
|
69
|
+
return null;
|
|
70
|
+
}
|
|
71
|
+
function readGlobalConnections(options = {}) {
|
|
72
|
+
const path = resolveGlobalConnectionsPath(options.env ?? process.env);
|
|
73
|
+
const payload = readJsonFile(path);
|
|
74
|
+
if (!payload || typeof payload !== "object" || Array.isArray(payload)) {
|
|
75
|
+
return { connections: {} };
|
|
76
|
+
}
|
|
77
|
+
const rawConnections = payload.connections;
|
|
78
|
+
const connections = {};
|
|
79
|
+
if (rawConnections && typeof rawConnections === "object" && !Array.isArray(rawConnections)) {
|
|
80
|
+
for (const [alias, raw] of Object.entries(rawConnections)) {
|
|
81
|
+
const connection = normalizeConnection(raw);
|
|
82
|
+
if (connection)
|
|
83
|
+
connections[alias] = connection;
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
return { connections };
|
|
87
|
+
}
|
|
88
|
+
function readRepoConnection(projectRoot) {
|
|
89
|
+
const payload = readJsonFile(resolveRepoConnectionPath(projectRoot));
|
|
90
|
+
if (!payload || typeof payload !== "object" || Array.isArray(payload))
|
|
91
|
+
return null;
|
|
92
|
+
const record = payload;
|
|
93
|
+
const selected = typeof record.selected === "string" ? record.selected.trim() : "";
|
|
94
|
+
if (!selected)
|
|
95
|
+
return null;
|
|
96
|
+
return {
|
|
97
|
+
selected,
|
|
98
|
+
project: typeof record.project === "string" ? record.project : undefined,
|
|
99
|
+
linkedAt: typeof record.linkedAt === "string" ? record.linkedAt : undefined,
|
|
100
|
+
serverProjectRoot: typeof record.serverProjectRoot === "string" && record.serverProjectRoot.trim() ? record.serverProjectRoot.trim() : undefined
|
|
101
|
+
};
|
|
102
|
+
}
|
|
103
|
+
function writeRepoConnection(projectRoot, state) {
|
|
104
|
+
writeJsonFile(resolveRepoConnectionPath(projectRoot), state);
|
|
105
|
+
}
|
|
106
|
+
function resolveSelectedConnection(projectRoot, options = {}) {
|
|
107
|
+
const repo = readRepoConnection(projectRoot);
|
|
108
|
+
if (!repo)
|
|
109
|
+
return null;
|
|
110
|
+
if (repo.selected === "local")
|
|
111
|
+
return { alias: "local", connection: { kind: "local", mode: "auto" }, serverProjectRoot: repo.serverProjectRoot };
|
|
112
|
+
const global = readGlobalConnections(options);
|
|
113
|
+
const connection = global.connections[repo.selected];
|
|
114
|
+
if (!connection) {
|
|
115
|
+
throw new CliError(`Selected Rig server "${repo.selected}" was not found. Run \`rig server list\` or \`rig server use local\`.`, 1);
|
|
116
|
+
}
|
|
117
|
+
return { alias: repo.selected, connection, serverProjectRoot: repo.serverProjectRoot };
|
|
118
|
+
}
|
|
119
|
+
function writeRepoServerProjectRoot(projectRoot, serverProjectRoot) {
|
|
120
|
+
const repo = readRepoConnection(projectRoot);
|
|
121
|
+
if (!repo)
|
|
122
|
+
return;
|
|
123
|
+
writeRepoConnection(projectRoot, { ...repo, serverProjectRoot });
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
// packages/cli/src/commands/_server-client.ts
|
|
127
|
+
var scopedGitHubBearerTokens = new Map;
|
|
128
|
+
var serverPhaseListener = null;
|
|
129
|
+
function reportServerPhase(label) {
|
|
130
|
+
serverPhaseListener?.(label);
|
|
131
|
+
}
|
|
132
|
+
function cleanToken(value) {
|
|
133
|
+
const trimmed = value?.trim();
|
|
134
|
+
return trimmed ? trimmed : null;
|
|
135
|
+
}
|
|
136
|
+
function readRemoteAuthState(projectRoot) {
|
|
137
|
+
const path = resolve2(projectRoot, ".rig", "state", "github-auth.json");
|
|
138
|
+
if (!existsSync2(path))
|
|
139
|
+
return null;
|
|
140
|
+
try {
|
|
141
|
+
const parsed = JSON.parse(readFileSync2(path, "utf8"));
|
|
142
|
+
return parsed && typeof parsed === "object" && !Array.isArray(parsed) ? parsed : null;
|
|
143
|
+
} catch {
|
|
144
|
+
return null;
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
function readPrivateRemoteSessionToken(projectRoot) {
|
|
148
|
+
const parsed = readRemoteAuthState(projectRoot);
|
|
149
|
+
return parsed ? cleanToken(typeof parsed.apiSessionToken === "string" ? parsed.apiSessionToken : typeof parsed.sessionToken === "string" ? parsed.sessionToken : undefined) : null;
|
|
150
|
+
}
|
|
151
|
+
function readGitHubBearerTokenForRemote(projectRoot) {
|
|
152
|
+
const scopedKey = resolve2(projectRoot);
|
|
153
|
+
if (scopedGitHubBearerTokens.has(scopedKey))
|
|
154
|
+
return scopedGitHubBearerTokens.get(scopedKey) ?? null;
|
|
155
|
+
const privateSession = readPrivateRemoteSessionToken(projectRoot);
|
|
156
|
+
if (privateSession)
|
|
157
|
+
return privateSession;
|
|
158
|
+
return cleanToken(process.env.RIG_SERVER_AUTH_TOKEN) ?? cleanToken(process.env.RIG_REMOTE_AUTH_TOKEN);
|
|
159
|
+
}
|
|
160
|
+
function readStoredGitHubAuthToken(projectRoot) {
|
|
161
|
+
const parsed = readRemoteAuthState(projectRoot);
|
|
162
|
+
return parsed ? cleanToken(typeof parsed.token === "string" ? parsed.token : undefined) : null;
|
|
163
|
+
}
|
|
164
|
+
function inferRemoteServerProjectRootFromAuthState(projectRoot) {
|
|
165
|
+
const repo = readRepoConnection(projectRoot);
|
|
166
|
+
const slug = repo?.project?.trim();
|
|
167
|
+
if (!slug || !/^[^/]+\/[^/]+$/.test(slug))
|
|
168
|
+
return null;
|
|
169
|
+
const auth = readRemoteAuthState(projectRoot);
|
|
170
|
+
const authUpdatedAt = typeof auth?.updatedAt === "string" ? Date.parse(auth.updatedAt) : Number.NaN;
|
|
171
|
+
const repoLinkedAt = typeof repo?.linkedAt === "string" ? Date.parse(repo.linkedAt) : Number.NaN;
|
|
172
|
+
if (Number.isFinite(authUpdatedAt) && Number.isFinite(repoLinkedAt) && authUpdatedAt + 1000 < repoLinkedAt)
|
|
173
|
+
return null;
|
|
174
|
+
const checkoutBaseDir = typeof auth?.checkoutBaseDir === "string" && auth.checkoutBaseDir.trim() ? auth.checkoutBaseDir.trim() : null;
|
|
175
|
+
if (!checkoutBaseDir)
|
|
176
|
+
return null;
|
|
177
|
+
const inferred = `${checkoutBaseDir.replace(/\/+$/, "")}/${slug}`;
|
|
178
|
+
writeRepoServerProjectRoot(projectRoot, inferred);
|
|
179
|
+
return inferred;
|
|
180
|
+
}
|
|
181
|
+
function readLocalConnectionFallbackToken(projectRoot) {
|
|
182
|
+
return readGitHubBearerTokenForRemote(projectRoot) ?? cleanToken(process.env.RIG_GITHUB_TOKEN) ?? readStoredGitHubAuthToken(projectRoot);
|
|
183
|
+
}
|
|
184
|
+
async function ensureServerForCli(projectRoot) {
|
|
185
|
+
try {
|
|
186
|
+
const selected = resolveSelectedConnection(projectRoot);
|
|
187
|
+
if (selected?.connection.kind === "remote") {
|
|
188
|
+
reportServerPhase(`Connecting to ${selected.alias}\u2026`);
|
|
189
|
+
const authToken = readGitHubBearerTokenForRemote(projectRoot);
|
|
190
|
+
const serverProjectRoot = selected.serverProjectRoot ?? inferRemoteServerProjectRootFromAuthState(projectRoot) ?? await backfillRemoteServerProjectRoot(projectRoot, selected.connection.baseUrl, authToken);
|
|
191
|
+
return {
|
|
192
|
+
baseUrl: selected.connection.baseUrl,
|
|
193
|
+
authToken,
|
|
194
|
+
connectionKind: "remote",
|
|
195
|
+
serverProjectRoot
|
|
196
|
+
};
|
|
197
|
+
}
|
|
198
|
+
reportServerPhase("Starting local Rig server\u2026");
|
|
199
|
+
const connection = await ensureLocalRigServerConnection(projectRoot);
|
|
200
|
+
return {
|
|
201
|
+
baseUrl: connection.baseUrl,
|
|
202
|
+
authToken: connection.authToken ?? readLocalConnectionFallbackToken(projectRoot),
|
|
203
|
+
connectionKind: "local",
|
|
204
|
+
serverProjectRoot: resolve2(projectRoot)
|
|
205
|
+
};
|
|
206
|
+
} catch (error) {
|
|
207
|
+
if (error instanceof Error) {
|
|
208
|
+
throw new CliError(error.message, 1);
|
|
209
|
+
}
|
|
210
|
+
throw error;
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
async function backfillRemoteServerProjectRoot(projectRoot, baseUrl, authToken) {
|
|
214
|
+
const repo = readRepoConnection(projectRoot);
|
|
215
|
+
const slug = repo?.project?.trim();
|
|
216
|
+
if (!slug)
|
|
217
|
+
return null;
|
|
218
|
+
try {
|
|
219
|
+
const url = new URL(`${baseUrl}/api/projects/${encodeURIComponent(slug)}`);
|
|
220
|
+
if (authToken && queryAuthFallbackEnabled())
|
|
221
|
+
url.searchParams.set("rt", authToken);
|
|
222
|
+
const response = await fetch(url, {
|
|
223
|
+
headers: mergeHeaders(undefined, authToken)
|
|
224
|
+
});
|
|
225
|
+
if (!response.ok)
|
|
226
|
+
return null;
|
|
227
|
+
const payload = await response.json();
|
|
228
|
+
const project = payload.project && typeof payload.project === "object" && !Array.isArray(payload.project) ? payload.project : null;
|
|
229
|
+
const checkouts = Array.isArray(project?.checkouts) ? project.checkouts : [];
|
|
230
|
+
const latestCheckout = [...checkouts].reverse().find((entry) => Boolean(entry && typeof entry === "object" && !Array.isArray(entry) && typeof entry.path === "string"));
|
|
231
|
+
const path = typeof latestCheckout?.path === "string" && latestCheckout.path.trim() ? latestCheckout.path.trim() : null;
|
|
232
|
+
if (path)
|
|
233
|
+
writeRepoServerProjectRoot(projectRoot, path);
|
|
234
|
+
return path;
|
|
235
|
+
} catch {
|
|
236
|
+
return null;
|
|
237
|
+
}
|
|
238
|
+
}
|
|
239
|
+
function mergeCookie(existing, name, value) {
|
|
240
|
+
const encoded = `${name}=${encodeURIComponent(value)}`;
|
|
241
|
+
if (!existing?.trim())
|
|
242
|
+
return encoded;
|
|
243
|
+
const parts = existing.split(";").map((part) => part.trim()).filter((part) => part && !part.startsWith(`${name}=`));
|
|
244
|
+
return [...parts, encoded].join("; ");
|
|
245
|
+
}
|
|
246
|
+
function queryAuthFallbackEnabled(env = process.env) {
|
|
247
|
+
return env.RIG_ENABLE_QUERY_AUTH_FALLBACK === "1" || env.RIG_QUERY_AUTH_FALLBACK === "1";
|
|
248
|
+
}
|
|
249
|
+
function mergeHeaders(headers, authToken) {
|
|
250
|
+
const merged = new Headers(headers);
|
|
251
|
+
if (authToken) {
|
|
252
|
+
const bearer = `Bearer ${authToken}`;
|
|
253
|
+
merged.set("authorization", bearer);
|
|
254
|
+
merged.set("x-auth", bearer);
|
|
255
|
+
merged.set("cookie", mergeCookie(merged.get("cookie"), "rig_auth", authToken));
|
|
256
|
+
}
|
|
257
|
+
return merged;
|
|
258
|
+
}
|
|
259
|
+
var serverReachabilityCache = new Map;
|
|
260
|
+
var RESUMABLE_RUN_STATUSES = new Set([
|
|
261
|
+
"created",
|
|
262
|
+
"preparing",
|
|
263
|
+
"running",
|
|
264
|
+
"validating",
|
|
265
|
+
"reviewing",
|
|
266
|
+
"stopped",
|
|
267
|
+
"failed",
|
|
268
|
+
"needs-attention",
|
|
269
|
+
"needs_attention"
|
|
270
|
+
]);
|
|
271
|
+
|
|
272
|
+
// packages/cli/src/commands/_server-events.ts
|
|
273
|
+
function buildRigWebSocketUrl(baseUrl, authToken) {
|
|
274
|
+
const url = new URL(baseUrl);
|
|
275
|
+
url.protocol = url.protocol === "https:" ? "wss:" : "ws:";
|
|
276
|
+
if (authToken)
|
|
277
|
+
url.searchParams.set("token", authToken);
|
|
278
|
+
return url.toString();
|
|
279
|
+
}
|
|
280
|
+
async function connectRigServerEvents(projectRoot, handlers) {
|
|
281
|
+
const { baseUrl, authToken } = await ensureServerForCli(projectRoot);
|
|
282
|
+
const transport = new WsTransport(buildRigWebSocketUrl(baseUrl, authToken));
|
|
283
|
+
const unsubscribers = [];
|
|
284
|
+
if (handlers.onSnapshotInvalidated)
|
|
285
|
+
unsubscribers.push(transport.subscribe(RIG_WS_CHANNELS.snapshotInvalidated, handlers.onSnapshotInvalidated));
|
|
286
|
+
if (handlers.onRunLogAppended)
|
|
287
|
+
unsubscribers.push(transport.subscribe(RIG_WS_CHANNELS.runLogAppended, handlers.onRunLogAppended));
|
|
288
|
+
if (handlers.onEngineEvent)
|
|
289
|
+
unsubscribers.push(transport.subscribe(RIG_WS_CHANNELS.event, handlers.onEngineEvent));
|
|
290
|
+
let status = "connecting";
|
|
291
|
+
unsubscribers.push(transport.subscribeState((state) => {
|
|
292
|
+
status = state.status;
|
|
293
|
+
handlers.onStatus?.(state.status);
|
|
294
|
+
}));
|
|
295
|
+
return {
|
|
296
|
+
connected: () => status === "connected",
|
|
297
|
+
close: () => {
|
|
298
|
+
for (const unsubscribe of unsubscribers) {
|
|
299
|
+
try {
|
|
300
|
+
unsubscribe();
|
|
301
|
+
} catch {}
|
|
302
|
+
}
|
|
303
|
+
transport.dispose();
|
|
304
|
+
}
|
|
305
|
+
};
|
|
306
|
+
}
|
|
307
|
+
export {
|
|
308
|
+
connectRigServerEvents,
|
|
309
|
+
buildRigWebSocketUrl
|
|
310
|
+
};
|
|
@@ -133,17 +133,21 @@ function cleanToken(value) {
|
|
|
133
133
|
const trimmed = value?.trim();
|
|
134
134
|
return trimmed ? trimmed : null;
|
|
135
135
|
}
|
|
136
|
-
function
|
|
136
|
+
function readRemoteAuthState(projectRoot) {
|
|
137
137
|
const path = resolve2(projectRoot, ".rig", "state", "github-auth.json");
|
|
138
138
|
if (!existsSync2(path))
|
|
139
139
|
return null;
|
|
140
140
|
try {
|
|
141
141
|
const parsed = JSON.parse(readFileSync2(path, "utf8"));
|
|
142
|
-
return
|
|
142
|
+
return parsed && typeof parsed === "object" && !Array.isArray(parsed) ? parsed : null;
|
|
143
143
|
} catch {
|
|
144
144
|
return null;
|
|
145
145
|
}
|
|
146
146
|
}
|
|
147
|
+
function readPrivateRemoteSessionToken(projectRoot) {
|
|
148
|
+
const parsed = readRemoteAuthState(projectRoot);
|
|
149
|
+
return parsed ? cleanToken(typeof parsed.apiSessionToken === "string" ? parsed.apiSessionToken : typeof parsed.sessionToken === "string" ? parsed.sessionToken : undefined) : null;
|
|
150
|
+
}
|
|
147
151
|
function readGitHubBearerTokenForRemote(projectRoot) {
|
|
148
152
|
const scopedKey = resolve2(projectRoot);
|
|
149
153
|
if (scopedGitHubBearerTokens.has(scopedKey))
|
|
@@ -154,15 +158,25 @@ function readGitHubBearerTokenForRemote(projectRoot) {
|
|
|
154
158
|
return cleanToken(process.env.RIG_SERVER_AUTH_TOKEN) ?? cleanToken(process.env.RIG_REMOTE_AUTH_TOKEN);
|
|
155
159
|
}
|
|
156
160
|
function readStoredGitHubAuthToken(projectRoot) {
|
|
157
|
-
const
|
|
158
|
-
|
|
161
|
+
const parsed = readRemoteAuthState(projectRoot);
|
|
162
|
+
return parsed ? cleanToken(typeof parsed.token === "string" ? parsed.token : undefined) : null;
|
|
163
|
+
}
|
|
164
|
+
function inferRemoteServerProjectRootFromAuthState(projectRoot) {
|
|
165
|
+
const repo = readRepoConnection(projectRoot);
|
|
166
|
+
const slug = repo?.project?.trim();
|
|
167
|
+
if (!slug || !/^[^/]+\/[^/]+$/.test(slug))
|
|
159
168
|
return null;
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
169
|
+
const auth = readRemoteAuthState(projectRoot);
|
|
170
|
+
const authUpdatedAt = typeof auth?.updatedAt === "string" ? Date.parse(auth.updatedAt) : Number.NaN;
|
|
171
|
+
const repoLinkedAt = typeof repo?.linkedAt === "string" ? Date.parse(repo.linkedAt) : Number.NaN;
|
|
172
|
+
if (Number.isFinite(authUpdatedAt) && Number.isFinite(repoLinkedAt) && authUpdatedAt + 1000 < repoLinkedAt)
|
|
164
173
|
return null;
|
|
165
|
-
|
|
174
|
+
const checkoutBaseDir = typeof auth?.checkoutBaseDir === "string" && auth.checkoutBaseDir.trim() ? auth.checkoutBaseDir.trim() : null;
|
|
175
|
+
if (!checkoutBaseDir)
|
|
176
|
+
return null;
|
|
177
|
+
const inferred = `${checkoutBaseDir.replace(/\/+$/, "")}/${slug}`;
|
|
178
|
+
writeRepoServerProjectRoot(projectRoot, inferred);
|
|
179
|
+
return inferred;
|
|
166
180
|
}
|
|
167
181
|
function readLocalConnectionFallbackToken(projectRoot) {
|
|
168
182
|
return readGitHubBearerTokenForRemote(projectRoot) ?? cleanToken(process.env.RIG_GITHUB_TOKEN) ?? readStoredGitHubAuthToken(projectRoot);
|
|
@@ -173,7 +187,7 @@ async function ensureServerForCli(projectRoot) {
|
|
|
173
187
|
if (selected?.connection.kind === "remote") {
|
|
174
188
|
reportServerPhase(`Connecting to ${selected.alias}\u2026`);
|
|
175
189
|
const authToken = readGitHubBearerTokenForRemote(projectRoot);
|
|
176
|
-
const serverProjectRoot = selected.serverProjectRoot ?? await backfillRemoteServerProjectRoot(projectRoot, selected.connection.baseUrl, authToken);
|
|
190
|
+
const serverProjectRoot = selected.serverProjectRoot ?? inferRemoteServerProjectRootFromAuthState(projectRoot) ?? await backfillRemoteServerProjectRoot(projectRoot, selected.connection.baseUrl, authToken);
|
|
177
191
|
return {
|
|
178
192
|
baseUrl: selected.connection.baseUrl,
|
|
179
193
|
authToken,
|
|
@@ -202,7 +216,10 @@ async function backfillRemoteServerProjectRoot(projectRoot, baseUrl, authToken)
|
|
|
202
216
|
if (!slug)
|
|
203
217
|
return null;
|
|
204
218
|
try {
|
|
205
|
-
const
|
|
219
|
+
const url = new URL(`${baseUrl}/api/projects/${encodeURIComponent(slug)}`);
|
|
220
|
+
if (authToken && queryAuthFallbackEnabled())
|
|
221
|
+
url.searchParams.set("rt", authToken);
|
|
222
|
+
const response = await fetch(url, {
|
|
206
223
|
headers: mergeHeaders(undefined, authToken)
|
|
207
224
|
});
|
|
208
225
|
if (!response.ok)
|
|
@@ -219,10 +236,23 @@ async function backfillRemoteServerProjectRoot(projectRoot, baseUrl, authToken)
|
|
|
219
236
|
return null;
|
|
220
237
|
}
|
|
221
238
|
}
|
|
239
|
+
function mergeCookie(existing, name, value) {
|
|
240
|
+
const encoded = `${name}=${encodeURIComponent(value)}`;
|
|
241
|
+
if (!existing?.trim())
|
|
242
|
+
return encoded;
|
|
243
|
+
const parts = existing.split(";").map((part) => part.trim()).filter((part) => part && !part.startsWith(`${name}=`));
|
|
244
|
+
return [...parts, encoded].join("; ");
|
|
245
|
+
}
|
|
246
|
+
function queryAuthFallbackEnabled(env = process.env) {
|
|
247
|
+
return env.RIG_ENABLE_QUERY_AUTH_FALLBACK === "1" || env.RIG_QUERY_AUTH_FALLBACK === "1";
|
|
248
|
+
}
|
|
222
249
|
function mergeHeaders(headers, authToken) {
|
|
223
250
|
const merged = new Headers(headers);
|
|
224
251
|
if (authToken) {
|
|
225
|
-
|
|
252
|
+
const bearer = `Bearer ${authToken}`;
|
|
253
|
+
merged.set("authorization", bearer);
|
|
254
|
+
merged.set("x-auth", bearer);
|
|
255
|
+
merged.set("cookie", mergeCookie(merged.get("cookie"), "rig_auth", authToken));
|
|
226
256
|
}
|
|
227
257
|
return merged;
|
|
228
258
|
}
|
|
@@ -283,15 +313,34 @@ async function buildServerFailureContext(projectRoot, server) {
|
|
|
283
313
|
hint: "Check the selected server with `rig server status`, or switch with `rig server use <alias|local>`."
|
|
284
314
|
};
|
|
285
315
|
}
|
|
316
|
+
function isLoopbackRemoteBaseUrl(baseUrl) {
|
|
317
|
+
try {
|
|
318
|
+
const host = new URL(baseUrl).hostname.toLowerCase();
|
|
319
|
+
return host === "localhost" || host === "127.0.0.1" || host === "::1" || host === "[::1]";
|
|
320
|
+
} catch {
|
|
321
|
+
return false;
|
|
322
|
+
}
|
|
323
|
+
}
|
|
324
|
+
function canUseRemoteWithoutProjectRoot(pathname) {
|
|
325
|
+
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/");
|
|
326
|
+
}
|
|
286
327
|
async function requestServerJson(context, pathname, init = {}) {
|
|
287
328
|
const server = await ensureServerForCli(context.projectRoot);
|
|
329
|
+
const requestUrl = new URL(`${server.baseUrl}${pathname}`);
|
|
330
|
+
if (server.connectionKind === "remote" && !server.serverProjectRoot && !canUseRemoteWithoutProjectRoot(requestUrl.pathname) && (server.authToken || !isLoopbackRemoteBaseUrl(server.baseUrl))) {
|
|
331
|
+
const repo = readRepoConnection(context.projectRoot);
|
|
332
|
+
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." });
|
|
333
|
+
}
|
|
288
334
|
const headers = mergeHeaders(init.headers, server.authToken);
|
|
289
335
|
if (server.serverProjectRoot)
|
|
290
336
|
headers.set("x-rig-project-root", server.serverProjectRoot);
|
|
337
|
+
if (server.connectionKind === "remote" && server.authToken && queryAuthFallbackEnabled()) {
|
|
338
|
+
requestUrl.searchParams.set("rt", server.authToken);
|
|
339
|
+
}
|
|
291
340
|
reportServerPhase(`${(init.method ?? "GET").toUpperCase()} ${pathname.split("?")[0]}\u2026`);
|
|
292
341
|
let response;
|
|
293
342
|
try {
|
|
294
|
-
response = await fetch(
|
|
343
|
+
response = await fetch(requestUrl, {
|
|
295
344
|
...init,
|
|
296
345
|
headers
|
|
297
346
|
});
|
|
@@ -82,18 +82,8 @@ function resolveControlPlaneMonorepoRoot(projectRoot) {
|
|
|
82
82
|
|
|
83
83
|
// packages/cli/src/commands/_authority-runs.ts
|
|
84
84
|
var RIG_WORKSPACE_ID = "rig-local-workspace";
|
|
85
|
-
function normalizeRuntimeAdapter(
|
|
86
|
-
|
|
87
|
-
if (!normalized) {
|
|
88
|
-
return "pi";
|
|
89
|
-
}
|
|
90
|
-
if (normalized === "codex" || normalized === "codex-cli" || normalized === "codex-app-server" || normalized === "gpt-codex") {
|
|
91
|
-
return "codex";
|
|
92
|
-
}
|
|
93
|
-
if (normalized === "pi" || normalized === "rig-pi" || normalized === "@rig/pi") {
|
|
94
|
-
return "pi";
|
|
95
|
-
}
|
|
96
|
-
return "claude-code";
|
|
85
|
+
function normalizeRuntimeAdapter(_value) {
|
|
86
|
+
return "pi";
|
|
97
87
|
}
|
|
98
88
|
function readLatestBeadRecord(projectRoot, taskId) {
|
|
99
89
|
const issuesPath = resolve(resolveControlPlaneMonorepoRoot(projectRoot), ".beads", "issues.jsonl");
|