@h-rig/cli 0.0.6-alpha.89 → 0.0.6-alpha.90
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/bin/build-rig-binaries.js +8 -40
- package/dist/bin/rig.js +12519 -26675
- package/dist/src/app/board.js +74 -693
- package/dist/src/app/drone-ui.js +1 -1
- package/dist/src/app/theme.js +0 -1
- package/dist/src/commands/_async-ui.js +1 -1
- package/dist/src/commands/_authority-runs.d.ts +1 -1
- package/dist/src/commands/_authority-runs.js +12 -2
- package/dist/src/commands/_cli-format.d.ts +1 -10
- package/dist/src/commands/_cli-format.js +2 -5
- package/dist/src/commands/_connection-state.d.ts +1 -11
- package/dist/src/commands/_connection-state.js +5 -50
- package/dist/src/commands/_doctor-checks.js +40 -501
- package/dist/src/commands/_help-catalog.js +17 -99
- package/dist/src/commands/_json-output.js +0 -4
- package/dist/src/commands/_operator-view.js +54 -575
- package/dist/src/commands/_pi-frontend.d.ts +0 -27
- package/dist/src/commands/_pi-frontend.js +52 -576
- package/dist/src/commands/_preflight.js +66 -553
- package/dist/src/commands/_server-client.d.ts +0 -33
- package/dist/src/commands/_server-client.js +46 -541
- package/dist/src/commands/_snapshot-upload.js +42 -503
- package/dist/src/commands/_spinner.js +1 -1
- package/dist/src/commands/agent.js +12 -2
- package/dist/src/commands/browser.js +1 -1
- package/dist/src/commands/connect.js +15 -626
- package/dist/src/commands/doctor.js +41 -502
- package/dist/src/commands/github.js +51 -584
- package/dist/src/commands/inbox.js +41 -502
- package/dist/src/commands/init.js +74 -561
- package/dist/src/commands/inspect.js +41 -502
- package/dist/src/commands/run.js +54 -578
- package/dist/src/commands/server.js +163 -692
- package/dist/src/commands/setup.js +46 -507
- package/dist/src/commands/stats.js +58 -601
- package/dist/src/commands/task-run-driver.js +58 -509
- package/dist/src/commands/task.js +110 -729
- package/dist/src/commands.js +237 -1112
- package/dist/src/index.js +244 -1123
- package/dist/src/launcher.js +0 -4
- package/package.json +9 -13
- package/dist/src/app-opentui/adapters/command.d.ts +0 -2
- package/dist/src/app-opentui/adapters/command.js +0 -329
- package/dist/src/app-opentui/adapters/common.d.ts +0 -56
- package/dist/src/app-opentui/adapters/common.js +0 -153
- package/dist/src/app-opentui/adapters/doctor.d.ts +0 -9
- package/dist/src/app-opentui/adapters/doctor.js +0 -1217
- package/dist/src/app-opentui/adapters/family.d.ts +0 -62
- package/dist/src/app-opentui/adapters/family.js +0 -14855
- package/dist/src/app-opentui/adapters/fleet.d.ts +0 -14
- package/dist/src/app-opentui/adapters/fleet.js +0 -1335
- package/dist/src/app-opentui/adapters/inbox.d.ts +0 -43
- package/dist/src/app-opentui/adapters/inbox.js +0 -1944
- package/dist/src/app-opentui/adapters/init.d.ts +0 -11
- package/dist/src/app-opentui/adapters/init.js +0 -2818
- package/dist/src/app-opentui/adapters/inspect.d.ts +0 -52
- package/dist/src/app-opentui/adapters/inspect.js +0 -1455
- package/dist/src/app-opentui/adapters/pi-attach.d.ts +0 -32
- package/dist/src/app-opentui/adapters/pi-attach.js +0 -2101
- package/dist/src/app-opentui/adapters/pi.d.ts +0 -23
- package/dist/src/app-opentui/adapters/pi.js +0 -363
- package/dist/src/app-opentui/adapters/plugin.d.ts +0 -84
- package/dist/src/app-opentui/adapters/plugin.js +0 -544
- package/dist/src/app-opentui/adapters/repo.d.ts +0 -37
- package/dist/src/app-opentui/adapters/repo.js +0 -186
- package/dist/src/app-opentui/adapters/run-detail.d.ts +0 -27
- package/dist/src/app-opentui/adapters/run-detail.js +0 -1441
- package/dist/src/app-opentui/adapters/server.d.ts +0 -48
- package/dist/src/app-opentui/adapters/server.js +0 -1561
- package/dist/src/app-opentui/adapters/tasks.d.ts +0 -64
- package/dist/src/app-opentui/adapters/tasks.js +0 -4524
- package/dist/src/app-opentui/adapters/workspace.d.ts +0 -49
- package/dist/src/app-opentui/adapters/workspace.js +0 -333
- package/dist/src/app-opentui/autocomplete.d.ts +0 -20
- package/dist/src/app-opentui/autocomplete.js +0 -578
- package/dist/src/app-opentui/bootstrap.d.ts +0 -11
- package/dist/src/app-opentui/bootstrap.js +0 -27833
- package/dist/src/app-opentui/command-palette.d.ts +0 -3
- package/dist/src/app-opentui/command-palette.js +0 -1038
- package/dist/src/app-opentui/command-pty-host.d.ts +0 -62
- package/dist/src/app-opentui/command-pty-host.js +0 -248
- package/dist/src/app-opentui/drone.d.ts +0 -12
- package/dist/src/app-opentui/drone.js +0 -230
- package/dist/src/app-opentui/events.d.ts +0 -7
- package/dist/src/app-opentui/events.js +0 -28
- package/dist/src/app-opentui/fleet-stats.d.ts +0 -32
- package/dist/src/app-opentui/fleet-stats.js +0 -114
- package/dist/src/app-opentui/focus-manager.d.ts +0 -14
- package/dist/src/app-opentui/focus-manager.js +0 -24
- package/dist/src/app-opentui/index.d.ts +0 -8
- package/dist/src/app-opentui/index.js +0 -5260
- package/dist/src/app-opentui/intent.d.ts +0 -3
- package/dist/src/app-opentui/intent.js +0 -365
- package/dist/src/app-opentui/keymap.d.ts +0 -21
- package/dist/src/app-opentui/keymap.js +0 -1377
- package/dist/src/app-opentui/launch-routing.d.ts +0 -16
- package/dist/src/app-opentui/launch-routing.js +0 -55
- package/dist/src/app-opentui/layout.d.ts +0 -22
- package/dist/src/app-opentui/layout.js +0 -51
- package/dist/src/app-opentui/list-search.d.ts +0 -36
- package/dist/src/app-opentui/list-search.js +0 -130
- package/dist/src/app-opentui/pi-host-child.d.ts +0 -2
- package/dist/src/app-opentui/pi-host-child.js +0 -1299
- package/dist/src/app-opentui/pi-pty-host.d.ts +0 -28
- package/dist/src/app-opentui/pi-pty-host.js +0 -10
- package/dist/src/app-opentui/react/App.d.ts +0 -9
- package/dist/src/app-opentui/react/App.js +0 -3866
- package/dist/src/app-opentui/react/Backdrop.d.ts +0 -5
- package/dist/src/app-opentui/react/Backdrop.js +0 -823
- package/dist/src/app-opentui/react/ChromeHost.d.ts +0 -5
- package/dist/src/app-opentui/react/ChromeHost.js +0 -1539
- package/dist/src/app-opentui/react/SceneFrameView.d.ts +0 -7
- package/dist/src/app-opentui/react/SceneFrameView.js +0 -579
- package/dist/src/app-opentui/react/context.d.ts +0 -17
- package/dist/src/app-opentui/react/context.js +0 -37
- package/dist/src/app-opentui/react/launch.d.ts +0 -2
- package/dist/src/app-opentui/react/launch.js +0 -4897
- package/dist/src/app-opentui/react/nav.d.ts +0 -18
- package/dist/src/app-opentui/react/nav.js +0 -54
- package/dist/src/app-opentui/react/scroll.d.ts +0 -12
- package/dist/src/app-opentui/react/scroll.js +0 -21
- package/dist/src/app-opentui/react/syntax.d.ts +0 -2
- package/dist/src/app-opentui/react/syntax.js +0 -65
- package/dist/src/app-opentui/registry.d.ts +0 -4
- package/dist/src/app-opentui/registry.js +0 -22876
- package/dist/src/app-opentui/remote-link.d.ts +0 -10
- package/dist/src/app-opentui/remote-link.js +0 -47
- package/dist/src/app-opentui/render/ascii-fleet.d.ts +0 -15
- package/dist/src/app-opentui/render/ascii-fleet.js +0 -82
- package/dist/src/app-opentui/render/constants.d.ts +0 -30
- package/dist/src/app-opentui/render/constants.js +0 -64
- package/dist/src/app-opentui/render/graphics.d.ts +0 -40
- package/dist/src/app-opentui/render/graphics.js +0 -713
- package/dist/src/app-opentui/render/hover.d.ts +0 -11
- package/dist/src/app-opentui/render/hover.js +0 -34
- package/dist/src/app-opentui/render/native-host.d.ts +0 -37
- package/dist/src/app-opentui/render/native-host.js +0 -180
- package/dist/src/app-opentui/render/panel-layout.d.ts +0 -38
- package/dist/src/app-opentui/render/panel-layout.js +0 -48
- package/dist/src/app-opentui/render/panels.d.ts +0 -12
- package/dist/src/app-opentui/render/panels.js +0 -288
- package/dist/src/app-opentui/render/preloader.d.ts +0 -10
- package/dist/src/app-opentui/render/preloader.js +0 -166
- package/dist/src/app-opentui/render/scene.d.ts +0 -68
- package/dist/src/app-opentui/render/scene.js +0 -300
- package/dist/src/app-opentui/render/terminal-handoff.d.ts +0 -16
- package/dist/src/app-opentui/render/terminal-handoff.js +0 -14
- package/dist/src/app-opentui/render/text.d.ts +0 -29
- package/dist/src/app-opentui/render/text.js +0 -177
- package/dist/src/app-opentui/render/type-bar.d.ts +0 -15
- package/dist/src/app-opentui/render/type-bar.js +0 -212
- package/dist/src/app-opentui/runtime-resources.d.ts +0 -16
- package/dist/src/app-opentui/runtime-resources.js +0 -62
- package/dist/src/app-opentui/runtime.d.ts +0 -45
- package/dist/src/app-opentui/runtime.js +0 -5232
- package/dist/src/app-opentui/scenes/command.d.ts +0 -3
- package/dist/src/app-opentui/scenes/command.js +0 -118
- package/dist/src/app-opentui/scenes/doctor.d.ts +0 -3
- package/dist/src/app-opentui/scenes/doctor.js +0 -296
- package/dist/src/app-opentui/scenes/error.d.ts +0 -2
- package/dist/src/app-opentui/scenes/error.js +0 -277
- package/dist/src/app-opentui/scenes/family-domains/agent.d.ts +0 -2
- package/dist/src/app-opentui/scenes/family-domains/agent.js +0 -349
- package/dist/src/app-opentui/scenes/family-domains/browser.d.ts +0 -2
- package/dist/src/app-opentui/scenes/family-domains/browser.js +0 -196
- package/dist/src/app-opentui/scenes/family-domains/dist.d.ts +0 -2
- package/dist/src/app-opentui/scenes/family-domains/dist.js +0 -244
- package/dist/src/app-opentui/scenes/family-domains/git.d.ts +0 -2
- package/dist/src/app-opentui/scenes/family-domains/git.js +0 -196
- package/dist/src/app-opentui/scenes/family-domains/github.d.ts +0 -2
- package/dist/src/app-opentui/scenes/family-domains/github.js +0 -275
- package/dist/src/app-opentui/scenes/family-domains/harness.d.ts +0 -2
- package/dist/src/app-opentui/scenes/family-domains/harness.js +0 -153
- package/dist/src/app-opentui/scenes/family-domains/index.d.ts +0 -4
- package/dist/src/app-opentui/scenes/family-domains/index.js +0 -1680
- package/dist/src/app-opentui/scenes/family-domains/kit.d.ts +0 -76
- package/dist/src/app-opentui/scenes/family-domains/kit.js +0 -306
- package/dist/src/app-opentui/scenes/family-domains/profile.d.ts +0 -2
- package/dist/src/app-opentui/scenes/family-domains/profile.js +0 -213
- package/dist/src/app-opentui/scenes/family-domains/queue.d.ts +0 -2
- package/dist/src/app-opentui/scenes/family-domains/queue.js +0 -147
- package/dist/src/app-opentui/scenes/family-domains/remote.d.ts +0 -2
- package/dist/src/app-opentui/scenes/family-domains/remote.js +0 -519
- package/dist/src/app-opentui/scenes/family-domains/review.d.ts +0 -2
- package/dist/src/app-opentui/scenes/family-domains/review.js +0 -281
- package/dist/src/app-opentui/scenes/family-domains/setup.d.ts +0 -2
- package/dist/src/app-opentui/scenes/family-domains/setup.js +0 -268
- package/dist/src/app-opentui/scenes/family-domains/stats.d.ts +0 -2
- package/dist/src/app-opentui/scenes/family-domains/stats.js +0 -371
- package/dist/src/app-opentui/scenes/family.d.ts +0 -3
- package/dist/src/app-opentui/scenes/family.js +0 -2199
- package/dist/src/app-opentui/scenes/fleet.d.ts +0 -3
- package/dist/src/app-opentui/scenes/fleet.js +0 -766
- package/dist/src/app-opentui/scenes/handoff.d.ts +0 -2
- package/dist/src/app-opentui/scenes/handoff.js +0 -455
- package/dist/src/app-opentui/scenes/help.d.ts +0 -3
- package/dist/src/app-opentui/scenes/help.js +0 -725
- package/dist/src/app-opentui/scenes/inbox.d.ts +0 -3
- package/dist/src/app-opentui/scenes/inbox.js +0 -427
- package/dist/src/app-opentui/scenes/init.d.ts +0 -3
- package/dist/src/app-opentui/scenes/init.js +0 -181
- package/dist/src/app-opentui/scenes/inspect.d.ts +0 -3
- package/dist/src/app-opentui/scenes/inspect.js +0 -794
- package/dist/src/app-opentui/scenes/main.d.ts +0 -3
- package/dist/src/app-opentui/scenes/main.js +0 -332
- package/dist/src/app-opentui/scenes/pi.d.ts +0 -3
- package/dist/src/app-opentui/scenes/pi.js +0 -509
- package/dist/src/app-opentui/scenes/plugin.d.ts +0 -3
- package/dist/src/app-opentui/scenes/plugin.js +0 -487
- package/dist/src/app-opentui/scenes/repo.d.ts +0 -3
- package/dist/src/app-opentui/scenes/repo.js +0 -425
- package/dist/src/app-opentui/scenes/run-detail.d.ts +0 -3
- package/dist/src/app-opentui/scenes/run-detail.js +0 -467
- package/dist/src/app-opentui/scenes/server.d.ts +0 -3
- package/dist/src/app-opentui/scenes/server.js +0 -378
- package/dist/src/app-opentui/scenes/tasks.d.ts +0 -3
- package/dist/src/app-opentui/scenes/tasks.js +0 -768
- package/dist/src/app-opentui/scenes/workspace.d.ts +0 -3
- package/dist/src/app-opentui/scenes/workspace.js +0 -427
- package/dist/src/app-opentui/selectable.d.ts +0 -19
- package/dist/src/app-opentui/selectable.js +0 -80
- package/dist/src/app-opentui/state.d.ts +0 -4
- package/dist/src/app-opentui/state.js +0 -399
- package/dist/src/app-opentui/surface-catalog.d.ts +0 -20
- package/dist/src/app-opentui/surface-catalog.js +0 -542
- package/dist/src/app-opentui/theme.d.ts +0 -59
- package/dist/src/app-opentui/theme.js +0 -142
- package/dist/src/app-opentui/types.d.ts +0 -313
- package/dist/src/app-opentui/types.js +0 -1
- package/dist/src/commands/_server-events.d.ts +0 -26
- package/dist/src/commands/_server-events.js +0 -715
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
// @bun
|
|
2
2
|
// packages/cli/src/commands/_server-client.ts
|
|
3
|
-
import { existsSync as existsSync2,
|
|
4
|
-
import {
|
|
3
|
+
import { existsSync as existsSync2, readFileSync as readFileSync2 } from "fs";
|
|
4
|
+
import { resolve as resolve2 } from "path";
|
|
5
5
|
|
|
6
6
|
// packages/cli/src/runner.ts
|
|
7
7
|
import { EventBus } from "@rig/runtime/control-plane/runtime/events";
|
|
@@ -93,28 +93,12 @@ function readRepoConnection(projectRoot) {
|
|
|
93
93
|
selected,
|
|
94
94
|
project: typeof record.project === "string" ? record.project : undefined,
|
|
95
95
|
linkedAt: typeof record.linkedAt === "string" ? record.linkedAt : undefined,
|
|
96
|
-
serverProjectRoot: typeof record.serverProjectRoot === "string" && record.serverProjectRoot.trim() ? record.serverProjectRoot.trim() : undefined
|
|
97
|
-
serverProjectRootAlias: typeof record.serverProjectRootAlias === "string" && record.serverProjectRootAlias.trim() ? record.serverProjectRootAlias.trim() : undefined,
|
|
98
|
-
serverProjectRootBaseUrl: typeof record.serverProjectRootBaseUrl === "string" && record.serverProjectRootBaseUrl.trim() ? record.serverProjectRootBaseUrl.trim().replace(/\/+$/, "") : undefined
|
|
96
|
+
serverProjectRoot: typeof record.serverProjectRoot === "string" && record.serverProjectRoot.trim() ? record.serverProjectRoot.trim() : undefined
|
|
99
97
|
};
|
|
100
98
|
}
|
|
101
99
|
function writeRepoConnection(projectRoot, state) {
|
|
102
100
|
writeJsonFile(resolveRepoConnectionPath(projectRoot), state);
|
|
103
101
|
}
|
|
104
|
-
function rootAllowedForSelection(repo, connection) {
|
|
105
|
-
const root = repo.serverProjectRoot?.trim();
|
|
106
|
-
if (!root)
|
|
107
|
-
return;
|
|
108
|
-
if (connection.kind === "remote") {
|
|
109
|
-
if (repo.serverProjectRootAlias !== repo.selected)
|
|
110
|
-
return;
|
|
111
|
-
if (repo.serverProjectRootBaseUrl !== connection.baseUrl)
|
|
112
|
-
return;
|
|
113
|
-
} else if (repo.serverProjectRootAlias && repo.serverProjectRootAlias !== repo.selected) {
|
|
114
|
-
return;
|
|
115
|
-
}
|
|
116
|
-
return root;
|
|
117
|
-
}
|
|
118
102
|
function resolveSelectedConnection(projectRoot, options = {}) {
|
|
119
103
|
const repo = readRepoConnection(projectRoot);
|
|
120
104
|
if (!repo)
|
|
@@ -126,41 +110,13 @@ function resolveSelectedConnection(projectRoot, options = {}) {
|
|
|
126
110
|
if (!connection) {
|
|
127
111
|
throw new CliError(`Selected Rig server "${repo.selected}" was not found. Run \`rig server list\` or \`rig server use local\`.`, 1);
|
|
128
112
|
}
|
|
129
|
-
return { alias: repo.selected, connection, serverProjectRoot:
|
|
113
|
+
return { alias: repo.selected, connection, serverProjectRoot: repo.serverProjectRoot };
|
|
130
114
|
}
|
|
131
|
-
function writeRepoServerProjectRoot(projectRoot, serverProjectRoot
|
|
115
|
+
function writeRepoServerProjectRoot(projectRoot, serverProjectRoot) {
|
|
132
116
|
const repo = readRepoConnection(projectRoot);
|
|
133
117
|
if (!repo)
|
|
134
118
|
return;
|
|
135
|
-
|
|
136
|
-
if (!inferred.alias || !inferred.baseUrl) {
|
|
137
|
-
try {
|
|
138
|
-
const selected = resolveSelectedConnection(projectRoot);
|
|
139
|
-
if (selected?.connection.kind === "remote") {
|
|
140
|
-
inferred = {
|
|
141
|
-
alias: inferred.alias ?? selected.alias,
|
|
142
|
-
baseUrl: inferred.baseUrl ?? selected.connection.baseUrl
|
|
143
|
-
};
|
|
144
|
-
}
|
|
145
|
-
} catch {}
|
|
146
|
-
}
|
|
147
|
-
writeRepoConnection(projectRoot, {
|
|
148
|
-
...repo,
|
|
149
|
-
...metadata.project ? { project: metadata.project } : {},
|
|
150
|
-
serverProjectRoot,
|
|
151
|
-
...inferred.alias ? { serverProjectRootAlias: inferred.alias } : {},
|
|
152
|
-
...inferred.baseUrl ? { serverProjectRootBaseUrl: inferred.baseUrl.replace(/\/+$/, "") } : {}
|
|
153
|
-
});
|
|
154
|
-
}
|
|
155
|
-
function clearRepoServerProjectRoot(projectRoot) {
|
|
156
|
-
const repo = readRepoConnection(projectRoot);
|
|
157
|
-
if (!repo)
|
|
158
|
-
return;
|
|
159
|
-
writeRepoConnection(projectRoot, {
|
|
160
|
-
selected: repo.selected,
|
|
161
|
-
...repo.project ? { project: repo.project } : {},
|
|
162
|
-
...repo.linkedAt ? { linkedAt: repo.linkedAt } : {}
|
|
163
|
-
});
|
|
119
|
+
writeRepoConnection(projectRoot, { ...repo, serverProjectRoot });
|
|
164
120
|
}
|
|
165
121
|
|
|
166
122
|
// packages/cli/src/commands/_server-client.ts
|
|
@@ -182,21 +138,17 @@ function setGitHubBearerTokenForCurrentProcess(token, projectRoot) {
|
|
|
182
138
|
const scopedKey = resolve2(projectRoot ?? process.cwd());
|
|
183
139
|
scopedGitHubBearerTokens.set(scopedKey, cleanToken(token ?? undefined));
|
|
184
140
|
}
|
|
185
|
-
function
|
|
141
|
+
function readPrivateRemoteSessionToken(projectRoot) {
|
|
186
142
|
const path = resolve2(projectRoot, ".rig", "state", "github-auth.json");
|
|
187
143
|
if (!existsSync2(path))
|
|
188
144
|
return null;
|
|
189
145
|
try {
|
|
190
146
|
const parsed = JSON.parse(readFileSync2(path, "utf8"));
|
|
191
|
-
return
|
|
147
|
+
return cleanToken(typeof parsed.apiSessionToken === "string" ? parsed.apiSessionToken : typeof parsed.sessionToken === "string" ? parsed.sessionToken : undefined);
|
|
192
148
|
} catch {
|
|
193
149
|
return null;
|
|
194
150
|
}
|
|
195
151
|
}
|
|
196
|
-
function readPrivateRemoteSessionToken(projectRoot) {
|
|
197
|
-
const parsed = readRemoteAuthState(projectRoot);
|
|
198
|
-
return parsed ? cleanToken(typeof parsed.apiSessionToken === "string" ? parsed.apiSessionToken : typeof parsed.sessionToken === "string" ? parsed.sessionToken : undefined) : null;
|
|
199
|
-
}
|
|
200
152
|
function readGitHubBearerTokenForRemote(projectRoot) {
|
|
201
153
|
const scopedKey = resolve2(projectRoot);
|
|
202
154
|
if (scopedGitHubBearerTokens.has(scopedKey))
|
|
@@ -207,428 +159,18 @@ function readGitHubBearerTokenForRemote(projectRoot) {
|
|
|
207
159
|
return cleanToken(process.env.RIG_SERVER_AUTH_TOKEN) ?? cleanToken(process.env.RIG_REMOTE_AUTH_TOKEN);
|
|
208
160
|
}
|
|
209
161
|
function readStoredGitHubAuthToken(projectRoot) {
|
|
210
|
-
const
|
|
211
|
-
return parsed ? cleanToken(typeof parsed.token === "string" ? parsed.token : undefined) : null;
|
|
212
|
-
}
|
|
213
|
-
function inferRemoteServerProjectRootCandidateFromAuthState(projectRoot, repoSlug) {
|
|
214
|
-
if (!/^[^/]+\/[^/]+$/.test(repoSlug))
|
|
215
|
-
return null;
|
|
216
|
-
const repo = readRepoConnection(projectRoot);
|
|
217
|
-
const auth = readRemoteAuthState(projectRoot);
|
|
218
|
-
const authUpdatedAt = typeof auth?.updatedAt === "string" ? Date.parse(auth.updatedAt) : Number.NaN;
|
|
219
|
-
const repoLinkedAt = typeof repo?.linkedAt === "string" ? Date.parse(repo.linkedAt) : Number.NaN;
|
|
220
|
-
if (Number.isFinite(authUpdatedAt) && Number.isFinite(repoLinkedAt) && authUpdatedAt + 1000 < repoLinkedAt)
|
|
221
|
-
return null;
|
|
222
|
-
const checkoutBaseDir = typeof auth?.checkoutBaseDir === "string" && auth.checkoutBaseDir.trim() ? auth.checkoutBaseDir.trim() : null;
|
|
223
|
-
if (!checkoutBaseDir)
|
|
224
|
-
return null;
|
|
225
|
-
return `${checkoutBaseDir.replace(/\/+$/, "")}/${repoSlug}`;
|
|
226
|
-
}
|
|
227
|
-
function readLocalConnectionFallbackToken(projectRoot) {
|
|
228
|
-
return readGitHubBearerTokenForRemote(projectRoot) ?? cleanToken(process.env.RIG_GITHUB_TOKEN) ?? readStoredGitHubAuthToken(projectRoot);
|
|
229
|
-
}
|
|
230
|
-
function normalizeRepoSlug(value) {
|
|
231
|
-
const slug = value?.trim();
|
|
232
|
-
return slug && /^[^/\s]+\/[^/\s]+$/.test(slug) ? slug : null;
|
|
233
|
-
}
|
|
234
|
-
function readProjectLinkSlug(projectRoot) {
|
|
235
|
-
const path = resolve2(projectRoot, ".rig", "state", "project-link.json");
|
|
162
|
+
const path = resolve2(projectRoot, ".rig", "state", "github-auth.json");
|
|
236
163
|
if (!existsSync2(path))
|
|
237
164
|
return null;
|
|
238
165
|
try {
|
|
239
166
|
const parsed = JSON.parse(readFileSync2(path, "utf8"));
|
|
240
|
-
|
|
241
|
-
return null;
|
|
242
|
-
return normalizeRepoSlug(typeof parsed.repoSlug === "string" ? String(parsed.repoSlug) : null);
|
|
167
|
+
return cleanToken(typeof parsed.token === "string" ? parsed.token : undefined);
|
|
243
168
|
} catch {
|
|
244
169
|
return null;
|
|
245
170
|
}
|
|
246
171
|
}
|
|
247
|
-
function
|
|
248
|
-
|
|
249
|
-
mkdirSync2(dirname2(path), { recursive: true });
|
|
250
|
-
writeFileSync2(path, `${JSON.stringify({ repoSlug, connection: alias, linkedAt: new Date().toISOString() }, null, 2)}
|
|
251
|
-
`, "utf8");
|
|
252
|
-
}
|
|
253
|
-
function remoteLinkRepairCommand(repoSlug) {
|
|
254
|
-
return repoSlug ? `rig server repair-link --repo ${repoSlug}` : "rig server repair-link --repo owner/repo";
|
|
255
|
-
}
|
|
256
|
-
function isRemoteProjectRootLinkError(error) {
|
|
257
|
-
const text = error instanceof Error ? error.message : String(error ?? "");
|
|
258
|
-
return /no server-host project root link|remote project(?:-root)? link|x-rig-project-root|serverProjectRoot/i.test(text);
|
|
259
|
-
}
|
|
260
|
-
function formatRemoteProjectLinkHint(resolution) {
|
|
261
|
-
const repair = remoteLinkRepairCommand(resolution.repoSlug);
|
|
262
|
-
if (resolution.status === "auth_required") {
|
|
263
|
-
return `Authenticate the selected remote${resolution.alias ? ` (${resolution.alias})` : ""}, then run \`${repair}\`. Use \`rig github auth import-gh\` or \`rig init --repair --server remote --github-auth device${resolution.repoSlug ? ` --repo ${resolution.repoSlug}` : ""}\`.`;
|
|
264
|
-
}
|
|
265
|
-
if (resolution.status === "missing_project") {
|
|
266
|
-
return `Record the repo slug, then run \`${repair}\` (or \`rig init --repo owner/repo --server remote --github-auth device\`).`;
|
|
267
|
-
}
|
|
268
|
-
if (resolution.status === "not_remote")
|
|
269
|
-
return "Select a remote server with `rig server use <alias>` before repairing a remote project link.";
|
|
270
|
-
return `Run \`${repair}\` to backfill or prepare a server-host checkout for the selected remote${resolution.alias ? ` (${resolution.alias})` : ""}.`;
|
|
271
|
-
}
|
|
272
|
-
function remoteProjectLinkFailure(input) {
|
|
273
|
-
const partial = {
|
|
274
|
-
status: input.status,
|
|
275
|
-
alias: input.alias,
|
|
276
|
-
baseUrl: input.baseUrl,
|
|
277
|
-
repoSlug: input.repoSlug
|
|
278
|
-
};
|
|
279
|
-
return {
|
|
280
|
-
ok: false,
|
|
281
|
-
...input,
|
|
282
|
-
hint: formatRemoteProjectLinkHint(partial),
|
|
283
|
-
next: remoteLinkRepairCommand(input.repoSlug)
|
|
284
|
-
};
|
|
285
|
-
}
|
|
286
|
-
function remoteProjectLinkSuccess(input) {
|
|
287
|
-
return {
|
|
288
|
-
ok: true,
|
|
289
|
-
status: input.status,
|
|
290
|
-
alias: input.alias,
|
|
291
|
-
baseUrl: input.baseUrl,
|
|
292
|
-
repoSlug: input.repoSlug,
|
|
293
|
-
serverProjectRoot: input.serverProjectRoot,
|
|
294
|
-
source: input.source,
|
|
295
|
-
prepared: input.prepared ?? input.status === "prepared",
|
|
296
|
-
validated: input.validated ?? false,
|
|
297
|
-
message: input.message ?? `Remote project link ready for ${input.repoSlug}.`,
|
|
298
|
-
hint: "Remote-scoped task/run/status requests will send x-rig-project-root for this server-host checkout.",
|
|
299
|
-
next: "rig task list"
|
|
300
|
-
};
|
|
301
|
-
}
|
|
302
|
-
function localCheckoutPathCandidate(projectRoot, candidate) {
|
|
303
|
-
try {
|
|
304
|
-
const local = resolve2(projectRoot);
|
|
305
|
-
const resolved = resolve2(candidate);
|
|
306
|
-
return resolved === local || resolved.startsWith(`${local}/`);
|
|
307
|
-
} catch {
|
|
308
|
-
return false;
|
|
309
|
-
}
|
|
310
|
-
}
|
|
311
|
-
async function requestRemoteProjectLinkJson(baseUrl, pathname, authToken, init = {}) {
|
|
312
|
-
const requestUrl = new URL(`${baseUrl}${pathname}`);
|
|
313
|
-
if (authToken && queryAuthFallbackEnabled())
|
|
314
|
-
requestUrl.searchParams.set("rt", authToken);
|
|
315
|
-
const response = await fetch(requestUrl, {
|
|
316
|
-
...init,
|
|
317
|
-
headers: mergeHeaders(init.headers, authToken)
|
|
318
|
-
});
|
|
319
|
-
const text = await response.text();
|
|
320
|
-
const payload = text.trim().length > 0 ? (() => {
|
|
321
|
-
try {
|
|
322
|
-
return JSON.parse(text);
|
|
323
|
-
} catch {
|
|
324
|
-
return null;
|
|
325
|
-
}
|
|
326
|
-
})() : null;
|
|
327
|
-
return { ok: response.ok, status: response.status, payload, text };
|
|
328
|
-
}
|
|
329
|
-
function payloadError(payload, fallback) {
|
|
330
|
-
if (payload && typeof payload === "object" && !Array.isArray(payload)) {
|
|
331
|
-
const record = payload;
|
|
332
|
-
const value = record.error ?? record.message ?? record.reason;
|
|
333
|
-
if (typeof value === "string" && value.trim())
|
|
334
|
-
return value.trim();
|
|
335
|
-
}
|
|
336
|
-
return fallback;
|
|
337
|
-
}
|
|
338
|
-
function checkoutPathsFromProjectPayload(payload) {
|
|
339
|
-
if (!payload || typeof payload !== "object" || Array.isArray(payload))
|
|
340
|
-
return [];
|
|
341
|
-
const project = payload.project;
|
|
342
|
-
if (!project || typeof project !== "object" || Array.isArray(project))
|
|
343
|
-
return [];
|
|
344
|
-
const checkouts = project.checkouts;
|
|
345
|
-
if (!Array.isArray(checkouts))
|
|
346
|
-
return [];
|
|
347
|
-
return [...checkouts].reverse().flatMap((entry) => {
|
|
348
|
-
if (!entry || typeof entry !== "object" || Array.isArray(entry))
|
|
349
|
-
return [];
|
|
350
|
-
const path = entry.path;
|
|
351
|
-
return typeof path === "string" && path.trim() ? [path.trim()] : [];
|
|
352
|
-
});
|
|
353
|
-
}
|
|
354
|
-
function checkoutPathFromPreparePayload(payload) {
|
|
355
|
-
if (!payload || typeof payload !== "object" || Array.isArray(payload))
|
|
356
|
-
return null;
|
|
357
|
-
const checkout = payload.checkout;
|
|
358
|
-
if (!checkout || typeof checkout !== "object" || Array.isArray(checkout))
|
|
359
|
-
return null;
|
|
360
|
-
const path = checkout.path;
|
|
361
|
-
return typeof path === "string" && path.trim() ? path.trim() : null;
|
|
362
|
-
}
|
|
363
|
-
async function validateAndPersistRemoteRoot(input) {
|
|
364
|
-
const candidate = input.candidate.trim();
|
|
365
|
-
if (!candidate || !isAbsolute(candidate)) {
|
|
366
|
-
return remoteProjectLinkFailure({
|
|
367
|
-
status: "invalid_root",
|
|
368
|
-
alias: input.alias,
|
|
369
|
-
baseUrl: input.baseUrl,
|
|
370
|
-
repoSlug: input.repoSlug,
|
|
371
|
-
message: `Remote project root candidate is not an absolute server-host path: ${candidate || "(empty)"}.`
|
|
372
|
-
});
|
|
373
|
-
}
|
|
374
|
-
if (localCheckoutPathCandidate(input.projectRoot, candidate)) {
|
|
375
|
-
return remoteProjectLinkFailure({
|
|
376
|
-
status: "invalid_root",
|
|
377
|
-
alias: input.alias,
|
|
378
|
-
baseUrl: input.baseUrl,
|
|
379
|
-
repoSlug: input.repoSlug,
|
|
380
|
-
message: `Refusing to use local checkout path ${candidate} as a remote server project root.`
|
|
381
|
-
});
|
|
382
|
-
}
|
|
383
|
-
let response;
|
|
384
|
-
try {
|
|
385
|
-
response = await requestRemoteProjectLinkJson(input.baseUrl, "/api/server/project-root", input.authToken, {
|
|
386
|
-
method: "POST",
|
|
387
|
-
headers: { "content-type": "application/json" },
|
|
388
|
-
body: JSON.stringify({ projectRoot: candidate })
|
|
389
|
-
});
|
|
390
|
-
} catch (error) {
|
|
391
|
-
return remoteProjectLinkFailure({
|
|
392
|
-
status: "error",
|
|
393
|
-
alias: input.alias,
|
|
394
|
-
baseUrl: input.baseUrl,
|
|
395
|
-
repoSlug: input.repoSlug,
|
|
396
|
-
message: `Could not validate remote project root ${candidate}: ${error instanceof Error ? error.message : String(error)}`
|
|
397
|
-
});
|
|
398
|
-
}
|
|
399
|
-
if (response.status === 401 || response.status === 403) {
|
|
400
|
-
return remoteProjectLinkFailure({
|
|
401
|
-
status: "auth_required",
|
|
402
|
-
alias: input.alias,
|
|
403
|
-
baseUrl: input.baseUrl,
|
|
404
|
-
repoSlug: input.repoSlug,
|
|
405
|
-
statusCode: response.status,
|
|
406
|
-
message: `Remote server ${input.alias} requires authentication before validating project root ${candidate}.`
|
|
407
|
-
});
|
|
408
|
-
}
|
|
409
|
-
if (!response.ok) {
|
|
410
|
-
return remoteProjectLinkFailure({
|
|
411
|
-
status: "invalid_root",
|
|
412
|
-
alias: input.alias,
|
|
413
|
-
baseUrl: input.baseUrl,
|
|
414
|
-
repoSlug: input.repoSlug,
|
|
415
|
-
statusCode: response.status,
|
|
416
|
-
message: `Remote server rejected project root ${candidate} (${response.status}): ${payloadError(response.payload, response.text || "project-root validation failed")}`
|
|
417
|
-
});
|
|
418
|
-
}
|
|
419
|
-
const record = response.payload && typeof response.payload === "object" && !Array.isArray(response.payload) ? response.payload : {};
|
|
420
|
-
if (record.ok !== true) {
|
|
421
|
-
return remoteProjectLinkFailure({
|
|
422
|
-
status: "invalid_root",
|
|
423
|
-
alias: input.alias,
|
|
424
|
-
baseUrl: input.baseUrl,
|
|
425
|
-
repoSlug: input.repoSlug,
|
|
426
|
-
message: `Remote server did not accept project root ${candidate}: ${payloadError(response.payload, "project-root validation failed")}`
|
|
427
|
-
});
|
|
428
|
-
}
|
|
429
|
-
const accepted = typeof record.projectRoot === "string" && record.projectRoot.trim() ? record.projectRoot.trim() : typeof record.requestedProjectRoot === "string" && record.requestedProjectRoot.trim() ? record.requestedProjectRoot.trim() : candidate;
|
|
430
|
-
writeRepoServerProjectRoot(input.projectRoot, accepted, { alias: input.alias, baseUrl: input.baseUrl, project: input.repoSlug });
|
|
431
|
-
writeProjectLinkConnection(input.projectRoot, input.repoSlug, input.alias);
|
|
432
|
-
const status = input.status ?? (input.prepared ? "prepared" : "backfilled");
|
|
433
|
-
return remoteProjectLinkSuccess({
|
|
434
|
-
status,
|
|
435
|
-
alias: input.alias,
|
|
436
|
-
baseUrl: input.baseUrl,
|
|
437
|
-
repoSlug: input.repoSlug,
|
|
438
|
-
serverProjectRoot: accepted,
|
|
439
|
-
source: input.source,
|
|
440
|
-
prepared: input.prepared,
|
|
441
|
-
validated: true,
|
|
442
|
-
message: status === "ready" ? `Remote project link already points to ${accepted} and was validated on ${input.alias}.` : input.prepared ? `Prepared and linked remote checkout ${accepted} for ${input.repoSlug}.` : `Backfilled remote checkout ${accepted} for ${input.repoSlug}.`
|
|
443
|
-
});
|
|
444
|
-
}
|
|
445
|
-
async function ensureRemoteProjectRootLink(projectRoot, options = {}) {
|
|
446
|
-
let selected;
|
|
447
|
-
try {
|
|
448
|
-
selected = resolveSelectedConnection(projectRoot);
|
|
449
|
-
} catch (error) {
|
|
450
|
-
return remoteProjectLinkFailure({ status: "error", message: error instanceof Error ? error.message : String(error) });
|
|
451
|
-
}
|
|
452
|
-
if (!selected || selected.connection.kind !== "remote") {
|
|
453
|
-
return remoteProjectLinkFailure({ status: "not_remote", message: "No remote Rig server is selected for this repo." });
|
|
454
|
-
}
|
|
455
|
-
const repo = readRepoConnection(projectRoot);
|
|
456
|
-
const explicitRepoRequested = options.repoSlug !== undefined;
|
|
457
|
-
const explicitRepoSlug = explicitRepoRequested ? normalizeRepoSlug(options.repoSlug) : null;
|
|
458
|
-
const repoProjectSlug = normalizeRepoSlug(repo?.project);
|
|
459
|
-
const repoSlug = explicitRepoSlug ?? repoProjectSlug ?? readProjectLinkSlug(projectRoot);
|
|
460
|
-
const alias = selected.alias;
|
|
461
|
-
const baseUrl = selected.connection.baseUrl;
|
|
462
|
-
const authToken = options.authToken === undefined ? readGitHubBearerTokenForRemote(projectRoot) : options.authToken;
|
|
463
|
-
const mode = options.mode ?? "backfill-only";
|
|
464
|
-
if (explicitRepoRequested && !explicitRepoSlug) {
|
|
465
|
-
return remoteProjectLinkFailure({
|
|
466
|
-
status: "missing_project",
|
|
467
|
-
alias,
|
|
468
|
-
baseUrl,
|
|
469
|
-
message: `Invalid --repo value ${JSON.stringify(options.repoSlug)}. Expected owner/repo.`
|
|
470
|
-
});
|
|
471
|
-
}
|
|
472
|
-
if (!repoSlug) {
|
|
473
|
-
return remoteProjectLinkFailure({
|
|
474
|
-
status: "missing_project",
|
|
475
|
-
alias,
|
|
476
|
-
baseUrl,
|
|
477
|
-
message: `Remote server ${alias} is selected, but this checkout has no GitHub repo slug recorded.`
|
|
478
|
-
});
|
|
479
|
-
}
|
|
480
|
-
const skippedCandidates = [];
|
|
481
|
-
const storedRoot = selected.serverProjectRoot?.trim() || null;
|
|
482
|
-
const storedRootMatchesResolvedRepo = repoProjectSlug === repoSlug;
|
|
483
|
-
if (storedRoot && storedRootMatchesResolvedRepo) {
|
|
484
|
-
if (localCheckoutPathCandidate(projectRoot, storedRoot)) {
|
|
485
|
-
clearRepoServerProjectRoot(projectRoot);
|
|
486
|
-
skippedCandidates.push(storedRoot);
|
|
487
|
-
} else {
|
|
488
|
-
const storedResult = await validateAndPersistRemoteRoot({
|
|
489
|
-
projectRoot,
|
|
490
|
-
alias,
|
|
491
|
-
baseUrl,
|
|
492
|
-
authToken,
|
|
493
|
-
repoSlug,
|
|
494
|
-
candidate: storedRoot,
|
|
495
|
-
source: "stored",
|
|
496
|
-
status: "ready"
|
|
497
|
-
});
|
|
498
|
-
if (storedResult.ok || storedResult.status === "auth_required" || storedResult.status === "error")
|
|
499
|
-
return storedResult;
|
|
500
|
-
clearRepoServerProjectRoot(projectRoot);
|
|
501
|
-
skippedCandidates.push(storedRoot);
|
|
502
|
-
}
|
|
503
|
-
} else if (storedRoot) {
|
|
504
|
-
skippedCandidates.push(storedRoot);
|
|
505
|
-
}
|
|
506
|
-
const authCandidate = inferRemoteServerProjectRootCandidateFromAuthState(projectRoot, repoSlug);
|
|
507
|
-
if (authCandidate) {
|
|
508
|
-
const authResult = await validateAndPersistRemoteRoot({ projectRoot, alias, baseUrl, authToken, repoSlug, candidate: authCandidate, source: "auth-state" });
|
|
509
|
-
if (authResult.ok || authResult.status === "auth_required")
|
|
510
|
-
return authResult;
|
|
511
|
-
}
|
|
512
|
-
let registryResponse;
|
|
513
|
-
try {
|
|
514
|
-
registryResponse = await requestRemoteProjectLinkJson(baseUrl, `/api/projects/${encodeURIComponent(repoSlug)}`, authToken);
|
|
515
|
-
} catch (error) {
|
|
516
|
-
return remoteProjectLinkFailure({
|
|
517
|
-
status: "error",
|
|
518
|
-
alias,
|
|
519
|
-
baseUrl,
|
|
520
|
-
repoSlug,
|
|
521
|
-
message: `Could not read remote project registry for ${repoSlug} on ${alias}: ${error instanceof Error ? error.message : String(error)}`
|
|
522
|
-
});
|
|
523
|
-
}
|
|
524
|
-
if (registryResponse.status === 401 || registryResponse.status === 403) {
|
|
525
|
-
return remoteProjectLinkFailure({
|
|
526
|
-
status: "auth_required",
|
|
527
|
-
alias,
|
|
528
|
-
baseUrl,
|
|
529
|
-
repoSlug,
|
|
530
|
-
statusCode: registryResponse.status,
|
|
531
|
-
message: `Remote server ${alias} requires authentication before reading the ${repoSlug} project registry.`
|
|
532
|
-
});
|
|
533
|
-
}
|
|
534
|
-
if (registryResponse.ok) {
|
|
535
|
-
const candidates = checkoutPathsFromProjectPayload(registryResponse.payload);
|
|
536
|
-
for (const candidate of candidates) {
|
|
537
|
-
const result = await validateAndPersistRemoteRoot({ projectRoot, alias, baseUrl, authToken, repoSlug, candidate, source: "registry" });
|
|
538
|
-
if (result.ok || result.status === "auth_required")
|
|
539
|
-
return result;
|
|
540
|
-
skippedCandidates.push(candidate);
|
|
541
|
-
}
|
|
542
|
-
if (mode === "backfill-only") {
|
|
543
|
-
return remoteProjectLinkFailure({
|
|
544
|
-
status: candidates.length > 0 ? "invalid_root" : "no_server_checkout",
|
|
545
|
-
alias,
|
|
546
|
-
baseUrl,
|
|
547
|
-
repoSlug,
|
|
548
|
-
message: candidates.length > 0 ? `Remote registry has checkout candidates for ${repoSlug}, but none validated for ${alias}.` : `Remote registry has ${repoSlug}, but no server checkout path is linked yet.`,
|
|
549
|
-
skippedCandidates
|
|
550
|
-
});
|
|
551
|
-
}
|
|
552
|
-
} else if (registryResponse.status === 404) {
|
|
553
|
-
if (mode === "backfill-only") {
|
|
554
|
-
return remoteProjectLinkFailure({
|
|
555
|
-
status: "project_not_registered",
|
|
556
|
-
alias,
|
|
557
|
-
baseUrl,
|
|
558
|
-
repoSlug,
|
|
559
|
-
statusCode: registryResponse.status,
|
|
560
|
-
message: `Remote server ${alias} has no registered project record for ${repoSlug}.`
|
|
561
|
-
});
|
|
562
|
-
}
|
|
563
|
-
} else {
|
|
564
|
-
return remoteProjectLinkFailure({
|
|
565
|
-
status: "error",
|
|
566
|
-
alias,
|
|
567
|
-
baseUrl,
|
|
568
|
-
repoSlug,
|
|
569
|
-
statusCode: registryResponse.status,
|
|
570
|
-
message: `Could not backfill ${repoSlug} from remote registry (${registryResponse.status}): ${payloadError(registryResponse.payload, registryResponse.text || "registry lookup failed")}`,
|
|
571
|
-
skippedCandidates
|
|
572
|
-
});
|
|
573
|
-
}
|
|
574
|
-
let prepareResponse;
|
|
575
|
-
try {
|
|
576
|
-
prepareResponse = await requestRemoteProjectLinkJson(baseUrl, `/api/projects/${encodeURIComponent(repoSlug)}/prepare-checkout`, authToken, {
|
|
577
|
-
method: "POST",
|
|
578
|
-
headers: { "content-type": "application/json" },
|
|
579
|
-
body: JSON.stringify({ checkout: { kind: "managed-clone" }, repoUrl: `https://github.com/${repoSlug}.git` })
|
|
580
|
-
});
|
|
581
|
-
} catch (error) {
|
|
582
|
-
return remoteProjectLinkFailure({
|
|
583
|
-
status: "error",
|
|
584
|
-
alias,
|
|
585
|
-
baseUrl,
|
|
586
|
-
repoSlug,
|
|
587
|
-
message: `Could not prepare remote checkout for ${repoSlug} on ${alias}: ${error instanceof Error ? error.message : String(error)}`
|
|
588
|
-
});
|
|
589
|
-
}
|
|
590
|
-
if (prepareResponse.status === 401 || prepareResponse.status === 403) {
|
|
591
|
-
return remoteProjectLinkFailure({
|
|
592
|
-
status: "auth_required",
|
|
593
|
-
alias,
|
|
594
|
-
baseUrl,
|
|
595
|
-
repoSlug,
|
|
596
|
-
statusCode: prepareResponse.status,
|
|
597
|
-
message: `Remote server ${alias} requires authentication before preparing ${repoSlug}.`
|
|
598
|
-
});
|
|
599
|
-
}
|
|
600
|
-
if (!prepareResponse.ok) {
|
|
601
|
-
return remoteProjectLinkFailure({
|
|
602
|
-
status: "error",
|
|
603
|
-
alias,
|
|
604
|
-
baseUrl,
|
|
605
|
-
repoSlug,
|
|
606
|
-
statusCode: prepareResponse.status,
|
|
607
|
-
message: `Remote checkout prepare failed for ${repoSlug} (${prepareResponse.status}): ${payloadError(prepareResponse.payload, prepareResponse.text || "prepare-checkout failed")}`,
|
|
608
|
-
skippedCandidates
|
|
609
|
-
});
|
|
610
|
-
}
|
|
611
|
-
const preparedPath = checkoutPathFromPreparePayload(prepareResponse.payload);
|
|
612
|
-
if (!preparedPath) {
|
|
613
|
-
return remoteProjectLinkFailure({
|
|
614
|
-
status: "invalid_root",
|
|
615
|
-
alias,
|
|
616
|
-
baseUrl,
|
|
617
|
-
repoSlug,
|
|
618
|
-
message: `Remote checkout prepare for ${repoSlug} did not return a checkout.path.`,
|
|
619
|
-
skippedCandidates
|
|
620
|
-
});
|
|
621
|
-
}
|
|
622
|
-
return validateAndPersistRemoteRoot({ projectRoot, alias, baseUrl, authToken, repoSlug, candidate: preparedPath, source: "prepare", prepared: true });
|
|
623
|
-
}
|
|
624
|
-
async function inspectRemoteProjectLink(projectRoot) {
|
|
625
|
-
return ensureRemoteProjectRootLink(projectRoot, { mode: "backfill-only" });
|
|
626
|
-
}
|
|
627
|
-
async function repairRemoteProjectRootLink(context, options = {}) {
|
|
628
|
-
const resolution = await ensureRemoteProjectRootLink(context.projectRoot, { mode: options.mode ?? "prepare-if-missing", repoSlug: options.repoSlug });
|
|
629
|
-
if (!resolution.ok)
|
|
630
|
-
throw new CliError(resolution.message, 1, { hint: resolution.hint });
|
|
631
|
-
return resolution;
|
|
172
|
+
function readLocalConnectionFallbackToken(projectRoot) {
|
|
173
|
+
return readGitHubBearerTokenForRemote(projectRoot) ?? cleanToken(process.env.RIG_GITHUB_TOKEN) ?? readStoredGitHubAuthToken(projectRoot);
|
|
632
174
|
}
|
|
633
175
|
async function ensureServerForCli(projectRoot) {
|
|
634
176
|
try {
|
|
@@ -636,12 +178,12 @@ async function ensureServerForCli(projectRoot) {
|
|
|
636
178
|
if (selected?.connection.kind === "remote") {
|
|
637
179
|
reportServerPhase(`Connecting to ${selected.alias}\u2026`);
|
|
638
180
|
const authToken = readGitHubBearerTokenForRemote(projectRoot);
|
|
639
|
-
const
|
|
181
|
+
const serverProjectRoot = selected.serverProjectRoot ?? await backfillRemoteServerProjectRoot(projectRoot, selected.connection.baseUrl, authToken);
|
|
640
182
|
return {
|
|
641
183
|
baseUrl: selected.connection.baseUrl,
|
|
642
184
|
authToken,
|
|
643
185
|
connectionKind: "remote",
|
|
644
|
-
serverProjectRoot
|
|
186
|
+
serverProjectRoot
|
|
645
187
|
};
|
|
646
188
|
}
|
|
647
189
|
reportServerPhase("Starting local Rig server\u2026");
|
|
@@ -659,6 +201,29 @@ async function ensureServerForCli(projectRoot) {
|
|
|
659
201
|
throw error;
|
|
660
202
|
}
|
|
661
203
|
}
|
|
204
|
+
async function backfillRemoteServerProjectRoot(projectRoot, baseUrl, authToken) {
|
|
205
|
+
const repo = readRepoConnection(projectRoot);
|
|
206
|
+
const slug = repo?.project?.trim();
|
|
207
|
+
if (!slug)
|
|
208
|
+
return null;
|
|
209
|
+
try {
|
|
210
|
+
const response = await fetch(`${baseUrl}/api/projects/${encodeURIComponent(slug)}`, {
|
|
211
|
+
headers: mergeHeaders(undefined, authToken)
|
|
212
|
+
});
|
|
213
|
+
if (!response.ok)
|
|
214
|
+
return null;
|
|
215
|
+
const payload = await response.json();
|
|
216
|
+
const project = payload.project && typeof payload.project === "object" && !Array.isArray(payload.project) ? payload.project : null;
|
|
217
|
+
const checkouts = Array.isArray(project?.checkouts) ? project.checkouts : [];
|
|
218
|
+
const latestCheckout = [...checkouts].reverse().find((entry) => Boolean(entry && typeof entry === "object" && !Array.isArray(entry) && typeof entry.path === "string"));
|
|
219
|
+
const path = typeof latestCheckout?.path === "string" && latestCheckout.path.trim() ? latestCheckout.path.trim() : null;
|
|
220
|
+
if (path)
|
|
221
|
+
writeRepoServerProjectRoot(projectRoot, path);
|
|
222
|
+
return path;
|
|
223
|
+
} catch {
|
|
224
|
+
return null;
|
|
225
|
+
}
|
|
226
|
+
}
|
|
662
227
|
function appendTaskFilterParams(url, filters) {
|
|
663
228
|
if (filters.assignee)
|
|
664
229
|
url.searchParams.set("assignee", filters.assignee);
|
|
@@ -669,23 +234,10 @@ function appendTaskFilterParams(url, filters) {
|
|
|
669
234
|
if (filters.limit !== undefined)
|
|
670
235
|
url.searchParams.set("limit", String(filters.limit));
|
|
671
236
|
}
|
|
672
|
-
function mergeCookie(existing, name, value) {
|
|
673
|
-
const encoded = `${name}=${encodeURIComponent(value)}`;
|
|
674
|
-
if (!existing?.trim())
|
|
675
|
-
return encoded;
|
|
676
|
-
const parts = existing.split(";").map((part) => part.trim()).filter((part) => part && !part.startsWith(`${name}=`));
|
|
677
|
-
return [...parts, encoded].join("; ");
|
|
678
|
-
}
|
|
679
|
-
function queryAuthFallbackEnabled(env = process.env) {
|
|
680
|
-
return env.RIG_ENABLE_QUERY_AUTH_FALLBACK === "1" || env.RIG_QUERY_AUTH_FALLBACK === "1";
|
|
681
|
-
}
|
|
682
237
|
function mergeHeaders(headers, authToken) {
|
|
683
238
|
const merged = new Headers(headers);
|
|
684
239
|
if (authToken) {
|
|
685
|
-
|
|
686
|
-
merged.set("authorization", bearer);
|
|
687
|
-
merged.set("x-auth", bearer);
|
|
688
|
-
merged.set("cookie", mergeCookie(merged.get("cookie"), "rig_auth", authToken));
|
|
240
|
+
merged.set("authorization", `Bearer ${authToken}`);
|
|
689
241
|
}
|
|
690
242
|
return merged;
|
|
691
243
|
}
|
|
@@ -746,41 +298,15 @@ async function buildServerFailureContext(projectRoot, server) {
|
|
|
746
298
|
hint: "Check the selected server with `rig server status`, or switch with `rig server use <alias|local>`."
|
|
747
299
|
};
|
|
748
300
|
}
|
|
749
|
-
function isLoopbackRemoteBaseUrl(baseUrl) {
|
|
750
|
-
try {
|
|
751
|
-
const host = new URL(baseUrl).hostname.toLowerCase();
|
|
752
|
-
return host === "localhost" || host === "127.0.0.1" || host === "::1" || host === "[::1]";
|
|
753
|
-
} catch {
|
|
754
|
-
return false;
|
|
755
|
-
}
|
|
756
|
-
}
|
|
757
|
-
function canUseRemoteWithoutProjectRoot(pathname) {
|
|
758
|
-
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/");
|
|
759
|
-
}
|
|
760
301
|
async function requestServerJson(context, pathname, init = {}) {
|
|
761
302
|
const server = await ensureServerForCli(context.projectRoot);
|
|
762
|
-
const requestUrl = new URL(`${server.baseUrl}${pathname}`);
|
|
763
|
-
let scopedServerProjectRoot = server.serverProjectRoot;
|
|
764
|
-
if (server.connectionKind === "remote" && !scopedServerProjectRoot && !canUseRemoteWithoutProjectRoot(requestUrl.pathname) && (server.authToken || !isLoopbackRemoteBaseUrl(server.baseUrl))) {
|
|
765
|
-
const link = await ensureRemoteProjectRootLink(context.projectRoot, { mode: "backfill-only", authToken: server.authToken });
|
|
766
|
-
if (link.ok && link.serverProjectRoot) {
|
|
767
|
-
scopedServerProjectRoot = link.serverProjectRoot;
|
|
768
|
-
} else {
|
|
769
|
-
const repo = readRepoConnection(context.projectRoot);
|
|
770
|
-
const target = link.alias && link.baseUrl ? `${link.alias} (${link.baseUrl})` : server.baseUrl;
|
|
771
|
-
throw new CliError(`Remote server ${target} is selected for ${link.repoSlug ?? repo?.project ?? "this repo"}, but this checkout has no server-host project root link. ${link.message}`, 1, { hint: link.hint });
|
|
772
|
-
}
|
|
773
|
-
}
|
|
774
303
|
const headers = mergeHeaders(init.headers, server.authToken);
|
|
775
|
-
if (
|
|
776
|
-
headers.set("x-rig-project-root",
|
|
777
|
-
if (server.connectionKind === "remote" && server.authToken && queryAuthFallbackEnabled()) {
|
|
778
|
-
requestUrl.searchParams.set("rt", server.authToken);
|
|
779
|
-
}
|
|
304
|
+
if (server.serverProjectRoot)
|
|
305
|
+
headers.set("x-rig-project-root", server.serverProjectRoot);
|
|
780
306
|
reportServerPhase(`${(init.method ?? "GET").toUpperCase()} ${pathname.split("?")[0]}\u2026`);
|
|
781
307
|
let response;
|
|
782
308
|
try {
|
|
783
|
-
response = await fetch(
|
|
309
|
+
response = await fetch(`${server.baseUrl}${pathname}`, {
|
|
784
310
|
...init,
|
|
785
311
|
headers
|
|
786
312
|
});
|
|
@@ -842,26 +368,11 @@ async function getGitHubAuthStatusViaServer(context) {
|
|
|
842
368
|
return payload && typeof payload === "object" && !Array.isArray(payload) ? payload : {};
|
|
843
369
|
}
|
|
844
370
|
async function postGitHubTokenViaServer(context, token, options = {}) {
|
|
845
|
-
const
|
|
846
|
-
|
|
847
|
-
|
|
848
|
-
|
|
849
|
-
|
|
850
|
-
response = await fetch(requestUrl, {
|
|
851
|
-
method: "POST",
|
|
852
|
-
headers: { "content-type": "application/json" },
|
|
853
|
-
body: JSON.stringify({ token, selectedRepo: options.selectedRepo, projectRoot: options.projectRoot ?? server.serverProjectRoot ?? undefined })
|
|
854
|
-
});
|
|
855
|
-
} catch (error) {
|
|
856
|
-
const failure = await buildServerFailureContext(context.projectRoot, server);
|
|
857
|
-
throw new CliError(`Rig server auth bootstrap failed: ${error instanceof Error ? error.message : String(error)}
|
|
858
|
-
${failure.contextLine}`, 1, { hint: failure.hint });
|
|
859
|
-
}
|
|
860
|
-
const payload = await response.json().catch(() => null);
|
|
861
|
-
if (!response.ok) {
|
|
862
|
-
const detail = payload && typeof payload === "object" && !Array.isArray(payload) ? String(payload.error ?? JSON.stringify(payload)) : `HTTP ${response.status}`;
|
|
863
|
-
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`." });
|
|
864
|
-
}
|
|
371
|
+
const payload = await requestServerJson(context, "/api/github/auth/token", {
|
|
372
|
+
method: "POST",
|
|
373
|
+
headers: { "content-type": "application/json" },
|
|
374
|
+
body: JSON.stringify({ token, selectedRepo: options.selectedRepo, projectRoot: options.projectRoot })
|
|
375
|
+
});
|
|
865
376
|
return payload && typeof payload === "object" && !Array.isArray(payload) ? payload : {};
|
|
866
377
|
}
|
|
867
378
|
async function prepareRemoteCheckoutViaServer(context, input) {
|
|
@@ -1146,16 +657,12 @@ export {
|
|
|
1146
657
|
respondRunPiExtensionUiViaServer,
|
|
1147
658
|
resolveServerConnectionLabel,
|
|
1148
659
|
requestServerJson,
|
|
1149
|
-
repairRemoteProjectRootLink,
|
|
1150
660
|
registerProjectViaServer,
|
|
1151
661
|
prepareRemoteCheckoutViaServer,
|
|
1152
662
|
postGitHubTokenViaServer,
|
|
1153
|
-
normalizeRepoSlug,
|
|
1154
663
|
listWorkspaceTasksViaServer,
|
|
1155
664
|
listRunsViaServer,
|
|
1156
665
|
listGitHubProjectsViaServer,
|
|
1157
|
-
isRemoteProjectRootLinkError,
|
|
1158
|
-
inspectRemoteProjectLink,
|
|
1159
666
|
getWorkspaceTaskViaServer,
|
|
1160
667
|
getRunTimelineViaServer,
|
|
1161
668
|
getRunPiStatusViaServer,
|
|
@@ -1167,10 +674,8 @@ export {
|
|
|
1167
674
|
getRunDetailsViaServer,
|
|
1168
675
|
getGitHubProjectStatusFieldViaServer,
|
|
1169
676
|
getGitHubAuthStatusViaServer,
|
|
1170
|
-
formatRemoteProjectLinkHint,
|
|
1171
677
|
ensureTaskLabelsViaServer,
|
|
1172
678
|
ensureServerForCli,
|
|
1173
|
-
ensureRemoteProjectRootLink,
|
|
1174
679
|
buildRunPiEventsWebSocketUrl,
|
|
1175
680
|
abortRunPiViaServer
|
|
1176
681
|
};
|