@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
|
@@ -59,6 +59,11 @@ function readJsonFile(path) {
|
|
|
59
59
|
throw new CliError2(`Invalid Rig connection state at ${path}: ${error instanceof Error ? error.message : String(error)}`, 1);
|
|
60
60
|
}
|
|
61
61
|
}
|
|
62
|
+
function writeJsonFile(path, value) {
|
|
63
|
+
mkdirSync(dirname(path), { recursive: true });
|
|
64
|
+
writeFileSync(path, `${JSON.stringify(value, null, 2)}
|
|
65
|
+
`, "utf8");
|
|
66
|
+
}
|
|
62
67
|
function normalizeConnection(value) {
|
|
63
68
|
if (!value || typeof value !== "object" || Array.isArray(value))
|
|
64
69
|
return null;
|
|
@@ -99,21 +104,31 @@ function readRepoConnection(projectRoot) {
|
|
|
99
104
|
return {
|
|
100
105
|
selected,
|
|
101
106
|
project: typeof record.project === "string" ? record.project : undefined,
|
|
102
|
-
linkedAt: typeof record.linkedAt === "string" ? record.linkedAt : undefined
|
|
107
|
+
linkedAt: typeof record.linkedAt === "string" ? record.linkedAt : undefined,
|
|
108
|
+
serverProjectRoot: typeof record.serverProjectRoot === "string" && record.serverProjectRoot.trim() ? record.serverProjectRoot.trim() : undefined
|
|
103
109
|
};
|
|
104
110
|
}
|
|
111
|
+
function writeRepoConnection(projectRoot, state) {
|
|
112
|
+
writeJsonFile(resolveRepoConnectionPath(projectRoot), state);
|
|
113
|
+
}
|
|
105
114
|
function resolveSelectedConnection(projectRoot, options = {}) {
|
|
106
115
|
const repo = readRepoConnection(projectRoot);
|
|
107
116
|
if (!repo)
|
|
108
117
|
return null;
|
|
109
118
|
if (repo.selected === "local")
|
|
110
|
-
return { alias: "local", connection: { kind: "local", mode: "auto" } };
|
|
119
|
+
return { alias: "local", connection: { kind: "local", mode: "auto" }, serverProjectRoot: repo.serverProjectRoot };
|
|
111
120
|
const global = readGlobalConnections(options);
|
|
112
121
|
const connection = global.connections[repo.selected];
|
|
113
122
|
if (!connection) {
|
|
114
123
|
throw new CliError2(`Selected Rig server "${repo.selected}" was not found. Run \`rig server list\` or \`rig server use local\`.`, 1);
|
|
115
124
|
}
|
|
116
|
-
return { alias: repo.selected, connection };
|
|
125
|
+
return { alias: repo.selected, connection, serverProjectRoot: repo.serverProjectRoot };
|
|
126
|
+
}
|
|
127
|
+
function writeRepoServerProjectRoot(projectRoot, serverProjectRoot) {
|
|
128
|
+
const repo = readRepoConnection(projectRoot);
|
|
129
|
+
if (!repo)
|
|
130
|
+
return;
|
|
131
|
+
writeRepoConnection(projectRoot, { ...repo, serverProjectRoot });
|
|
117
132
|
}
|
|
118
133
|
|
|
119
134
|
// packages/cli/src/commands/_server-client.ts
|
|
@@ -146,17 +161,21 @@ async function ensureServerForCli(projectRoot) {
|
|
|
146
161
|
try {
|
|
147
162
|
const selected = resolveSelectedConnection(projectRoot);
|
|
148
163
|
if (selected?.connection.kind === "remote") {
|
|
164
|
+
const authToken = readGitHubBearerTokenForRemote(projectRoot);
|
|
165
|
+
const serverProjectRoot = selected.serverProjectRoot ?? await backfillRemoteServerProjectRoot(projectRoot, selected.connection.baseUrl, authToken);
|
|
149
166
|
return {
|
|
150
167
|
baseUrl: selected.connection.baseUrl,
|
|
151
|
-
authToken
|
|
152
|
-
connectionKind: "remote"
|
|
168
|
+
authToken,
|
|
169
|
+
connectionKind: "remote",
|
|
170
|
+
serverProjectRoot
|
|
153
171
|
};
|
|
154
172
|
}
|
|
155
173
|
const connection = await ensureLocalRigServerConnection(projectRoot);
|
|
156
174
|
return {
|
|
157
175
|
baseUrl: connection.baseUrl,
|
|
158
176
|
authToken: connection.authToken,
|
|
159
|
-
connectionKind: "local"
|
|
177
|
+
connectionKind: "local",
|
|
178
|
+
serverProjectRoot: resolve2(projectRoot)
|
|
160
179
|
};
|
|
161
180
|
} catch (error) {
|
|
162
181
|
if (error instanceof Error) {
|
|
@@ -165,6 +184,29 @@ async function ensureServerForCli(projectRoot) {
|
|
|
165
184
|
throw error;
|
|
166
185
|
}
|
|
167
186
|
}
|
|
187
|
+
async function backfillRemoteServerProjectRoot(projectRoot, baseUrl, authToken) {
|
|
188
|
+
const repo = readRepoConnection(projectRoot);
|
|
189
|
+
const slug = repo?.project?.trim();
|
|
190
|
+
if (!slug)
|
|
191
|
+
return null;
|
|
192
|
+
try {
|
|
193
|
+
const response = await fetch(`${baseUrl}/api/projects/${encodeURIComponent(slug)}`, {
|
|
194
|
+
headers: mergeHeaders(undefined, authToken)
|
|
195
|
+
});
|
|
196
|
+
if (!response.ok)
|
|
197
|
+
return null;
|
|
198
|
+
const payload = await response.json();
|
|
199
|
+
const project = payload.project && typeof payload.project === "object" && !Array.isArray(payload.project) ? payload.project : null;
|
|
200
|
+
const checkouts = Array.isArray(project?.checkouts) ? project.checkouts : [];
|
|
201
|
+
const latestCheckout = [...checkouts].reverse().find((entry) => Boolean(entry && typeof entry === "object" && !Array.isArray(entry) && typeof entry.path === "string"));
|
|
202
|
+
const path = typeof latestCheckout?.path === "string" && latestCheckout.path.trim() ? latestCheckout.path.trim() : null;
|
|
203
|
+
if (path)
|
|
204
|
+
writeRepoServerProjectRoot(projectRoot, path);
|
|
205
|
+
return path;
|
|
206
|
+
} catch {
|
|
207
|
+
return null;
|
|
208
|
+
}
|
|
209
|
+
}
|
|
168
210
|
function mergeHeaders(headers, authToken) {
|
|
169
211
|
const merged = new Headers(headers);
|
|
170
212
|
if (authToken) {
|
|
@@ -189,9 +231,12 @@ function diagnosticMessage(payload) {
|
|
|
189
231
|
}
|
|
190
232
|
async function requestServerJson(context, pathname, init = {}) {
|
|
191
233
|
const server = await ensureServerForCli(context.projectRoot);
|
|
234
|
+
const headers = mergeHeaders(init.headers, server.authToken);
|
|
235
|
+
if (server.serverProjectRoot)
|
|
236
|
+
headers.set("x-rig-project-root", server.serverProjectRoot);
|
|
192
237
|
const response = await fetch(`${server.baseUrl}${pathname}`, {
|
|
193
238
|
...init,
|
|
194
|
-
headers
|
|
239
|
+
headers
|
|
195
240
|
});
|
|
196
241
|
const text = await response.text();
|
|
197
242
|
const payload = text.trim().length > 0 ? (() => {
|
|
@@ -150,6 +150,11 @@ function readJsonFile(path) {
|
|
|
150
150
|
throw new CliError2(`Invalid Rig connection state at ${path}: ${error instanceof Error ? error.message : String(error)}`, 1);
|
|
151
151
|
}
|
|
152
152
|
}
|
|
153
|
+
function writeJsonFile(path, value) {
|
|
154
|
+
mkdirSync(dirname(path), { recursive: true });
|
|
155
|
+
writeFileSync(path, `${JSON.stringify(value, null, 2)}
|
|
156
|
+
`, "utf8");
|
|
157
|
+
}
|
|
153
158
|
function normalizeConnection(value) {
|
|
154
159
|
if (!value || typeof value !== "object" || Array.isArray(value))
|
|
155
160
|
return null;
|
|
@@ -190,21 +195,31 @@ function readRepoConnection(projectRoot) {
|
|
|
190
195
|
return {
|
|
191
196
|
selected,
|
|
192
197
|
project: typeof record.project === "string" ? record.project : undefined,
|
|
193
|
-
linkedAt: typeof record.linkedAt === "string" ? record.linkedAt : undefined
|
|
198
|
+
linkedAt: typeof record.linkedAt === "string" ? record.linkedAt : undefined,
|
|
199
|
+
serverProjectRoot: typeof record.serverProjectRoot === "string" && record.serverProjectRoot.trim() ? record.serverProjectRoot.trim() : undefined
|
|
194
200
|
};
|
|
195
201
|
}
|
|
202
|
+
function writeRepoConnection(projectRoot, state) {
|
|
203
|
+
writeJsonFile(resolveRepoConnectionPath(projectRoot), state);
|
|
204
|
+
}
|
|
196
205
|
function resolveSelectedConnection(projectRoot, options = {}) {
|
|
197
206
|
const repo = readRepoConnection(projectRoot);
|
|
198
207
|
if (!repo)
|
|
199
208
|
return null;
|
|
200
209
|
if (repo.selected === "local")
|
|
201
|
-
return { alias: "local", connection: { kind: "local", mode: "auto" } };
|
|
210
|
+
return { alias: "local", connection: { kind: "local", mode: "auto" }, serverProjectRoot: repo.serverProjectRoot };
|
|
202
211
|
const global = readGlobalConnections(options);
|
|
203
212
|
const connection = global.connections[repo.selected];
|
|
204
213
|
if (!connection) {
|
|
205
214
|
throw new CliError2(`Selected Rig server "${repo.selected}" was not found. Run \`rig server list\` or \`rig server use local\`.`, 1);
|
|
206
215
|
}
|
|
207
|
-
return { alias: repo.selected, connection };
|
|
216
|
+
return { alias: repo.selected, connection, serverProjectRoot: repo.serverProjectRoot };
|
|
217
|
+
}
|
|
218
|
+
function writeRepoServerProjectRoot(projectRoot, serverProjectRoot) {
|
|
219
|
+
const repo = readRepoConnection(projectRoot);
|
|
220
|
+
if (!repo)
|
|
221
|
+
return;
|
|
222
|
+
writeRepoConnection(projectRoot, { ...repo, serverProjectRoot });
|
|
208
223
|
}
|
|
209
224
|
|
|
210
225
|
// packages/cli/src/commands/_server-client.ts
|
|
@@ -240,17 +255,21 @@ async function ensureServerForCli(projectRoot) {
|
|
|
240
255
|
try {
|
|
241
256
|
const selected = resolveSelectedConnection(projectRoot);
|
|
242
257
|
if (selected?.connection.kind === "remote") {
|
|
258
|
+
const authToken = readGitHubBearerTokenForRemote(projectRoot);
|
|
259
|
+
const serverProjectRoot = selected.serverProjectRoot ?? await backfillRemoteServerProjectRoot(projectRoot, selected.connection.baseUrl, authToken);
|
|
243
260
|
return {
|
|
244
261
|
baseUrl: selected.connection.baseUrl,
|
|
245
|
-
authToken
|
|
246
|
-
connectionKind: "remote"
|
|
262
|
+
authToken,
|
|
263
|
+
connectionKind: "remote",
|
|
264
|
+
serverProjectRoot
|
|
247
265
|
};
|
|
248
266
|
}
|
|
249
267
|
const connection = await ensureLocalRigServerConnection(projectRoot);
|
|
250
268
|
return {
|
|
251
269
|
baseUrl: connection.baseUrl,
|
|
252
270
|
authToken: connection.authToken,
|
|
253
|
-
connectionKind: "local"
|
|
271
|
+
connectionKind: "local",
|
|
272
|
+
serverProjectRoot: resolve2(projectRoot)
|
|
254
273
|
};
|
|
255
274
|
} catch (error) {
|
|
256
275
|
if (error instanceof Error) {
|
|
@@ -259,6 +278,29 @@ async function ensureServerForCli(projectRoot) {
|
|
|
259
278
|
throw error;
|
|
260
279
|
}
|
|
261
280
|
}
|
|
281
|
+
async function backfillRemoteServerProjectRoot(projectRoot, baseUrl, authToken) {
|
|
282
|
+
const repo = readRepoConnection(projectRoot);
|
|
283
|
+
const slug = repo?.project?.trim();
|
|
284
|
+
if (!slug)
|
|
285
|
+
return null;
|
|
286
|
+
try {
|
|
287
|
+
const response = await fetch(`${baseUrl}/api/projects/${encodeURIComponent(slug)}`, {
|
|
288
|
+
headers: mergeHeaders(undefined, authToken)
|
|
289
|
+
});
|
|
290
|
+
if (!response.ok)
|
|
291
|
+
return null;
|
|
292
|
+
const payload = await response.json();
|
|
293
|
+
const project = payload.project && typeof payload.project === "object" && !Array.isArray(payload.project) ? payload.project : null;
|
|
294
|
+
const checkouts = Array.isArray(project?.checkouts) ? project.checkouts : [];
|
|
295
|
+
const latestCheckout = [...checkouts].reverse().find((entry) => Boolean(entry && typeof entry === "object" && !Array.isArray(entry) && typeof entry.path === "string"));
|
|
296
|
+
const path = typeof latestCheckout?.path === "string" && latestCheckout.path.trim() ? latestCheckout.path.trim() : null;
|
|
297
|
+
if (path)
|
|
298
|
+
writeRepoServerProjectRoot(projectRoot, path);
|
|
299
|
+
return path;
|
|
300
|
+
} catch {
|
|
301
|
+
return null;
|
|
302
|
+
}
|
|
303
|
+
}
|
|
262
304
|
function mergeHeaders(headers, authToken) {
|
|
263
305
|
const merged = new Headers(headers);
|
|
264
306
|
if (authToken) {
|
|
@@ -283,9 +325,12 @@ function diagnosticMessage(payload) {
|
|
|
283
325
|
}
|
|
284
326
|
async function requestServerJson(context, pathname, init = {}) {
|
|
285
327
|
const server = await ensureServerForCli(context.projectRoot);
|
|
328
|
+
const headers = mergeHeaders(init.headers, server.authToken);
|
|
329
|
+
if (server.serverProjectRoot)
|
|
330
|
+
headers.set("x-rig-project-root", server.serverProjectRoot);
|
|
286
331
|
const response = await fetch(`${server.baseUrl}${pathname}`, {
|
|
287
332
|
...init,
|
|
288
|
-
headers
|
|
333
|
+
headers
|
|
289
334
|
});
|
|
290
335
|
const text = await response.text();
|
|
291
336
|
const payload = text.trim().length > 0 ? (() => {
|
|
@@ -131,7 +131,8 @@ function readRepoConnection(projectRoot) {
|
|
|
131
131
|
return {
|
|
132
132
|
selected,
|
|
133
133
|
project: typeof record.project === "string" ? record.project : undefined,
|
|
134
|
-
linkedAt: typeof record.linkedAt === "string" ? record.linkedAt : undefined
|
|
134
|
+
linkedAt: typeof record.linkedAt === "string" ? record.linkedAt : undefined,
|
|
135
|
+
serverProjectRoot: typeof record.serverProjectRoot === "string" && record.serverProjectRoot.trim() ? record.serverProjectRoot.trim() : undefined
|
|
135
136
|
};
|
|
136
137
|
}
|
|
137
138
|
function writeRepoConnection(projectRoot, state) {
|
|
@@ -142,13 +143,19 @@ function resolveSelectedConnection(projectRoot, options = {}) {
|
|
|
142
143
|
if (!repo)
|
|
143
144
|
return null;
|
|
144
145
|
if (repo.selected === "local")
|
|
145
|
-
return { alias: "local", connection: { kind: "local", mode: "auto" } };
|
|
146
|
+
return { alias: "local", connection: { kind: "local", mode: "auto" }, serverProjectRoot: repo.serverProjectRoot };
|
|
146
147
|
const global = readGlobalConnections(options);
|
|
147
148
|
const connection = global.connections[repo.selected];
|
|
148
149
|
if (!connection) {
|
|
149
150
|
throw new CliError2(`Selected Rig server "${repo.selected}" was not found. Run \`rig server list\` or \`rig server use local\`.`, 1);
|
|
150
151
|
}
|
|
151
|
-
return { alias: repo.selected, connection };
|
|
152
|
+
return { alias: repo.selected, connection, serverProjectRoot: repo.serverProjectRoot };
|
|
153
|
+
}
|
|
154
|
+
function writeRepoServerProjectRoot(projectRoot, serverProjectRoot) {
|
|
155
|
+
const repo = readRepoConnection(projectRoot);
|
|
156
|
+
if (!repo)
|
|
157
|
+
return;
|
|
158
|
+
writeRepoConnection(projectRoot, { ...repo, serverProjectRoot });
|
|
152
159
|
}
|
|
153
160
|
|
|
154
161
|
// packages/cli/src/commands/_server-client.ts
|
|
@@ -188,17 +195,21 @@ async function ensureServerForCli(projectRoot) {
|
|
|
188
195
|
try {
|
|
189
196
|
const selected = resolveSelectedConnection(projectRoot);
|
|
190
197
|
if (selected?.connection.kind === "remote") {
|
|
198
|
+
const authToken = readGitHubBearerTokenForRemote(projectRoot);
|
|
199
|
+
const serverProjectRoot = selected.serverProjectRoot ?? await backfillRemoteServerProjectRoot(projectRoot, selected.connection.baseUrl, authToken);
|
|
191
200
|
return {
|
|
192
201
|
baseUrl: selected.connection.baseUrl,
|
|
193
|
-
authToken
|
|
194
|
-
connectionKind: "remote"
|
|
202
|
+
authToken,
|
|
203
|
+
connectionKind: "remote",
|
|
204
|
+
serverProjectRoot
|
|
195
205
|
};
|
|
196
206
|
}
|
|
197
207
|
const connection = await ensureLocalRigServerConnection(projectRoot);
|
|
198
208
|
return {
|
|
199
209
|
baseUrl: connection.baseUrl,
|
|
200
210
|
authToken: connection.authToken,
|
|
201
|
-
connectionKind: "local"
|
|
211
|
+
connectionKind: "local",
|
|
212
|
+
serverProjectRoot: resolve2(projectRoot)
|
|
202
213
|
};
|
|
203
214
|
} catch (error) {
|
|
204
215
|
if (error instanceof Error) {
|
|
@@ -207,6 +218,29 @@ async function ensureServerForCli(projectRoot) {
|
|
|
207
218
|
throw error;
|
|
208
219
|
}
|
|
209
220
|
}
|
|
221
|
+
async function backfillRemoteServerProjectRoot(projectRoot, baseUrl, authToken) {
|
|
222
|
+
const repo = readRepoConnection(projectRoot);
|
|
223
|
+
const slug = repo?.project?.trim();
|
|
224
|
+
if (!slug)
|
|
225
|
+
return null;
|
|
226
|
+
try {
|
|
227
|
+
const response = await fetch(`${baseUrl}/api/projects/${encodeURIComponent(slug)}`, {
|
|
228
|
+
headers: mergeHeaders(undefined, authToken)
|
|
229
|
+
});
|
|
230
|
+
if (!response.ok)
|
|
231
|
+
return null;
|
|
232
|
+
const payload = await response.json();
|
|
233
|
+
const project = payload.project && typeof payload.project === "object" && !Array.isArray(payload.project) ? payload.project : null;
|
|
234
|
+
const checkouts = Array.isArray(project?.checkouts) ? project.checkouts : [];
|
|
235
|
+
const latestCheckout = [...checkouts].reverse().find((entry) => Boolean(entry && typeof entry === "object" && !Array.isArray(entry) && typeof entry.path === "string"));
|
|
236
|
+
const path = typeof latestCheckout?.path === "string" && latestCheckout.path.trim() ? latestCheckout.path.trim() : null;
|
|
237
|
+
if (path)
|
|
238
|
+
writeRepoServerProjectRoot(projectRoot, path);
|
|
239
|
+
return path;
|
|
240
|
+
} catch {
|
|
241
|
+
return null;
|
|
242
|
+
}
|
|
243
|
+
}
|
|
210
244
|
function mergeHeaders(headers, authToken) {
|
|
211
245
|
const merged = new Headers(headers);
|
|
212
246
|
if (authToken) {
|
|
@@ -231,9 +265,12 @@ function diagnosticMessage(payload) {
|
|
|
231
265
|
}
|
|
232
266
|
async function requestServerJson(context, pathname, init = {}) {
|
|
233
267
|
const server = await ensureServerForCli(context.projectRoot);
|
|
268
|
+
const headers = mergeHeaders(init.headers, server.authToken);
|
|
269
|
+
if (server.serverProjectRoot)
|
|
270
|
+
headers.set("x-rig-project-root", server.serverProjectRoot);
|
|
234
271
|
const response = await fetch(`${server.baseUrl}${pathname}`, {
|
|
235
272
|
...init,
|
|
236
|
-
headers
|
|
273
|
+
headers
|
|
237
274
|
});
|
|
238
275
|
const text = await response.text();
|
|
239
276
|
const payload = text.trim().length > 0 ? (() => {
|
|
@@ -307,22 +344,12 @@ async function switchServerProjectRootViaServer(context, projectRoot, options =
|
|
|
307
344
|
if (!switched) {
|
|
308
345
|
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);
|
|
309
346
|
}
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
const record = status;
|
|
315
|
-
if (record.projectRoot === projectRoot) {
|
|
316
|
-
return { ok: true, switched, status: record };
|
|
317
|
-
}
|
|
318
|
-
lastError = `server projectRoot=${String(record.projectRoot ?? "unknown")}`;
|
|
319
|
-
}
|
|
320
|
-
} catch (error) {
|
|
321
|
-
lastError = error;
|
|
322
|
-
}
|
|
323
|
-
await sleep(pollMs);
|
|
347
|
+
const record = switched && typeof switched === "object" && !Array.isArray(switched) ? switched : {};
|
|
348
|
+
if (record.ok === true) {
|
|
349
|
+
writeRepoServerProjectRoot(context.projectRoot, projectRoot);
|
|
350
|
+
return { ok: true, switched: record };
|
|
324
351
|
}
|
|
325
|
-
throw new CliError2(`Rig server
|
|
352
|
+
throw new CliError2(`Rig server rejected project-root scope ${projectRoot}: ${String(record.message ?? record.error ?? "unknown error")}`, 1);
|
|
326
353
|
}
|
|
327
354
|
async function ensureTaskLabelsViaServer(context) {
|
|
328
355
|
const payload = await requestServerJson(context, "/api/workspace/task-labels", { method: "POST" });
|
|
@@ -84,6 +84,11 @@ function readJsonFile(path) {
|
|
|
84
84
|
throw new CliError2(`Invalid Rig connection state at ${path}: ${error instanceof Error ? error.message : String(error)}`, 1);
|
|
85
85
|
}
|
|
86
86
|
}
|
|
87
|
+
function writeJsonFile(path, value) {
|
|
88
|
+
mkdirSync(dirname(path), { recursive: true });
|
|
89
|
+
writeFileSync(path, `${JSON.stringify(value, null, 2)}
|
|
90
|
+
`, "utf8");
|
|
91
|
+
}
|
|
87
92
|
function normalizeConnection(value) {
|
|
88
93
|
if (!value || typeof value !== "object" || Array.isArray(value))
|
|
89
94
|
return null;
|
|
@@ -124,21 +129,31 @@ function readRepoConnection(projectRoot) {
|
|
|
124
129
|
return {
|
|
125
130
|
selected,
|
|
126
131
|
project: typeof record.project === "string" ? record.project : undefined,
|
|
127
|
-
linkedAt: typeof record.linkedAt === "string" ? record.linkedAt : undefined
|
|
132
|
+
linkedAt: typeof record.linkedAt === "string" ? record.linkedAt : undefined,
|
|
133
|
+
serverProjectRoot: typeof record.serverProjectRoot === "string" && record.serverProjectRoot.trim() ? record.serverProjectRoot.trim() : undefined
|
|
128
134
|
};
|
|
129
135
|
}
|
|
136
|
+
function writeRepoConnection(projectRoot, state) {
|
|
137
|
+
writeJsonFile(resolveRepoConnectionPath(projectRoot), state);
|
|
138
|
+
}
|
|
130
139
|
function resolveSelectedConnection(projectRoot, options = {}) {
|
|
131
140
|
const repo = readRepoConnection(projectRoot);
|
|
132
141
|
if (!repo)
|
|
133
142
|
return null;
|
|
134
143
|
if (repo.selected === "local")
|
|
135
|
-
return { alias: "local", connection: { kind: "local", mode: "auto" } };
|
|
144
|
+
return { alias: "local", connection: { kind: "local", mode: "auto" }, serverProjectRoot: repo.serverProjectRoot };
|
|
136
145
|
const global = readGlobalConnections(options);
|
|
137
146
|
const connection = global.connections[repo.selected];
|
|
138
147
|
if (!connection) {
|
|
139
148
|
throw new CliError2(`Selected Rig server "${repo.selected}" was not found. Run \`rig server list\` or \`rig server use local\`.`, 1);
|
|
140
149
|
}
|
|
141
|
-
return { alias: repo.selected, connection };
|
|
150
|
+
return { alias: repo.selected, connection, serverProjectRoot: repo.serverProjectRoot };
|
|
151
|
+
}
|
|
152
|
+
function writeRepoServerProjectRoot(projectRoot, serverProjectRoot) {
|
|
153
|
+
const repo = readRepoConnection(projectRoot);
|
|
154
|
+
if (!repo)
|
|
155
|
+
return;
|
|
156
|
+
writeRepoConnection(projectRoot, { ...repo, serverProjectRoot });
|
|
142
157
|
}
|
|
143
158
|
|
|
144
159
|
// packages/cli/src/commands/_server-client.ts
|
|
@@ -171,17 +186,21 @@ async function ensureServerForCli(projectRoot) {
|
|
|
171
186
|
try {
|
|
172
187
|
const selected = resolveSelectedConnection(projectRoot);
|
|
173
188
|
if (selected?.connection.kind === "remote") {
|
|
189
|
+
const authToken = readGitHubBearerTokenForRemote(projectRoot);
|
|
190
|
+
const serverProjectRoot = selected.serverProjectRoot ?? await backfillRemoteServerProjectRoot(projectRoot, selected.connection.baseUrl, authToken);
|
|
174
191
|
return {
|
|
175
192
|
baseUrl: selected.connection.baseUrl,
|
|
176
|
-
authToken
|
|
177
|
-
connectionKind: "remote"
|
|
193
|
+
authToken,
|
|
194
|
+
connectionKind: "remote",
|
|
195
|
+
serverProjectRoot
|
|
178
196
|
};
|
|
179
197
|
}
|
|
180
198
|
const connection = await ensureLocalRigServerConnection(projectRoot);
|
|
181
199
|
return {
|
|
182
200
|
baseUrl: connection.baseUrl,
|
|
183
201
|
authToken: connection.authToken,
|
|
184
|
-
connectionKind: "local"
|
|
202
|
+
connectionKind: "local",
|
|
203
|
+
serverProjectRoot: resolve2(projectRoot)
|
|
185
204
|
};
|
|
186
205
|
} catch (error) {
|
|
187
206
|
if (error instanceof Error) {
|
|
@@ -190,6 +209,29 @@ async function ensureServerForCli(projectRoot) {
|
|
|
190
209
|
throw error;
|
|
191
210
|
}
|
|
192
211
|
}
|
|
212
|
+
async function backfillRemoteServerProjectRoot(projectRoot, baseUrl, authToken) {
|
|
213
|
+
const repo = readRepoConnection(projectRoot);
|
|
214
|
+
const slug = repo?.project?.trim();
|
|
215
|
+
if (!slug)
|
|
216
|
+
return null;
|
|
217
|
+
try {
|
|
218
|
+
const response = await fetch(`${baseUrl}/api/projects/${encodeURIComponent(slug)}`, {
|
|
219
|
+
headers: mergeHeaders(undefined, authToken)
|
|
220
|
+
});
|
|
221
|
+
if (!response.ok)
|
|
222
|
+
return null;
|
|
223
|
+
const payload = await response.json();
|
|
224
|
+
const project = payload.project && typeof payload.project === "object" && !Array.isArray(payload.project) ? payload.project : null;
|
|
225
|
+
const checkouts = Array.isArray(project?.checkouts) ? project.checkouts : [];
|
|
226
|
+
const latestCheckout = [...checkouts].reverse().find((entry) => Boolean(entry && typeof entry === "object" && !Array.isArray(entry) && typeof entry.path === "string"));
|
|
227
|
+
const path = typeof latestCheckout?.path === "string" && latestCheckout.path.trim() ? latestCheckout.path.trim() : null;
|
|
228
|
+
if (path)
|
|
229
|
+
writeRepoServerProjectRoot(projectRoot, path);
|
|
230
|
+
return path;
|
|
231
|
+
} catch {
|
|
232
|
+
return null;
|
|
233
|
+
}
|
|
234
|
+
}
|
|
193
235
|
function mergeHeaders(headers, authToken) {
|
|
194
236
|
const merged = new Headers(headers);
|
|
195
237
|
if (authToken) {
|
|
@@ -214,9 +256,12 @@ function diagnosticMessage(payload) {
|
|
|
214
256
|
}
|
|
215
257
|
async function requestServerJson(context, pathname, init = {}) {
|
|
216
258
|
const server = await ensureServerForCli(context.projectRoot);
|
|
259
|
+
const headers = mergeHeaders(init.headers, server.authToken);
|
|
260
|
+
if (server.serverProjectRoot)
|
|
261
|
+
headers.set("x-rig-project-root", server.serverProjectRoot);
|
|
217
262
|
const response = await fetch(`${server.baseUrl}${pathname}`, {
|
|
218
263
|
...init,
|
|
219
|
-
headers
|
|
264
|
+
headers
|
|
220
265
|
});
|
|
221
266
|
const text = await response.text();
|
|
222
267
|
const payload = text.trim().length > 0 ? (() => {
|
package/dist/src/commands/run.js
CHANGED
|
@@ -108,6 +108,11 @@ function readJsonFile(path) {
|
|
|
108
108
|
throw new CliError2(`Invalid Rig connection state at ${path}: ${error instanceof Error ? error.message : String(error)}`, 1);
|
|
109
109
|
}
|
|
110
110
|
}
|
|
111
|
+
function writeJsonFile(path, value) {
|
|
112
|
+
mkdirSync(dirname(path), { recursive: true });
|
|
113
|
+
writeFileSync(path, `${JSON.stringify(value, null, 2)}
|
|
114
|
+
`, "utf8");
|
|
115
|
+
}
|
|
111
116
|
function normalizeConnection(value) {
|
|
112
117
|
if (!value || typeof value !== "object" || Array.isArray(value))
|
|
113
118
|
return null;
|
|
@@ -148,21 +153,31 @@ function readRepoConnection(projectRoot) {
|
|
|
148
153
|
return {
|
|
149
154
|
selected,
|
|
150
155
|
project: typeof record.project === "string" ? record.project : undefined,
|
|
151
|
-
linkedAt: typeof record.linkedAt === "string" ? record.linkedAt : undefined
|
|
156
|
+
linkedAt: typeof record.linkedAt === "string" ? record.linkedAt : undefined,
|
|
157
|
+
serverProjectRoot: typeof record.serverProjectRoot === "string" && record.serverProjectRoot.trim() ? record.serverProjectRoot.trim() : undefined
|
|
152
158
|
};
|
|
153
159
|
}
|
|
160
|
+
function writeRepoConnection(projectRoot, state) {
|
|
161
|
+
writeJsonFile(resolveRepoConnectionPath(projectRoot), state);
|
|
162
|
+
}
|
|
154
163
|
function resolveSelectedConnection(projectRoot, options = {}) {
|
|
155
164
|
const repo = readRepoConnection(projectRoot);
|
|
156
165
|
if (!repo)
|
|
157
166
|
return null;
|
|
158
167
|
if (repo.selected === "local")
|
|
159
|
-
return { alias: "local", connection: { kind: "local", mode: "auto" } };
|
|
168
|
+
return { alias: "local", connection: { kind: "local", mode: "auto" }, serverProjectRoot: repo.serverProjectRoot };
|
|
160
169
|
const global = readGlobalConnections(options);
|
|
161
170
|
const connection = global.connections[repo.selected];
|
|
162
171
|
if (!connection) {
|
|
163
172
|
throw new CliError2(`Selected Rig server "${repo.selected}" was not found. Run \`rig server list\` or \`rig server use local\`.`, 1);
|
|
164
173
|
}
|
|
165
|
-
return { alias: repo.selected, connection };
|
|
174
|
+
return { alias: repo.selected, connection, serverProjectRoot: repo.serverProjectRoot };
|
|
175
|
+
}
|
|
176
|
+
function writeRepoServerProjectRoot(projectRoot, serverProjectRoot) {
|
|
177
|
+
const repo = readRepoConnection(projectRoot);
|
|
178
|
+
if (!repo)
|
|
179
|
+
return;
|
|
180
|
+
writeRepoConnection(projectRoot, { ...repo, serverProjectRoot });
|
|
166
181
|
}
|
|
167
182
|
|
|
168
183
|
// packages/cli/src/commands/_server-client.ts
|
|
@@ -195,17 +210,21 @@ async function ensureServerForCli(projectRoot) {
|
|
|
195
210
|
try {
|
|
196
211
|
const selected = resolveSelectedConnection(projectRoot);
|
|
197
212
|
if (selected?.connection.kind === "remote") {
|
|
213
|
+
const authToken = readGitHubBearerTokenForRemote(projectRoot);
|
|
214
|
+
const serverProjectRoot = selected.serverProjectRoot ?? await backfillRemoteServerProjectRoot(projectRoot, selected.connection.baseUrl, authToken);
|
|
198
215
|
return {
|
|
199
216
|
baseUrl: selected.connection.baseUrl,
|
|
200
|
-
authToken
|
|
201
|
-
connectionKind: "remote"
|
|
217
|
+
authToken,
|
|
218
|
+
connectionKind: "remote",
|
|
219
|
+
serverProjectRoot
|
|
202
220
|
};
|
|
203
221
|
}
|
|
204
222
|
const connection = await ensureLocalRigServerConnection(projectRoot);
|
|
205
223
|
return {
|
|
206
224
|
baseUrl: connection.baseUrl,
|
|
207
225
|
authToken: connection.authToken,
|
|
208
|
-
connectionKind: "local"
|
|
226
|
+
connectionKind: "local",
|
|
227
|
+
serverProjectRoot: resolve2(projectRoot)
|
|
209
228
|
};
|
|
210
229
|
} catch (error) {
|
|
211
230
|
if (error instanceof Error) {
|
|
@@ -214,6 +233,29 @@ async function ensureServerForCli(projectRoot) {
|
|
|
214
233
|
throw error;
|
|
215
234
|
}
|
|
216
235
|
}
|
|
236
|
+
async function backfillRemoteServerProjectRoot(projectRoot, baseUrl, authToken) {
|
|
237
|
+
const repo = readRepoConnection(projectRoot);
|
|
238
|
+
const slug = repo?.project?.trim();
|
|
239
|
+
if (!slug)
|
|
240
|
+
return null;
|
|
241
|
+
try {
|
|
242
|
+
const response = await fetch(`${baseUrl}/api/projects/${encodeURIComponent(slug)}`, {
|
|
243
|
+
headers: mergeHeaders(undefined, authToken)
|
|
244
|
+
});
|
|
245
|
+
if (!response.ok)
|
|
246
|
+
return null;
|
|
247
|
+
const payload = await response.json();
|
|
248
|
+
const project = payload.project && typeof payload.project === "object" && !Array.isArray(payload.project) ? payload.project : null;
|
|
249
|
+
const checkouts = Array.isArray(project?.checkouts) ? project.checkouts : [];
|
|
250
|
+
const latestCheckout = [...checkouts].reverse().find((entry) => Boolean(entry && typeof entry === "object" && !Array.isArray(entry) && typeof entry.path === "string"));
|
|
251
|
+
const path = typeof latestCheckout?.path === "string" && latestCheckout.path.trim() ? latestCheckout.path.trim() : null;
|
|
252
|
+
if (path)
|
|
253
|
+
writeRepoServerProjectRoot(projectRoot, path);
|
|
254
|
+
return path;
|
|
255
|
+
} catch {
|
|
256
|
+
return null;
|
|
257
|
+
}
|
|
258
|
+
}
|
|
217
259
|
function mergeHeaders(headers, authToken) {
|
|
218
260
|
const merged = new Headers(headers);
|
|
219
261
|
if (authToken) {
|
|
@@ -238,9 +280,12 @@ function diagnosticMessage(payload) {
|
|
|
238
280
|
}
|
|
239
281
|
async function requestServerJson(context, pathname, init = {}) {
|
|
240
282
|
const server = await ensureServerForCli(context.projectRoot);
|
|
283
|
+
const headers = mergeHeaders(init.headers, server.authToken);
|
|
284
|
+
if (server.serverProjectRoot)
|
|
285
|
+
headers.set("x-rig-project-root", server.serverProjectRoot);
|
|
241
286
|
const response = await fetch(`${server.baseUrl}${pathname}`, {
|
|
242
287
|
...init,
|
|
243
|
-
headers
|
|
288
|
+
headers
|
|
244
289
|
});
|
|
245
290
|
const text = await response.text();
|
|
246
291
|
const payload = text.trim().length > 0 ? (() => {
|
|
@@ -365,6 +410,8 @@ async function buildRunPiEventsWebSocketUrl(context, runId) {
|
|
|
365
410
|
url.protocol = url.protocol === "https:" ? "wss:" : "ws:";
|
|
366
411
|
if (server.authToken)
|
|
367
412
|
url.searchParams.set("token", server.authToken);
|
|
413
|
+
if (server.serverProjectRoot)
|
|
414
|
+
url.searchParams.set("rigProjectRoot", server.serverProjectRoot);
|
|
368
415
|
return url.toString();
|
|
369
416
|
}
|
|
370
417
|
|