@vendian/cli 0.0.40 → 0.0.42
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/cli-wrapper.mjs +868 -729
- package/dev-ui/js/settings.js +30 -6
- package/package.json +1 -1
package/cli-wrapper.mjs
CHANGED
|
@@ -33,446 +33,115 @@ var init_constants = __esm({
|
|
|
33
33
|
}
|
|
34
34
|
});
|
|
35
35
|
|
|
36
|
-
// src/
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
cloudConfigPath: () => cloudConfigPath,
|
|
46
|
-
fetchPackageCredentials: () => fetchPackageCredentials,
|
|
47
|
-
loadCloudConfig: () => loadCloudConfig,
|
|
48
|
-
loginWithVendianOAuth: () => loginWithVendianOAuth,
|
|
49
|
-
resolveApiUrl: () => resolveApiUrl,
|
|
50
|
-
saveCloudToken: () => saveCloudToken
|
|
51
|
-
});
|
|
52
|
-
import crypto from "node:crypto";
|
|
53
|
-
import http from "node:http";
|
|
54
|
-
import fs6 from "node:fs";
|
|
55
|
-
import path5 from "node:path";
|
|
56
|
-
import { spawnSync as spawnSync3 } from "node:child_process";
|
|
57
|
-
function resolveApiUrl({ backend, apiUrl, env = process.env } = {}) {
|
|
58
|
-
if (apiUrl) {
|
|
59
|
-
return apiUrl.replace(/\/$/, "");
|
|
36
|
+
// src/paths.js
|
|
37
|
+
import os from "node:os";
|
|
38
|
+
import path from "node:path";
|
|
39
|
+
function pathApi(platform) {
|
|
40
|
+
return platform === "win32" ? path.win32 : path.posix;
|
|
41
|
+
}
|
|
42
|
+
function homeDir(env, platform) {
|
|
43
|
+
if (platform === "win32") {
|
|
44
|
+
return env.USERPROFILE || os.homedir();
|
|
60
45
|
}
|
|
61
|
-
|
|
62
|
-
|
|
46
|
+
return env.HOME || os.homedir();
|
|
47
|
+
}
|
|
48
|
+
function vendianHome(env = process.env, platform = process.platform) {
|
|
49
|
+
if (env.VENDIAN_CLI_HOME) {
|
|
50
|
+
return pathApi(platform).resolve(env.VENDIAN_CLI_HOME);
|
|
63
51
|
}
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
throw new Error(`Unknown Vendian backend '${target}'. Use local, dev, staging, prod, or --api-url.`);
|
|
52
|
+
if (platform === "win32") {
|
|
53
|
+
const root = env.LOCALAPPDATA || path.win32.join(homeDir(env, platform), "AppData", "Local");
|
|
54
|
+
return path.win32.join(root, "Vendian", "cli");
|
|
68
55
|
}
|
|
69
|
-
return
|
|
56
|
+
return path.posix.join(homeDir(env, platform), ".vendian", "cli");
|
|
70
57
|
}
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
try {
|
|
75
|
-
const config = await getOAuthConfig(resolvedApiUrl, callback.redirectUri);
|
|
76
|
-
const codeVerifier = crypto.randomBytes(48).toString("base64url");
|
|
77
|
-
const codeChallenge = crypto.createHash("sha256").update(codeVerifier).digest("base64url");
|
|
78
|
-
const state = crypto.randomBytes(18).toString("base64url");
|
|
79
|
-
const authorizationUrl = authorizationUrlFor(config, { state, codeChallenge });
|
|
80
|
-
if (noBrowser) {
|
|
81
|
-
console.error(`Open this URL to authenticate the CLI: ${authorizationUrl}`);
|
|
82
|
-
} else {
|
|
83
|
-
openBrowser(authorizationUrl);
|
|
84
|
-
}
|
|
85
|
-
const callbackResult = await callback.wait();
|
|
86
|
-
if (callbackResult.state !== state) {
|
|
87
|
-
throw new Error("OAuth state mismatch");
|
|
88
|
-
}
|
|
89
|
-
const clerkToken = await exchangeCodeForClerkToken(config, callbackResult.code, codeVerifier, callback.redirectUri);
|
|
90
|
-
const exchange = await postJson(`${resolvedApiUrl}/api/v1/cli/auth/oauth/exchange`, {
|
|
91
|
-
clerkToken
|
|
92
|
-
});
|
|
93
|
-
return { apiUrl: resolvedApiUrl, ...exchange };
|
|
94
|
-
} finally {
|
|
95
|
-
await callback.close();
|
|
58
|
+
function configPath(env = process.env, platform = process.platform) {
|
|
59
|
+
if (env.VENDIAN_CLI_CONFIG) {
|
|
60
|
+
return pathApi(platform).resolve(env.VENDIAN_CLI_CONFIG);
|
|
96
61
|
}
|
|
62
|
+
return pathApi(platform).join(vendianHome(env, platform), "config.json");
|
|
97
63
|
}
|
|
98
|
-
|
|
99
|
-
if (
|
|
100
|
-
return
|
|
64
|
+
function managedVenvPath(env = process.env, platform = process.platform) {
|
|
65
|
+
if (env.VENDIAN_CLI_VENV) {
|
|
66
|
+
return pathApi(platform).resolve(env.VENDIAN_CLI_VENV);
|
|
101
67
|
}
|
|
102
|
-
|
|
103
|
-
Authorization: `Bearer ${accessToken}`
|
|
104
|
-
});
|
|
105
|
-
return payload.packageCredentials;
|
|
68
|
+
return pathApi(platform).join(vendianHome(env, platform), ".venv");
|
|
106
69
|
}
|
|
107
|
-
function
|
|
108
|
-
|
|
70
|
+
function venvPython(venvPath, platform = process.platform) {
|
|
71
|
+
return platform === "win32" ? path.win32.join(venvPath, "Scripts", "python.exe") : path.posix.join(venvPath, "bin", "python");
|
|
72
|
+
}
|
|
73
|
+
function venvVendian(venvPath, platform = process.platform) {
|
|
74
|
+
return platform === "win32" ? path.win32.join(venvPath, "Scripts", "vendian.exe") : path.posix.join(venvPath, "bin", "vendian");
|
|
75
|
+
}
|
|
76
|
+
var init_paths = __esm({
|
|
77
|
+
"src/paths.js"() {
|
|
78
|
+
}
|
|
79
|
+
});
|
|
80
|
+
|
|
81
|
+
// src/config.js
|
|
82
|
+
import fs from "node:fs";
|
|
83
|
+
import path2 from "node:path";
|
|
84
|
+
function loadConfig(env = process.env, platform = process.platform) {
|
|
85
|
+
const file = configPath(env, platform);
|
|
109
86
|
try {
|
|
110
|
-
const parsed = JSON.parse(
|
|
111
|
-
return
|
|
112
|
-
} catch {
|
|
87
|
+
const parsed = JSON.parse(fs.readFileSync(file, "utf8"));
|
|
88
|
+
return typeof parsed === "object" && parsed !== null ? parsed : {};
|
|
89
|
+
} catch (error) {
|
|
90
|
+
if (error && error.code === "ENOENT") {
|
|
91
|
+
return {};
|
|
92
|
+
}
|
|
113
93
|
return {};
|
|
114
94
|
}
|
|
115
95
|
}
|
|
116
|
-
function
|
|
117
|
-
const
|
|
118
|
-
|
|
119
|
-
const
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
expiresAt: typeof profile?.expires_at === "string" ? profile.expires_at : void 0,
|
|
128
|
-
profiles
|
|
129
|
-
};
|
|
130
|
-
}
|
|
131
|
-
function activeCloudAuthStatus({ env = process.env, platform = process.platform } = {}) {
|
|
132
|
-
const config = loadCloudConfig(env, platform);
|
|
133
|
-
const profiles = config.profiles && typeof config.profiles === "object" ? config.profiles : {};
|
|
134
|
-
const apiUrl = typeof config.active_api_url === "string" ? config.active_api_url : void 0;
|
|
135
|
-
const profile = apiUrl ? profiles[apiUrl] : void 0;
|
|
136
|
-
return {
|
|
137
|
-
apiUrl,
|
|
138
|
-
activeApiUrl: apiUrl,
|
|
139
|
-
profile: profile && typeof profile === "object" ? profile : void 0,
|
|
140
|
-
authenticated: Boolean(profile?.access_token),
|
|
141
|
-
email: typeof profile?.email === "string" ? profile.email : void 0,
|
|
142
|
-
expiresAt: typeof profile?.expires_at === "string" ? profile.expires_at : void 0,
|
|
143
|
-
profiles
|
|
144
|
-
};
|
|
96
|
+
function saveConfig(config, env = process.env, platform = process.platform) {
|
|
97
|
+
const file = configPath(env, platform);
|
|
98
|
+
fs.mkdirSync(path2.dirname(file), { recursive: true });
|
|
99
|
+
const next = { version: CONFIG_VERSION, ...config };
|
|
100
|
+
fs.writeFileSync(file, `${JSON.stringify(next, null, 2)}
|
|
101
|
+
`, { encoding: "utf8", mode: 384 });
|
|
102
|
+
try {
|
|
103
|
+
fs.chmodSync(file, 384);
|
|
104
|
+
} catch {
|
|
105
|
+
}
|
|
106
|
+
return file;
|
|
145
107
|
}
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
const profile = profiles[targetApiUrl];
|
|
151
|
-
if (!profile || typeof profile !== "object" || !profile.access_token) {
|
|
152
|
-
return {
|
|
153
|
-
apiUrl: targetApiUrl,
|
|
154
|
-
activeApiUrl: typeof config.active_api_url === "string" ? config.active_api_url : void 0,
|
|
155
|
-
authenticated: false,
|
|
156
|
-
activated: false,
|
|
157
|
-
profiles
|
|
158
|
-
};
|
|
108
|
+
var init_config = __esm({
|
|
109
|
+
"src/config.js"() {
|
|
110
|
+
init_constants();
|
|
111
|
+
init_paths();
|
|
159
112
|
}
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
};
|
|
166
|
-
|
|
113
|
+
});
|
|
114
|
+
|
|
115
|
+
// src/process.js
|
|
116
|
+
import { spawn, spawnSync } from "node:child_process";
|
|
117
|
+
function commandExists(command) {
|
|
118
|
+
const result = spawnSync(command, ["--version"], { stdio: "ignore", shell: false });
|
|
119
|
+
return result.status === 0;
|
|
120
|
+
}
|
|
121
|
+
function runCapture(command, args, options = {}) {
|
|
122
|
+
const result = spawnSync(command, args, {
|
|
123
|
+
encoding: "utf8",
|
|
124
|
+
shell: false,
|
|
125
|
+
...options
|
|
126
|
+
});
|
|
127
|
+
const errorMessage = result.error && typeof result.error.message === "string" ? result.error.message : "";
|
|
167
128
|
return {
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
activated: true,
|
|
173
|
-
email: typeof profile.email === "string" ? profile.email : void 0,
|
|
174
|
-
expiresAt: typeof profile.expires_at === "string" ? profile.expires_at : void 0,
|
|
175
|
-
profiles
|
|
129
|
+
ok: !result.error && result.status === 0,
|
|
130
|
+
status: result.status ?? 1,
|
|
131
|
+
stdout: result.stdout || "",
|
|
132
|
+
stderr: result.stderr || errorMessage
|
|
176
133
|
};
|
|
177
134
|
}
|
|
178
|
-
|
|
179
|
-
const
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
function authorizationUrlFor(config, { state, codeChallenge }) {
|
|
184
|
-
const url = new URL(String(config.authorizationUrl));
|
|
185
|
-
url.searchParams.set("response_type", "code");
|
|
186
|
-
url.searchParams.set("client_id", String(config.clientId));
|
|
187
|
-
url.searchParams.set("redirect_uri", String(config.redirectUri));
|
|
188
|
-
url.searchParams.set("scope", String(config.scopes || "email profile"));
|
|
189
|
-
url.searchParams.set("state", state);
|
|
190
|
-
url.searchParams.set("code_challenge", codeChallenge);
|
|
191
|
-
url.searchParams.set("code_challenge_method", "S256");
|
|
192
|
-
return url.toString();
|
|
193
|
-
}
|
|
194
|
-
async function exchangeCodeForClerkToken(config, code, codeVerifier, redirectUri) {
|
|
195
|
-
const body = new URLSearchParams({
|
|
196
|
-
grant_type: "authorization_code",
|
|
197
|
-
client_id: String(config.clientId),
|
|
198
|
-
code,
|
|
199
|
-
redirect_uri: redirectUri,
|
|
200
|
-
code_verifier: codeVerifier
|
|
135
|
+
function runInherit(command, args, options = {}) {
|
|
136
|
+
const result = spawnSync(command, args, {
|
|
137
|
+
stdio: "inherit",
|
|
138
|
+
shell: false,
|
|
139
|
+
...options
|
|
201
140
|
});
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
if (!token) {
|
|
205
|
-
throw new Error("OAuth token exchange did not return a Clerk token");
|
|
141
|
+
if (result.error) {
|
|
142
|
+
throw result.error;
|
|
206
143
|
}
|
|
207
|
-
return
|
|
208
|
-
}
|
|
209
|
-
async function getJson(url, headers = {}) {
|
|
210
|
-
const response = await fetch(url, { headers: { Accept: "application/json", ...headers } });
|
|
211
|
-
return decodeResponse(response);
|
|
212
|
-
}
|
|
213
|
-
async function postJson(url, body) {
|
|
214
|
-
const response = await fetch(url, {
|
|
215
|
-
method: "POST",
|
|
216
|
-
headers: { Accept: "application/json", "Content-Type": "application/json" },
|
|
217
|
-
body: JSON.stringify(body)
|
|
218
|
-
});
|
|
219
|
-
return decodeResponse(response);
|
|
220
|
-
}
|
|
221
|
-
async function postForm(url, body) {
|
|
222
|
-
const response = await fetch(url, {
|
|
223
|
-
method: "POST",
|
|
224
|
-
headers: { Accept: "application/json", "Content-Type": "application/x-www-form-urlencoded" },
|
|
225
|
-
body
|
|
226
|
-
});
|
|
227
|
-
return decodeResponse(response);
|
|
228
|
-
}
|
|
229
|
-
async function decodeResponse(response) {
|
|
230
|
-
const text = await response.text();
|
|
231
|
-
let payload = {};
|
|
232
|
-
if (text) {
|
|
233
|
-
try {
|
|
234
|
-
payload = JSON.parse(text);
|
|
235
|
-
} catch {
|
|
236
|
-
payload = { error: text };
|
|
237
|
-
}
|
|
238
|
-
}
|
|
239
|
-
if (!response.ok) {
|
|
240
|
-
throw new Error(payload.message || payload.error || `HTTP ${response.status}`);
|
|
241
|
-
}
|
|
242
|
-
return payload;
|
|
243
|
-
}
|
|
244
|
-
async function createCallbackServer(env) {
|
|
245
|
-
const port = Number(env.VENDIAN_CLI_OAUTH_REDIRECT_PORT || DEFAULT_OAUTH_REDIRECT_PORT);
|
|
246
|
-
let server;
|
|
247
|
-
let settled = false;
|
|
248
|
-
let timeout;
|
|
249
|
-
const waitPromise = new Promise((resolve, reject) => {
|
|
250
|
-
timeout = setTimeout(() => {
|
|
251
|
-
settled = true;
|
|
252
|
-
reject(new Error("OAuth login timed out before callback completed"));
|
|
253
|
-
}, 3e5);
|
|
254
|
-
server = http.createServer((req, res) => {
|
|
255
|
-
const url = new URL(req.url || "/", `http://${req.headers.host || "127.0.0.1"}`);
|
|
256
|
-
const code = url.searchParams.get("code");
|
|
257
|
-
const state = url.searchParams.get("state");
|
|
258
|
-
const error = url.searchParams.get("error");
|
|
259
|
-
const body = code ? "Vendian CLI authentication complete. You can close this window." : "Vendian CLI authentication failed. Return to the terminal.";
|
|
260
|
-
res.writeHead(code ? 200 : 400, {
|
|
261
|
-
"Content-Type": "text/plain; charset=utf-8",
|
|
262
|
-
"Content-Length": Buffer.byteLength(body)
|
|
263
|
-
});
|
|
264
|
-
res.end(body);
|
|
265
|
-
clearTimeout(timeout);
|
|
266
|
-
if (settled) {
|
|
267
|
-
return;
|
|
268
|
-
}
|
|
269
|
-
settled = true;
|
|
270
|
-
if (error) {
|
|
271
|
-
reject(new Error(`OAuth login failed: ${error}`));
|
|
272
|
-
return;
|
|
273
|
-
}
|
|
274
|
-
if (!code) {
|
|
275
|
-
reject(new Error("OAuth callback did not include an authorization code"));
|
|
276
|
-
return;
|
|
277
|
-
}
|
|
278
|
-
resolve({ code, state });
|
|
279
|
-
});
|
|
280
|
-
server.on("error", reject);
|
|
281
|
-
server.listen(port, "127.0.0.1");
|
|
282
|
-
});
|
|
283
|
-
await new Promise((resolve, reject) => {
|
|
284
|
-
server.once("listening", resolve);
|
|
285
|
-
server.once("error", reject);
|
|
286
|
-
});
|
|
287
|
-
return {
|
|
288
|
-
redirectUri: `http://127.0.0.1:${server.address().port}/callback`,
|
|
289
|
-
wait: () => waitPromise,
|
|
290
|
-
close: () => new Promise((resolve) => server.close(resolve))
|
|
291
|
-
};
|
|
292
|
-
}
|
|
293
|
-
function openBrowser(url) {
|
|
294
|
-
const platform = process.platform;
|
|
295
|
-
if (platform === "win32") {
|
|
296
|
-
spawnSync3(...buildWindowsOpenCommand(url), { stdio: "ignore", shell: false });
|
|
297
|
-
return;
|
|
298
|
-
}
|
|
299
|
-
if (platform === "darwin") {
|
|
300
|
-
spawnSync3(...buildMacOpenCommand(url), { stdio: "ignore", shell: false });
|
|
301
|
-
return;
|
|
302
|
-
}
|
|
303
|
-
spawnSync3(...buildLinuxOpenCommand(url), { stdio: "ignore", shell: false });
|
|
304
|
-
}
|
|
305
|
-
function buildWindowsOpenCommand(url) {
|
|
306
|
-
return ["rundll32.exe", ["url.dll,FileProtocolHandler", String(url)]];
|
|
307
|
-
}
|
|
308
|
-
function buildMacOpenCommand(url) {
|
|
309
|
-
return ["open", [String(url)]];
|
|
310
|
-
}
|
|
311
|
-
function buildLinuxOpenCommand(url) {
|
|
312
|
-
return ["xdg-open", [String(url)]];
|
|
313
|
-
}
|
|
314
|
-
function cloudConfigPath(env = process.env, platform = process.platform) {
|
|
315
|
-
if (env.VENDIAN_CLOUD_CONFIG) {
|
|
316
|
-
return path5.resolve(env.VENDIAN_CLOUD_CONFIG);
|
|
317
|
-
}
|
|
318
|
-
if (platform === "win32") {
|
|
319
|
-
const root2 = env.APPDATA || path5.join(process.env.USERPROFILE || "", "AppData", "Roaming");
|
|
320
|
-
return path5.win32.join(root2, "Vendian", "cloud-auth.json");
|
|
321
|
-
}
|
|
322
|
-
const root = env.XDG_CONFIG_HOME || path5.join(process.env.HOME || "", ".config");
|
|
323
|
-
return path5.posix.join(root, "vendian", "cloud-auth.json");
|
|
324
|
-
}
|
|
325
|
-
function saveCloudToken(token, { env = process.env, platform = process.platform } = {}) {
|
|
326
|
-
const tokenApiUrl = String(token.apiUrl || "").replace(/\/$/, "");
|
|
327
|
-
let raw = { version: 2, profiles: {}, active_api_url: token.apiUrl };
|
|
328
|
-
try {
|
|
329
|
-
const existing = loadCloudConfig(env, platform);
|
|
330
|
-
if (existing && typeof existing === "object" && existing.profiles && typeof existing.profiles === "object") {
|
|
331
|
-
raw.profiles = existing.profiles;
|
|
332
|
-
}
|
|
333
|
-
} catch {
|
|
334
|
-
}
|
|
335
|
-
raw.active_api_url = tokenApiUrl;
|
|
336
|
-
raw.profiles[tokenApiUrl] = {
|
|
337
|
-
api_url: tokenApiUrl,
|
|
338
|
-
access_token: token.accessToken,
|
|
339
|
-
user_id: token.userId,
|
|
340
|
-
email: token.email,
|
|
341
|
-
expires_at: token.expiresAt,
|
|
342
|
-
scopes: token.scopes,
|
|
343
|
-
tooling_eligible: token.toolingEligible
|
|
344
|
-
};
|
|
345
|
-
return writeCloudConfig(raw, { env, platform });
|
|
346
|
-
}
|
|
347
|
-
function writeCloudConfig(raw, { env = process.env, platform = process.platform } = {}) {
|
|
348
|
-
const file = cloudConfigPath(env, platform);
|
|
349
|
-
fs6.mkdirSync(path5.dirname(file), { recursive: true });
|
|
350
|
-
fs6.writeFileSync(file, `${JSON.stringify(raw, null, 2)}
|
|
351
|
-
`, { encoding: "utf8", mode: 384 });
|
|
352
|
-
try {
|
|
353
|
-
fs6.chmodSync(file, 384);
|
|
354
|
-
} catch {
|
|
355
|
-
}
|
|
356
|
-
return file;
|
|
357
|
-
}
|
|
358
|
-
var DEFAULT_OAUTH_REDIRECT_PORT;
|
|
359
|
-
var init_auth = __esm({
|
|
360
|
-
"src/auth.js"() {
|
|
361
|
-
init_constants();
|
|
362
|
-
DEFAULT_OAUTH_REDIRECT_PORT = 8765;
|
|
363
|
-
}
|
|
364
|
-
});
|
|
365
|
-
|
|
366
|
-
// src/doctor.js
|
|
367
|
-
import fs4 from "node:fs";
|
|
368
|
-
|
|
369
|
-
// src/config.js
|
|
370
|
-
init_constants();
|
|
371
|
-
import fs from "node:fs";
|
|
372
|
-
import path2 from "node:path";
|
|
373
|
-
|
|
374
|
-
// src/paths.js
|
|
375
|
-
import os from "node:os";
|
|
376
|
-
import path from "node:path";
|
|
377
|
-
function pathApi(platform) {
|
|
378
|
-
return platform === "win32" ? path.win32 : path.posix;
|
|
379
|
-
}
|
|
380
|
-
function homeDir(env, platform) {
|
|
381
|
-
if (platform === "win32") {
|
|
382
|
-
return env.USERPROFILE || os.homedir();
|
|
383
|
-
}
|
|
384
|
-
return env.HOME || os.homedir();
|
|
385
|
-
}
|
|
386
|
-
function vendianHome(env = process.env, platform = process.platform) {
|
|
387
|
-
if (env.VENDIAN_CLI_HOME) {
|
|
388
|
-
return pathApi(platform).resolve(env.VENDIAN_CLI_HOME);
|
|
389
|
-
}
|
|
390
|
-
if (platform === "win32") {
|
|
391
|
-
const root = env.LOCALAPPDATA || path.win32.join(homeDir(env, platform), "AppData", "Local");
|
|
392
|
-
return path.win32.join(root, "Vendian", "cli");
|
|
393
|
-
}
|
|
394
|
-
return path.posix.join(homeDir(env, platform), ".vendian", "cli");
|
|
395
|
-
}
|
|
396
|
-
function configPath(env = process.env, platform = process.platform) {
|
|
397
|
-
if (env.VENDIAN_CLI_CONFIG) {
|
|
398
|
-
return pathApi(platform).resolve(env.VENDIAN_CLI_CONFIG);
|
|
399
|
-
}
|
|
400
|
-
return pathApi(platform).join(vendianHome(env, platform), "config.json");
|
|
401
|
-
}
|
|
402
|
-
function managedVenvPath(env = process.env, platform = process.platform) {
|
|
403
|
-
if (env.VENDIAN_CLI_VENV) {
|
|
404
|
-
return pathApi(platform).resolve(env.VENDIAN_CLI_VENV);
|
|
405
|
-
}
|
|
406
|
-
return pathApi(platform).join(vendianHome(env, platform), ".venv");
|
|
407
|
-
}
|
|
408
|
-
function venvPython(venvPath, platform = process.platform) {
|
|
409
|
-
return platform === "win32" ? path.win32.join(venvPath, "Scripts", "python.exe") : path.posix.join(venvPath, "bin", "python");
|
|
410
|
-
}
|
|
411
|
-
function venvVendian(venvPath, platform = process.platform) {
|
|
412
|
-
return platform === "win32" ? path.win32.join(venvPath, "Scripts", "vendian.exe") : path.posix.join(venvPath, "bin", "vendian");
|
|
413
|
-
}
|
|
414
|
-
|
|
415
|
-
// src/config.js
|
|
416
|
-
function loadConfig(env = process.env, platform = process.platform) {
|
|
417
|
-
const file = configPath(env, platform);
|
|
418
|
-
try {
|
|
419
|
-
const parsed = JSON.parse(fs.readFileSync(file, "utf8"));
|
|
420
|
-
return typeof parsed === "object" && parsed !== null ? parsed : {};
|
|
421
|
-
} catch (error) {
|
|
422
|
-
if (error && error.code === "ENOENT") {
|
|
423
|
-
return {};
|
|
424
|
-
}
|
|
425
|
-
return {};
|
|
426
|
-
}
|
|
427
|
-
}
|
|
428
|
-
function saveConfig(config, env = process.env, platform = process.platform) {
|
|
429
|
-
const file = configPath(env, platform);
|
|
430
|
-
fs.mkdirSync(path2.dirname(file), { recursive: true });
|
|
431
|
-
const next = { version: CONFIG_VERSION, ...config };
|
|
432
|
-
fs.writeFileSync(file, `${JSON.stringify(next, null, 2)}
|
|
433
|
-
`, { encoding: "utf8", mode: 384 });
|
|
434
|
-
try {
|
|
435
|
-
fs.chmodSync(file, 384);
|
|
436
|
-
} catch {
|
|
437
|
-
}
|
|
438
|
-
return file;
|
|
439
|
-
}
|
|
440
|
-
|
|
441
|
-
// src/install.js
|
|
442
|
-
init_constants();
|
|
443
|
-
import fs3 from "node:fs";
|
|
444
|
-
import path3 from "node:path";
|
|
445
|
-
|
|
446
|
-
// src/process.js
|
|
447
|
-
import { spawn, spawnSync } from "node:child_process";
|
|
448
|
-
function commandExists(command) {
|
|
449
|
-
const result = spawnSync(command, ["--version"], { stdio: "ignore", shell: false });
|
|
450
|
-
return result.status === 0;
|
|
451
|
-
}
|
|
452
|
-
function runCapture(command, args, options = {}) {
|
|
453
|
-
const result = spawnSync(command, args, {
|
|
454
|
-
encoding: "utf8",
|
|
455
|
-
shell: false,
|
|
456
|
-
...options
|
|
457
|
-
});
|
|
458
|
-
const errorMessage = result.error && typeof result.error.message === "string" ? result.error.message : "";
|
|
459
|
-
return {
|
|
460
|
-
ok: !result.error && result.status === 0,
|
|
461
|
-
status: result.status ?? 1,
|
|
462
|
-
stdout: result.stdout || "",
|
|
463
|
-
stderr: result.stderr || errorMessage
|
|
464
|
-
};
|
|
465
|
-
}
|
|
466
|
-
function runInherit(command, args, options = {}) {
|
|
467
|
-
const result = spawnSync(command, args, {
|
|
468
|
-
stdio: "inherit",
|
|
469
|
-
shell: false,
|
|
470
|
-
...options
|
|
471
|
-
});
|
|
472
|
-
if (result.error) {
|
|
473
|
-
throw result.error;
|
|
474
|
-
}
|
|
475
|
-
return result.status ?? 1;
|
|
144
|
+
return result.status ?? 1;
|
|
476
145
|
}
|
|
477
146
|
function spawnForward(command, args, options = {}) {
|
|
478
147
|
return new Promise((resolve, reject) => {
|
|
@@ -514,6 +183,10 @@ function killProcessTree(child, {
|
|
|
514
183
|
return false;
|
|
515
184
|
}
|
|
516
185
|
}
|
|
186
|
+
var init_process = __esm({
|
|
187
|
+
"src/process.js"() {
|
|
188
|
+
}
|
|
189
|
+
});
|
|
517
190
|
|
|
518
191
|
// src/python.js
|
|
519
192
|
import fs2 from "node:fs";
|
|
@@ -601,12 +274,15 @@ function verifyVendianImports(pythonPath) {
|
|
|
601
274
|
]);
|
|
602
275
|
return result.ok;
|
|
603
276
|
}
|
|
277
|
+
var init_python = __esm({
|
|
278
|
+
"src/python.js"() {
|
|
279
|
+
init_process();
|
|
280
|
+
init_paths();
|
|
281
|
+
}
|
|
282
|
+
});
|
|
604
283
|
|
|
605
284
|
// src/secret-store.js
|
|
606
285
|
import { spawnSync as spawnSync2 } from "node:child_process";
|
|
607
|
-
var SERVICE = "Vendian CLI";
|
|
608
|
-
var PACKAGE_TOKEN_ACCOUNT = "gitlab-package-token";
|
|
609
|
-
var SECRET_STORE_TIMEOUT_MS = 5e3;
|
|
610
286
|
function run(command, args, options = {}) {
|
|
611
287
|
return spawnSync2(command, args, {
|
|
612
288
|
encoding: "utf8",
|
|
@@ -703,9 +379,19 @@ function loadPackageTokenSecret(config = {}, platform = process.platform) {
|
|
|
703
379
|
}
|
|
704
380
|
return "";
|
|
705
381
|
}
|
|
382
|
+
var SERVICE, PACKAGE_TOKEN_ACCOUNT, SECRET_STORE_TIMEOUT_MS;
|
|
383
|
+
var init_secret_store = __esm({
|
|
384
|
+
"src/secret-store.js"() {
|
|
385
|
+
SERVICE = "Vendian CLI";
|
|
386
|
+
PACKAGE_TOKEN_ACCOUNT = "gitlab-package-token";
|
|
387
|
+
SECRET_STORE_TIMEOUT_MS = 5e3;
|
|
388
|
+
}
|
|
389
|
+
});
|
|
706
390
|
|
|
707
391
|
// src/install.js
|
|
708
|
-
|
|
392
|
+
import fs3 from "node:fs";
|
|
393
|
+
import path3 from "node:path";
|
|
394
|
+
function registryConfig(config = {}, env = process.env, platform = process.platform) {
|
|
709
395
|
const gitlabHost = env.GITLAB_HOST || config.gitlabHost || DEFAULT_GITLAB_HOST;
|
|
710
396
|
const username = env.GITLAB_USERNAME || config.gitlabUsername || "__token__";
|
|
711
397
|
const sdkProjectId = env.SDK_PUBLIC_PROJECT_ID || config.sdkPublicProjectId || DEFAULT_SDK_PUBLIC_PROJECT_ID;
|
|
@@ -786,267 +472,379 @@ function writeInstallMarker(venvPath, marker) {
|
|
|
786
472
|
"utf8"
|
|
787
473
|
);
|
|
788
474
|
}
|
|
475
|
+
var init_install = __esm({
|
|
476
|
+
"src/install.js"() {
|
|
477
|
+
init_constants();
|
|
478
|
+
init_process();
|
|
479
|
+
init_python();
|
|
480
|
+
init_secret_store();
|
|
481
|
+
}
|
|
482
|
+
});
|
|
789
483
|
|
|
790
|
-
// src/
|
|
791
|
-
|
|
792
|
-
|
|
793
|
-
|
|
794
|
-
|
|
795
|
-
|
|
796
|
-
|
|
797
|
-
|
|
798
|
-
|
|
799
|
-
|
|
800
|
-
|
|
801
|
-
|
|
802
|
-
|
|
803
|
-
|
|
804
|
-
|
|
484
|
+
// src/auth.js
|
|
485
|
+
var auth_exports = {};
|
|
486
|
+
__export(auth_exports, {
|
|
487
|
+
activateCloudProfile: () => activateCloudProfile,
|
|
488
|
+
activeCloudAuthStatus: () => activeCloudAuthStatus,
|
|
489
|
+
buildLinuxOpenCommand: () => buildLinuxOpenCommand,
|
|
490
|
+
buildMacOpenCommand: () => buildMacOpenCommand,
|
|
491
|
+
buildWindowsOpenCommand: () => buildWindowsOpenCommand,
|
|
492
|
+
cloudAuthStatus: () => cloudAuthStatus,
|
|
493
|
+
cloudConfigPath: () => cloudConfigPath,
|
|
494
|
+
defaultOAuthRedirectUri: () => defaultOAuthRedirectUri,
|
|
495
|
+
fetchPackageCredentials: () => fetchPackageCredentials,
|
|
496
|
+
formatOAuthError: () => formatOAuthError,
|
|
497
|
+
loadCloudConfig: () => loadCloudConfig,
|
|
498
|
+
loginWithVendianOAuth: () => loginWithVendianOAuth,
|
|
499
|
+
resolveApiUrl: () => resolveApiUrl,
|
|
500
|
+
saveCloudToken: () => saveCloudToken
|
|
501
|
+
});
|
|
502
|
+
import crypto from "node:crypto";
|
|
503
|
+
import http from "node:http";
|
|
504
|
+
import fs6 from "node:fs";
|
|
505
|
+
import path5 from "node:path";
|
|
506
|
+
import { spawnSync as spawnSync3 } from "node:child_process";
|
|
507
|
+
function resolveApiUrl({ backend, apiUrl, env = process.env } = {}) {
|
|
508
|
+
if (apiUrl) {
|
|
509
|
+
return apiUrl.replace(/\/$/, "");
|
|
510
|
+
}
|
|
511
|
+
if (env.VENDIAN_API_URL) {
|
|
512
|
+
return env.VENDIAN_API_URL.replace(/\/$/, "");
|
|
513
|
+
}
|
|
514
|
+
const target = backend || "prod";
|
|
515
|
+
const resolved = BACKEND_TARGETS[target];
|
|
516
|
+
if (!resolved) {
|
|
517
|
+
throw new Error(`Unknown Vendian backend '${target}'. Use local, dev, staging, prod, or --api-url.`);
|
|
518
|
+
}
|
|
519
|
+
return resolved;
|
|
805
520
|
}
|
|
806
|
-
|
|
807
|
-
|
|
808
|
-
|
|
809
|
-
|
|
810
|
-
|
|
811
|
-
|
|
812
|
-
|
|
813
|
-
|
|
814
|
-
|
|
815
|
-
|
|
816
|
-
|
|
817
|
-
|
|
818
|
-
|
|
819
|
-
".agents",
|
|
820
|
-
".git",
|
|
821
|
-
".hg",
|
|
822
|
-
".mypy_cache",
|
|
823
|
-
".pytest_cache",
|
|
824
|
-
".ruff_cache",
|
|
825
|
-
".svn",
|
|
826
|
-
".venv",
|
|
827
|
-
"__pycache__",
|
|
828
|
-
"build",
|
|
829
|
-
"dist",
|
|
830
|
-
"node_modules",
|
|
831
|
-
"venv"
|
|
832
|
-
]);
|
|
833
|
-
var MANIFEST_NAMES = /* @__PURE__ */ new Set(["manifest.yaml", "manifest.yml"]);
|
|
834
|
-
function findAgentDirectoryCandidates({
|
|
835
|
-
cwd = process.cwd(),
|
|
836
|
-
home = os2.homedir(),
|
|
837
|
-
maxDepth = 6,
|
|
838
|
-
maxDirs = 1e3,
|
|
839
|
-
limit = 12
|
|
840
|
-
} = {}) {
|
|
841
|
-
const start = path4.resolve(cwd);
|
|
842
|
-
const candidates = [];
|
|
843
|
-
const seen = /* @__PURE__ */ new Set();
|
|
844
|
-
function addCandidate(candidatePath) {
|
|
845
|
-
const resolved = safeResolve(candidatePath);
|
|
846
|
-
if (!resolved || seen.has(resolved)) {
|
|
847
|
-
return;
|
|
521
|
+
async function loginWithVendianOAuth({ backend, apiUrl, noBrowser = false, env = process.env } = {}) {
|
|
522
|
+
const resolvedApiUrl = resolveApiUrl({ backend, apiUrl, env });
|
|
523
|
+
const callback = await createCallbackServer(env);
|
|
524
|
+
try {
|
|
525
|
+
const config = await getOAuthConfig(resolvedApiUrl, callback.redirectUri);
|
|
526
|
+
const codeVerifier = crypto.randomBytes(48).toString("base64url");
|
|
527
|
+
const codeChallenge = crypto.createHash("sha256").update(codeVerifier).digest("base64url");
|
|
528
|
+
const state = crypto.randomBytes(18).toString("base64url");
|
|
529
|
+
const authorizationUrl = authorizationUrlFor(config, { state, codeChallenge });
|
|
530
|
+
if (noBrowser) {
|
|
531
|
+
console.error(`Open this URL to authenticate the CLI: ${authorizationUrl}`);
|
|
532
|
+
} else {
|
|
533
|
+
openBrowser(authorizationUrl);
|
|
848
534
|
}
|
|
849
|
-
const
|
|
850
|
-
if (
|
|
851
|
-
|
|
535
|
+
const callbackResult = await callback.wait();
|
|
536
|
+
if (callbackResult.state !== state) {
|
|
537
|
+
throw new Error("OAuth state mismatch");
|
|
852
538
|
}
|
|
853
|
-
|
|
854
|
-
|
|
855
|
-
|
|
856
|
-
absolutePath: resolved,
|
|
857
|
-
agentCount: count
|
|
539
|
+
const clerkToken = await exchangeCodeForClerkToken(config, callbackResult.code, codeVerifier, callback.redirectUri);
|
|
540
|
+
const exchange = await postJson(`${resolvedApiUrl}/api/v1/cli/auth/oauth/exchange`, {
|
|
541
|
+
clerkToken
|
|
858
542
|
});
|
|
543
|
+
return { apiUrl: resolvedApiUrl, ...exchange };
|
|
544
|
+
} catch (error) {
|
|
545
|
+
throw new Error(formatOAuthError(error, {
|
|
546
|
+
apiUrl: resolvedApiUrl,
|
|
547
|
+
redirectUri: callback.redirectUri
|
|
548
|
+
}), { cause: error });
|
|
549
|
+
} finally {
|
|
550
|
+
await callback.close();
|
|
859
551
|
}
|
|
860
|
-
|
|
861
|
-
|
|
862
|
-
|
|
863
|
-
|
|
864
|
-
for (const parent of parentDirs(start, 3)) {
|
|
865
|
-
addCandidate(path4.join(parent, "agents"));
|
|
866
|
-
}
|
|
867
|
-
const roots = uniqueExistingDirs([
|
|
868
|
-
start,
|
|
869
|
-
...commonSearchRoots(home)
|
|
870
|
-
]);
|
|
871
|
-
for (const root of roots) {
|
|
872
|
-
for (const manifest of findManifestFiles(root, { maxDepth, maxDirs, limit: 250 })) {
|
|
873
|
-
const workspace = nearestAgentsAncestor(manifest, root);
|
|
874
|
-
addCandidate(workspace || path4.dirname(manifest));
|
|
875
|
-
if (candidates.length >= limit) {
|
|
876
|
-
return candidates.slice(0, limit);
|
|
877
|
-
}
|
|
878
|
-
}
|
|
552
|
+
}
|
|
553
|
+
async function fetchPackageCredentials({ apiUrl, accessToken }) {
|
|
554
|
+
if (!apiUrl || !accessToken) {
|
|
555
|
+
return void 0;
|
|
879
556
|
}
|
|
880
|
-
|
|
557
|
+
const payload = await getJson(`${String(apiUrl).replace(/\/$/, "")}/api/v1/cli/package-credentials`, {
|
|
558
|
+
Authorization: `Bearer ${accessToken}`
|
|
559
|
+
});
|
|
560
|
+
return payload.packageCredentials;
|
|
881
561
|
}
|
|
882
|
-
function
|
|
883
|
-
|
|
884
|
-
|
|
885
|
-
|
|
886
|
-
|
|
887
|
-
}
|
|
888
|
-
|
|
889
|
-
const resolvedRoot = safeResolve(root || ".");
|
|
890
|
-
if (!resolvedRoot) {
|
|
891
|
-
return [];
|
|
562
|
+
function loadCloudConfig(env = process.env, platform = process.platform) {
|
|
563
|
+
const file = cloudConfigPath(env, platform);
|
|
564
|
+
try {
|
|
565
|
+
const parsed = JSON.parse(fs6.readFileSync(file, "utf8"));
|
|
566
|
+
return parsed && typeof parsed === "object" ? parsed : {};
|
|
567
|
+
} catch {
|
|
568
|
+
return {};
|
|
892
569
|
}
|
|
893
|
-
|
|
894
|
-
|
|
895
|
-
|
|
896
|
-
|
|
897
|
-
|
|
898
|
-
|
|
899
|
-
|
|
900
|
-
|
|
901
|
-
|
|
902
|
-
|
|
903
|
-
|
|
904
|
-
|
|
905
|
-
|
|
906
|
-
|
|
570
|
+
}
|
|
571
|
+
function cloudAuthStatus({ backend, apiUrl, env = process.env, platform = process.platform } = {}) {
|
|
572
|
+
const targetApiUrl = resolveApiUrl({ backend, apiUrl, env });
|
|
573
|
+
const config = loadCloudConfig(env, platform);
|
|
574
|
+
const profiles = config.profiles && typeof config.profiles === "object" ? config.profiles : {};
|
|
575
|
+
const profile = profiles[targetApiUrl];
|
|
576
|
+
return {
|
|
577
|
+
apiUrl: targetApiUrl,
|
|
578
|
+
activeApiUrl: typeof config.active_api_url === "string" ? config.active_api_url : void 0,
|
|
579
|
+
profile: profile && typeof profile === "object" ? profile : void 0,
|
|
580
|
+
authenticated: Boolean(profile?.access_token),
|
|
581
|
+
email: typeof profile?.email === "string" ? profile.email : void 0,
|
|
582
|
+
expiresAt: typeof profile?.expires_at === "string" ? profile.expires_at : void 0,
|
|
583
|
+
profiles
|
|
584
|
+
};
|
|
585
|
+
}
|
|
586
|
+
function activeCloudAuthStatus({ env = process.env, platform = process.platform } = {}) {
|
|
587
|
+
const config = loadCloudConfig(env, platform);
|
|
588
|
+
const profiles = config.profiles && typeof config.profiles === "object" ? config.profiles : {};
|
|
589
|
+
const apiUrl = typeof config.active_api_url === "string" ? config.active_api_url : void 0;
|
|
590
|
+
const profile = apiUrl ? profiles[apiUrl] : void 0;
|
|
591
|
+
return {
|
|
592
|
+
apiUrl,
|
|
593
|
+
activeApiUrl: apiUrl,
|
|
594
|
+
profile: profile && typeof profile === "object" ? profile : void 0,
|
|
595
|
+
authenticated: Boolean(profile?.access_token),
|
|
596
|
+
email: typeof profile?.email === "string" ? profile.email : void 0,
|
|
597
|
+
expiresAt: typeof profile?.expires_at === "string" ? profile.expires_at : void 0,
|
|
598
|
+
profiles
|
|
599
|
+
};
|
|
600
|
+
}
|
|
601
|
+
function activateCloudProfile({ backend, apiUrl, env = process.env, platform = process.platform } = {}) {
|
|
602
|
+
const targetApiUrl = resolveApiUrl({ backend, apiUrl, env });
|
|
603
|
+
const config = loadCloudConfig(env, platform);
|
|
604
|
+
const profiles = config.profiles && typeof config.profiles === "object" ? config.profiles : {};
|
|
605
|
+
const profile = profiles[targetApiUrl];
|
|
606
|
+
if (!profile || typeof profile !== "object" || !profile.access_token) {
|
|
607
|
+
return {
|
|
608
|
+
apiUrl: targetApiUrl,
|
|
609
|
+
activeApiUrl: typeof config.active_api_url === "string" ? config.active_api_url : void 0,
|
|
610
|
+
authenticated: false,
|
|
611
|
+
activated: false,
|
|
612
|
+
profiles
|
|
613
|
+
};
|
|
907
614
|
}
|
|
908
|
-
|
|
615
|
+
const next = {
|
|
616
|
+
...config,
|
|
617
|
+
version: config.version || 2,
|
|
618
|
+
profiles,
|
|
619
|
+
active_api_url: targetApiUrl
|
|
620
|
+
};
|
|
621
|
+
writeCloudConfig(next, { env, platform });
|
|
622
|
+
return {
|
|
623
|
+
apiUrl: targetApiUrl,
|
|
624
|
+
activeApiUrl: targetApiUrl,
|
|
625
|
+
profile,
|
|
626
|
+
authenticated: true,
|
|
627
|
+
activated: true,
|
|
628
|
+
email: typeof profile.email === "string" ? profile.email : void 0,
|
|
629
|
+
expiresAt: typeof profile.expires_at === "string" ? profile.expires_at : void 0,
|
|
630
|
+
profiles
|
|
631
|
+
};
|
|
909
632
|
}
|
|
910
|
-
function
|
|
911
|
-
|
|
912
|
-
|
|
913
|
-
|
|
914
|
-
|
|
915
|
-
|
|
916
|
-
|
|
633
|
+
function defaultOAuthRedirectUri(env = process.env) {
|
|
634
|
+
const port = Number(env.VENDIAN_CLI_OAUTH_REDIRECT_PORT || DEFAULT_OAUTH_REDIRECT_PORT);
|
|
635
|
+
return `http://127.0.0.1:${port}/callback`;
|
|
636
|
+
}
|
|
637
|
+
function formatOAuthError(error, { apiUrl, redirectUri } = {}) {
|
|
638
|
+
const message = error && typeof error.message === "string" ? error.message : String(error || "OAuth login failed");
|
|
639
|
+
const hint = oauthRedirectHint(message, { apiUrl, redirectUri });
|
|
640
|
+
return hint ? `${message}
|
|
641
|
+
${hint}` : message;
|
|
642
|
+
}
|
|
643
|
+
async function getOAuthConfig(apiUrl, redirectUri) {
|
|
644
|
+
const url = new URL(`${apiUrl}/api/v1/cli/auth/oauth/config`);
|
|
645
|
+
url.searchParams.set("redirectUri", redirectUri);
|
|
646
|
+
return getJson(url);
|
|
647
|
+
}
|
|
648
|
+
function authorizationUrlFor(config, { state, codeChallenge }) {
|
|
649
|
+
const url = new URL(String(config.authorizationUrl));
|
|
650
|
+
url.searchParams.set("response_type", "code");
|
|
651
|
+
url.searchParams.set("client_id", String(config.clientId));
|
|
652
|
+
url.searchParams.set("redirect_uri", String(config.redirectUri));
|
|
653
|
+
url.searchParams.set("scope", String(config.scopes || "email profile"));
|
|
654
|
+
url.searchParams.set("state", state);
|
|
655
|
+
url.searchParams.set("code_challenge", codeChallenge);
|
|
656
|
+
url.searchParams.set("code_challenge_method", "S256");
|
|
657
|
+
return url.toString();
|
|
658
|
+
}
|
|
659
|
+
async function exchangeCodeForClerkToken(config, code, codeVerifier, redirectUri) {
|
|
660
|
+
const body = new URLSearchParams({
|
|
661
|
+
grant_type: "authorization_code",
|
|
662
|
+
client_id: String(config.clientId),
|
|
663
|
+
code,
|
|
664
|
+
redirect_uri: redirectUri,
|
|
665
|
+
code_verifier: codeVerifier
|
|
666
|
+
});
|
|
667
|
+
const payload = await postForm(String(config.tokenUrl), body);
|
|
668
|
+
const token = payload.access_token || payload.id_token;
|
|
669
|
+
if (!token) {
|
|
670
|
+
throw new Error("OAuth token exchange did not return a Clerk token");
|
|
917
671
|
}
|
|
918
|
-
return
|
|
672
|
+
return String(token);
|
|
919
673
|
}
|
|
920
|
-
function
|
|
921
|
-
const
|
|
922
|
-
|
|
923
|
-
|
|
924
|
-
|
|
925
|
-
|
|
926
|
-
|
|
927
|
-
|
|
928
|
-
|
|
929
|
-
|
|
930
|
-
|
|
931
|
-
|
|
674
|
+
async function getJson(url, headers = {}) {
|
|
675
|
+
const response = await fetch(url, { headers: { Accept: "application/json", ...headers } });
|
|
676
|
+
return decodeResponse(response);
|
|
677
|
+
}
|
|
678
|
+
async function postJson(url, body) {
|
|
679
|
+
const response = await fetch(url, {
|
|
680
|
+
method: "POST",
|
|
681
|
+
headers: { Accept: "application/json", "Content-Type": "application/json" },
|
|
682
|
+
body: JSON.stringify(body)
|
|
683
|
+
});
|
|
684
|
+
return decodeResponse(response);
|
|
685
|
+
}
|
|
686
|
+
async function postForm(url, body) {
|
|
687
|
+
const response = await fetch(url, {
|
|
688
|
+
method: "POST",
|
|
689
|
+
headers: { Accept: "application/json", "Content-Type": "application/x-www-form-urlencoded" },
|
|
690
|
+
body
|
|
691
|
+
});
|
|
692
|
+
return decodeResponse(response);
|
|
693
|
+
}
|
|
694
|
+
async function decodeResponse(response) {
|
|
695
|
+
const text = await response.text();
|
|
696
|
+
let payload = {};
|
|
697
|
+
if (text) {
|
|
932
698
|
try {
|
|
933
|
-
|
|
699
|
+
payload = JSON.parse(text);
|
|
934
700
|
} catch {
|
|
935
|
-
|
|
936
|
-
}
|
|
937
|
-
for (const entry of entries) {
|
|
938
|
-
if (entry.isFile() && MANIFEST_NAMES.has(entry.name)) {
|
|
939
|
-
manifests.push(path4.join(resolved, entry.name));
|
|
940
|
-
if (manifests.length >= limit) {
|
|
941
|
-
break;
|
|
942
|
-
}
|
|
943
|
-
}
|
|
944
|
-
}
|
|
945
|
-
if (depth >= maxDepth) {
|
|
946
|
-
continue;
|
|
947
|
-
}
|
|
948
|
-
for (const entry of entries) {
|
|
949
|
-
if (!entry.isDirectory() || SKIP_DIRS.has(entry.name)) {
|
|
950
|
-
continue;
|
|
951
|
-
}
|
|
952
|
-
stack.push({ dir: path4.join(resolved, entry.name), depth: depth + 1 });
|
|
701
|
+
payload = { error: text };
|
|
953
702
|
}
|
|
954
703
|
}
|
|
955
|
-
|
|
956
|
-
}
|
|
957
|
-
function nearestAgentsAncestor(manifestPath, root) {
|
|
958
|
-
let current = path4.dirname(manifestPath);
|
|
959
|
-
const stop = path4.resolve(root);
|
|
960
|
-
while (current.startsWith(stop)) {
|
|
961
|
-
if (path4.basename(current).toLowerCase() === "agents") {
|
|
962
|
-
return current;
|
|
963
|
-
}
|
|
964
|
-
const parent = path4.dirname(current);
|
|
965
|
-
if (parent === current) {
|
|
966
|
-
break;
|
|
967
|
-
}
|
|
968
|
-
current = parent;
|
|
704
|
+
if (!response.ok) {
|
|
705
|
+
throw new Error(payload.message || payload.error || `HTTP ${response.status}`);
|
|
969
706
|
}
|
|
970
|
-
return
|
|
707
|
+
return payload;
|
|
971
708
|
}
|
|
972
|
-
function
|
|
973
|
-
|
|
709
|
+
async function createCallbackServer(env) {
|
|
710
|
+
const port = Number(env.VENDIAN_CLI_OAUTH_REDIRECT_PORT || DEFAULT_OAUTH_REDIRECT_PORT);
|
|
711
|
+
let server;
|
|
712
|
+
let settled = false;
|
|
713
|
+
let timeout;
|
|
714
|
+
const waitPromise = new Promise((resolve, reject) => {
|
|
715
|
+
timeout = setTimeout(() => {
|
|
716
|
+
settled = true;
|
|
717
|
+
reject(new Error("OAuth login timed out before callback completed"));
|
|
718
|
+
}, 3e5);
|
|
719
|
+
server = http.createServer((req, res) => {
|
|
720
|
+
const url = new URL(req.url || "/", `http://${req.headers.host || "127.0.0.1"}`);
|
|
721
|
+
const code = url.searchParams.get("code");
|
|
722
|
+
const state = url.searchParams.get("state");
|
|
723
|
+
const error = url.searchParams.get("error");
|
|
724
|
+
const errorDescription = url.searchParams.get("error_description");
|
|
725
|
+
const body = code ? "Vendian CLI authentication complete. You can close this window." : "Vendian CLI authentication failed. Return to the terminal.";
|
|
726
|
+
res.writeHead(code ? 200 : 400, {
|
|
727
|
+
"Content-Type": "text/plain; charset=utf-8",
|
|
728
|
+
"Content-Length": Buffer.byteLength(body)
|
|
729
|
+
});
|
|
730
|
+
res.end(body);
|
|
731
|
+
clearTimeout(timeout);
|
|
732
|
+
if (settled) {
|
|
733
|
+
return;
|
|
734
|
+
}
|
|
735
|
+
settled = true;
|
|
736
|
+
if (error) {
|
|
737
|
+
const details = errorDescription ? `${error} (${errorDescription})` : error;
|
|
738
|
+
reject(new Error(`OAuth login failed: ${details}`));
|
|
739
|
+
return;
|
|
740
|
+
}
|
|
741
|
+
if (!code) {
|
|
742
|
+
reject(new Error("OAuth callback did not include an authorization code"));
|
|
743
|
+
return;
|
|
744
|
+
}
|
|
745
|
+
resolve({ code, state });
|
|
746
|
+
});
|
|
747
|
+
server.on("error", reject);
|
|
748
|
+
server.listen(port, "127.0.0.1");
|
|
749
|
+
});
|
|
750
|
+
await new Promise((resolve, reject) => {
|
|
751
|
+
server.once("listening", resolve);
|
|
752
|
+
server.once("error", reject);
|
|
753
|
+
});
|
|
754
|
+
return {
|
|
755
|
+
redirectUri: `http://127.0.0.1:${server.address().port}/callback`,
|
|
756
|
+
wait: () => waitPromise,
|
|
757
|
+
close: () => new Promise((resolve) => server.close(resolve))
|
|
758
|
+
};
|
|
974
759
|
}
|
|
975
|
-
function
|
|
976
|
-
|
|
977
|
-
|
|
978
|
-
return ".";
|
|
760
|
+
function oauthRedirectHint(message, { apiUrl, redirectUri } = {}) {
|
|
761
|
+
if (!redirectUri) {
|
|
762
|
+
return "";
|
|
979
763
|
}
|
|
980
|
-
|
|
981
|
-
|
|
764
|
+
const lower = String(message || "").toLowerCase();
|
|
765
|
+
const looksLikeRedirectMismatch = lower.includes("redirect_uri") || lower.includes("pre-registered redirect") || lower.includes("oauth login timed out before callback completed") || lower.includes("oauth callback did not include an authorization code");
|
|
766
|
+
if (!looksLikeRedirectMismatch) {
|
|
767
|
+
return "";
|
|
982
768
|
}
|
|
983
|
-
|
|
769
|
+
const target = apiUrl || "this backend";
|
|
770
|
+
return `If Clerk rejected the CLI callback for ${target}, add this redirect URL in Clerk: ${redirectUri}`;
|
|
984
771
|
}
|
|
985
|
-
function
|
|
986
|
-
|
|
987
|
-
|
|
772
|
+
function openBrowser(url) {
|
|
773
|
+
const platform = process.platform;
|
|
774
|
+
if (platform === "win32") {
|
|
775
|
+
spawnSync3(...buildWindowsOpenCommand(url), { stdio: "ignore", shell: false });
|
|
776
|
+
return;
|
|
988
777
|
}
|
|
989
|
-
|
|
990
|
-
"
|
|
991
|
-
|
|
992
|
-
"Code",
|
|
993
|
-
"Developer",
|
|
994
|
-
"Documents",
|
|
995
|
-
"Desktop"
|
|
996
|
-
].map((name) => path4.join(home, name));
|
|
997
|
-
}
|
|
998
|
-
function uniqueExistingDirs(paths) {
|
|
999
|
-
const result = [];
|
|
1000
|
-
const seen = /* @__PURE__ */ new Set();
|
|
1001
|
-
for (const item of paths) {
|
|
1002
|
-
const resolved = safeResolve(item);
|
|
1003
|
-
if (!resolved || seen.has(resolved)) {
|
|
1004
|
-
continue;
|
|
1005
|
-
}
|
|
1006
|
-
try {
|
|
1007
|
-
if (!fs5.statSync(resolved).isDirectory()) {
|
|
1008
|
-
continue;
|
|
1009
|
-
}
|
|
1010
|
-
} catch {
|
|
1011
|
-
continue;
|
|
1012
|
-
}
|
|
1013
|
-
seen.add(resolved);
|
|
1014
|
-
result.push(resolved);
|
|
778
|
+
if (platform === "darwin") {
|
|
779
|
+
spawnSync3(...buildMacOpenCommand(url), { stdio: "ignore", shell: false });
|
|
780
|
+
return;
|
|
1015
781
|
}
|
|
1016
|
-
|
|
782
|
+
spawnSync3(...buildLinuxOpenCommand(url), { stdio: "ignore", shell: false });
|
|
1017
783
|
}
|
|
1018
|
-
function
|
|
1019
|
-
|
|
1020
|
-
|
|
1021
|
-
|
|
1022
|
-
|
|
1023
|
-
|
|
1024
|
-
|
|
784
|
+
function buildWindowsOpenCommand(url) {
|
|
785
|
+
return ["rundll32.exe", ["url.dll,FileProtocolHandler", String(url)]];
|
|
786
|
+
}
|
|
787
|
+
function buildMacOpenCommand(url) {
|
|
788
|
+
return ["open", [String(url)]];
|
|
789
|
+
}
|
|
790
|
+
function buildLinuxOpenCommand(url) {
|
|
791
|
+
return ["xdg-open", [String(url)]];
|
|
792
|
+
}
|
|
793
|
+
function cloudConfigPath(env = process.env, platform = process.platform) {
|
|
794
|
+
if (env.VENDIAN_CLOUD_CONFIG) {
|
|
795
|
+
return path5.resolve(env.VENDIAN_CLOUD_CONFIG);
|
|
796
|
+
}
|
|
797
|
+
if (platform === "win32") {
|
|
798
|
+
const root2 = env.APPDATA || path5.join(process.env.USERPROFILE || "", "AppData", "Roaming");
|
|
799
|
+
return path5.win32.join(root2, "Vendian", "cloud-auth.json");
|
|
800
|
+
}
|
|
801
|
+
const root = env.XDG_CONFIG_HOME || path5.join(process.env.HOME || "", ".config");
|
|
802
|
+
return path5.posix.join(root, "vendian", "cloud-auth.json");
|
|
803
|
+
}
|
|
804
|
+
function saveCloudToken(token, { env = process.env, platform = process.platform } = {}) {
|
|
805
|
+
const tokenApiUrl = String(token.apiUrl || "").replace(/\/$/, "");
|
|
806
|
+
let raw = { version: 2, profiles: {}, active_api_url: token.apiUrl };
|
|
807
|
+
try {
|
|
808
|
+
const existing = loadCloudConfig(env, platform);
|
|
809
|
+
if (existing && typeof existing === "object" && existing.profiles && typeof existing.profiles === "object") {
|
|
810
|
+
raw.profiles = existing.profiles;
|
|
1025
811
|
}
|
|
1026
|
-
|
|
1027
|
-
current = parent;
|
|
812
|
+
} catch {
|
|
1028
813
|
}
|
|
1029
|
-
|
|
814
|
+
raw.active_api_url = tokenApiUrl;
|
|
815
|
+
raw.profiles[tokenApiUrl] = {
|
|
816
|
+
api_url: tokenApiUrl,
|
|
817
|
+
access_token: token.accessToken,
|
|
818
|
+
user_id: token.userId,
|
|
819
|
+
email: token.email,
|
|
820
|
+
expires_at: token.expiresAt,
|
|
821
|
+
scopes: token.scopes,
|
|
822
|
+
tooling_eligible: token.toolingEligible
|
|
823
|
+
};
|
|
824
|
+
return writeCloudConfig(raw, { env, platform });
|
|
1030
825
|
}
|
|
1031
|
-
function
|
|
826
|
+
function writeCloudConfig(raw, { env = process.env, platform = process.platform } = {}) {
|
|
827
|
+
const file = cloudConfigPath(env, platform);
|
|
828
|
+
fs6.mkdirSync(path5.dirname(file), { recursive: true });
|
|
829
|
+
fs6.writeFileSync(file, `${JSON.stringify(raw, null, 2)}
|
|
830
|
+
`, { encoding: "utf8", mode: 384 });
|
|
1032
831
|
try {
|
|
1033
|
-
|
|
832
|
+
fs6.chmodSync(file, 384);
|
|
1034
833
|
} catch {
|
|
1035
|
-
return null;
|
|
1036
834
|
}
|
|
835
|
+
return file;
|
|
1037
836
|
}
|
|
1038
|
-
|
|
1039
|
-
|
|
1040
|
-
|
|
1041
|
-
init_constants();
|
|
1042
|
-
|
|
1043
|
-
|
|
1044
|
-
|
|
837
|
+
var DEFAULT_OAUTH_REDIRECT_PORT;
|
|
838
|
+
var init_auth = __esm({
|
|
839
|
+
"src/auth.js"() {
|
|
840
|
+
init_constants();
|
|
841
|
+
DEFAULT_OAUTH_REDIRECT_PORT = 8765;
|
|
842
|
+
}
|
|
843
|
+
});
|
|
1045
844
|
|
|
1046
845
|
// src/docs.js
|
|
1047
846
|
import fs7 from "node:fs";
|
|
1048
847
|
import path6 from "node:path";
|
|
1049
|
-
var DOC_WORKSPACES_KEY = "agentDocWorkspaces";
|
|
1050
848
|
function pathApi2(platform) {
|
|
1051
849
|
return platform === "win32" ? path6.win32 : path6.posix;
|
|
1052
850
|
}
|
|
@@ -1132,10 +930,24 @@ function refreshAgentDocsWorkspaces({
|
|
|
1132
930
|
saveConfig(next, env, platform);
|
|
1133
931
|
return { config: next, refreshed, failed };
|
|
1134
932
|
}
|
|
933
|
+
var DOC_WORKSPACES_KEY;
|
|
934
|
+
var init_docs = __esm({
|
|
935
|
+
"src/docs.js"() {
|
|
936
|
+
init_config();
|
|
937
|
+
init_paths();
|
|
938
|
+
init_process();
|
|
939
|
+
DOC_WORKSPACES_KEY = "agentDocWorkspaces";
|
|
940
|
+
}
|
|
941
|
+
});
|
|
1135
942
|
|
|
1136
943
|
// src/setup.js
|
|
1137
|
-
|
|
1138
|
-
|
|
944
|
+
var setup_exports = {};
|
|
945
|
+
__export(setup_exports, {
|
|
946
|
+
refreshPackageAccessFromCloudAuth: () => refreshPackageAccessFromCloudAuth,
|
|
947
|
+
savePackageCredentials: () => savePackageCredentials,
|
|
948
|
+
setup: () => setup,
|
|
949
|
+
setupCompletionLines: () => setupCompletionLines
|
|
950
|
+
});
|
|
1139
951
|
import fs8 from "node:fs";
|
|
1140
952
|
async function setup({
|
|
1141
953
|
nonInteractive = false,
|
|
@@ -1188,103 +1000,381 @@ async function setup({
|
|
|
1188
1000
|
if (!installRegistry.token) {
|
|
1189
1001
|
throw new Error("Package access is missing. Run interactive `vendian login` to sign in.");
|
|
1190
1002
|
}
|
|
1191
|
-
const python = findPython(platform);
|
|
1192
|
-
if (!python) {
|
|
1193
|
-
throw new Error("Python 3.11+ was not found. Install Python 3.11 or newer, then rerun `vendian login`.");
|
|
1003
|
+
const python = findPython(platform);
|
|
1004
|
+
if (!python) {
|
|
1005
|
+
throw new Error("Python 3.11+ was not found. Install Python 3.11 or newer, then rerun `vendian login`.");
|
|
1006
|
+
}
|
|
1007
|
+
const venvPath = managedVenvPath(env, platform);
|
|
1008
|
+
console.log(`Managed environment: ${venvPath}`);
|
|
1009
|
+
const pythonPath = ensureVenv(venvPath, python, platform);
|
|
1010
|
+
console.log(`Python: ${pythonVersion(pythonPath) || pythonPath}`);
|
|
1011
|
+
saveConfig(next, env, platform);
|
|
1012
|
+
installVendianPackages({ pythonPath, venvPath, config: next, env, platform });
|
|
1013
|
+
const updated = { ...next, lastManagedUpdateAt: (/* @__PURE__ */ new Date()).toISOString() };
|
|
1014
|
+
refreshAgentDocsWorkspaces({ config: updated, venvPath, env, platform });
|
|
1015
|
+
if (!verifyVendianImports(pythonPath)) {
|
|
1016
|
+
throw new Error("Vendian packages installed, but import verification failed.");
|
|
1017
|
+
}
|
|
1018
|
+
const vendianPath = venvVendian(venvPath, platform);
|
|
1019
|
+
if (!fs8.existsSync(vendianPath)) {
|
|
1020
|
+
throw new Error("Vendian executable was not found after install.");
|
|
1021
|
+
}
|
|
1022
|
+
console.log("");
|
|
1023
|
+
for (const line of setupCompletionLines({ cloudAuthApiUrl, backend, apiUrl })) {
|
|
1024
|
+
console.log(line);
|
|
1025
|
+
}
|
|
1026
|
+
}
|
|
1027
|
+
function savePackageCredentials(config, packageCredentials, { platform = process.platform } = {}) {
|
|
1028
|
+
if (!packageCredentials?.token) {
|
|
1029
|
+
return false;
|
|
1030
|
+
}
|
|
1031
|
+
config.gitlabHost = packageCredentials.gitlabHost || config.gitlabHost;
|
|
1032
|
+
config.gitlabUsername = packageCredentials.username || config.gitlabUsername;
|
|
1033
|
+
config.sdkPublicProjectId = packageCredentials.sdkProjectId || config.sdkPublicProjectId;
|
|
1034
|
+
config.sdkRuntimeProjectId = packageCredentials.runtimeProjectId || config.sdkRuntimeProjectId;
|
|
1035
|
+
config.vendianAgentsVersion = packageCredentials.vendianAgentsVersion || config.vendianAgentsVersion;
|
|
1036
|
+
config.vendianAgentsRuntimeVersion = packageCredentials.vendianAgentsRuntimeVersion || config.vendianAgentsRuntimeVersion;
|
|
1037
|
+
const secret = savePackageTokenSecret(packageCredentials.token, config, platform);
|
|
1038
|
+
if (secret.ok) {
|
|
1039
|
+
Object.assign(config, secret.config || {});
|
|
1040
|
+
delete config.gitlabToken;
|
|
1041
|
+
console.log("Package access saved.");
|
|
1042
|
+
} else {
|
|
1043
|
+
config.gitlabToken = packageCredentials.token;
|
|
1044
|
+
console.log("Package access saved in the local Vendian CLI config file.");
|
|
1045
|
+
}
|
|
1046
|
+
return true;
|
|
1047
|
+
}
|
|
1048
|
+
async function refreshPackageAccessFromCloudAuth({
|
|
1049
|
+
config,
|
|
1050
|
+
auth,
|
|
1051
|
+
env = process.env,
|
|
1052
|
+
platform = process.platform,
|
|
1053
|
+
save = true
|
|
1054
|
+
} = {}) {
|
|
1055
|
+
const currentConfig = config || loadConfig(env, platform);
|
|
1056
|
+
const registry = registryConfig(currentConfig, env, platform);
|
|
1057
|
+
if (registry.token) {
|
|
1058
|
+
return { config: currentConfig, refreshed: false };
|
|
1059
|
+
}
|
|
1060
|
+
const status = auth || activeCloudAuthStatus({ env, platform });
|
|
1061
|
+
if (!status.authenticated || !status.apiUrl || !status.profile?.access_token) {
|
|
1062
|
+
return { config: currentConfig, refreshed: false };
|
|
1063
|
+
}
|
|
1064
|
+
const packageCredentials = await fetchPackageCredentials({
|
|
1065
|
+
apiUrl: status.apiUrl,
|
|
1066
|
+
accessToken: status.profile.access_token
|
|
1067
|
+
});
|
|
1068
|
+
if (!packageCredentials?.token) {
|
|
1069
|
+
return { config: currentConfig, refreshed: false };
|
|
1070
|
+
}
|
|
1071
|
+
const next = { ...currentConfig };
|
|
1072
|
+
const stored = savePackageCredentials(next, packageCredentials, { platform });
|
|
1073
|
+
if (stored && save) {
|
|
1074
|
+
saveConfig(next, env, platform);
|
|
1075
|
+
}
|
|
1076
|
+
return { config: next, refreshed: stored };
|
|
1077
|
+
}
|
|
1078
|
+
function setupCompletionLines({ cloudAuthApiUrl, backend, apiUrl } = {}) {
|
|
1079
|
+
const lines = ["Vendian login complete."];
|
|
1080
|
+
if (cloudAuthApiUrl) {
|
|
1081
|
+
lines.push(`Connected to ${cloudAuthApiUrl}.`);
|
|
1082
|
+
lines.push("Next: vendian cloud local serve --agents-dir ./agents");
|
|
1083
|
+
return lines;
|
|
1084
|
+
}
|
|
1085
|
+
lines.push(`Next: ${cloudAuthLoginCommand({ backend, apiUrl })}`);
|
|
1086
|
+
lines.push("Then: vendian cloud local serve --agents-dir ./agents");
|
|
1087
|
+
return lines;
|
|
1088
|
+
}
|
|
1089
|
+
function cloudAuthLoginCommand({ backend, apiUrl } = {}) {
|
|
1090
|
+
if (apiUrl) {
|
|
1091
|
+
return `vendian cloud auth login --api-url ${apiUrl}`;
|
|
1092
|
+
}
|
|
1093
|
+
if (backend) {
|
|
1094
|
+
return `vendian cloud auth login --backend ${backend}`;
|
|
1095
|
+
}
|
|
1096
|
+
return "vendian cloud auth login";
|
|
1097
|
+
}
|
|
1098
|
+
var init_setup = __esm({
|
|
1099
|
+
"src/setup.js"() {
|
|
1100
|
+
init_auth();
|
|
1101
|
+
init_constants();
|
|
1102
|
+
init_config();
|
|
1103
|
+
init_docs();
|
|
1104
|
+
init_install();
|
|
1105
|
+
init_paths();
|
|
1106
|
+
init_python();
|
|
1107
|
+
init_secret_store();
|
|
1108
|
+
}
|
|
1109
|
+
});
|
|
1110
|
+
|
|
1111
|
+
// src/doctor.js
|
|
1112
|
+
init_config();
|
|
1113
|
+
init_install();
|
|
1114
|
+
init_paths();
|
|
1115
|
+
init_python();
|
|
1116
|
+
import fs4 from "node:fs";
|
|
1117
|
+
function doctor({ env = process.env, platform = process.platform } = {}) {
|
|
1118
|
+
const config = loadConfig(env, platform);
|
|
1119
|
+
const venvPath = managedVenvPath(env, platform);
|
|
1120
|
+
const pythonPath = venvPython(venvPath, platform);
|
|
1121
|
+
const vendianPath = venvVendian(venvPath, platform);
|
|
1122
|
+
const registry = registryConfig(config, env, platform);
|
|
1123
|
+
console.log("Vendian doctor");
|
|
1124
|
+
console.log(`Home: ${venvPath}`);
|
|
1125
|
+
console.log(`System Python 3.11+: ${findPython(platform) ? "yes" : "no"}`);
|
|
1126
|
+
console.log(`uv: ${hasUv() ? "yes" : "no, will use pip fallback"}`);
|
|
1127
|
+
console.log(`Managed Python: ${fs4.existsSync(pythonPath) ? pythonVersion(pythonPath) || "present" : "missing"}`);
|
|
1128
|
+
console.log(`Vendian executable: ${fs4.existsSync(vendianPath) ? vendianPath : "missing"}`);
|
|
1129
|
+
console.log(`Vendian imports: ${fs4.existsSync(pythonPath) && verifyVendianImports(pythonPath) ? "ok" : "missing"}`);
|
|
1130
|
+
console.log(`Package access: ${registry.token ? `configured (${registry.tokenSource})` : "missing"}`);
|
|
1131
|
+
}
|
|
1132
|
+
|
|
1133
|
+
// src/dev-server.js
|
|
1134
|
+
import http2 from "node:http";
|
|
1135
|
+
import fs11 from "node:fs";
|
|
1136
|
+
import path8 from "node:path";
|
|
1137
|
+
import { fileURLToPath } from "node:url";
|
|
1138
|
+
import { spawn as spawn3 } from "node:child_process";
|
|
1139
|
+
|
|
1140
|
+
// src/agent-discovery.js
|
|
1141
|
+
import fs5 from "node:fs";
|
|
1142
|
+
import os2 from "node:os";
|
|
1143
|
+
import path4 from "node:path";
|
|
1144
|
+
var SKIP_DIRS = /* @__PURE__ */ new Set([
|
|
1145
|
+
".agents",
|
|
1146
|
+
".git",
|
|
1147
|
+
".hg",
|
|
1148
|
+
".mypy_cache",
|
|
1149
|
+
".pytest_cache",
|
|
1150
|
+
".ruff_cache",
|
|
1151
|
+
".svn",
|
|
1152
|
+
".venv",
|
|
1153
|
+
"__pycache__",
|
|
1154
|
+
"build",
|
|
1155
|
+
"dist",
|
|
1156
|
+
"node_modules",
|
|
1157
|
+
"venv"
|
|
1158
|
+
]);
|
|
1159
|
+
var MANIFEST_NAMES = /* @__PURE__ */ new Set(["manifest.yaml", "manifest.yml"]);
|
|
1160
|
+
function findAgentDirectoryCandidates({
|
|
1161
|
+
cwd = process.cwd(),
|
|
1162
|
+
home = os2.homedir(),
|
|
1163
|
+
maxDepth = 6,
|
|
1164
|
+
maxDirs = 1e3,
|
|
1165
|
+
limit = 12
|
|
1166
|
+
} = {}) {
|
|
1167
|
+
const start = path4.resolve(cwd);
|
|
1168
|
+
const candidates = [];
|
|
1169
|
+
const seen = /* @__PURE__ */ new Set();
|
|
1170
|
+
function addCandidate(candidatePath) {
|
|
1171
|
+
const resolved = safeResolve(candidatePath);
|
|
1172
|
+
if (!resolved || seen.has(resolved)) {
|
|
1173
|
+
return;
|
|
1174
|
+
}
|
|
1175
|
+
const count = countAgentManifests(resolved, { maxDepth, maxDirs, limit: 100 });
|
|
1176
|
+
if (count < 1) {
|
|
1177
|
+
return;
|
|
1178
|
+
}
|
|
1179
|
+
seen.add(resolved);
|
|
1180
|
+
candidates.push({
|
|
1181
|
+
path: displayPath(resolved, start),
|
|
1182
|
+
absolutePath: resolved,
|
|
1183
|
+
agentCount: count
|
|
1184
|
+
});
|
|
1185
|
+
}
|
|
1186
|
+
addCandidate(path4.join(start, "agents"));
|
|
1187
|
+
if (hasDirectManifest(start)) {
|
|
1188
|
+
addCandidate(start);
|
|
1189
|
+
}
|
|
1190
|
+
for (const parent of parentDirs(start, 3)) {
|
|
1191
|
+
addCandidate(path4.join(parent, "agents"));
|
|
1194
1192
|
}
|
|
1195
|
-
const
|
|
1196
|
-
|
|
1197
|
-
|
|
1198
|
-
|
|
1199
|
-
|
|
1200
|
-
|
|
1201
|
-
|
|
1202
|
-
|
|
1203
|
-
|
|
1204
|
-
|
|
1193
|
+
const roots = uniqueExistingDirs([
|
|
1194
|
+
start,
|
|
1195
|
+
...commonSearchRoots(home)
|
|
1196
|
+
]);
|
|
1197
|
+
for (const root of roots) {
|
|
1198
|
+
for (const manifest of findManifestFiles(root, { maxDepth, maxDirs, limit: 250 })) {
|
|
1199
|
+
const workspace = nearestAgentsAncestor(manifest, root);
|
|
1200
|
+
addCandidate(workspace || path4.dirname(manifest));
|
|
1201
|
+
if (candidates.length >= limit) {
|
|
1202
|
+
return candidates.slice(0, limit);
|
|
1203
|
+
}
|
|
1204
|
+
}
|
|
1205
1205
|
}
|
|
1206
|
-
|
|
1207
|
-
|
|
1208
|
-
|
|
1206
|
+
return candidates.slice(0, limit);
|
|
1207
|
+
}
|
|
1208
|
+
function findAgentFolders(root, {
|
|
1209
|
+
cwd = process.cwd(),
|
|
1210
|
+
maxDepth = 6,
|
|
1211
|
+
maxDirs = 1e3,
|
|
1212
|
+
limit = 100
|
|
1213
|
+
} = {}) {
|
|
1214
|
+
const start = path4.resolve(cwd);
|
|
1215
|
+
const resolvedRoot = safeResolve(root || ".");
|
|
1216
|
+
if (!resolvedRoot) {
|
|
1217
|
+
return [];
|
|
1209
1218
|
}
|
|
1210
|
-
|
|
1211
|
-
|
|
1212
|
-
|
|
1219
|
+
const seen = /* @__PURE__ */ new Set();
|
|
1220
|
+
const folders = [];
|
|
1221
|
+
for (const manifest of findManifestFiles(resolvedRoot, { maxDepth, maxDirs, limit })) {
|
|
1222
|
+
const folder = path4.dirname(manifest);
|
|
1223
|
+
const resolved = safeResolve(folder);
|
|
1224
|
+
if (!resolved || seen.has(resolved)) {
|
|
1225
|
+
continue;
|
|
1226
|
+
}
|
|
1227
|
+
seen.add(resolved);
|
|
1228
|
+
folders.push({
|
|
1229
|
+
path: displayPath(resolved, start),
|
|
1230
|
+
absolutePath: resolved,
|
|
1231
|
+
name: path4.basename(resolved) || displayPath(resolved, start)
|
|
1232
|
+
});
|
|
1213
1233
|
}
|
|
1234
|
+
return folders.sort((a, b) => a.path.localeCompare(b.path)).slice(0, limit);
|
|
1214
1235
|
}
|
|
1215
|
-
function
|
|
1216
|
-
|
|
1217
|
-
|
|
1236
|
+
function countAgentManifests(root, { maxDepth = 6, maxDirs = 1e3, limit = 100 } = {}) {
|
|
1237
|
+
let count = 0;
|
|
1238
|
+
for (const _manifest of findManifestFiles(root, { maxDepth, maxDirs, limit })) {
|
|
1239
|
+
count += 1;
|
|
1240
|
+
if (count >= limit) {
|
|
1241
|
+
return count;
|
|
1242
|
+
}
|
|
1218
1243
|
}
|
|
1219
|
-
|
|
1220
|
-
|
|
1221
|
-
|
|
1222
|
-
|
|
1223
|
-
|
|
1224
|
-
|
|
1225
|
-
|
|
1226
|
-
|
|
1227
|
-
|
|
1228
|
-
|
|
1229
|
-
|
|
1230
|
-
|
|
1231
|
-
|
|
1232
|
-
|
|
1244
|
+
return count;
|
|
1245
|
+
}
|
|
1246
|
+
function findManifestFiles(root, { maxDepth, maxDirs, limit }) {
|
|
1247
|
+
const manifests = [];
|
|
1248
|
+
const stack = [{ dir: root, depth: 0 }];
|
|
1249
|
+
const seen = /* @__PURE__ */ new Set();
|
|
1250
|
+
while (stack.length && manifests.length < limit && seen.size < maxDirs) {
|
|
1251
|
+
const { dir, depth } = stack.pop();
|
|
1252
|
+
const resolved = safeResolve(dir);
|
|
1253
|
+
if (!resolved || seen.has(resolved)) {
|
|
1254
|
+
continue;
|
|
1255
|
+
}
|
|
1256
|
+
seen.add(resolved);
|
|
1257
|
+
let entries;
|
|
1258
|
+
try {
|
|
1259
|
+
entries = fs5.readdirSync(resolved, { withFileTypes: true });
|
|
1260
|
+
} catch {
|
|
1261
|
+
continue;
|
|
1262
|
+
}
|
|
1263
|
+
for (const entry of entries) {
|
|
1264
|
+
if (entry.isFile() && MANIFEST_NAMES.has(entry.name)) {
|
|
1265
|
+
manifests.push(path4.join(resolved, entry.name));
|
|
1266
|
+
if (manifests.length >= limit) {
|
|
1267
|
+
break;
|
|
1268
|
+
}
|
|
1269
|
+
}
|
|
1270
|
+
}
|
|
1271
|
+
if (depth >= maxDepth) {
|
|
1272
|
+
continue;
|
|
1273
|
+
}
|
|
1274
|
+
for (const entry of entries) {
|
|
1275
|
+
if (!entry.isDirectory() || SKIP_DIRS.has(entry.name)) {
|
|
1276
|
+
continue;
|
|
1277
|
+
}
|
|
1278
|
+
stack.push({ dir: path4.join(resolved, entry.name), depth: depth + 1 });
|
|
1279
|
+
}
|
|
1233
1280
|
}
|
|
1234
|
-
return
|
|
1281
|
+
return manifests;
|
|
1235
1282
|
}
|
|
1236
|
-
|
|
1237
|
-
|
|
1238
|
-
|
|
1239
|
-
|
|
1240
|
-
|
|
1241
|
-
|
|
1242
|
-
}
|
|
1243
|
-
|
|
1244
|
-
|
|
1245
|
-
|
|
1246
|
-
|
|
1283
|
+
function nearestAgentsAncestor(manifestPath, root) {
|
|
1284
|
+
let current = path4.dirname(manifestPath);
|
|
1285
|
+
const stop = path4.resolve(root);
|
|
1286
|
+
while (current.startsWith(stop)) {
|
|
1287
|
+
if (path4.basename(current).toLowerCase() === "agents") {
|
|
1288
|
+
return current;
|
|
1289
|
+
}
|
|
1290
|
+
const parent = path4.dirname(current);
|
|
1291
|
+
if (parent === current) {
|
|
1292
|
+
break;
|
|
1293
|
+
}
|
|
1294
|
+
current = parent;
|
|
1247
1295
|
}
|
|
1248
|
-
|
|
1249
|
-
|
|
1250
|
-
|
|
1296
|
+
return null;
|
|
1297
|
+
}
|
|
1298
|
+
function hasDirectManifest(root) {
|
|
1299
|
+
return [...MANIFEST_NAMES].some((name) => fs5.existsSync(path4.join(root, name)));
|
|
1300
|
+
}
|
|
1301
|
+
function displayPath(target, cwd) {
|
|
1302
|
+
const relative = path4.relative(cwd, target);
|
|
1303
|
+
if (!relative) {
|
|
1304
|
+
return ".";
|
|
1251
1305
|
}
|
|
1252
|
-
|
|
1253
|
-
|
|
1254
|
-
accessToken: status.profile.access_token
|
|
1255
|
-
});
|
|
1256
|
-
if (!packageCredentials?.token) {
|
|
1257
|
-
return { config: currentConfig, refreshed: false };
|
|
1306
|
+
if (relative.startsWith("..") || path4.isAbsolute(relative)) {
|
|
1307
|
+
return target;
|
|
1258
1308
|
}
|
|
1259
|
-
|
|
1260
|
-
|
|
1261
|
-
|
|
1262
|
-
|
|
1309
|
+
return relative.startsWith(".") ? relative : `.${path4.sep}${relative}`;
|
|
1310
|
+
}
|
|
1311
|
+
function commonSearchRoots(home) {
|
|
1312
|
+
if (!home) {
|
|
1313
|
+
return [];
|
|
1263
1314
|
}
|
|
1264
|
-
return
|
|
1315
|
+
return [
|
|
1316
|
+
"agents",
|
|
1317
|
+
"Projects",
|
|
1318
|
+
"Code",
|
|
1319
|
+
"Developer",
|
|
1320
|
+
"Documents",
|
|
1321
|
+
"Desktop"
|
|
1322
|
+
].map((name) => path4.join(home, name));
|
|
1265
1323
|
}
|
|
1266
|
-
function
|
|
1267
|
-
const
|
|
1268
|
-
|
|
1269
|
-
|
|
1270
|
-
|
|
1271
|
-
|
|
1324
|
+
function uniqueExistingDirs(paths) {
|
|
1325
|
+
const result = [];
|
|
1326
|
+
const seen = /* @__PURE__ */ new Set();
|
|
1327
|
+
for (const item of paths) {
|
|
1328
|
+
const resolved = safeResolve(item);
|
|
1329
|
+
if (!resolved || seen.has(resolved)) {
|
|
1330
|
+
continue;
|
|
1331
|
+
}
|
|
1332
|
+
try {
|
|
1333
|
+
if (!fs5.statSync(resolved).isDirectory()) {
|
|
1334
|
+
continue;
|
|
1335
|
+
}
|
|
1336
|
+
} catch {
|
|
1337
|
+
continue;
|
|
1338
|
+
}
|
|
1339
|
+
seen.add(resolved);
|
|
1340
|
+
result.push(resolved);
|
|
1272
1341
|
}
|
|
1273
|
-
|
|
1274
|
-
lines.push("Then: vendian cloud local serve --agents-dir ./agents");
|
|
1275
|
-
return lines;
|
|
1342
|
+
return result;
|
|
1276
1343
|
}
|
|
1277
|
-
function
|
|
1278
|
-
|
|
1279
|
-
|
|
1344
|
+
function parentDirs(start, limit) {
|
|
1345
|
+
const parents = [];
|
|
1346
|
+
let current = path4.resolve(start);
|
|
1347
|
+
for (let index = 0; index < limit; index += 1) {
|
|
1348
|
+
const parent = path4.dirname(current);
|
|
1349
|
+
if (!parent || parent === current) {
|
|
1350
|
+
break;
|
|
1351
|
+
}
|
|
1352
|
+
parents.push(parent);
|
|
1353
|
+
current = parent;
|
|
1280
1354
|
}
|
|
1281
|
-
|
|
1282
|
-
|
|
1355
|
+
return parents;
|
|
1356
|
+
}
|
|
1357
|
+
function safeResolve(candidate) {
|
|
1358
|
+
try {
|
|
1359
|
+
return path4.resolve(candidate);
|
|
1360
|
+
} catch {
|
|
1361
|
+
return null;
|
|
1283
1362
|
}
|
|
1284
|
-
return "vendian cloud auth login";
|
|
1285
1363
|
}
|
|
1286
1364
|
|
|
1365
|
+
// src/dev-server.js
|
|
1366
|
+
init_auth();
|
|
1367
|
+
init_constants();
|
|
1368
|
+
init_config();
|
|
1369
|
+
|
|
1287
1370
|
// src/forward.js
|
|
1371
|
+
init_config();
|
|
1372
|
+
init_docs();
|
|
1373
|
+
init_install();
|
|
1374
|
+
init_paths();
|
|
1375
|
+
init_process();
|
|
1376
|
+
init_setup();
|
|
1377
|
+
import fs9 from "node:fs";
|
|
1288
1378
|
var AUTO_UPDATE_INTERVAL_MS = 24 * 60 * 60 * 1e3;
|
|
1289
1379
|
async function forwardToPythonVendian(args, { env = process.env, platform = process.platform } = {}) {
|
|
1290
1380
|
const invocation = await preparePythonVendianInvocation(args, { env, platform });
|
|
@@ -1415,7 +1505,12 @@ function reportProgress(onProgress, message) {
|
|
|
1415
1505
|
}
|
|
1416
1506
|
}
|
|
1417
1507
|
|
|
1508
|
+
// src/dev-server.js
|
|
1509
|
+
init_process();
|
|
1510
|
+
init_paths();
|
|
1511
|
+
|
|
1418
1512
|
// src/workspaces.js
|
|
1513
|
+
init_process();
|
|
1419
1514
|
async function listCloudWorkspaces({
|
|
1420
1515
|
env = process.env,
|
|
1421
1516
|
platform = process.platform,
|
|
@@ -2265,6 +2360,7 @@ function formatSeconds(value) {
|
|
|
2265
2360
|
}
|
|
2266
2361
|
|
|
2267
2362
|
// src/serve-log-store.js
|
|
2363
|
+
init_paths();
|
|
2268
2364
|
import crypto2 from "node:crypto";
|
|
2269
2365
|
import fs10 from "node:fs";
|
|
2270
2366
|
import path7 from "node:path";
|
|
@@ -2404,7 +2500,7 @@ function buildLocalServeEventStreamArgs({ agentsDir: agentsDir2 = "./agents", co
|
|
|
2404
2500
|
}
|
|
2405
2501
|
|
|
2406
2502
|
// src/version.js
|
|
2407
|
-
var CLI_VERSION = true ? "0.0.
|
|
2503
|
+
var CLI_VERSION = true ? "0.0.42" : process.env.npm_package_version || "0.0.0-dev";
|
|
2408
2504
|
|
|
2409
2505
|
// src/dev-server.js
|
|
2410
2506
|
var __dirname = path8.dirname(fileURLToPath(import.meta.url));
|
|
@@ -2541,6 +2637,8 @@ async function handleApi(req, res, pathname, parsed) {
|
|
|
2541
2637
|
return apiServeStop(req, res);
|
|
2542
2638
|
case "/api/create":
|
|
2543
2639
|
return await apiCreate(req, res, body);
|
|
2640
|
+
case "/api/auth/login":
|
|
2641
|
+
return await apiAuthLogin(req, res, body);
|
|
2544
2642
|
case "/api/auth/switch":
|
|
2545
2643
|
return await apiAuthSwitch(req, res, body);
|
|
2546
2644
|
default:
|
|
@@ -2707,6 +2805,36 @@ async function apiAuthSwitch(req, res, body) {
|
|
|
2707
2805
|
jsonResponse(res, { ok: false, error: err.message });
|
|
2708
2806
|
}
|
|
2709
2807
|
}
|
|
2808
|
+
async function loginOrActivateBackend({ backend, env, setupFn, statusFn, activateFn } = {}) {
|
|
2809
|
+
if (!backend || !BACKEND_TARGETS[backend]) {
|
|
2810
|
+
return { ok: false, error: `Unknown backend: ${backend}` };
|
|
2811
|
+
}
|
|
2812
|
+
const authEnv = { ...env || process.env, VENDIAN_API_URL: "" };
|
|
2813
|
+
const readStatus = statusFn || cloudAuthStatus;
|
|
2814
|
+
const activate = activateFn || activateCloudProfile;
|
|
2815
|
+
const status = readStatus({ backend, env: authEnv });
|
|
2816
|
+
if (status.authenticated) {
|
|
2817
|
+
const result = activate({ backend, env: authEnv });
|
|
2818
|
+
if (result?.activated) {
|
|
2819
|
+
return { ok: true, switched: true, backend };
|
|
2820
|
+
}
|
|
2821
|
+
}
|
|
2822
|
+
const runSetup = setupFn || (async (options) => {
|
|
2823
|
+
const { setup: setup2 } = await Promise.resolve().then(() => (init_setup(), setup_exports));
|
|
2824
|
+
return setup2(options);
|
|
2825
|
+
});
|
|
2826
|
+
await runSetup({ backend, forceAuth: true });
|
|
2827
|
+
return { ok: true, loggedIn: true, backend };
|
|
2828
|
+
}
|
|
2829
|
+
async function apiAuthLogin(req, res, body) {
|
|
2830
|
+
try {
|
|
2831
|
+
const result = await loginOrActivateBackend({ backend: body.backend });
|
|
2832
|
+
const status = result.ok ? 200 : 400;
|
|
2833
|
+
jsonResponse(res, result, status);
|
|
2834
|
+
} catch (err) {
|
|
2835
|
+
jsonResponse(res, { ok: false, error: err.message || "Login failed" }, 500);
|
|
2836
|
+
}
|
|
2837
|
+
}
|
|
2710
2838
|
async function apiValidate(req, res, body) {
|
|
2711
2839
|
const targetPath = body.path || agentsDir;
|
|
2712
2840
|
const invocation = await preparePythonVendianInvocation(
|
|
@@ -2995,16 +3123,27 @@ function openUrl(url) {
|
|
|
2995
3123
|
spawn3(shell || cmd, shell ? args : [url], { detached: true, stdio: "ignore" }).unref();
|
|
2996
3124
|
}
|
|
2997
3125
|
|
|
3126
|
+
// src/main.js
|
|
3127
|
+
init_setup();
|
|
3128
|
+
|
|
2998
3129
|
// src/tui.js
|
|
2999
3130
|
init_constants();
|
|
3000
3131
|
init_auth();
|
|
3132
|
+
init_config();
|
|
3001
3133
|
import fs12 from "node:fs";
|
|
3002
3134
|
import path9 from "node:path";
|
|
3003
3135
|
import readline from "node:readline";
|
|
3136
|
+
init_install();
|
|
3004
3137
|
|
|
3005
3138
|
// src/npm-update.js
|
|
3139
|
+
init_config();
|
|
3006
3140
|
var NPM_CHECK_INTERVAL_MS = 30 * 60 * 1e3;
|
|
3007
3141
|
|
|
3142
|
+
// src/tui.js
|
|
3143
|
+
init_paths();
|
|
3144
|
+
init_setup();
|
|
3145
|
+
init_process();
|
|
3146
|
+
|
|
3008
3147
|
// src/ui/figures.js
|
|
3009
3148
|
var supportsUnicode = process.platform !== "win32" || Boolean(
|
|
3010
3149
|
process.env.WT_SESSION || // Windows Terminal
|