@h-rig/cli 0.0.6-alpha.45 → 0.0.6-alpha.47
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/rig.js +51 -22
- package/dist/src/commands/_connection-state.js +11 -3
- package/dist/src/commands/_doctor-checks.js +52 -7
- package/dist/src/commands/_operator-view.js +54 -7
- package/dist/src/commands/_pi-frontend.js +54 -7
- package/dist/src/commands/_pi-remote-session.js +54 -7
- package/dist/src/commands/_pi-worker-bridge-extension.js +54 -7
- package/dist/src/commands/_preflight.js +52 -7
- package/dist/src/commands/_server-client.js +59 -22
- package/dist/src/commands/_snapshot-upload.js +52 -7
- package/dist/src/commands/connect.js +2 -1
- package/dist/src/commands/doctor.js +52 -7
- package/dist/src/commands/github.js +52 -7
- package/dist/src/commands/inbox.js +52 -7
- package/dist/src/commands/init.js +49 -22
- package/dist/src/commands/inspect.js +52 -7
- package/dist/src/commands/run.js +54 -7
- package/dist/src/commands/server.js +44 -7
- package/dist/src/commands/setup.js +52 -7
- package/dist/src/commands/task-run-driver.js +52 -7
- package/dist/src/commands/task.js +54 -7
- package/dist/src/commands.js +51 -22
- package/dist/src/index.js +51 -22
- package/package.json +6 -6
package/dist/bin/rig.js
CHANGED
|
@@ -2888,7 +2888,8 @@ function readRepoConnection(projectRoot) {
|
|
|
2888
2888
|
return {
|
|
2889
2889
|
selected,
|
|
2890
2890
|
project: typeof record.project === "string" ? record.project : undefined,
|
|
2891
|
-
linkedAt: typeof record.linkedAt === "string" ? record.linkedAt : undefined
|
|
2891
|
+
linkedAt: typeof record.linkedAt === "string" ? record.linkedAt : undefined,
|
|
2892
|
+
serverProjectRoot: typeof record.serverProjectRoot === "string" && record.serverProjectRoot.trim() ? record.serverProjectRoot.trim() : undefined
|
|
2892
2893
|
};
|
|
2893
2894
|
}
|
|
2894
2895
|
function writeRepoConnection(projectRoot, state) {
|
|
@@ -2899,13 +2900,19 @@ function resolveSelectedConnection(projectRoot, options = {}) {
|
|
|
2899
2900
|
if (!repo)
|
|
2900
2901
|
return null;
|
|
2901
2902
|
if (repo.selected === "local")
|
|
2902
|
-
return { alias: "local", connection: { kind: "local", mode: "auto" } };
|
|
2903
|
+
return { alias: "local", connection: { kind: "local", mode: "auto" }, serverProjectRoot: repo.serverProjectRoot };
|
|
2903
2904
|
const global = readGlobalConnections(options);
|
|
2904
2905
|
const connection = global.connections[repo.selected];
|
|
2905
2906
|
if (!connection) {
|
|
2906
2907
|
throw new CliError2(`Selected Rig server "${repo.selected}" was not found. Run \`rig server list\` or \`rig server use local\`.`, 1);
|
|
2907
2908
|
}
|
|
2908
|
-
return { alias: repo.selected, connection };
|
|
2909
|
+
return { alias: repo.selected, connection, serverProjectRoot: repo.serverProjectRoot };
|
|
2910
|
+
}
|
|
2911
|
+
function writeRepoServerProjectRoot(projectRoot, serverProjectRoot) {
|
|
2912
|
+
const repo = readRepoConnection(projectRoot);
|
|
2913
|
+
if (!repo)
|
|
2914
|
+
return;
|
|
2915
|
+
writeRepoConnection(projectRoot, { ...repo, serverProjectRoot });
|
|
2909
2916
|
}
|
|
2910
2917
|
|
|
2911
2918
|
// packages/cli/src/commands/_server-client.ts
|
|
@@ -2946,17 +2953,21 @@ async function ensureServerForCli(projectRoot) {
|
|
|
2946
2953
|
try {
|
|
2947
2954
|
const selected = resolveSelectedConnection(projectRoot);
|
|
2948
2955
|
if (selected?.connection.kind === "remote") {
|
|
2956
|
+
const authToken = readGitHubBearerTokenForRemote(projectRoot);
|
|
2957
|
+
const serverProjectRoot = selected.serverProjectRoot ?? await backfillRemoteServerProjectRoot(projectRoot, selected.connection.baseUrl, authToken);
|
|
2949
2958
|
return {
|
|
2950
2959
|
baseUrl: selected.connection.baseUrl,
|
|
2951
|
-
authToken
|
|
2952
|
-
connectionKind: "remote"
|
|
2960
|
+
authToken,
|
|
2961
|
+
connectionKind: "remote",
|
|
2962
|
+
serverProjectRoot
|
|
2953
2963
|
};
|
|
2954
2964
|
}
|
|
2955
2965
|
const connection = await ensureLocalRigServerConnection(projectRoot);
|
|
2956
2966
|
return {
|
|
2957
2967
|
baseUrl: connection.baseUrl,
|
|
2958
2968
|
authToken: connection.authToken,
|
|
2959
|
-
connectionKind: "local"
|
|
2969
|
+
connectionKind: "local",
|
|
2970
|
+
serverProjectRoot: resolve11(projectRoot)
|
|
2960
2971
|
};
|
|
2961
2972
|
} catch (error) {
|
|
2962
2973
|
if (error instanceof Error) {
|
|
@@ -2965,6 +2976,29 @@ async function ensureServerForCli(projectRoot) {
|
|
|
2965
2976
|
throw error;
|
|
2966
2977
|
}
|
|
2967
2978
|
}
|
|
2979
|
+
async function backfillRemoteServerProjectRoot(projectRoot, baseUrl, authToken) {
|
|
2980
|
+
const repo = readRepoConnection(projectRoot);
|
|
2981
|
+
const slug = repo?.project?.trim();
|
|
2982
|
+
if (!slug)
|
|
2983
|
+
return null;
|
|
2984
|
+
try {
|
|
2985
|
+
const response = await fetch(`${baseUrl}/api/projects/${encodeURIComponent(slug)}`, {
|
|
2986
|
+
headers: mergeHeaders(undefined, authToken)
|
|
2987
|
+
});
|
|
2988
|
+
if (!response.ok)
|
|
2989
|
+
return null;
|
|
2990
|
+
const payload = await response.json();
|
|
2991
|
+
const project = payload.project && typeof payload.project === "object" && !Array.isArray(payload.project) ? payload.project : null;
|
|
2992
|
+
const checkouts = Array.isArray(project?.checkouts) ? project.checkouts : [];
|
|
2993
|
+
const latestCheckout = [...checkouts].reverse().find((entry) => Boolean(entry && typeof entry === "object" && !Array.isArray(entry) && typeof entry.path === "string"));
|
|
2994
|
+
const path = typeof latestCheckout?.path === "string" && latestCheckout.path.trim() ? latestCheckout.path.trim() : null;
|
|
2995
|
+
if (path)
|
|
2996
|
+
writeRepoServerProjectRoot(projectRoot, path);
|
|
2997
|
+
return path;
|
|
2998
|
+
} catch {
|
|
2999
|
+
return null;
|
|
3000
|
+
}
|
|
3001
|
+
}
|
|
2968
3002
|
function appendTaskFilterParams(url, filters) {
|
|
2969
3003
|
if (filters.assignee)
|
|
2970
3004
|
url.searchParams.set("assignee", filters.assignee);
|
|
@@ -2999,9 +3033,12 @@ function diagnosticMessage(payload) {
|
|
|
2999
3033
|
}
|
|
3000
3034
|
async function requestServerJson(context, pathname, init = {}) {
|
|
3001
3035
|
const server = await ensureServerForCli(context.projectRoot);
|
|
3036
|
+
const headers = mergeHeaders(init.headers, server.authToken);
|
|
3037
|
+
if (server.serverProjectRoot)
|
|
3038
|
+
headers.set("x-rig-project-root", server.serverProjectRoot);
|
|
3002
3039
|
const response = await fetch(`${server.baseUrl}${pathname}`, {
|
|
3003
3040
|
...init,
|
|
3004
|
-
headers
|
|
3041
|
+
headers
|
|
3005
3042
|
});
|
|
3006
3043
|
const text2 = await response.text();
|
|
3007
3044
|
const payload = text2.trim().length > 0 ? (() => {
|
|
@@ -3108,22 +3145,12 @@ async function switchServerProjectRootViaServer(context, projectRoot, options =
|
|
|
3108
3145
|
if (!switched) {
|
|
3109
3146
|
throw new CliError2(`Rig server did not accept project-root switch to ${projectRoot} before timeout (${lastError instanceof Error ? lastError.message : String(lastError ?? "no response")}).`, 1);
|
|
3110
3147
|
}
|
|
3111
|
-
|
|
3112
|
-
|
|
3113
|
-
|
|
3114
|
-
|
|
3115
|
-
const record = status;
|
|
3116
|
-
if (record.projectRoot === projectRoot) {
|
|
3117
|
-
return { ok: true, switched, status: record };
|
|
3118
|
-
}
|
|
3119
|
-
lastError = `server projectRoot=${String(record.projectRoot ?? "unknown")}`;
|
|
3120
|
-
}
|
|
3121
|
-
} catch (error) {
|
|
3122
|
-
lastError = error;
|
|
3123
|
-
}
|
|
3124
|
-
await sleep(pollMs);
|
|
3148
|
+
const record = switched && typeof switched === "object" && !Array.isArray(switched) ? switched : {};
|
|
3149
|
+
if (record.ok === true) {
|
|
3150
|
+
writeRepoServerProjectRoot(context.projectRoot, projectRoot);
|
|
3151
|
+
return { ok: true, switched: record };
|
|
3125
3152
|
}
|
|
3126
|
-
throw new CliError2(`Rig server
|
|
3153
|
+
throw new CliError2(`Rig server rejected project-root scope ${projectRoot}: ${String(record.message ?? record.error ?? "unknown error")}`, 1);
|
|
3127
3154
|
}
|
|
3128
3155
|
async function listRunsViaServer(context, options = {}) {
|
|
3129
3156
|
const url = new URL("http://rig.local/api/runs");
|
|
@@ -3255,6 +3282,8 @@ async function buildRunPiEventsWebSocketUrl(context, runId) {
|
|
|
3255
3282
|
url.protocol = url.protocol === "https:" ? "wss:" : "ws:";
|
|
3256
3283
|
if (server.authToken)
|
|
3257
3284
|
url.searchParams.set("token", server.authToken);
|
|
3285
|
+
if (server.serverProjectRoot)
|
|
3286
|
+
url.searchParams.set("rigProjectRoot", server.serverProjectRoot);
|
|
3258
3287
|
return url.toString();
|
|
3259
3288
|
}
|
|
3260
3289
|
async function submitTaskRunViaServer(context, input) {
|
|
@@ -90,7 +90,8 @@ function readRepoConnection(projectRoot) {
|
|
|
90
90
|
return {
|
|
91
91
|
selected,
|
|
92
92
|
project: typeof record.project === "string" ? record.project : undefined,
|
|
93
|
-
linkedAt: typeof record.linkedAt === "string" ? record.linkedAt : undefined
|
|
93
|
+
linkedAt: typeof record.linkedAt === "string" ? record.linkedAt : undefined,
|
|
94
|
+
serverProjectRoot: typeof record.serverProjectRoot === "string" && record.serverProjectRoot.trim() ? record.serverProjectRoot.trim() : undefined
|
|
94
95
|
};
|
|
95
96
|
}
|
|
96
97
|
function writeRepoConnection(projectRoot, state) {
|
|
@@ -101,15 +102,22 @@ function resolveSelectedConnection(projectRoot, options = {}) {
|
|
|
101
102
|
if (!repo)
|
|
102
103
|
return null;
|
|
103
104
|
if (repo.selected === "local")
|
|
104
|
-
return { alias: "local", connection: { kind: "local", mode: "auto" } };
|
|
105
|
+
return { alias: "local", connection: { kind: "local", mode: "auto" }, serverProjectRoot: repo.serverProjectRoot };
|
|
105
106
|
const global = readGlobalConnections(options);
|
|
106
107
|
const connection = global.connections[repo.selected];
|
|
107
108
|
if (!connection) {
|
|
108
109
|
throw new CliError2(`Selected Rig server "${repo.selected}" was not found. Run \`rig server list\` or \`rig server use local\`.`, 1);
|
|
109
110
|
}
|
|
110
|
-
return { alias: repo.selected, connection };
|
|
111
|
+
return { alias: repo.selected, connection, serverProjectRoot: repo.serverProjectRoot };
|
|
112
|
+
}
|
|
113
|
+
function writeRepoServerProjectRoot(projectRoot, serverProjectRoot) {
|
|
114
|
+
const repo = readRepoConnection(projectRoot);
|
|
115
|
+
if (!repo)
|
|
116
|
+
return;
|
|
117
|
+
writeRepoConnection(projectRoot, { ...repo, serverProjectRoot });
|
|
111
118
|
}
|
|
112
119
|
export {
|
|
120
|
+
writeRepoServerProjectRoot,
|
|
113
121
|
writeRepoConnection,
|
|
114
122
|
writeGlobalConnections,
|
|
115
123
|
upsertGlobalConnection,
|
|
@@ -40,6 +40,11 @@ function readJsonFile(path) {
|
|
|
40
40
|
throw new CliError2(`Invalid Rig connection state at ${path}: ${error instanceof Error ? error.message : String(error)}`, 1);
|
|
41
41
|
}
|
|
42
42
|
}
|
|
43
|
+
function writeJsonFile(path, value) {
|
|
44
|
+
mkdirSync(dirname(path), { recursive: true });
|
|
45
|
+
writeFileSync(path, `${JSON.stringify(value, null, 2)}
|
|
46
|
+
`, "utf8");
|
|
47
|
+
}
|
|
43
48
|
function normalizeConnection(value) {
|
|
44
49
|
if (!value || typeof value !== "object" || Array.isArray(value))
|
|
45
50
|
return null;
|
|
@@ -80,21 +85,31 @@ function readRepoConnection(projectRoot) {
|
|
|
80
85
|
return {
|
|
81
86
|
selected,
|
|
82
87
|
project: typeof record.project === "string" ? record.project : undefined,
|
|
83
|
-
linkedAt: typeof record.linkedAt === "string" ? record.linkedAt : undefined
|
|
88
|
+
linkedAt: typeof record.linkedAt === "string" ? record.linkedAt : undefined,
|
|
89
|
+
serverProjectRoot: typeof record.serverProjectRoot === "string" && record.serverProjectRoot.trim() ? record.serverProjectRoot.trim() : undefined
|
|
84
90
|
};
|
|
85
91
|
}
|
|
92
|
+
function writeRepoConnection(projectRoot, state) {
|
|
93
|
+
writeJsonFile(resolveRepoConnectionPath(projectRoot), state);
|
|
94
|
+
}
|
|
86
95
|
function resolveSelectedConnection(projectRoot, options = {}) {
|
|
87
96
|
const repo = readRepoConnection(projectRoot);
|
|
88
97
|
if (!repo)
|
|
89
98
|
return null;
|
|
90
99
|
if (repo.selected === "local")
|
|
91
|
-
return { alias: "local", connection: { kind: "local", mode: "auto" } };
|
|
100
|
+
return { alias: "local", connection: { kind: "local", mode: "auto" }, serverProjectRoot: repo.serverProjectRoot };
|
|
92
101
|
const global = readGlobalConnections(options);
|
|
93
102
|
const connection = global.connections[repo.selected];
|
|
94
103
|
if (!connection) {
|
|
95
104
|
throw new CliError2(`Selected Rig server "${repo.selected}" was not found. Run \`rig server list\` or \`rig server use local\`.`, 1);
|
|
96
105
|
}
|
|
97
|
-
return { alias: repo.selected, connection };
|
|
106
|
+
return { alias: repo.selected, connection, serverProjectRoot: repo.serverProjectRoot };
|
|
107
|
+
}
|
|
108
|
+
function writeRepoServerProjectRoot(projectRoot, serverProjectRoot) {
|
|
109
|
+
const repo = readRepoConnection(projectRoot);
|
|
110
|
+
if (!repo)
|
|
111
|
+
return;
|
|
112
|
+
writeRepoConnection(projectRoot, { ...repo, serverProjectRoot });
|
|
98
113
|
}
|
|
99
114
|
|
|
100
115
|
// packages/cli/src/commands/_server-client.ts
|
|
@@ -130,17 +145,21 @@ async function ensureServerForCli(projectRoot) {
|
|
|
130
145
|
try {
|
|
131
146
|
const selected = resolveSelectedConnection(projectRoot);
|
|
132
147
|
if (selected?.connection.kind === "remote") {
|
|
148
|
+
const authToken = readGitHubBearerTokenForRemote(projectRoot);
|
|
149
|
+
const serverProjectRoot = selected.serverProjectRoot ?? await backfillRemoteServerProjectRoot(projectRoot, selected.connection.baseUrl, authToken);
|
|
133
150
|
return {
|
|
134
151
|
baseUrl: selected.connection.baseUrl,
|
|
135
|
-
authToken
|
|
136
|
-
connectionKind: "remote"
|
|
152
|
+
authToken,
|
|
153
|
+
connectionKind: "remote",
|
|
154
|
+
serverProjectRoot
|
|
137
155
|
};
|
|
138
156
|
}
|
|
139
157
|
const connection = await ensureLocalRigServerConnection(projectRoot);
|
|
140
158
|
return {
|
|
141
159
|
baseUrl: connection.baseUrl,
|
|
142
160
|
authToken: connection.authToken,
|
|
143
|
-
connectionKind: "local"
|
|
161
|
+
connectionKind: "local",
|
|
162
|
+
serverProjectRoot: resolve2(projectRoot)
|
|
144
163
|
};
|
|
145
164
|
} catch (error) {
|
|
146
165
|
if (error instanceof Error) {
|
|
@@ -149,6 +168,29 @@ async function ensureServerForCli(projectRoot) {
|
|
|
149
168
|
throw error;
|
|
150
169
|
}
|
|
151
170
|
}
|
|
171
|
+
async function backfillRemoteServerProjectRoot(projectRoot, baseUrl, authToken) {
|
|
172
|
+
const repo = readRepoConnection(projectRoot);
|
|
173
|
+
const slug = repo?.project?.trim();
|
|
174
|
+
if (!slug)
|
|
175
|
+
return null;
|
|
176
|
+
try {
|
|
177
|
+
const response = await fetch(`${baseUrl}/api/projects/${encodeURIComponent(slug)}`, {
|
|
178
|
+
headers: mergeHeaders(undefined, authToken)
|
|
179
|
+
});
|
|
180
|
+
if (!response.ok)
|
|
181
|
+
return null;
|
|
182
|
+
const payload = await response.json();
|
|
183
|
+
const project = payload.project && typeof payload.project === "object" && !Array.isArray(payload.project) ? payload.project : null;
|
|
184
|
+
const checkouts = Array.isArray(project?.checkouts) ? project.checkouts : [];
|
|
185
|
+
const latestCheckout = [...checkouts].reverse().find((entry) => Boolean(entry && typeof entry === "object" && !Array.isArray(entry) && typeof entry.path === "string"));
|
|
186
|
+
const path = typeof latestCheckout?.path === "string" && latestCheckout.path.trim() ? latestCheckout.path.trim() : null;
|
|
187
|
+
if (path)
|
|
188
|
+
writeRepoServerProjectRoot(projectRoot, path);
|
|
189
|
+
return path;
|
|
190
|
+
} catch {
|
|
191
|
+
return null;
|
|
192
|
+
}
|
|
193
|
+
}
|
|
152
194
|
function mergeHeaders(headers, authToken) {
|
|
153
195
|
const merged = new Headers(headers);
|
|
154
196
|
if (authToken) {
|
|
@@ -173,9 +215,12 @@ function diagnosticMessage(payload) {
|
|
|
173
215
|
}
|
|
174
216
|
async function requestServerJson(context, pathname, init = {}) {
|
|
175
217
|
const server = await ensureServerForCli(context.projectRoot);
|
|
218
|
+
const headers = mergeHeaders(init.headers, server.authToken);
|
|
219
|
+
if (server.serverProjectRoot)
|
|
220
|
+
headers.set("x-rig-project-root", server.serverProjectRoot);
|
|
176
221
|
const response = await fetch(`${server.baseUrl}${pathname}`, {
|
|
177
222
|
...init,
|
|
178
|
-
headers
|
|
223
|
+
headers
|
|
179
224
|
});
|
|
180
225
|
const text = await response.text();
|
|
181
226
|
const payload = text.trim().length > 0 ? (() => {
|
|
@@ -38,6 +38,11 @@ function readJsonFile(path) {
|
|
|
38
38
|
throw new CliError2(`Invalid Rig connection state at ${path}: ${error instanceof Error ? error.message : String(error)}`, 1);
|
|
39
39
|
}
|
|
40
40
|
}
|
|
41
|
+
function writeJsonFile(path, value) {
|
|
42
|
+
mkdirSync(dirname(path), { recursive: true });
|
|
43
|
+
writeFileSync(path, `${JSON.stringify(value, null, 2)}
|
|
44
|
+
`, "utf8");
|
|
45
|
+
}
|
|
41
46
|
function normalizeConnection(value) {
|
|
42
47
|
if (!value || typeof value !== "object" || Array.isArray(value))
|
|
43
48
|
return null;
|
|
@@ -78,21 +83,31 @@ function readRepoConnection(projectRoot) {
|
|
|
78
83
|
return {
|
|
79
84
|
selected,
|
|
80
85
|
project: typeof record.project === "string" ? record.project : undefined,
|
|
81
|
-
linkedAt: typeof record.linkedAt === "string" ? record.linkedAt : undefined
|
|
86
|
+
linkedAt: typeof record.linkedAt === "string" ? record.linkedAt : undefined,
|
|
87
|
+
serverProjectRoot: typeof record.serverProjectRoot === "string" && record.serverProjectRoot.trim() ? record.serverProjectRoot.trim() : undefined
|
|
82
88
|
};
|
|
83
89
|
}
|
|
90
|
+
function writeRepoConnection(projectRoot, state) {
|
|
91
|
+
writeJsonFile(resolveRepoConnectionPath(projectRoot), state);
|
|
92
|
+
}
|
|
84
93
|
function resolveSelectedConnection(projectRoot, options = {}) {
|
|
85
94
|
const repo = readRepoConnection(projectRoot);
|
|
86
95
|
if (!repo)
|
|
87
96
|
return null;
|
|
88
97
|
if (repo.selected === "local")
|
|
89
|
-
return { alias: "local", connection: { kind: "local", mode: "auto" } };
|
|
98
|
+
return { alias: "local", connection: { kind: "local", mode: "auto" }, serverProjectRoot: repo.serverProjectRoot };
|
|
90
99
|
const global = readGlobalConnections(options);
|
|
91
100
|
const connection = global.connections[repo.selected];
|
|
92
101
|
if (!connection) {
|
|
93
102
|
throw new CliError2(`Selected Rig server "${repo.selected}" was not found. Run \`rig server list\` or \`rig server use local\`.`, 1);
|
|
94
103
|
}
|
|
95
|
-
return { alias: repo.selected, connection };
|
|
104
|
+
return { alias: repo.selected, connection, serverProjectRoot: repo.serverProjectRoot };
|
|
105
|
+
}
|
|
106
|
+
function writeRepoServerProjectRoot(projectRoot, serverProjectRoot) {
|
|
107
|
+
const repo = readRepoConnection(projectRoot);
|
|
108
|
+
if (!repo)
|
|
109
|
+
return;
|
|
110
|
+
writeRepoConnection(projectRoot, { ...repo, serverProjectRoot });
|
|
96
111
|
}
|
|
97
112
|
|
|
98
113
|
// packages/cli/src/commands/_server-client.ts
|
|
@@ -125,17 +140,21 @@ async function ensureServerForCli(projectRoot) {
|
|
|
125
140
|
try {
|
|
126
141
|
const selected = resolveSelectedConnection(projectRoot);
|
|
127
142
|
if (selected?.connection.kind === "remote") {
|
|
143
|
+
const authToken = readGitHubBearerTokenForRemote(projectRoot);
|
|
144
|
+
const serverProjectRoot = selected.serverProjectRoot ?? await backfillRemoteServerProjectRoot(projectRoot, selected.connection.baseUrl, authToken);
|
|
128
145
|
return {
|
|
129
146
|
baseUrl: selected.connection.baseUrl,
|
|
130
|
-
authToken
|
|
131
|
-
connectionKind: "remote"
|
|
147
|
+
authToken,
|
|
148
|
+
connectionKind: "remote",
|
|
149
|
+
serverProjectRoot
|
|
132
150
|
};
|
|
133
151
|
}
|
|
134
152
|
const connection = await ensureLocalRigServerConnection(projectRoot);
|
|
135
153
|
return {
|
|
136
154
|
baseUrl: connection.baseUrl,
|
|
137
155
|
authToken: connection.authToken,
|
|
138
|
-
connectionKind: "local"
|
|
156
|
+
connectionKind: "local",
|
|
157
|
+
serverProjectRoot: resolve2(projectRoot)
|
|
139
158
|
};
|
|
140
159
|
} catch (error) {
|
|
141
160
|
if (error instanceof Error) {
|
|
@@ -144,6 +163,29 @@ async function ensureServerForCli(projectRoot) {
|
|
|
144
163
|
throw error;
|
|
145
164
|
}
|
|
146
165
|
}
|
|
166
|
+
async function backfillRemoteServerProjectRoot(projectRoot, baseUrl, authToken) {
|
|
167
|
+
const repo = readRepoConnection(projectRoot);
|
|
168
|
+
const slug = repo?.project?.trim();
|
|
169
|
+
if (!slug)
|
|
170
|
+
return null;
|
|
171
|
+
try {
|
|
172
|
+
const response = await fetch(`${baseUrl}/api/projects/${encodeURIComponent(slug)}`, {
|
|
173
|
+
headers: mergeHeaders(undefined, authToken)
|
|
174
|
+
});
|
|
175
|
+
if (!response.ok)
|
|
176
|
+
return null;
|
|
177
|
+
const payload = await response.json();
|
|
178
|
+
const project = payload.project && typeof payload.project === "object" && !Array.isArray(payload.project) ? payload.project : null;
|
|
179
|
+
const checkouts = Array.isArray(project?.checkouts) ? project.checkouts : [];
|
|
180
|
+
const latestCheckout = [...checkouts].reverse().find((entry) => Boolean(entry && typeof entry === "object" && !Array.isArray(entry) && typeof entry.path === "string"));
|
|
181
|
+
const path = typeof latestCheckout?.path === "string" && latestCheckout.path.trim() ? latestCheckout.path.trim() : null;
|
|
182
|
+
if (path)
|
|
183
|
+
writeRepoServerProjectRoot(projectRoot, path);
|
|
184
|
+
return path;
|
|
185
|
+
} catch {
|
|
186
|
+
return null;
|
|
187
|
+
}
|
|
188
|
+
}
|
|
147
189
|
function mergeHeaders(headers, authToken) {
|
|
148
190
|
const merged = new Headers(headers);
|
|
149
191
|
if (authToken) {
|
|
@@ -168,9 +210,12 @@ function diagnosticMessage(payload) {
|
|
|
168
210
|
}
|
|
169
211
|
async function requestServerJson(context, pathname, init = {}) {
|
|
170
212
|
const server = await ensureServerForCli(context.projectRoot);
|
|
213
|
+
const headers = mergeHeaders(init.headers, server.authToken);
|
|
214
|
+
if (server.serverProjectRoot)
|
|
215
|
+
headers.set("x-rig-project-root", server.serverProjectRoot);
|
|
171
216
|
const response = await fetch(`${server.baseUrl}${pathname}`, {
|
|
172
217
|
...init,
|
|
173
|
-
headers
|
|
218
|
+
headers
|
|
174
219
|
});
|
|
175
220
|
const text = await response.text();
|
|
176
221
|
const payload = text.trim().length > 0 ? (() => {
|
|
@@ -287,6 +332,8 @@ async function buildRunPiEventsWebSocketUrl(context, runId) {
|
|
|
287
332
|
url.protocol = url.protocol === "https:" ? "wss:" : "ws:";
|
|
288
333
|
if (server.authToken)
|
|
289
334
|
url.searchParams.set("token", server.authToken);
|
|
335
|
+
if (server.serverProjectRoot)
|
|
336
|
+
url.searchParams.set("rigProjectRoot", server.serverProjectRoot);
|
|
290
337
|
return url.toString();
|
|
291
338
|
}
|
|
292
339
|
|
|
@@ -48,6 +48,11 @@ function readJsonFile(path) {
|
|
|
48
48
|
throw new CliError2(`Invalid Rig connection state at ${path}: ${error instanceof Error ? error.message : String(error)}`, 1);
|
|
49
49
|
}
|
|
50
50
|
}
|
|
51
|
+
function writeJsonFile(path, value) {
|
|
52
|
+
mkdirSync(dirname(path), { recursive: true });
|
|
53
|
+
writeFileSync(path, `${JSON.stringify(value, null, 2)}
|
|
54
|
+
`, "utf8");
|
|
55
|
+
}
|
|
51
56
|
function normalizeConnection(value) {
|
|
52
57
|
if (!value || typeof value !== "object" || Array.isArray(value))
|
|
53
58
|
return null;
|
|
@@ -88,21 +93,31 @@ function readRepoConnection(projectRoot) {
|
|
|
88
93
|
return {
|
|
89
94
|
selected,
|
|
90
95
|
project: typeof record.project === "string" ? record.project : undefined,
|
|
91
|
-
linkedAt: typeof record.linkedAt === "string" ? record.linkedAt : undefined
|
|
96
|
+
linkedAt: typeof record.linkedAt === "string" ? record.linkedAt : undefined,
|
|
97
|
+
serverProjectRoot: typeof record.serverProjectRoot === "string" && record.serverProjectRoot.trim() ? record.serverProjectRoot.trim() : undefined
|
|
92
98
|
};
|
|
93
99
|
}
|
|
100
|
+
function writeRepoConnection(projectRoot, state) {
|
|
101
|
+
writeJsonFile(resolveRepoConnectionPath(projectRoot), state);
|
|
102
|
+
}
|
|
94
103
|
function resolveSelectedConnection(projectRoot, options = {}) {
|
|
95
104
|
const repo = readRepoConnection(projectRoot);
|
|
96
105
|
if (!repo)
|
|
97
106
|
return null;
|
|
98
107
|
if (repo.selected === "local")
|
|
99
|
-
return { alias: "local", connection: { kind: "local", mode: "auto" } };
|
|
108
|
+
return { alias: "local", connection: { kind: "local", mode: "auto" }, serverProjectRoot: repo.serverProjectRoot };
|
|
100
109
|
const global = readGlobalConnections(options);
|
|
101
110
|
const connection = global.connections[repo.selected];
|
|
102
111
|
if (!connection) {
|
|
103
112
|
throw new CliError2(`Selected Rig server "${repo.selected}" was not found. Run \`rig server list\` or \`rig server use local\`.`, 1);
|
|
104
113
|
}
|
|
105
|
-
return { alias: repo.selected, connection };
|
|
114
|
+
return { alias: repo.selected, connection, serverProjectRoot: repo.serverProjectRoot };
|
|
115
|
+
}
|
|
116
|
+
function writeRepoServerProjectRoot(projectRoot, serverProjectRoot) {
|
|
117
|
+
const repo = readRepoConnection(projectRoot);
|
|
118
|
+
if (!repo)
|
|
119
|
+
return;
|
|
120
|
+
writeRepoConnection(projectRoot, { ...repo, serverProjectRoot });
|
|
106
121
|
}
|
|
107
122
|
|
|
108
123
|
// packages/cli/src/commands/_server-client.ts
|
|
@@ -135,17 +150,21 @@ async function ensureServerForCli(projectRoot) {
|
|
|
135
150
|
try {
|
|
136
151
|
const selected = resolveSelectedConnection(projectRoot);
|
|
137
152
|
if (selected?.connection.kind === "remote") {
|
|
153
|
+
const authToken = readGitHubBearerTokenForRemote(projectRoot);
|
|
154
|
+
const serverProjectRoot = selected.serverProjectRoot ?? await backfillRemoteServerProjectRoot(projectRoot, selected.connection.baseUrl, authToken);
|
|
138
155
|
return {
|
|
139
156
|
baseUrl: selected.connection.baseUrl,
|
|
140
|
-
authToken
|
|
141
|
-
connectionKind: "remote"
|
|
157
|
+
authToken,
|
|
158
|
+
connectionKind: "remote",
|
|
159
|
+
serverProjectRoot
|
|
142
160
|
};
|
|
143
161
|
}
|
|
144
162
|
const connection = await ensureLocalRigServerConnection(projectRoot);
|
|
145
163
|
return {
|
|
146
164
|
baseUrl: connection.baseUrl,
|
|
147
165
|
authToken: connection.authToken,
|
|
148
|
-
connectionKind: "local"
|
|
166
|
+
connectionKind: "local",
|
|
167
|
+
serverProjectRoot: resolve2(projectRoot)
|
|
149
168
|
};
|
|
150
169
|
} catch (error) {
|
|
151
170
|
if (error instanceof Error) {
|
|
@@ -154,6 +173,29 @@ async function ensureServerForCli(projectRoot) {
|
|
|
154
173
|
throw error;
|
|
155
174
|
}
|
|
156
175
|
}
|
|
176
|
+
async function backfillRemoteServerProjectRoot(projectRoot, baseUrl, authToken) {
|
|
177
|
+
const repo = readRepoConnection(projectRoot);
|
|
178
|
+
const slug = repo?.project?.trim();
|
|
179
|
+
if (!slug)
|
|
180
|
+
return null;
|
|
181
|
+
try {
|
|
182
|
+
const response = await fetch(`${baseUrl}/api/projects/${encodeURIComponent(slug)}`, {
|
|
183
|
+
headers: mergeHeaders(undefined, authToken)
|
|
184
|
+
});
|
|
185
|
+
if (!response.ok)
|
|
186
|
+
return null;
|
|
187
|
+
const payload = await response.json();
|
|
188
|
+
const project = payload.project && typeof payload.project === "object" && !Array.isArray(payload.project) ? payload.project : null;
|
|
189
|
+
const checkouts = Array.isArray(project?.checkouts) ? project.checkouts : [];
|
|
190
|
+
const latestCheckout = [...checkouts].reverse().find((entry) => Boolean(entry && typeof entry === "object" && !Array.isArray(entry) && typeof entry.path === "string"));
|
|
191
|
+
const path = typeof latestCheckout?.path === "string" && latestCheckout.path.trim() ? latestCheckout.path.trim() : null;
|
|
192
|
+
if (path)
|
|
193
|
+
writeRepoServerProjectRoot(projectRoot, path);
|
|
194
|
+
return path;
|
|
195
|
+
} catch {
|
|
196
|
+
return null;
|
|
197
|
+
}
|
|
198
|
+
}
|
|
157
199
|
function mergeHeaders(headers, authToken) {
|
|
158
200
|
const merged = new Headers(headers);
|
|
159
201
|
if (authToken) {
|
|
@@ -178,9 +220,12 @@ function diagnosticMessage(payload) {
|
|
|
178
220
|
}
|
|
179
221
|
async function requestServerJson(context, pathname, init = {}) {
|
|
180
222
|
const server = await ensureServerForCli(context.projectRoot);
|
|
223
|
+
const headers = mergeHeaders(init.headers, server.authToken);
|
|
224
|
+
if (server.serverProjectRoot)
|
|
225
|
+
headers.set("x-rig-project-root", server.serverProjectRoot);
|
|
181
226
|
const response = await fetch(`${server.baseUrl}${pathname}`, {
|
|
182
227
|
...init,
|
|
183
|
-
headers
|
|
228
|
+
headers
|
|
184
229
|
});
|
|
185
230
|
const text = await response.text();
|
|
186
231
|
const payload = text.trim().length > 0 ? (() => {
|
|
@@ -263,6 +308,8 @@ async function buildRunPiEventsWebSocketUrl(context, runId) {
|
|
|
263
308
|
url.protocol = url.protocol === "https:" ? "wss:" : "ws:";
|
|
264
309
|
if (server.authToken)
|
|
265
310
|
url.searchParams.set("token", server.authToken);
|
|
311
|
+
if (server.serverProjectRoot)
|
|
312
|
+
url.searchParams.set("rigProjectRoot", server.serverProjectRoot);
|
|
266
313
|
return url.toString();
|
|
267
314
|
}
|
|
268
315
|
|
|
@@ -41,6 +41,11 @@ function readJsonFile(path) {
|
|
|
41
41
|
throw new CliError2(`Invalid Rig connection state at ${path}: ${error instanceof Error ? error.message : String(error)}`, 1);
|
|
42
42
|
}
|
|
43
43
|
}
|
|
44
|
+
function writeJsonFile(path, value) {
|
|
45
|
+
mkdirSync(dirname(path), { recursive: true });
|
|
46
|
+
writeFileSync(path, `${JSON.stringify(value, null, 2)}
|
|
47
|
+
`, "utf8");
|
|
48
|
+
}
|
|
44
49
|
function normalizeConnection(value) {
|
|
45
50
|
if (!value || typeof value !== "object" || Array.isArray(value))
|
|
46
51
|
return null;
|
|
@@ -81,21 +86,31 @@ function readRepoConnection(projectRoot) {
|
|
|
81
86
|
return {
|
|
82
87
|
selected,
|
|
83
88
|
project: typeof record.project === "string" ? record.project : undefined,
|
|
84
|
-
linkedAt: typeof record.linkedAt === "string" ? record.linkedAt : undefined
|
|
89
|
+
linkedAt: typeof record.linkedAt === "string" ? record.linkedAt : undefined,
|
|
90
|
+
serverProjectRoot: typeof record.serverProjectRoot === "string" && record.serverProjectRoot.trim() ? record.serverProjectRoot.trim() : undefined
|
|
85
91
|
};
|
|
86
92
|
}
|
|
93
|
+
function writeRepoConnection(projectRoot, state) {
|
|
94
|
+
writeJsonFile(resolveRepoConnectionPath(projectRoot), state);
|
|
95
|
+
}
|
|
87
96
|
function resolveSelectedConnection(projectRoot, options = {}) {
|
|
88
97
|
const repo = readRepoConnection(projectRoot);
|
|
89
98
|
if (!repo)
|
|
90
99
|
return null;
|
|
91
100
|
if (repo.selected === "local")
|
|
92
|
-
return { alias: "local", connection: { kind: "local", mode: "auto" } };
|
|
101
|
+
return { alias: "local", connection: { kind: "local", mode: "auto" }, serverProjectRoot: repo.serverProjectRoot };
|
|
93
102
|
const global = readGlobalConnections(options);
|
|
94
103
|
const connection = global.connections[repo.selected];
|
|
95
104
|
if (!connection) {
|
|
96
105
|
throw new CliError2(`Selected Rig server "${repo.selected}" was not found. Run \`rig server list\` or \`rig server use local\`.`, 1);
|
|
97
106
|
}
|
|
98
|
-
return { alias: repo.selected, connection };
|
|
107
|
+
return { alias: repo.selected, connection, serverProjectRoot: repo.serverProjectRoot };
|
|
108
|
+
}
|
|
109
|
+
function writeRepoServerProjectRoot(projectRoot, serverProjectRoot) {
|
|
110
|
+
const repo = readRepoConnection(projectRoot);
|
|
111
|
+
if (!repo)
|
|
112
|
+
return;
|
|
113
|
+
writeRepoConnection(projectRoot, { ...repo, serverProjectRoot });
|
|
99
114
|
}
|
|
100
115
|
|
|
101
116
|
// packages/cli/src/commands/_server-client.ts
|
|
@@ -128,17 +143,21 @@ async function ensureServerForCli(projectRoot) {
|
|
|
128
143
|
try {
|
|
129
144
|
const selected = resolveSelectedConnection(projectRoot);
|
|
130
145
|
if (selected?.connection.kind === "remote") {
|
|
146
|
+
const authToken = readGitHubBearerTokenForRemote(projectRoot);
|
|
147
|
+
const serverProjectRoot = selected.serverProjectRoot ?? await backfillRemoteServerProjectRoot(projectRoot, selected.connection.baseUrl, authToken);
|
|
131
148
|
return {
|
|
132
149
|
baseUrl: selected.connection.baseUrl,
|
|
133
|
-
authToken
|
|
134
|
-
connectionKind: "remote"
|
|
150
|
+
authToken,
|
|
151
|
+
connectionKind: "remote",
|
|
152
|
+
serverProjectRoot
|
|
135
153
|
};
|
|
136
154
|
}
|
|
137
155
|
const connection = await ensureLocalRigServerConnection(projectRoot);
|
|
138
156
|
return {
|
|
139
157
|
baseUrl: connection.baseUrl,
|
|
140
158
|
authToken: connection.authToken,
|
|
141
|
-
connectionKind: "local"
|
|
159
|
+
connectionKind: "local",
|
|
160
|
+
serverProjectRoot: resolve2(projectRoot)
|
|
142
161
|
};
|
|
143
162
|
} catch (error) {
|
|
144
163
|
if (error instanceof Error) {
|
|
@@ -147,6 +166,29 @@ async function ensureServerForCli(projectRoot) {
|
|
|
147
166
|
throw error;
|
|
148
167
|
}
|
|
149
168
|
}
|
|
169
|
+
async function backfillRemoteServerProjectRoot(projectRoot, baseUrl, authToken) {
|
|
170
|
+
const repo = readRepoConnection(projectRoot);
|
|
171
|
+
const slug = repo?.project?.trim();
|
|
172
|
+
if (!slug)
|
|
173
|
+
return null;
|
|
174
|
+
try {
|
|
175
|
+
const response = await fetch(`${baseUrl}/api/projects/${encodeURIComponent(slug)}`, {
|
|
176
|
+
headers: mergeHeaders(undefined, authToken)
|
|
177
|
+
});
|
|
178
|
+
if (!response.ok)
|
|
179
|
+
return null;
|
|
180
|
+
const payload = await response.json();
|
|
181
|
+
const project = payload.project && typeof payload.project === "object" && !Array.isArray(payload.project) ? payload.project : null;
|
|
182
|
+
const checkouts = Array.isArray(project?.checkouts) ? project.checkouts : [];
|
|
183
|
+
const latestCheckout = [...checkouts].reverse().find((entry) => Boolean(entry && typeof entry === "object" && !Array.isArray(entry) && typeof entry.path === "string"));
|
|
184
|
+
const path = typeof latestCheckout?.path === "string" && latestCheckout.path.trim() ? latestCheckout.path.trim() : null;
|
|
185
|
+
if (path)
|
|
186
|
+
writeRepoServerProjectRoot(projectRoot, path);
|
|
187
|
+
return path;
|
|
188
|
+
} catch {
|
|
189
|
+
return null;
|
|
190
|
+
}
|
|
191
|
+
}
|
|
150
192
|
function mergeHeaders(headers, authToken) {
|
|
151
193
|
const merged = new Headers(headers);
|
|
152
194
|
if (authToken) {
|
|
@@ -171,9 +213,12 @@ function diagnosticMessage(payload) {
|
|
|
171
213
|
}
|
|
172
214
|
async function requestServerJson(context, pathname, init = {}) {
|
|
173
215
|
const server = await ensureServerForCli(context.projectRoot);
|
|
216
|
+
const headers = mergeHeaders(init.headers, server.authToken);
|
|
217
|
+
if (server.serverProjectRoot)
|
|
218
|
+
headers.set("x-rig-project-root", server.serverProjectRoot);
|
|
174
219
|
const response = await fetch(`${server.baseUrl}${pathname}`, {
|
|
175
220
|
...init,
|
|
176
|
-
headers
|
|
221
|
+
headers
|
|
177
222
|
});
|
|
178
223
|
const text = await response.text();
|
|
179
224
|
const payload = text.trim().length > 0 ? (() => {
|
|
@@ -244,6 +289,8 @@ async function buildRunPiEventsWebSocketUrl(context, runId) {
|
|
|
244
289
|
url.protocol = url.protocol === "https:" ? "wss:" : "ws:";
|
|
245
290
|
if (server.authToken)
|
|
246
291
|
url.searchParams.set("token", server.authToken);
|
|
292
|
+
if (server.serverProjectRoot)
|
|
293
|
+
url.searchParams.set("rigProjectRoot", server.serverProjectRoot);
|
|
247
294
|
return url.toString();
|
|
248
295
|
}
|
|
249
296
|
|