@h-rig/cli 0.0.6-alpha.44 → 0.0.6-alpha.46
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 +50 -22
- package/dist/src/commands/_connection-state.js +11 -3
- package/dist/src/commands/_doctor-checks.js +51 -7
- package/dist/src/commands/_operator-view.js +53 -7
- package/dist/src/commands/_pi-frontend.js +53 -7
- package/dist/src/commands/_pi-remote-session.js +53 -7
- package/dist/src/commands/_pi-worker-bridge-extension.js +53 -7
- package/dist/src/commands/_preflight.js +51 -7
- package/dist/src/commands/_server-client.js +58 -22
- package/dist/src/commands/_snapshot-upload.js +51 -7
- package/dist/src/commands/connect.js +2 -1
- package/dist/src/commands/doctor.js +51 -7
- package/dist/src/commands/github.js +51 -7
- package/dist/src/commands/inbox.js +51 -7
- package/dist/src/commands/init.js +48 -22
- package/dist/src/commands/inspect.js +51 -7
- package/dist/src/commands/run.js +53 -7
- package/dist/src/commands/server.js +43 -7
- package/dist/src/commands/setup.js +51 -7
- package/dist/src/commands/task-run-driver.js +51 -7
- package/dist/src/commands/task.js +53 -7
- package/dist/src/commands.js +50 -22
- package/dist/src/index.js +50 -22
- package/package.json +6 -6
|
@@ -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,28 @@ 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 checkout = project?.checkout && typeof project.checkout === "object" && !Array.isArray(project.checkout) ? project.checkout : null;
|
|
180
|
+
const path = typeof checkout?.path === "string" && checkout.path.trim() ? checkout.path.trim() : typeof project?.path === "string" && project.path.trim() ? project.path.trim() : null;
|
|
181
|
+
if (path)
|
|
182
|
+
writeRepoServerProjectRoot(projectRoot, path);
|
|
183
|
+
return path;
|
|
184
|
+
} catch {
|
|
185
|
+
return null;
|
|
186
|
+
}
|
|
187
|
+
}
|
|
147
188
|
function mergeHeaders(headers, authToken) {
|
|
148
189
|
const merged = new Headers(headers);
|
|
149
190
|
if (authToken) {
|
|
@@ -168,9 +209,12 @@ function diagnosticMessage(payload) {
|
|
|
168
209
|
}
|
|
169
210
|
async function requestServerJson(context, pathname, init = {}) {
|
|
170
211
|
const server = await ensureServerForCli(context.projectRoot);
|
|
212
|
+
const headers = mergeHeaders(init.headers, server.authToken);
|
|
213
|
+
if (server.serverProjectRoot)
|
|
214
|
+
headers.set("x-rig-project-root", server.serverProjectRoot);
|
|
171
215
|
const response = await fetch(`${server.baseUrl}${pathname}`, {
|
|
172
216
|
...init,
|
|
173
|
-
headers
|
|
217
|
+
headers
|
|
174
218
|
});
|
|
175
219
|
const text = await response.text();
|
|
176
220
|
const payload = text.trim().length > 0 ? (() => {
|
|
@@ -249,6 +293,8 @@ async function buildRunPiEventsWebSocketUrl(context, runId) {
|
|
|
249
293
|
url.protocol = url.protocol === "https:" ? "wss:" : "ws:";
|
|
250
294
|
if (server.authToken)
|
|
251
295
|
url.searchParams.set("token", server.authToken);
|
|
296
|
+
if (server.serverProjectRoot)
|
|
297
|
+
url.searchParams.set("rigProjectRoot", server.serverProjectRoot);
|
|
252
298
|
return url.toString();
|
|
253
299
|
}
|
|
254
300
|
|
|
@@ -34,6 +34,11 @@ function readJsonFile(path) {
|
|
|
34
34
|
throw new CliError2(`Invalid Rig connection state at ${path}: ${error instanceof Error ? error.message : String(error)}`, 1);
|
|
35
35
|
}
|
|
36
36
|
}
|
|
37
|
+
function writeJsonFile(path, value) {
|
|
38
|
+
mkdirSync(dirname(path), { recursive: true });
|
|
39
|
+
writeFileSync(path, `${JSON.stringify(value, null, 2)}
|
|
40
|
+
`, "utf8");
|
|
41
|
+
}
|
|
37
42
|
function normalizeConnection(value) {
|
|
38
43
|
if (!value || typeof value !== "object" || Array.isArray(value))
|
|
39
44
|
return null;
|
|
@@ -74,21 +79,31 @@ function readRepoConnection(projectRoot) {
|
|
|
74
79
|
return {
|
|
75
80
|
selected,
|
|
76
81
|
project: typeof record.project === "string" ? record.project : undefined,
|
|
77
|
-
linkedAt: typeof record.linkedAt === "string" ? record.linkedAt : undefined
|
|
82
|
+
linkedAt: typeof record.linkedAt === "string" ? record.linkedAt : undefined,
|
|
83
|
+
serverProjectRoot: typeof record.serverProjectRoot === "string" && record.serverProjectRoot.trim() ? record.serverProjectRoot.trim() : undefined
|
|
78
84
|
};
|
|
79
85
|
}
|
|
86
|
+
function writeRepoConnection(projectRoot, state) {
|
|
87
|
+
writeJsonFile(resolveRepoConnectionPath(projectRoot), state);
|
|
88
|
+
}
|
|
80
89
|
function resolveSelectedConnection(projectRoot, options = {}) {
|
|
81
90
|
const repo = readRepoConnection(projectRoot);
|
|
82
91
|
if (!repo)
|
|
83
92
|
return null;
|
|
84
93
|
if (repo.selected === "local")
|
|
85
|
-
return { alias: "local", connection: { kind: "local", mode: "auto" } };
|
|
94
|
+
return { alias: "local", connection: { kind: "local", mode: "auto" }, serverProjectRoot: repo.serverProjectRoot };
|
|
86
95
|
const global = readGlobalConnections(options);
|
|
87
96
|
const connection = global.connections[repo.selected];
|
|
88
97
|
if (!connection) {
|
|
89
98
|
throw new CliError2(`Selected Rig server "${repo.selected}" was not found. Run \`rig server list\` or \`rig server use local\`.`, 1);
|
|
90
99
|
}
|
|
91
|
-
return { alias: repo.selected, connection };
|
|
100
|
+
return { alias: repo.selected, connection, serverProjectRoot: repo.serverProjectRoot };
|
|
101
|
+
}
|
|
102
|
+
function writeRepoServerProjectRoot(projectRoot, serverProjectRoot) {
|
|
103
|
+
const repo = readRepoConnection(projectRoot);
|
|
104
|
+
if (!repo)
|
|
105
|
+
return;
|
|
106
|
+
writeRepoConnection(projectRoot, { ...repo, serverProjectRoot });
|
|
92
107
|
}
|
|
93
108
|
|
|
94
109
|
// packages/cli/src/commands/_server-client.ts
|
|
@@ -124,17 +139,21 @@ async function ensureServerForCli(projectRoot) {
|
|
|
124
139
|
try {
|
|
125
140
|
const selected = resolveSelectedConnection(projectRoot);
|
|
126
141
|
if (selected?.connection.kind === "remote") {
|
|
142
|
+
const authToken = readGitHubBearerTokenForRemote(projectRoot);
|
|
143
|
+
const serverProjectRoot = selected.serverProjectRoot ?? await backfillRemoteServerProjectRoot(projectRoot, selected.connection.baseUrl, authToken);
|
|
127
144
|
return {
|
|
128
145
|
baseUrl: selected.connection.baseUrl,
|
|
129
|
-
authToken
|
|
130
|
-
connectionKind: "remote"
|
|
146
|
+
authToken,
|
|
147
|
+
connectionKind: "remote",
|
|
148
|
+
serverProjectRoot
|
|
131
149
|
};
|
|
132
150
|
}
|
|
133
151
|
const connection = await ensureLocalRigServerConnection(projectRoot);
|
|
134
152
|
return {
|
|
135
153
|
baseUrl: connection.baseUrl,
|
|
136
154
|
authToken: connection.authToken,
|
|
137
|
-
connectionKind: "local"
|
|
155
|
+
connectionKind: "local",
|
|
156
|
+
serverProjectRoot: resolve2(projectRoot)
|
|
138
157
|
};
|
|
139
158
|
} catch (error) {
|
|
140
159
|
if (error instanceof Error) {
|
|
@@ -143,6 +162,28 @@ async function ensureServerForCli(projectRoot) {
|
|
|
143
162
|
throw error;
|
|
144
163
|
}
|
|
145
164
|
}
|
|
165
|
+
async function backfillRemoteServerProjectRoot(projectRoot, baseUrl, authToken) {
|
|
166
|
+
const repo = readRepoConnection(projectRoot);
|
|
167
|
+
const slug = repo?.project?.trim();
|
|
168
|
+
if (!slug)
|
|
169
|
+
return null;
|
|
170
|
+
try {
|
|
171
|
+
const response = await fetch(`${baseUrl}/api/projects/${encodeURIComponent(slug)}`, {
|
|
172
|
+
headers: mergeHeaders(undefined, authToken)
|
|
173
|
+
});
|
|
174
|
+
if (!response.ok)
|
|
175
|
+
return null;
|
|
176
|
+
const payload = await response.json();
|
|
177
|
+
const project = payload.project && typeof payload.project === "object" && !Array.isArray(payload.project) ? payload.project : null;
|
|
178
|
+
const checkout = project?.checkout && typeof project.checkout === "object" && !Array.isArray(project.checkout) ? project.checkout : null;
|
|
179
|
+
const path = typeof checkout?.path === "string" && checkout.path.trim() ? checkout.path.trim() : typeof project?.path === "string" && project.path.trim() ? project.path.trim() : null;
|
|
180
|
+
if (path)
|
|
181
|
+
writeRepoServerProjectRoot(projectRoot, path);
|
|
182
|
+
return path;
|
|
183
|
+
} catch {
|
|
184
|
+
return null;
|
|
185
|
+
}
|
|
186
|
+
}
|
|
146
187
|
function mergeHeaders(headers, authToken) {
|
|
147
188
|
const merged = new Headers(headers);
|
|
148
189
|
if (authToken) {
|
|
@@ -167,9 +208,12 @@ function diagnosticMessage(payload) {
|
|
|
167
208
|
}
|
|
168
209
|
async function requestServerJson(context, pathname, init = {}) {
|
|
169
210
|
const server = await ensureServerForCli(context.projectRoot);
|
|
211
|
+
const headers = mergeHeaders(init.headers, server.authToken);
|
|
212
|
+
if (server.serverProjectRoot)
|
|
213
|
+
headers.set("x-rig-project-root", server.serverProjectRoot);
|
|
170
214
|
const response = await fetch(`${server.baseUrl}${pathname}`, {
|
|
171
215
|
...init,
|
|
172
|
-
headers
|
|
216
|
+
headers
|
|
173
217
|
});
|
|
174
218
|
const text = await response.text();
|
|
175
219
|
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
|
|
@@ -129,17 +144,21 @@ async function ensureServerForCli(projectRoot) {
|
|
|
129
144
|
try {
|
|
130
145
|
const selected = resolveSelectedConnection(projectRoot);
|
|
131
146
|
if (selected?.connection.kind === "remote") {
|
|
147
|
+
const authToken = readGitHubBearerTokenForRemote(projectRoot);
|
|
148
|
+
const serverProjectRoot = selected.serverProjectRoot ?? await backfillRemoteServerProjectRoot(projectRoot, selected.connection.baseUrl, authToken);
|
|
132
149
|
return {
|
|
133
150
|
baseUrl: selected.connection.baseUrl,
|
|
134
|
-
authToken
|
|
135
|
-
connectionKind: "remote"
|
|
151
|
+
authToken,
|
|
152
|
+
connectionKind: "remote",
|
|
153
|
+
serverProjectRoot
|
|
136
154
|
};
|
|
137
155
|
}
|
|
138
156
|
const connection = await ensureLocalRigServerConnection(projectRoot);
|
|
139
157
|
return {
|
|
140
158
|
baseUrl: connection.baseUrl,
|
|
141
159
|
authToken: connection.authToken,
|
|
142
|
-
connectionKind: "local"
|
|
160
|
+
connectionKind: "local",
|
|
161
|
+
serverProjectRoot: resolve2(projectRoot)
|
|
143
162
|
};
|
|
144
163
|
} catch (error) {
|
|
145
164
|
if (error instanceof Error) {
|
|
@@ -148,6 +167,28 @@ async function ensureServerForCli(projectRoot) {
|
|
|
148
167
|
throw error;
|
|
149
168
|
}
|
|
150
169
|
}
|
|
170
|
+
async function backfillRemoteServerProjectRoot(projectRoot, baseUrl, authToken) {
|
|
171
|
+
const repo = readRepoConnection(projectRoot);
|
|
172
|
+
const slug = repo?.project?.trim();
|
|
173
|
+
if (!slug)
|
|
174
|
+
return null;
|
|
175
|
+
try {
|
|
176
|
+
const response = await fetch(`${baseUrl}/api/projects/${encodeURIComponent(slug)}`, {
|
|
177
|
+
headers: mergeHeaders(undefined, authToken)
|
|
178
|
+
});
|
|
179
|
+
if (!response.ok)
|
|
180
|
+
return null;
|
|
181
|
+
const payload = await response.json();
|
|
182
|
+
const project = payload.project && typeof payload.project === "object" && !Array.isArray(payload.project) ? payload.project : null;
|
|
183
|
+
const checkout = project?.checkout && typeof project.checkout === "object" && !Array.isArray(project.checkout) ? project.checkout : null;
|
|
184
|
+
const path = typeof checkout?.path === "string" && checkout.path.trim() ? checkout.path.trim() : typeof project?.path === "string" && project.path.trim() ? project.path.trim() : null;
|
|
185
|
+
if (path)
|
|
186
|
+
writeRepoServerProjectRoot(projectRoot, path);
|
|
187
|
+
return path;
|
|
188
|
+
} catch {
|
|
189
|
+
return null;
|
|
190
|
+
}
|
|
191
|
+
}
|
|
151
192
|
function appendTaskFilterParams(url, filters) {
|
|
152
193
|
if (filters.assignee)
|
|
153
194
|
url.searchParams.set("assignee", filters.assignee);
|
|
@@ -182,9 +223,12 @@ function diagnosticMessage(payload) {
|
|
|
182
223
|
}
|
|
183
224
|
async function requestServerJson(context, pathname, init = {}) {
|
|
184
225
|
const server = await ensureServerForCli(context.projectRoot);
|
|
226
|
+
const headers = mergeHeaders(init.headers, server.authToken);
|
|
227
|
+
if (server.serverProjectRoot)
|
|
228
|
+
headers.set("x-rig-project-root", server.serverProjectRoot);
|
|
185
229
|
const response = await fetch(`${server.baseUrl}${pathname}`, {
|
|
186
230
|
...init,
|
|
187
|
-
headers
|
|
231
|
+
headers
|
|
188
232
|
});
|
|
189
233
|
const text = await response.text();
|
|
190
234
|
const payload = text.trim().length > 0 ? (() => {
|
|
@@ -291,22 +335,12 @@ async function switchServerProjectRootViaServer(context, projectRoot, options =
|
|
|
291
335
|
if (!switched) {
|
|
292
336
|
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);
|
|
293
337
|
}
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
const record = status;
|
|
299
|
-
if (record.projectRoot === projectRoot) {
|
|
300
|
-
return { ok: true, switched, status: record };
|
|
301
|
-
}
|
|
302
|
-
lastError = `server projectRoot=${String(record.projectRoot ?? "unknown")}`;
|
|
303
|
-
}
|
|
304
|
-
} catch (error) {
|
|
305
|
-
lastError = error;
|
|
306
|
-
}
|
|
307
|
-
await sleep(pollMs);
|
|
338
|
+
const record = switched && typeof switched === "object" && !Array.isArray(switched) ? switched : {};
|
|
339
|
+
if (record.ok === true) {
|
|
340
|
+
writeRepoServerProjectRoot(context.projectRoot, projectRoot);
|
|
341
|
+
return { ok: true, switched: record };
|
|
308
342
|
}
|
|
309
|
-
throw new CliError2(`Rig server
|
|
343
|
+
throw new CliError2(`Rig server rejected project-root scope ${projectRoot}: ${String(record.message ?? record.error ?? "unknown error")}`, 1);
|
|
310
344
|
}
|
|
311
345
|
async function listRunsViaServer(context, options = {}) {
|
|
312
346
|
const url = new URL("http://rig.local/api/runs");
|
|
@@ -438,6 +472,8 @@ async function buildRunPiEventsWebSocketUrl(context, runId) {
|
|
|
438
472
|
url.protocol = url.protocol === "https:" ? "wss:" : "ws:";
|
|
439
473
|
if (server.authToken)
|
|
440
474
|
url.searchParams.set("token", server.authToken);
|
|
475
|
+
if (server.serverProjectRoot)
|
|
476
|
+
url.searchParams.set("rigProjectRoot", server.serverProjectRoot);
|
|
441
477
|
return url.toString();
|
|
442
478
|
}
|
|
443
479
|
async function submitTaskRunViaServer(context, input) {
|
|
@@ -42,6 +42,11 @@ function readJsonFile(path) {
|
|
|
42
42
|
throw new CliError2(`Invalid Rig connection state at ${path}: ${error instanceof Error ? error.message : String(error)}`, 1);
|
|
43
43
|
}
|
|
44
44
|
}
|
|
45
|
+
function writeJsonFile(path, value) {
|
|
46
|
+
mkdirSync(dirname(path), { recursive: true });
|
|
47
|
+
writeFileSync(path, `${JSON.stringify(value, null, 2)}
|
|
48
|
+
`, "utf8");
|
|
49
|
+
}
|
|
45
50
|
function normalizeConnection(value) {
|
|
46
51
|
if (!value || typeof value !== "object" || Array.isArray(value))
|
|
47
52
|
return null;
|
|
@@ -82,21 +87,31 @@ function readRepoConnection(projectRoot) {
|
|
|
82
87
|
return {
|
|
83
88
|
selected,
|
|
84
89
|
project: typeof record.project === "string" ? record.project : undefined,
|
|
85
|
-
linkedAt: typeof record.linkedAt === "string" ? record.linkedAt : undefined
|
|
90
|
+
linkedAt: typeof record.linkedAt === "string" ? record.linkedAt : undefined,
|
|
91
|
+
serverProjectRoot: typeof record.serverProjectRoot === "string" && record.serverProjectRoot.trim() ? record.serverProjectRoot.trim() : undefined
|
|
86
92
|
};
|
|
87
93
|
}
|
|
94
|
+
function writeRepoConnection(projectRoot, state) {
|
|
95
|
+
writeJsonFile(resolveRepoConnectionPath(projectRoot), state);
|
|
96
|
+
}
|
|
88
97
|
function resolveSelectedConnection(projectRoot, options = {}) {
|
|
89
98
|
const repo = readRepoConnection(projectRoot);
|
|
90
99
|
if (!repo)
|
|
91
100
|
return null;
|
|
92
101
|
if (repo.selected === "local")
|
|
93
|
-
return { alias: "local", connection: { kind: "local", mode: "auto" } };
|
|
102
|
+
return { alias: "local", connection: { kind: "local", mode: "auto" }, serverProjectRoot: repo.serverProjectRoot };
|
|
94
103
|
const global = readGlobalConnections(options);
|
|
95
104
|
const connection = global.connections[repo.selected];
|
|
96
105
|
if (!connection) {
|
|
97
106
|
throw new CliError2(`Selected Rig server "${repo.selected}" was not found. Run \`rig server list\` or \`rig server use local\`.`, 1);
|
|
98
107
|
}
|
|
99
|
-
return { alias: repo.selected, connection };
|
|
108
|
+
return { alias: repo.selected, connection, serverProjectRoot: repo.serverProjectRoot };
|
|
109
|
+
}
|
|
110
|
+
function writeRepoServerProjectRoot(projectRoot, serverProjectRoot) {
|
|
111
|
+
const repo = readRepoConnection(projectRoot);
|
|
112
|
+
if (!repo)
|
|
113
|
+
return;
|
|
114
|
+
writeRepoConnection(projectRoot, { ...repo, serverProjectRoot });
|
|
100
115
|
}
|
|
101
116
|
|
|
102
117
|
// packages/cli/src/commands/_server-client.ts
|
|
@@ -129,17 +144,21 @@ async function ensureServerForCli(projectRoot) {
|
|
|
129
144
|
try {
|
|
130
145
|
const selected = resolveSelectedConnection(projectRoot);
|
|
131
146
|
if (selected?.connection.kind === "remote") {
|
|
147
|
+
const authToken = readGitHubBearerTokenForRemote(projectRoot);
|
|
148
|
+
const serverProjectRoot = selected.serverProjectRoot ?? await backfillRemoteServerProjectRoot(projectRoot, selected.connection.baseUrl, authToken);
|
|
132
149
|
return {
|
|
133
150
|
baseUrl: selected.connection.baseUrl,
|
|
134
|
-
authToken
|
|
135
|
-
connectionKind: "remote"
|
|
151
|
+
authToken,
|
|
152
|
+
connectionKind: "remote",
|
|
153
|
+
serverProjectRoot
|
|
136
154
|
};
|
|
137
155
|
}
|
|
138
156
|
const connection = await ensureLocalRigServerConnection(projectRoot);
|
|
139
157
|
return {
|
|
140
158
|
baseUrl: connection.baseUrl,
|
|
141
159
|
authToken: connection.authToken,
|
|
142
|
-
connectionKind: "local"
|
|
160
|
+
connectionKind: "local",
|
|
161
|
+
serverProjectRoot: resolve2(projectRoot)
|
|
143
162
|
};
|
|
144
163
|
} catch (error) {
|
|
145
164
|
if (error instanceof Error) {
|
|
@@ -148,6 +167,28 @@ async function ensureServerForCli(projectRoot) {
|
|
|
148
167
|
throw error;
|
|
149
168
|
}
|
|
150
169
|
}
|
|
170
|
+
async function backfillRemoteServerProjectRoot(projectRoot, baseUrl, authToken) {
|
|
171
|
+
const repo = readRepoConnection(projectRoot);
|
|
172
|
+
const slug = repo?.project?.trim();
|
|
173
|
+
if (!slug)
|
|
174
|
+
return null;
|
|
175
|
+
try {
|
|
176
|
+
const response = await fetch(`${baseUrl}/api/projects/${encodeURIComponent(slug)}`, {
|
|
177
|
+
headers: mergeHeaders(undefined, authToken)
|
|
178
|
+
});
|
|
179
|
+
if (!response.ok)
|
|
180
|
+
return null;
|
|
181
|
+
const payload = await response.json();
|
|
182
|
+
const project = payload.project && typeof payload.project === "object" && !Array.isArray(payload.project) ? payload.project : null;
|
|
183
|
+
const checkout = project?.checkout && typeof project.checkout === "object" && !Array.isArray(project.checkout) ? project.checkout : null;
|
|
184
|
+
const path = typeof checkout?.path === "string" && checkout.path.trim() ? checkout.path.trim() : typeof project?.path === "string" && project.path.trim() ? project.path.trim() : null;
|
|
185
|
+
if (path)
|
|
186
|
+
writeRepoServerProjectRoot(projectRoot, path);
|
|
187
|
+
return path;
|
|
188
|
+
} catch {
|
|
189
|
+
return null;
|
|
190
|
+
}
|
|
191
|
+
}
|
|
151
192
|
function mergeHeaders(headers, authToken) {
|
|
152
193
|
const merged = new Headers(headers);
|
|
153
194
|
if (authToken) {
|
|
@@ -172,9 +213,12 @@ function diagnosticMessage(payload) {
|
|
|
172
213
|
}
|
|
173
214
|
async function requestServerJson(context, pathname, init = {}) {
|
|
174
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);
|
|
175
219
|
const response = await fetch(`${server.baseUrl}${pathname}`, {
|
|
176
220
|
...init,
|
|
177
|
-
headers
|
|
221
|
+
headers
|
|
178
222
|
});
|
|
179
223
|
const text = await response.text();
|
|
180
224
|
const payload = text.trim().length > 0 ? (() => {
|
|
@@ -97,7 +97,8 @@ function readRepoConnection(projectRoot) {
|
|
|
97
97
|
return {
|
|
98
98
|
selected,
|
|
99
99
|
project: typeof record.project === "string" ? record.project : undefined,
|
|
100
|
-
linkedAt: typeof record.linkedAt === "string" ? record.linkedAt : undefined
|
|
100
|
+
linkedAt: typeof record.linkedAt === "string" ? record.linkedAt : undefined,
|
|
101
|
+
serverProjectRoot: typeof record.serverProjectRoot === "string" && record.serverProjectRoot.trim() ? record.serverProjectRoot.trim() : undefined
|
|
101
102
|
};
|
|
102
103
|
}
|
|
103
104
|
function writeRepoConnection(projectRoot, state) {
|
|
@@ -44,6 +44,11 @@ function readJsonFile(path) {
|
|
|
44
44
|
throw new CliError2(`Invalid Rig connection state at ${path}: ${error instanceof Error ? error.message : String(error)}`, 1);
|
|
45
45
|
}
|
|
46
46
|
}
|
|
47
|
+
function writeJsonFile(path, value) {
|
|
48
|
+
mkdirSync(dirname(path), { recursive: true });
|
|
49
|
+
writeFileSync(path, `${JSON.stringify(value, null, 2)}
|
|
50
|
+
`, "utf8");
|
|
51
|
+
}
|
|
47
52
|
function normalizeConnection(value) {
|
|
48
53
|
if (!value || typeof value !== "object" || Array.isArray(value))
|
|
49
54
|
return null;
|
|
@@ -84,21 +89,31 @@ function readRepoConnection(projectRoot) {
|
|
|
84
89
|
return {
|
|
85
90
|
selected,
|
|
86
91
|
project: typeof record.project === "string" ? record.project : undefined,
|
|
87
|
-
linkedAt: typeof record.linkedAt === "string" ? record.linkedAt : undefined
|
|
92
|
+
linkedAt: typeof record.linkedAt === "string" ? record.linkedAt : undefined,
|
|
93
|
+
serverProjectRoot: typeof record.serverProjectRoot === "string" && record.serverProjectRoot.trim() ? record.serverProjectRoot.trim() : undefined
|
|
88
94
|
};
|
|
89
95
|
}
|
|
96
|
+
function writeRepoConnection(projectRoot, state) {
|
|
97
|
+
writeJsonFile(resolveRepoConnectionPath(projectRoot), state);
|
|
98
|
+
}
|
|
90
99
|
function resolveSelectedConnection(projectRoot, options = {}) {
|
|
91
100
|
const repo = readRepoConnection(projectRoot);
|
|
92
101
|
if (!repo)
|
|
93
102
|
return null;
|
|
94
103
|
if (repo.selected === "local")
|
|
95
|
-
return { alias: "local", connection: { kind: "local", mode: "auto" } };
|
|
104
|
+
return { alias: "local", connection: { kind: "local", mode: "auto" }, serverProjectRoot: repo.serverProjectRoot };
|
|
96
105
|
const global = readGlobalConnections(options);
|
|
97
106
|
const connection = global.connections[repo.selected];
|
|
98
107
|
if (!connection) {
|
|
99
108
|
throw new CliError2(`Selected Rig server "${repo.selected}" was not found. Run \`rig server list\` or \`rig server use local\`.`, 1);
|
|
100
109
|
}
|
|
101
|
-
return { alias: repo.selected, connection };
|
|
110
|
+
return { alias: repo.selected, connection, serverProjectRoot: repo.serverProjectRoot };
|
|
111
|
+
}
|
|
112
|
+
function writeRepoServerProjectRoot(projectRoot, serverProjectRoot) {
|
|
113
|
+
const repo = readRepoConnection(projectRoot);
|
|
114
|
+
if (!repo)
|
|
115
|
+
return;
|
|
116
|
+
writeRepoConnection(projectRoot, { ...repo, serverProjectRoot });
|
|
102
117
|
}
|
|
103
118
|
|
|
104
119
|
// packages/cli/src/commands/_server-client.ts
|
|
@@ -134,17 +149,21 @@ async function ensureServerForCli(projectRoot) {
|
|
|
134
149
|
try {
|
|
135
150
|
const selected = resolveSelectedConnection(projectRoot);
|
|
136
151
|
if (selected?.connection.kind === "remote") {
|
|
152
|
+
const authToken = readGitHubBearerTokenForRemote(projectRoot);
|
|
153
|
+
const serverProjectRoot = selected.serverProjectRoot ?? await backfillRemoteServerProjectRoot(projectRoot, selected.connection.baseUrl, authToken);
|
|
137
154
|
return {
|
|
138
155
|
baseUrl: selected.connection.baseUrl,
|
|
139
|
-
authToken
|
|
140
|
-
connectionKind: "remote"
|
|
156
|
+
authToken,
|
|
157
|
+
connectionKind: "remote",
|
|
158
|
+
serverProjectRoot
|
|
141
159
|
};
|
|
142
160
|
}
|
|
143
161
|
const connection = await ensureLocalRigServerConnection(projectRoot);
|
|
144
162
|
return {
|
|
145
163
|
baseUrl: connection.baseUrl,
|
|
146
164
|
authToken: connection.authToken,
|
|
147
|
-
connectionKind: "local"
|
|
165
|
+
connectionKind: "local",
|
|
166
|
+
serverProjectRoot: resolve2(projectRoot)
|
|
148
167
|
};
|
|
149
168
|
} catch (error) {
|
|
150
169
|
if (error instanceof Error) {
|
|
@@ -153,6 +172,28 @@ async function ensureServerForCli(projectRoot) {
|
|
|
153
172
|
throw error;
|
|
154
173
|
}
|
|
155
174
|
}
|
|
175
|
+
async function backfillRemoteServerProjectRoot(projectRoot, baseUrl, authToken) {
|
|
176
|
+
const repo = readRepoConnection(projectRoot);
|
|
177
|
+
const slug = repo?.project?.trim();
|
|
178
|
+
if (!slug)
|
|
179
|
+
return null;
|
|
180
|
+
try {
|
|
181
|
+
const response = await fetch(`${baseUrl}/api/projects/${encodeURIComponent(slug)}`, {
|
|
182
|
+
headers: mergeHeaders(undefined, authToken)
|
|
183
|
+
});
|
|
184
|
+
if (!response.ok)
|
|
185
|
+
return null;
|
|
186
|
+
const payload = await response.json();
|
|
187
|
+
const project = payload.project && typeof payload.project === "object" && !Array.isArray(payload.project) ? payload.project : null;
|
|
188
|
+
const checkout = project?.checkout && typeof project.checkout === "object" && !Array.isArray(project.checkout) ? project.checkout : null;
|
|
189
|
+
const path = typeof checkout?.path === "string" && checkout.path.trim() ? checkout.path.trim() : typeof project?.path === "string" && project.path.trim() ? project.path.trim() : null;
|
|
190
|
+
if (path)
|
|
191
|
+
writeRepoServerProjectRoot(projectRoot, path);
|
|
192
|
+
return path;
|
|
193
|
+
} catch {
|
|
194
|
+
return null;
|
|
195
|
+
}
|
|
196
|
+
}
|
|
156
197
|
function mergeHeaders(headers, authToken) {
|
|
157
198
|
const merged = new Headers(headers);
|
|
158
199
|
if (authToken) {
|
|
@@ -177,9 +218,12 @@ function diagnosticMessage(payload) {
|
|
|
177
218
|
}
|
|
178
219
|
async function requestServerJson(context, pathname, init = {}) {
|
|
179
220
|
const server = await ensureServerForCli(context.projectRoot);
|
|
221
|
+
const headers = mergeHeaders(init.headers, server.authToken);
|
|
222
|
+
if (server.serverProjectRoot)
|
|
223
|
+
headers.set("x-rig-project-root", server.serverProjectRoot);
|
|
180
224
|
const response = await fetch(`${server.baseUrl}${pathname}`, {
|
|
181
225
|
...init,
|
|
182
|
-
headers
|
|
226
|
+
headers
|
|
183
227
|
});
|
|
184
228
|
const text = await response.text();
|
|
185
229
|
const payload = text.trim().length > 0 ? (() => {
|