@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
|
@@ -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 ? (() => {
|
|
@@ -249,6 +294,8 @@ async function buildRunPiEventsWebSocketUrl(context, runId) {
|
|
|
249
294
|
url.protocol = url.protocol === "https:" ? "wss:" : "ws:";
|
|
250
295
|
if (server.authToken)
|
|
251
296
|
url.searchParams.set("token", server.authToken);
|
|
297
|
+
if (server.serverProjectRoot)
|
|
298
|
+
url.searchParams.set("rigProjectRoot", server.serverProjectRoot);
|
|
252
299
|
return url.toString();
|
|
253
300
|
}
|
|
254
301
|
|
|
@@ -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,29 @@ 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 checkouts = Array.isArray(project?.checkouts) ? project.checkouts : [];
|
|
179
|
+
const latestCheckout = [...checkouts].reverse().find((entry) => Boolean(entry && typeof entry === "object" && !Array.isArray(entry) && typeof entry.path === "string"));
|
|
180
|
+
const path = typeof latestCheckout?.path === "string" && latestCheckout.path.trim() ? latestCheckout.path.trim() : null;
|
|
181
|
+
if (path)
|
|
182
|
+
writeRepoServerProjectRoot(projectRoot, path);
|
|
183
|
+
return path;
|
|
184
|
+
} catch {
|
|
185
|
+
return null;
|
|
186
|
+
}
|
|
187
|
+
}
|
|
146
188
|
function mergeHeaders(headers, authToken) {
|
|
147
189
|
const merged = new Headers(headers);
|
|
148
190
|
if (authToken) {
|
|
@@ -167,9 +209,12 @@ function diagnosticMessage(payload) {
|
|
|
167
209
|
}
|
|
168
210
|
async function requestServerJson(context, pathname, init = {}) {
|
|
169
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);
|
|
170
215
|
const response = await fetch(`${server.baseUrl}${pathname}`, {
|
|
171
216
|
...init,
|
|
172
|
-
headers
|
|
217
|
+
headers
|
|
173
218
|
});
|
|
174
219
|
const text = await response.text();
|
|
175
220
|
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,29 @@ 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 checkouts = Array.isArray(project?.checkouts) ? project.checkouts : [];
|
|
184
|
+
const latestCheckout = [...checkouts].reverse().find((entry) => Boolean(entry && typeof entry === "object" && !Array.isArray(entry) && typeof entry.path === "string"));
|
|
185
|
+
const path = typeof latestCheckout?.path === "string" && latestCheckout.path.trim() ? latestCheckout.path.trim() : null;
|
|
186
|
+
if (path)
|
|
187
|
+
writeRepoServerProjectRoot(projectRoot, path);
|
|
188
|
+
return path;
|
|
189
|
+
} catch {
|
|
190
|
+
return null;
|
|
191
|
+
}
|
|
192
|
+
}
|
|
151
193
|
function appendTaskFilterParams(url, filters) {
|
|
152
194
|
if (filters.assignee)
|
|
153
195
|
url.searchParams.set("assignee", filters.assignee);
|
|
@@ -182,9 +224,12 @@ function diagnosticMessage(payload) {
|
|
|
182
224
|
}
|
|
183
225
|
async function requestServerJson(context, pathname, init = {}) {
|
|
184
226
|
const server = await ensureServerForCli(context.projectRoot);
|
|
227
|
+
const headers = mergeHeaders(init.headers, server.authToken);
|
|
228
|
+
if (server.serverProjectRoot)
|
|
229
|
+
headers.set("x-rig-project-root", server.serverProjectRoot);
|
|
185
230
|
const response = await fetch(`${server.baseUrl}${pathname}`, {
|
|
186
231
|
...init,
|
|
187
|
-
headers
|
|
232
|
+
headers
|
|
188
233
|
});
|
|
189
234
|
const text = await response.text();
|
|
190
235
|
const payload = text.trim().length > 0 ? (() => {
|
|
@@ -291,22 +336,12 @@ async function switchServerProjectRootViaServer(context, projectRoot, options =
|
|
|
291
336
|
if (!switched) {
|
|
292
337
|
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
338
|
}
|
|
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);
|
|
339
|
+
const record = switched && typeof switched === "object" && !Array.isArray(switched) ? switched : {};
|
|
340
|
+
if (record.ok === true) {
|
|
341
|
+
writeRepoServerProjectRoot(context.projectRoot, projectRoot);
|
|
342
|
+
return { ok: true, switched: record };
|
|
308
343
|
}
|
|
309
|
-
throw new CliError2(`Rig server
|
|
344
|
+
throw new CliError2(`Rig server rejected project-root scope ${projectRoot}: ${String(record.message ?? record.error ?? "unknown error")}`, 1);
|
|
310
345
|
}
|
|
311
346
|
async function listRunsViaServer(context, options = {}) {
|
|
312
347
|
const url = new URL("http://rig.local/api/runs");
|
|
@@ -438,6 +473,8 @@ async function buildRunPiEventsWebSocketUrl(context, runId) {
|
|
|
438
473
|
url.protocol = url.protocol === "https:" ? "wss:" : "ws:";
|
|
439
474
|
if (server.authToken)
|
|
440
475
|
url.searchParams.set("token", server.authToken);
|
|
476
|
+
if (server.serverProjectRoot)
|
|
477
|
+
url.searchParams.set("rigProjectRoot", server.serverProjectRoot);
|
|
441
478
|
return url.toString();
|
|
442
479
|
}
|
|
443
480
|
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,29 @@ 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 checkouts = Array.isArray(project?.checkouts) ? project.checkouts : [];
|
|
184
|
+
const latestCheckout = [...checkouts].reverse().find((entry) => Boolean(entry && typeof entry === "object" && !Array.isArray(entry) && typeof entry.path === "string"));
|
|
185
|
+
const path = typeof latestCheckout?.path === "string" && latestCheckout.path.trim() ? latestCheckout.path.trim() : null;
|
|
186
|
+
if (path)
|
|
187
|
+
writeRepoServerProjectRoot(projectRoot, path);
|
|
188
|
+
return path;
|
|
189
|
+
} catch {
|
|
190
|
+
return null;
|
|
191
|
+
}
|
|
192
|
+
}
|
|
151
193
|
function mergeHeaders(headers, authToken) {
|
|
152
194
|
const merged = new Headers(headers);
|
|
153
195
|
if (authToken) {
|
|
@@ -172,9 +214,12 @@ function diagnosticMessage(payload) {
|
|
|
172
214
|
}
|
|
173
215
|
async function requestServerJson(context, pathname, init = {}) {
|
|
174
216
|
const server = await ensureServerForCli(context.projectRoot);
|
|
217
|
+
const headers = mergeHeaders(init.headers, server.authToken);
|
|
218
|
+
if (server.serverProjectRoot)
|
|
219
|
+
headers.set("x-rig-project-root", server.serverProjectRoot);
|
|
175
220
|
const response = await fetch(`${server.baseUrl}${pathname}`, {
|
|
176
221
|
...init,
|
|
177
|
-
headers
|
|
222
|
+
headers
|
|
178
223
|
});
|
|
179
224
|
const text = await response.text();
|
|
180
225
|
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,29 @@ 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 checkouts = Array.isArray(project?.checkouts) ? project.checkouts : [];
|
|
189
|
+
const latestCheckout = [...checkouts].reverse().find((entry) => Boolean(entry && typeof entry === "object" && !Array.isArray(entry) && typeof entry.path === "string"));
|
|
190
|
+
const path = typeof latestCheckout?.path === "string" && latestCheckout.path.trim() ? latestCheckout.path.trim() : null;
|
|
191
|
+
if (path)
|
|
192
|
+
writeRepoServerProjectRoot(projectRoot, path);
|
|
193
|
+
return path;
|
|
194
|
+
} catch {
|
|
195
|
+
return null;
|
|
196
|
+
}
|
|
197
|
+
}
|
|
156
198
|
function mergeHeaders(headers, authToken) {
|
|
157
199
|
const merged = new Headers(headers);
|
|
158
200
|
if (authToken) {
|
|
@@ -177,9 +219,12 @@ function diagnosticMessage(payload) {
|
|
|
177
219
|
}
|
|
178
220
|
async function requestServerJson(context, pathname, init = {}) {
|
|
179
221
|
const server = await ensureServerForCli(context.projectRoot);
|
|
222
|
+
const headers = mergeHeaders(init.headers, server.authToken);
|
|
223
|
+
if (server.serverProjectRoot)
|
|
224
|
+
headers.set("x-rig-project-root", server.serverProjectRoot);
|
|
180
225
|
const response = await fetch(`${server.baseUrl}${pathname}`, {
|
|
181
226
|
...init,
|
|
182
|
-
headers
|
|
227
|
+
headers
|
|
183
228
|
});
|
|
184
229
|
const text = await response.text();
|
|
185
230
|
const payload = text.trim().length > 0 ? (() => {
|