@vendian/cli 0.0.41 → 0.0.43
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 +1030 -902
- package/dev-ui/js/settings.js +30 -6
- package/package.json +1 -1
package/cli-wrapper.mjs
CHANGED
|
@@ -33,611 +33,256 @@ var init_constants = __esm({
|
|
|
33
33
|
}
|
|
34
34
|
});
|
|
35
35
|
|
|
36
|
-
// src/
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
cloudConfigPath: () => cloudConfigPath,
|
|
46
|
-
defaultOAuthRedirectUri: () => defaultOAuthRedirectUri,
|
|
47
|
-
fetchPackageCredentials: () => fetchPackageCredentials,
|
|
48
|
-
formatOAuthError: () => formatOAuthError,
|
|
49
|
-
loadCloudConfig: () => loadCloudConfig,
|
|
50
|
-
loginWithVendianOAuth: () => loginWithVendianOAuth,
|
|
51
|
-
resolveApiUrl: () => resolveApiUrl,
|
|
52
|
-
saveCloudToken: () => saveCloudToken
|
|
53
|
-
});
|
|
54
|
-
import crypto from "node:crypto";
|
|
55
|
-
import http from "node:http";
|
|
56
|
-
import fs6 from "node:fs";
|
|
57
|
-
import path5 from "node:path";
|
|
58
|
-
import { spawnSync as spawnSync3 } from "node:child_process";
|
|
59
|
-
function resolveApiUrl({ backend, apiUrl, env = process.env } = {}) {
|
|
60
|
-
if (apiUrl) {
|
|
61
|
-
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();
|
|
62
45
|
}
|
|
63
|
-
|
|
64
|
-
|
|
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);
|
|
65
51
|
}
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
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");
|
|
70
55
|
}
|
|
71
|
-
return
|
|
56
|
+
return path.posix.join(homeDir(env, platform), ".vendian", "cli");
|
|
72
57
|
}
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
try {
|
|
77
|
-
const config = await getOAuthConfig(resolvedApiUrl, callback.redirectUri);
|
|
78
|
-
const codeVerifier = crypto.randomBytes(48).toString("base64url");
|
|
79
|
-
const codeChallenge = crypto.createHash("sha256").update(codeVerifier).digest("base64url");
|
|
80
|
-
const state = crypto.randomBytes(18).toString("base64url");
|
|
81
|
-
const authorizationUrl = authorizationUrlFor(config, { state, codeChallenge });
|
|
82
|
-
if (noBrowser) {
|
|
83
|
-
console.error(`Open this URL to authenticate the CLI: ${authorizationUrl}`);
|
|
84
|
-
} else {
|
|
85
|
-
openBrowser(authorizationUrl);
|
|
86
|
-
}
|
|
87
|
-
const callbackResult = await callback.wait();
|
|
88
|
-
if (callbackResult.state !== state) {
|
|
89
|
-
throw new Error("OAuth state mismatch");
|
|
90
|
-
}
|
|
91
|
-
const clerkToken = await exchangeCodeForClerkToken(config, callbackResult.code, codeVerifier, callback.redirectUri);
|
|
92
|
-
const exchange = await postJson(`${resolvedApiUrl}/api/v1/cli/auth/oauth/exchange`, {
|
|
93
|
-
clerkToken
|
|
94
|
-
});
|
|
95
|
-
return { apiUrl: resolvedApiUrl, ...exchange };
|
|
96
|
-
} catch (error) {
|
|
97
|
-
throw new Error(formatOAuthError(error, {
|
|
98
|
-
apiUrl: resolvedApiUrl,
|
|
99
|
-
redirectUri: callback.redirectUri
|
|
100
|
-
}), { cause: error });
|
|
101
|
-
} finally {
|
|
102
|
-
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);
|
|
103
61
|
}
|
|
62
|
+
return pathApi(platform).join(vendianHome(env, platform), "config.json");
|
|
104
63
|
}
|
|
105
|
-
|
|
106
|
-
if (
|
|
107
|
-
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);
|
|
108
67
|
}
|
|
109
|
-
|
|
110
|
-
Authorization: `Bearer ${accessToken}`
|
|
111
|
-
});
|
|
112
|
-
return payload.packageCredentials;
|
|
68
|
+
return pathApi(platform).join(vendianHome(env, platform), ".venv");
|
|
113
69
|
}
|
|
114
|
-
function
|
|
115
|
-
|
|
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);
|
|
116
86
|
try {
|
|
117
|
-
const parsed = JSON.parse(
|
|
118
|
-
return
|
|
119
|
-
} 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
|
+
}
|
|
120
93
|
return {};
|
|
121
94
|
}
|
|
122
95
|
}
|
|
123
|
-
function
|
|
124
|
-
const
|
|
125
|
-
|
|
126
|
-
const
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
expiresAt: typeof profile?.expires_at === "string" ? profile.expires_at : void 0,
|
|
135
|
-
profiles
|
|
136
|
-
};
|
|
137
|
-
}
|
|
138
|
-
function activeCloudAuthStatus({ env = process.env, platform = process.platform } = {}) {
|
|
139
|
-
const config = loadCloudConfig(env, platform);
|
|
140
|
-
const profiles = config.profiles && typeof config.profiles === "object" ? config.profiles : {};
|
|
141
|
-
const apiUrl = typeof config.active_api_url === "string" ? config.active_api_url : void 0;
|
|
142
|
-
const profile = apiUrl ? profiles[apiUrl] : void 0;
|
|
143
|
-
return {
|
|
144
|
-
apiUrl,
|
|
145
|
-
activeApiUrl: apiUrl,
|
|
146
|
-
profile: profile && typeof profile === "object" ? profile : void 0,
|
|
147
|
-
authenticated: Boolean(profile?.access_token),
|
|
148
|
-
email: typeof profile?.email === "string" ? profile.email : void 0,
|
|
149
|
-
expiresAt: typeof profile?.expires_at === "string" ? profile.expires_at : void 0,
|
|
150
|
-
profiles
|
|
151
|
-
};
|
|
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;
|
|
152
107
|
}
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
const profile = profiles[targetApiUrl];
|
|
158
|
-
if (!profile || typeof profile !== "object" || !profile.access_token) {
|
|
159
|
-
return {
|
|
160
|
-
apiUrl: targetApiUrl,
|
|
161
|
-
activeApiUrl: typeof config.active_api_url === "string" ? config.active_api_url : void 0,
|
|
162
|
-
authenticated: false,
|
|
163
|
-
activated: false,
|
|
164
|
-
profiles
|
|
165
|
-
};
|
|
108
|
+
var init_config = __esm({
|
|
109
|
+
"src/config.js"() {
|
|
110
|
+
init_constants();
|
|
111
|
+
init_paths();
|
|
166
112
|
}
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
};
|
|
173
|
-
|
|
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 : "";
|
|
174
128
|
return {
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
activated: true,
|
|
180
|
-
email: typeof profile.email === "string" ? profile.email : void 0,
|
|
181
|
-
expiresAt: typeof profile.expires_at === "string" ? profile.expires_at : void 0,
|
|
182
|
-
profiles
|
|
129
|
+
ok: !result.error && result.status === 0,
|
|
130
|
+
status: result.status ?? 1,
|
|
131
|
+
stdout: result.stdout || "",
|
|
132
|
+
stderr: result.stderr || errorMessage
|
|
183
133
|
};
|
|
184
134
|
}
|
|
185
|
-
function
|
|
186
|
-
const
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
const message = error && typeof error.message === "string" ? error.message : String(error || "OAuth login failed");
|
|
191
|
-
const hint = oauthRedirectHint(message, { apiUrl, redirectUri });
|
|
192
|
-
return hint ? `${message}
|
|
193
|
-
${hint}` : message;
|
|
194
|
-
}
|
|
195
|
-
async function getOAuthConfig(apiUrl, redirectUri) {
|
|
196
|
-
const url = new URL(`${apiUrl}/api/v1/cli/auth/oauth/config`);
|
|
197
|
-
url.searchParams.set("redirectUri", redirectUri);
|
|
198
|
-
return getJson(url);
|
|
199
|
-
}
|
|
200
|
-
function authorizationUrlFor(config, { state, codeChallenge }) {
|
|
201
|
-
const url = new URL(String(config.authorizationUrl));
|
|
202
|
-
url.searchParams.set("response_type", "code");
|
|
203
|
-
url.searchParams.set("client_id", String(config.clientId));
|
|
204
|
-
url.searchParams.set("redirect_uri", String(config.redirectUri));
|
|
205
|
-
url.searchParams.set("scope", String(config.scopes || "email profile"));
|
|
206
|
-
url.searchParams.set("state", state);
|
|
207
|
-
url.searchParams.set("code_challenge", codeChallenge);
|
|
208
|
-
url.searchParams.set("code_challenge_method", "S256");
|
|
209
|
-
return url.toString();
|
|
210
|
-
}
|
|
211
|
-
async function exchangeCodeForClerkToken(config, code, codeVerifier, redirectUri) {
|
|
212
|
-
const body = new URLSearchParams({
|
|
213
|
-
grant_type: "authorization_code",
|
|
214
|
-
client_id: String(config.clientId),
|
|
215
|
-
code,
|
|
216
|
-
redirect_uri: redirectUri,
|
|
217
|
-
code_verifier: codeVerifier
|
|
135
|
+
function runInherit(command, args, options = {}) {
|
|
136
|
+
const result = spawnSync(command, args, {
|
|
137
|
+
stdio: "inherit",
|
|
138
|
+
shell: false,
|
|
139
|
+
...options
|
|
218
140
|
});
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
if (!token) {
|
|
222
|
-
throw new Error("OAuth token exchange did not return a Clerk token");
|
|
141
|
+
if (result.error) {
|
|
142
|
+
throw result.error;
|
|
223
143
|
}
|
|
224
|
-
return
|
|
144
|
+
return result.status ?? 1;
|
|
225
145
|
}
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
146
|
+
function spawnForward(command, args, options = {}) {
|
|
147
|
+
return new Promise((resolve, reject) => {
|
|
148
|
+
const child = spawn(command, args, {
|
|
149
|
+
stdio: "inherit",
|
|
150
|
+
shell: false,
|
|
151
|
+
...options
|
|
152
|
+
});
|
|
153
|
+
child.on("error", reject);
|
|
154
|
+
child.on("exit", (code, signal) => {
|
|
155
|
+
if (signal) {
|
|
156
|
+
reject(new Error(`command terminated by ${signal}`));
|
|
157
|
+
return;
|
|
158
|
+
}
|
|
159
|
+
resolve(code ?? 1);
|
|
160
|
+
});
|
|
235
161
|
});
|
|
236
|
-
return decodeResponse(response);
|
|
237
162
|
}
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
method: "POST",
|
|
241
|
-
headers: { Accept: "application/json", "Content-Type": "application/x-www-form-urlencoded" },
|
|
242
|
-
body
|
|
243
|
-
});
|
|
244
|
-
return decodeResponse(response);
|
|
163
|
+
function childProcessIsRunning(child) {
|
|
164
|
+
return Boolean(child) && child.exitCode == null && child.signalCode == null;
|
|
245
165
|
}
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
166
|
+
function killProcessTree(child, {
|
|
167
|
+
platform = process.platform,
|
|
168
|
+
signal = "SIGTERM",
|
|
169
|
+
spawnSyncFn = spawnSync
|
|
170
|
+
} = {}) {
|
|
171
|
+
if (!childProcessIsRunning(child)) return false;
|
|
172
|
+
if (platform === "win32") {
|
|
250
173
|
try {
|
|
251
|
-
|
|
174
|
+
spawnSyncFn("taskkill", ["/F", "/T", "/PID", String(child.pid)], { stdio: "ignore" });
|
|
252
175
|
} catch {
|
|
253
|
-
payload = { error: text };
|
|
254
176
|
}
|
|
177
|
+
return true;
|
|
255
178
|
}
|
|
256
|
-
|
|
257
|
-
|
|
179
|
+
try {
|
|
180
|
+
child.kill(signal);
|
|
181
|
+
return true;
|
|
182
|
+
} catch {
|
|
183
|
+
return false;
|
|
258
184
|
}
|
|
259
|
-
return payload;
|
|
260
|
-
}
|
|
261
|
-
async function createCallbackServer(env) {
|
|
262
|
-
const port = Number(env.VENDIAN_CLI_OAUTH_REDIRECT_PORT || DEFAULT_OAUTH_REDIRECT_PORT);
|
|
263
|
-
let server;
|
|
264
|
-
let settled = false;
|
|
265
|
-
let timeout;
|
|
266
|
-
const waitPromise = new Promise((resolve, reject) => {
|
|
267
|
-
timeout = setTimeout(() => {
|
|
268
|
-
settled = true;
|
|
269
|
-
reject(new Error("OAuth login timed out before callback completed"));
|
|
270
|
-
}, 3e5);
|
|
271
|
-
server = http.createServer((req, res) => {
|
|
272
|
-
const url = new URL(req.url || "/", `http://${req.headers.host || "127.0.0.1"}`);
|
|
273
|
-
const code = url.searchParams.get("code");
|
|
274
|
-
const state = url.searchParams.get("state");
|
|
275
|
-
const error = url.searchParams.get("error");
|
|
276
|
-
const errorDescription = url.searchParams.get("error_description");
|
|
277
|
-
const body = code ? "Vendian CLI authentication complete. You can close this window." : "Vendian CLI authentication failed. Return to the terminal.";
|
|
278
|
-
res.writeHead(code ? 200 : 400, {
|
|
279
|
-
"Content-Type": "text/plain; charset=utf-8",
|
|
280
|
-
"Content-Length": Buffer.byteLength(body)
|
|
281
|
-
});
|
|
282
|
-
res.end(body);
|
|
283
|
-
clearTimeout(timeout);
|
|
284
|
-
if (settled) {
|
|
285
|
-
return;
|
|
286
|
-
}
|
|
287
|
-
settled = true;
|
|
288
|
-
if (error) {
|
|
289
|
-
const details = errorDescription ? `${error} (${errorDescription})` : error;
|
|
290
|
-
reject(new Error(`OAuth login failed: ${details}`));
|
|
291
|
-
return;
|
|
292
|
-
}
|
|
293
|
-
if (!code) {
|
|
294
|
-
reject(new Error("OAuth callback did not include an authorization code"));
|
|
295
|
-
return;
|
|
296
|
-
}
|
|
297
|
-
resolve({ code, state });
|
|
298
|
-
});
|
|
299
|
-
server.on("error", reject);
|
|
300
|
-
server.listen(port, "127.0.0.1");
|
|
301
|
-
});
|
|
302
|
-
await new Promise((resolve, reject) => {
|
|
303
|
-
server.once("listening", resolve);
|
|
304
|
-
server.once("error", reject);
|
|
305
|
-
});
|
|
306
|
-
return {
|
|
307
|
-
redirectUri: `http://127.0.0.1:${server.address().port}/callback`,
|
|
308
|
-
wait: () => waitPromise,
|
|
309
|
-
close: () => new Promise((resolve) => server.close(resolve))
|
|
310
|
-
};
|
|
311
185
|
}
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
return "";
|
|
315
|
-
}
|
|
316
|
-
const lower = String(message || "").toLowerCase();
|
|
317
|
-
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");
|
|
318
|
-
if (!looksLikeRedirectMismatch) {
|
|
319
|
-
return "";
|
|
186
|
+
var init_process = __esm({
|
|
187
|
+
"src/process.js"() {
|
|
320
188
|
}
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
189
|
+
});
|
|
190
|
+
|
|
191
|
+
// src/python.js
|
|
192
|
+
import fs2 from "node:fs";
|
|
193
|
+
function pythonCandidates(platform = process.platform) {
|
|
326
194
|
if (platform === "win32") {
|
|
327
|
-
|
|
328
|
-
|
|
195
|
+
return [
|
|
196
|
+
{ command: "py", args: ["-3.11"] },
|
|
197
|
+
{ command: "python", args: [] },
|
|
198
|
+
{ command: "python3", args: [] }
|
|
199
|
+
];
|
|
329
200
|
}
|
|
330
201
|
if (platform === "darwin") {
|
|
331
|
-
|
|
332
|
-
|
|
202
|
+
return [
|
|
203
|
+
{ command: "/opt/homebrew/bin/python3.12", args: [] },
|
|
204
|
+
{ command: "/opt/homebrew/bin/python3.11", args: [] },
|
|
205
|
+
{ command: "/usr/local/bin/python3.12", args: [] },
|
|
206
|
+
{ command: "/usr/local/bin/python3.11", args: [] },
|
|
207
|
+
{ command: "python3.12", args: [] },
|
|
208
|
+
{ command: "python3.11", args: [] },
|
|
209
|
+
{ command: "python3", args: [] },
|
|
210
|
+
{ command: "python", args: [] }
|
|
211
|
+
];
|
|
333
212
|
}
|
|
334
|
-
|
|
335
|
-
}
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
}
|
|
339
|
-
|
|
340
|
-
return ["open", [String(url)]];
|
|
213
|
+
return [
|
|
214
|
+
{ command: "python3.12", args: [] },
|
|
215
|
+
{ command: "python3.11", args: [] },
|
|
216
|
+
{ command: "python3", args: [] },
|
|
217
|
+
{ command: "python", args: [] }
|
|
218
|
+
];
|
|
341
219
|
}
|
|
342
|
-
function
|
|
343
|
-
|
|
220
|
+
function findPython(platform = process.platform) {
|
|
221
|
+
const candidates = pythonCandidates(platform);
|
|
222
|
+
for (const candidate of candidates) {
|
|
223
|
+
const version = runCapture(candidate.command, [
|
|
224
|
+
...candidate.args,
|
|
225
|
+
"-c",
|
|
226
|
+
'import sys; print(f"{sys.version_info.major}.{sys.version_info.minor}.{sys.version_info.micro}")'
|
|
227
|
+
]);
|
|
228
|
+
if (!version.ok) {
|
|
229
|
+
continue;
|
|
230
|
+
}
|
|
231
|
+
const match = version.stdout.trim().match(/^(\d+)\.(\d+)\./);
|
|
232
|
+
if (!match) {
|
|
233
|
+
continue;
|
|
234
|
+
}
|
|
235
|
+
const major = Number(match[1]);
|
|
236
|
+
const minor = Number(match[2]);
|
|
237
|
+
if (major > 3 || major === 3 && minor >= 11) {
|
|
238
|
+
return candidate;
|
|
239
|
+
}
|
|
240
|
+
}
|
|
241
|
+
return null;
|
|
344
242
|
}
|
|
345
|
-
function
|
|
346
|
-
|
|
347
|
-
|
|
243
|
+
function ensureVenv(venvPath, pythonCandidate, platform = process.platform) {
|
|
244
|
+
const pythonPath = venvPython(venvPath, platform);
|
|
245
|
+
if (fs2.existsSync(pythonPath)) {
|
|
246
|
+
return pythonPath;
|
|
348
247
|
}
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
248
|
+
fs2.mkdirSync(venvPath, { recursive: true });
|
|
249
|
+
const status = runInherit(pythonCandidate.command, [
|
|
250
|
+
...pythonCandidate.args,
|
|
251
|
+
"-m",
|
|
252
|
+
"venv",
|
|
253
|
+
venvPath
|
|
254
|
+
]);
|
|
255
|
+
if (status !== 0) {
|
|
256
|
+
throw new Error("Could not create the managed Vendian Python environment.");
|
|
352
257
|
}
|
|
353
|
-
|
|
354
|
-
return path5.posix.join(root, "vendian", "cloud-auth.json");
|
|
258
|
+
return pythonPath;
|
|
355
259
|
}
|
|
356
|
-
function
|
|
357
|
-
|
|
358
|
-
let raw = { version: 2, profiles: {}, active_api_url: token.apiUrl };
|
|
359
|
-
try {
|
|
360
|
-
const existing = loadCloudConfig(env, platform);
|
|
361
|
-
if (existing && typeof existing === "object" && existing.profiles && typeof existing.profiles === "object") {
|
|
362
|
-
raw.profiles = existing.profiles;
|
|
363
|
-
}
|
|
364
|
-
} catch {
|
|
365
|
-
}
|
|
366
|
-
raw.active_api_url = tokenApiUrl;
|
|
367
|
-
raw.profiles[tokenApiUrl] = {
|
|
368
|
-
api_url: tokenApiUrl,
|
|
369
|
-
access_token: token.accessToken,
|
|
370
|
-
user_id: token.userId,
|
|
371
|
-
email: token.email,
|
|
372
|
-
expires_at: token.expiresAt,
|
|
373
|
-
scopes: token.scopes,
|
|
374
|
-
tooling_eligible: token.toolingEligible
|
|
375
|
-
};
|
|
376
|
-
return writeCloudConfig(raw, { env, platform });
|
|
260
|
+
function hasUv() {
|
|
261
|
+
return commandExists("uv");
|
|
377
262
|
}
|
|
378
|
-
function
|
|
379
|
-
const
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
fs6.chmodSync(file, 384);
|
|
385
|
-
} catch {
|
|
386
|
-
}
|
|
387
|
-
return file;
|
|
263
|
+
function pythonVersion(pythonPath) {
|
|
264
|
+
const result = runCapture(pythonPath, [
|
|
265
|
+
"-c",
|
|
266
|
+
'import sys; print(f"{sys.version_info.major}.{sys.version_info.minor}.{sys.version_info.micro}")'
|
|
267
|
+
]);
|
|
268
|
+
return result.ok ? result.stdout.trim() : null;
|
|
388
269
|
}
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
270
|
+
function verifyVendianImports(pythonPath) {
|
|
271
|
+
const result = runCapture(pythonPath, [
|
|
272
|
+
"-c",
|
|
273
|
+
'import vendian_agents, vendian_agents_runtime; print("ok")'
|
|
274
|
+
]);
|
|
275
|
+
return result.ok;
|
|
276
|
+
}
|
|
277
|
+
var init_python = __esm({
|
|
278
|
+
"src/python.js"() {
|
|
279
|
+
init_process();
|
|
280
|
+
init_paths();
|
|
394
281
|
}
|
|
395
282
|
});
|
|
396
283
|
|
|
397
|
-
// src/doctor.js
|
|
398
|
-
import fs4 from "node:fs";
|
|
399
|
-
|
|
400
|
-
// src/config.js
|
|
401
|
-
init_constants();
|
|
402
|
-
import fs from "node:fs";
|
|
403
|
-
import path2 from "node:path";
|
|
404
|
-
|
|
405
|
-
// src/paths.js
|
|
406
|
-
import os from "node:os";
|
|
407
|
-
import path from "node:path";
|
|
408
|
-
function pathApi(platform) {
|
|
409
|
-
return platform === "win32" ? path.win32 : path.posix;
|
|
410
|
-
}
|
|
411
|
-
function homeDir(env, platform) {
|
|
412
|
-
if (platform === "win32") {
|
|
413
|
-
return env.USERPROFILE || os.homedir();
|
|
414
|
-
}
|
|
415
|
-
return env.HOME || os.homedir();
|
|
416
|
-
}
|
|
417
|
-
function vendianHome(env = process.env, platform = process.platform) {
|
|
418
|
-
if (env.VENDIAN_CLI_HOME) {
|
|
419
|
-
return pathApi(platform).resolve(env.VENDIAN_CLI_HOME);
|
|
420
|
-
}
|
|
421
|
-
if (platform === "win32") {
|
|
422
|
-
const root = env.LOCALAPPDATA || path.win32.join(homeDir(env, platform), "AppData", "Local");
|
|
423
|
-
return path.win32.join(root, "Vendian", "cli");
|
|
424
|
-
}
|
|
425
|
-
return path.posix.join(homeDir(env, platform), ".vendian", "cli");
|
|
426
|
-
}
|
|
427
|
-
function configPath(env = process.env, platform = process.platform) {
|
|
428
|
-
if (env.VENDIAN_CLI_CONFIG) {
|
|
429
|
-
return pathApi(platform).resolve(env.VENDIAN_CLI_CONFIG);
|
|
430
|
-
}
|
|
431
|
-
return pathApi(platform).join(vendianHome(env, platform), "config.json");
|
|
432
|
-
}
|
|
433
|
-
function managedVenvPath(env = process.env, platform = process.platform) {
|
|
434
|
-
if (env.VENDIAN_CLI_VENV) {
|
|
435
|
-
return pathApi(platform).resolve(env.VENDIAN_CLI_VENV);
|
|
436
|
-
}
|
|
437
|
-
return pathApi(platform).join(vendianHome(env, platform), ".venv");
|
|
438
|
-
}
|
|
439
|
-
function venvPython(venvPath, platform = process.platform) {
|
|
440
|
-
return platform === "win32" ? path.win32.join(venvPath, "Scripts", "python.exe") : path.posix.join(venvPath, "bin", "python");
|
|
441
|
-
}
|
|
442
|
-
function venvVendian(venvPath, platform = process.platform) {
|
|
443
|
-
return platform === "win32" ? path.win32.join(venvPath, "Scripts", "vendian.exe") : path.posix.join(venvPath, "bin", "vendian");
|
|
444
|
-
}
|
|
445
|
-
|
|
446
|
-
// src/config.js
|
|
447
|
-
function loadConfig(env = process.env, platform = process.platform) {
|
|
448
|
-
const file = configPath(env, platform);
|
|
449
|
-
try {
|
|
450
|
-
const parsed = JSON.parse(fs.readFileSync(file, "utf8"));
|
|
451
|
-
return typeof parsed === "object" && parsed !== null ? parsed : {};
|
|
452
|
-
} catch (error) {
|
|
453
|
-
if (error && error.code === "ENOENT") {
|
|
454
|
-
return {};
|
|
455
|
-
}
|
|
456
|
-
return {};
|
|
457
|
-
}
|
|
458
|
-
}
|
|
459
|
-
function saveConfig(config, env = process.env, platform = process.platform) {
|
|
460
|
-
const file = configPath(env, platform);
|
|
461
|
-
fs.mkdirSync(path2.dirname(file), { recursive: true });
|
|
462
|
-
const next = { version: CONFIG_VERSION, ...config };
|
|
463
|
-
fs.writeFileSync(file, `${JSON.stringify(next, null, 2)}
|
|
464
|
-
`, { encoding: "utf8", mode: 384 });
|
|
465
|
-
try {
|
|
466
|
-
fs.chmodSync(file, 384);
|
|
467
|
-
} catch {
|
|
468
|
-
}
|
|
469
|
-
return file;
|
|
470
|
-
}
|
|
471
|
-
|
|
472
|
-
// src/install.js
|
|
473
|
-
init_constants();
|
|
474
|
-
import fs3 from "node:fs";
|
|
475
|
-
import path3 from "node:path";
|
|
476
|
-
|
|
477
|
-
// src/process.js
|
|
478
|
-
import { spawn, spawnSync } from "node:child_process";
|
|
479
|
-
function commandExists(command) {
|
|
480
|
-
const result = spawnSync(command, ["--version"], { stdio: "ignore", shell: false });
|
|
481
|
-
return result.status === 0;
|
|
482
|
-
}
|
|
483
|
-
function runCapture(command, args, options = {}) {
|
|
484
|
-
const result = spawnSync(command, args, {
|
|
485
|
-
encoding: "utf8",
|
|
486
|
-
shell: false,
|
|
487
|
-
...options
|
|
488
|
-
});
|
|
489
|
-
const errorMessage = result.error && typeof result.error.message === "string" ? result.error.message : "";
|
|
490
|
-
return {
|
|
491
|
-
ok: !result.error && result.status === 0,
|
|
492
|
-
status: result.status ?? 1,
|
|
493
|
-
stdout: result.stdout || "",
|
|
494
|
-
stderr: result.stderr || errorMessage
|
|
495
|
-
};
|
|
496
|
-
}
|
|
497
|
-
function runInherit(command, args, options = {}) {
|
|
498
|
-
const result = spawnSync(command, args, {
|
|
499
|
-
stdio: "inherit",
|
|
500
|
-
shell: false,
|
|
501
|
-
...options
|
|
502
|
-
});
|
|
503
|
-
if (result.error) {
|
|
504
|
-
throw result.error;
|
|
505
|
-
}
|
|
506
|
-
return result.status ?? 1;
|
|
507
|
-
}
|
|
508
|
-
function spawnForward(command, args, options = {}) {
|
|
509
|
-
return new Promise((resolve, reject) => {
|
|
510
|
-
const child = spawn(command, args, {
|
|
511
|
-
stdio: "inherit",
|
|
512
|
-
shell: false,
|
|
513
|
-
...options
|
|
514
|
-
});
|
|
515
|
-
child.on("error", reject);
|
|
516
|
-
child.on("exit", (code, signal) => {
|
|
517
|
-
if (signal) {
|
|
518
|
-
reject(new Error(`command terminated by ${signal}`));
|
|
519
|
-
return;
|
|
520
|
-
}
|
|
521
|
-
resolve(code ?? 1);
|
|
522
|
-
});
|
|
523
|
-
});
|
|
524
|
-
}
|
|
525
|
-
function childProcessIsRunning(child) {
|
|
526
|
-
return Boolean(child) && child.exitCode == null && child.signalCode == null;
|
|
527
|
-
}
|
|
528
|
-
function killProcessTree(child, {
|
|
529
|
-
platform = process.platform,
|
|
530
|
-
signal = "SIGTERM",
|
|
531
|
-
spawnSyncFn = spawnSync
|
|
532
|
-
} = {}) {
|
|
533
|
-
if (!childProcessIsRunning(child)) return false;
|
|
534
|
-
if (platform === "win32") {
|
|
535
|
-
try {
|
|
536
|
-
spawnSyncFn("taskkill", ["/F", "/T", "/PID", String(child.pid)], { stdio: "ignore" });
|
|
537
|
-
} catch {
|
|
538
|
-
}
|
|
539
|
-
return true;
|
|
540
|
-
}
|
|
541
|
-
try {
|
|
542
|
-
child.kill(signal);
|
|
543
|
-
return true;
|
|
544
|
-
} catch {
|
|
545
|
-
return false;
|
|
546
|
-
}
|
|
547
|
-
}
|
|
548
|
-
|
|
549
|
-
// src/python.js
|
|
550
|
-
import fs2 from "node:fs";
|
|
551
|
-
function pythonCandidates(platform = process.platform) {
|
|
552
|
-
if (platform === "win32") {
|
|
553
|
-
return [
|
|
554
|
-
{ command: "py", args: ["-3.11"] },
|
|
555
|
-
{ command: "python", args: [] },
|
|
556
|
-
{ command: "python3", args: [] }
|
|
557
|
-
];
|
|
558
|
-
}
|
|
559
|
-
if (platform === "darwin") {
|
|
560
|
-
return [
|
|
561
|
-
{ command: "/opt/homebrew/bin/python3.12", args: [] },
|
|
562
|
-
{ command: "/opt/homebrew/bin/python3.11", args: [] },
|
|
563
|
-
{ command: "/usr/local/bin/python3.12", args: [] },
|
|
564
|
-
{ command: "/usr/local/bin/python3.11", args: [] },
|
|
565
|
-
{ command: "python3.12", args: [] },
|
|
566
|
-
{ command: "python3.11", args: [] },
|
|
567
|
-
{ command: "python3", args: [] },
|
|
568
|
-
{ command: "python", args: [] }
|
|
569
|
-
];
|
|
570
|
-
}
|
|
571
|
-
return [
|
|
572
|
-
{ command: "python3.12", args: [] },
|
|
573
|
-
{ command: "python3.11", args: [] },
|
|
574
|
-
{ command: "python3", args: [] },
|
|
575
|
-
{ command: "python", args: [] }
|
|
576
|
-
];
|
|
577
|
-
}
|
|
578
|
-
function findPython(platform = process.platform) {
|
|
579
|
-
const candidates = pythonCandidates(platform);
|
|
580
|
-
for (const candidate of candidates) {
|
|
581
|
-
const version = runCapture(candidate.command, [
|
|
582
|
-
...candidate.args,
|
|
583
|
-
"-c",
|
|
584
|
-
'import sys; print(f"{sys.version_info.major}.{sys.version_info.minor}.{sys.version_info.micro}")'
|
|
585
|
-
]);
|
|
586
|
-
if (!version.ok) {
|
|
587
|
-
continue;
|
|
588
|
-
}
|
|
589
|
-
const match = version.stdout.trim().match(/^(\d+)\.(\d+)\./);
|
|
590
|
-
if (!match) {
|
|
591
|
-
continue;
|
|
592
|
-
}
|
|
593
|
-
const major = Number(match[1]);
|
|
594
|
-
const minor = Number(match[2]);
|
|
595
|
-
if (major > 3 || major === 3 && minor >= 11) {
|
|
596
|
-
return candidate;
|
|
597
|
-
}
|
|
598
|
-
}
|
|
599
|
-
return null;
|
|
600
|
-
}
|
|
601
|
-
function ensureVenv(venvPath, pythonCandidate, platform = process.platform) {
|
|
602
|
-
const pythonPath = venvPython(venvPath, platform);
|
|
603
|
-
if (fs2.existsSync(pythonPath)) {
|
|
604
|
-
return pythonPath;
|
|
605
|
-
}
|
|
606
|
-
fs2.mkdirSync(venvPath, { recursive: true });
|
|
607
|
-
const status = runInherit(pythonCandidate.command, [
|
|
608
|
-
...pythonCandidate.args,
|
|
609
|
-
"-m",
|
|
610
|
-
"venv",
|
|
611
|
-
venvPath
|
|
612
|
-
]);
|
|
613
|
-
if (status !== 0) {
|
|
614
|
-
throw new Error("Could not create the managed Vendian Python environment.");
|
|
615
|
-
}
|
|
616
|
-
return pythonPath;
|
|
617
|
-
}
|
|
618
|
-
function hasUv() {
|
|
619
|
-
return commandExists("uv");
|
|
620
|
-
}
|
|
621
|
-
function pythonVersion(pythonPath) {
|
|
622
|
-
const result = runCapture(pythonPath, [
|
|
623
|
-
"-c",
|
|
624
|
-
'import sys; print(f"{sys.version_info.major}.{sys.version_info.minor}.{sys.version_info.micro}")'
|
|
625
|
-
]);
|
|
626
|
-
return result.ok ? result.stdout.trim() : null;
|
|
627
|
-
}
|
|
628
|
-
function verifyVendianImports(pythonPath) {
|
|
629
|
-
const result = runCapture(pythonPath, [
|
|
630
|
-
"-c",
|
|
631
|
-
'import vendian_agents, vendian_agents_runtime; print("ok")'
|
|
632
|
-
]);
|
|
633
|
-
return result.ok;
|
|
634
|
-
}
|
|
635
|
-
|
|
636
284
|
// src/secret-store.js
|
|
637
285
|
import { spawnSync as spawnSync2 } from "node:child_process";
|
|
638
|
-
var SERVICE = "Vendian CLI";
|
|
639
|
-
var PACKAGE_TOKEN_ACCOUNT = "gitlab-package-token";
|
|
640
|
-
var SECRET_STORE_TIMEOUT_MS = 5e3;
|
|
641
286
|
function run(command, args, options = {}) {
|
|
642
287
|
return spawnSync2(command, args, {
|
|
643
288
|
encoding: "utf8",
|
|
@@ -734,8 +379,18 @@ function loadPackageTokenSecret(config = {}, platform = process.platform) {
|
|
|
734
379
|
}
|
|
735
380
|
return "";
|
|
736
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
|
+
});
|
|
737
390
|
|
|
738
391
|
// src/install.js
|
|
392
|
+
import fs3 from "node:fs";
|
|
393
|
+
import path3 from "node:path";
|
|
739
394
|
function registryConfig(config = {}, env = process.env, platform = process.platform) {
|
|
740
395
|
const gitlabHost = env.GITLAB_HOST || config.gitlabHost || DEFAULT_GITLAB_HOST;
|
|
741
396
|
const username = env.GITLAB_USERNAME || config.gitlabUsername || "__token__";
|
|
@@ -794,31 +449,671 @@ function installVendianPackages({ pythonPath, venvPath, config, env = process.en
|
|
|
794
449
|
if (hasUv()) {
|
|
795
450
|
status = runInherit("uv", ["pip", ...installArgs, "--python", pythonPath]);
|
|
796
451
|
} else {
|
|
797
|
-
status = runInherit(pythonPath, ["-m", "pip", ...installArgs]);
|
|
452
|
+
status = runInherit(pythonPath, ["-m", "pip", ...installArgs]);
|
|
453
|
+
}
|
|
454
|
+
if (status !== 0) {
|
|
455
|
+
throw new Error("Could not install Vendian runtime packages. Check package access and try again.");
|
|
456
|
+
}
|
|
457
|
+
writeInstallMarker(venvPath, {
|
|
458
|
+
gitlabHost: registry.gitlabHost,
|
|
459
|
+
gitlabUsername: registry.username,
|
|
460
|
+
sdkProjectId: registry.sdkProjectId,
|
|
461
|
+
runtimeProjectId: registry.runtimeProjectId,
|
|
462
|
+
vendianAgentsVersion: registry.vendianAgentsVersion || "latest",
|
|
463
|
+
vendianAgentsRuntimeVersion: registry.vendianRuntimeVersion || "latest"
|
|
464
|
+
});
|
|
465
|
+
}
|
|
466
|
+
function writeInstallMarker(venvPath, marker) {
|
|
467
|
+
fs3.mkdirSync(venvPath, { recursive: true });
|
|
468
|
+
fs3.writeFileSync(
|
|
469
|
+
path3.join(venvPath, ".vendian-bootstrap.json"),
|
|
470
|
+
`${JSON.stringify({ version: 1, ...marker }, null, 2)}
|
|
471
|
+
`,
|
|
472
|
+
"utf8"
|
|
473
|
+
);
|
|
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
|
+
});
|
|
483
|
+
|
|
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;
|
|
520
|
+
}
|
|
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);
|
|
534
|
+
}
|
|
535
|
+
const callbackResult = await callback.wait();
|
|
536
|
+
if (callbackResult.state !== state) {
|
|
537
|
+
throw new Error("OAuth state mismatch");
|
|
538
|
+
}
|
|
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
|
|
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();
|
|
551
|
+
}
|
|
552
|
+
}
|
|
553
|
+
async function fetchPackageCredentials({ apiUrl, accessToken }) {
|
|
554
|
+
if (!apiUrl || !accessToken) {
|
|
555
|
+
return void 0;
|
|
556
|
+
}
|
|
557
|
+
const payload = await getJson(`${String(apiUrl).replace(/\/$/, "")}/api/v1/cli/package-credentials`, {
|
|
558
|
+
Authorization: `Bearer ${accessToken}`
|
|
559
|
+
});
|
|
560
|
+
return payload.packageCredentials;
|
|
561
|
+
}
|
|
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 {};
|
|
569
|
+
}
|
|
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
|
+
};
|
|
614
|
+
}
|
|
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
|
+
};
|
|
632
|
+
}
|
|
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");
|
|
671
|
+
}
|
|
672
|
+
return String(token);
|
|
673
|
+
}
|
|
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) {
|
|
698
|
+
try {
|
|
699
|
+
payload = JSON.parse(text);
|
|
700
|
+
} catch {
|
|
701
|
+
payload = { error: text };
|
|
702
|
+
}
|
|
703
|
+
}
|
|
704
|
+
if (!response.ok) {
|
|
705
|
+
throw new Error(payload.message || payload.error || `HTTP ${response.status}`);
|
|
706
|
+
}
|
|
707
|
+
return payload;
|
|
708
|
+
}
|
|
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
|
+
};
|
|
759
|
+
}
|
|
760
|
+
function oauthRedirectHint(message, { apiUrl, redirectUri } = {}) {
|
|
761
|
+
if (!redirectUri) {
|
|
762
|
+
return "";
|
|
763
|
+
}
|
|
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 "";
|
|
768
|
+
}
|
|
769
|
+
const target = apiUrl || "this backend";
|
|
770
|
+
return `If Clerk rejected the CLI callback for ${target}, add this redirect URL in Clerk: ${redirectUri}`;
|
|
771
|
+
}
|
|
772
|
+
function openBrowser(url) {
|
|
773
|
+
const platform = process.platform;
|
|
774
|
+
if (platform === "win32") {
|
|
775
|
+
spawnSync3(...buildWindowsOpenCommand(url), { stdio: "ignore", shell: false });
|
|
776
|
+
return;
|
|
777
|
+
}
|
|
778
|
+
if (platform === "darwin") {
|
|
779
|
+
spawnSync3(...buildMacOpenCommand(url), { stdio: "ignore", shell: false });
|
|
780
|
+
return;
|
|
781
|
+
}
|
|
782
|
+
spawnSync3(...buildLinuxOpenCommand(url), { stdio: "ignore", shell: false });
|
|
783
|
+
}
|
|
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;
|
|
811
|
+
}
|
|
812
|
+
} catch {
|
|
813
|
+
}
|
|
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 });
|
|
825
|
+
}
|
|
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 });
|
|
831
|
+
try {
|
|
832
|
+
fs6.chmodSync(file, 384);
|
|
833
|
+
} catch {
|
|
834
|
+
}
|
|
835
|
+
return file;
|
|
836
|
+
}
|
|
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
|
+
});
|
|
844
|
+
|
|
845
|
+
// src/docs.js
|
|
846
|
+
import fs7 from "node:fs";
|
|
847
|
+
import path6 from "node:path";
|
|
848
|
+
function pathApi2(platform) {
|
|
849
|
+
return platform === "win32" ? path6.win32 : path6.posix;
|
|
850
|
+
}
|
|
851
|
+
function commandWritesAgentDocs(args = []) {
|
|
852
|
+
return args[0] === "init";
|
|
853
|
+
}
|
|
854
|
+
function initOutputDir(args = []) {
|
|
855
|
+
for (let index = 1; index < args.length; index += 1) {
|
|
856
|
+
const arg = args[index];
|
|
857
|
+
if (arg === "--output-dir" || arg === "-o") {
|
|
858
|
+
return args[index + 1] || ".";
|
|
859
|
+
}
|
|
860
|
+
if (arg.startsWith("--output-dir=")) {
|
|
861
|
+
return arg.slice("--output-dir=".length) || ".";
|
|
862
|
+
}
|
|
863
|
+
}
|
|
864
|
+
return ".";
|
|
865
|
+
}
|
|
866
|
+
function resolveWorkspacePath(outputDir, { cwd = process.cwd(), platform = process.platform } = {}) {
|
|
867
|
+
const api = pathApi2(platform);
|
|
868
|
+
const raw = outputDir || ".";
|
|
869
|
+
return api.isAbsolute(raw) ? api.normalize(raw) : api.resolve(cwd, raw);
|
|
870
|
+
}
|
|
871
|
+
function recordAgentDocsWorkspace(outputDir, {
|
|
872
|
+
env = process.env,
|
|
873
|
+
platform = process.platform,
|
|
874
|
+
cwd = process.cwd(),
|
|
875
|
+
now = /* @__PURE__ */ new Date()
|
|
876
|
+
} = {}) {
|
|
877
|
+
const workspacePath = resolveWorkspacePath(outputDir, { cwd, platform });
|
|
878
|
+
const config = loadConfig(env, platform);
|
|
879
|
+
const existing = Array.isArray(config[DOC_WORKSPACES_KEY]) ? config[DOC_WORKSPACES_KEY] : [];
|
|
880
|
+
const workspaces = [workspacePath, ...existing.filter((entry) => entry !== workspacePath)];
|
|
881
|
+
const next = {
|
|
882
|
+
...config,
|
|
883
|
+
[DOC_WORKSPACES_KEY]: workspaces,
|
|
884
|
+
lastAgentDocsInitAt: now.toISOString()
|
|
885
|
+
};
|
|
886
|
+
saveConfig(next, env, platform);
|
|
887
|
+
return next;
|
|
888
|
+
}
|
|
889
|
+
function recordForwardedDocsCommand(args, code, options = {}) {
|
|
890
|
+
if (code !== 0 || !commandWritesAgentDocs(args)) {
|
|
891
|
+
return false;
|
|
892
|
+
}
|
|
893
|
+
recordAgentDocsWorkspace(initOutputDir(args), options);
|
|
894
|
+
return true;
|
|
895
|
+
}
|
|
896
|
+
function refreshAgentDocsWorkspaces({
|
|
897
|
+
config,
|
|
898
|
+
venvPath,
|
|
899
|
+
env = process.env,
|
|
900
|
+
platform = process.platform,
|
|
901
|
+
run: run2 = runInherit,
|
|
902
|
+
now = /* @__PURE__ */ new Date()
|
|
903
|
+
} = {}) {
|
|
904
|
+
const currentConfig = config || loadConfig(env, platform);
|
|
905
|
+
const workspaces = Array.isArray(currentConfig[DOC_WORKSPACES_KEY]) ? [...new Set(currentConfig[DOC_WORKSPACES_KEY].filter((entry) => typeof entry === "string" && entry.trim()))] : [];
|
|
906
|
+
if (workspaces.length === 0) {
|
|
907
|
+
saveConfig(currentConfig, env, platform);
|
|
908
|
+
return { config: currentConfig, refreshed: 0, failed: 0 };
|
|
909
|
+
}
|
|
910
|
+
const vendianPath = venvVendian(venvPath, platform);
|
|
911
|
+
let refreshed = 0;
|
|
912
|
+
let failed = 0;
|
|
913
|
+
for (const workspace of workspaces) {
|
|
914
|
+
if (!fs7.existsSync(workspace)) {
|
|
915
|
+
continue;
|
|
916
|
+
}
|
|
917
|
+
const status = run2(vendianPath, ["init", "--output-dir", workspace, "--yes"]);
|
|
918
|
+
if (status === 0) {
|
|
919
|
+
refreshed += 1;
|
|
920
|
+
} else {
|
|
921
|
+
failed += 1;
|
|
922
|
+
console.error(`[vendian] Could not refresh SDK docs in ${workspace}`);
|
|
923
|
+
}
|
|
924
|
+
}
|
|
925
|
+
const next = {
|
|
926
|
+
...currentConfig,
|
|
927
|
+
[DOC_WORKSPACES_KEY]: workspaces,
|
|
928
|
+
lastAgentDocsRefreshAt: now.toISOString()
|
|
929
|
+
};
|
|
930
|
+
saveConfig(next, env, platform);
|
|
931
|
+
return { config: next, refreshed, failed };
|
|
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
|
+
});
|
|
942
|
+
|
|
943
|
+
// src/setup.js
|
|
944
|
+
var setup_exports = {};
|
|
945
|
+
__export(setup_exports, {
|
|
946
|
+
refreshPackageAccessFromCloudAuth: () => refreshPackageAccessFromCloudAuth,
|
|
947
|
+
savePackageCredentials: () => savePackageCredentials,
|
|
948
|
+
setup: () => setup,
|
|
949
|
+
setupCompletionLines: () => setupCompletionLines
|
|
950
|
+
});
|
|
951
|
+
import fs8 from "node:fs";
|
|
952
|
+
async function setup({
|
|
953
|
+
nonInteractive = false,
|
|
954
|
+
backend = void 0,
|
|
955
|
+
apiUrl = void 0,
|
|
956
|
+
noBrowser = false,
|
|
957
|
+
forceAuth = false,
|
|
958
|
+
env = process.env,
|
|
959
|
+
platform = process.platform
|
|
960
|
+
} = {}) {
|
|
961
|
+
const existing = loadConfig(env, platform);
|
|
962
|
+
const registry = registryConfig(existing, env, platform);
|
|
963
|
+
const auth = cloudAuthStatus({ backend, apiUrl, env, platform });
|
|
964
|
+
const next = {
|
|
965
|
+
...existing,
|
|
966
|
+
gitlabHost: registry.gitlabHost || DEFAULT_GITLAB_HOST,
|
|
967
|
+
gitlabUsername: registry.username || "__token__",
|
|
968
|
+
sdkPublicProjectId: registry.sdkProjectId || DEFAULT_SDK_PUBLIC_PROJECT_ID,
|
|
969
|
+
sdkRuntimeProjectId: registry.runtimeProjectId || DEFAULT_SDK_RUNTIME_PROJECT_ID
|
|
970
|
+
};
|
|
971
|
+
let cloudAuthApiUrl = auth.authenticated ? auth.apiUrl : void 0;
|
|
972
|
+
console.log("Vendian login");
|
|
973
|
+
console.log("This signs in to Vendian and prepares the local runtime.");
|
|
974
|
+
console.log("Agent requirements.txt files stay reserved for agent-owned dependencies.");
|
|
975
|
+
console.log("");
|
|
976
|
+
if ((!auth.authenticated || forceAuth) && !nonInteractive) {
|
|
977
|
+
console.log(`Opening Vendian sign-in for ${auth.apiUrl}...`);
|
|
978
|
+
const login = await loginWithVendianOAuth({ backend, apiUrl, noBrowser, env });
|
|
979
|
+
saveCloudToken(login, { env, platform });
|
|
980
|
+
cloudAuthApiUrl = login.apiUrl;
|
|
981
|
+
const packageCredentials = login.packageCredentials;
|
|
982
|
+
if (!registry.token && !packageCredentials?.token) {
|
|
983
|
+
throw new Error("Vendian login succeeded, but the backend did not return package credentials. Configure CLI_PACKAGE_REGISTRY_TOKEN on the backend.");
|
|
984
|
+
}
|
|
985
|
+
if (packageCredentials) {
|
|
986
|
+
savePackageCredentials(next, packageCredentials, { platform });
|
|
987
|
+
}
|
|
988
|
+
} else if (auth.authenticated) {
|
|
989
|
+
activateCloudProfile({ backend, apiUrl, env, platform });
|
|
990
|
+
console.log(`Cloud authentication already saved for ${auth.apiUrl}${auth.email ? ` (${auth.email})` : ""}.`);
|
|
991
|
+
const refreshed = await refreshPackageAccessFromCloudAuth({ config: next, auth, env, platform });
|
|
992
|
+
Object.assign(next, refreshed.config);
|
|
993
|
+
} else if (registry.token) {
|
|
994
|
+
if (registry.tokenSource !== "secret-store") {
|
|
995
|
+
next.gitlabToken = registry.token;
|
|
996
|
+
}
|
|
997
|
+
console.log("Using saved package access.");
|
|
998
|
+
}
|
|
999
|
+
const installRegistry = registryConfig(next, env, platform);
|
|
1000
|
+
if (!installRegistry.token) {
|
|
1001
|
+
throw new Error("Package access is missing. Run interactive `vendian login` to sign in.");
|
|
1002
|
+
}
|
|
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.");
|
|
798
1045
|
}
|
|
799
|
-
|
|
800
|
-
|
|
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 };
|
|
801
1059
|
}
|
|
802
|
-
|
|
803
|
-
|
|
804
|
-
|
|
805
|
-
|
|
806
|
-
|
|
807
|
-
|
|
808
|
-
|
|
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
|
|
809
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 };
|
|
810
1077
|
}
|
|
811
|
-
function
|
|
812
|
-
|
|
813
|
-
|
|
814
|
-
|
|
815
|
-
|
|
816
|
-
|
|
817
|
-
|
|
818
|
-
);
|
|
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";
|
|
819
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
|
+
});
|
|
820
1110
|
|
|
821
1111
|
// src/doctor.js
|
|
1112
|
+
init_config();
|
|
1113
|
+
init_install();
|
|
1114
|
+
init_paths();
|
|
1115
|
+
init_python();
|
|
1116
|
+
import fs4 from "node:fs";
|
|
822
1117
|
function doctor({ env = process.env, platform = process.platform } = {}) {
|
|
823
1118
|
const config = loadConfig(env, platform);
|
|
824
1119
|
const venvPath = managedVenvPath(env, platform);
|
|
@@ -971,351 +1266,115 @@ function findManifestFiles(root, { maxDepth, maxDirs, limit }) {
|
|
|
971
1266
|
if (manifests.length >= limit) {
|
|
972
1267
|
break;
|
|
973
1268
|
}
|
|
974
|
-
}
|
|
975
|
-
}
|
|
976
|
-
if (depth >= maxDepth) {
|
|
977
|
-
continue;
|
|
978
|
-
}
|
|
979
|
-
for (const entry of entries) {
|
|
980
|
-
if (!entry.isDirectory() || SKIP_DIRS.has(entry.name)) {
|
|
981
|
-
continue;
|
|
982
|
-
}
|
|
983
|
-
stack.push({ dir: path4.join(resolved, entry.name), depth: depth + 1 });
|
|
984
|
-
}
|
|
985
|
-
}
|
|
986
|
-
return manifests;
|
|
987
|
-
}
|
|
988
|
-
function nearestAgentsAncestor(manifestPath, root) {
|
|
989
|
-
let current = path4.dirname(manifestPath);
|
|
990
|
-
const stop = path4.resolve(root);
|
|
991
|
-
while (current.startsWith(stop)) {
|
|
992
|
-
if (path4.basename(current).toLowerCase() === "agents") {
|
|
993
|
-
return current;
|
|
994
|
-
}
|
|
995
|
-
const parent = path4.dirname(current);
|
|
996
|
-
if (parent === current) {
|
|
997
|
-
break;
|
|
998
|
-
}
|
|
999
|
-
current = parent;
|
|
1000
|
-
}
|
|
1001
|
-
return null;
|
|
1002
|
-
}
|
|
1003
|
-
function hasDirectManifest(root) {
|
|
1004
|
-
return [...MANIFEST_NAMES].some((name) => fs5.existsSync(path4.join(root, name)));
|
|
1005
|
-
}
|
|
1006
|
-
function displayPath(target, cwd) {
|
|
1007
|
-
const relative = path4.relative(cwd, target);
|
|
1008
|
-
if (!relative) {
|
|
1009
|
-
return ".";
|
|
1010
|
-
}
|
|
1011
|
-
if (relative.startsWith("..") || path4.isAbsolute(relative)) {
|
|
1012
|
-
return target;
|
|
1013
|
-
}
|
|
1014
|
-
return relative.startsWith(".") ? relative : `.${path4.sep}${relative}`;
|
|
1015
|
-
}
|
|
1016
|
-
function commonSearchRoots(home) {
|
|
1017
|
-
if (!home) {
|
|
1018
|
-
return [];
|
|
1019
|
-
}
|
|
1020
|
-
return [
|
|
1021
|
-
"agents",
|
|
1022
|
-
"Projects",
|
|
1023
|
-
"Code",
|
|
1024
|
-
"Developer",
|
|
1025
|
-
"Documents",
|
|
1026
|
-
"Desktop"
|
|
1027
|
-
].map((name) => path4.join(home, name));
|
|
1028
|
-
}
|
|
1029
|
-
function uniqueExistingDirs(paths) {
|
|
1030
|
-
const result = [];
|
|
1031
|
-
const seen = /* @__PURE__ */ new Set();
|
|
1032
|
-
for (const item of paths) {
|
|
1033
|
-
const resolved = safeResolve(item);
|
|
1034
|
-
if (!resolved || seen.has(resolved)) {
|
|
1035
|
-
continue;
|
|
1036
|
-
}
|
|
1037
|
-
try {
|
|
1038
|
-
if (!fs5.statSync(resolved).isDirectory()) {
|
|
1039
|
-
continue;
|
|
1040
|
-
}
|
|
1041
|
-
} catch {
|
|
1042
|
-
continue;
|
|
1043
|
-
}
|
|
1044
|
-
seen.add(resolved);
|
|
1045
|
-
result.push(resolved);
|
|
1046
|
-
}
|
|
1047
|
-
return result;
|
|
1048
|
-
}
|
|
1049
|
-
function parentDirs(start, limit) {
|
|
1050
|
-
const parents = [];
|
|
1051
|
-
let current = path4.resolve(start);
|
|
1052
|
-
for (let index = 0; index < limit; index += 1) {
|
|
1053
|
-
const parent = path4.dirname(current);
|
|
1054
|
-
if (!parent || parent === current) {
|
|
1055
|
-
break;
|
|
1056
|
-
}
|
|
1057
|
-
parents.push(parent);
|
|
1058
|
-
current = parent;
|
|
1059
|
-
}
|
|
1060
|
-
return parents;
|
|
1061
|
-
}
|
|
1062
|
-
function safeResolve(candidate) {
|
|
1063
|
-
try {
|
|
1064
|
-
return path4.resolve(candidate);
|
|
1065
|
-
} catch {
|
|
1066
|
-
return null;
|
|
1067
|
-
}
|
|
1068
|
-
}
|
|
1069
|
-
|
|
1070
|
-
// src/dev-server.js
|
|
1071
|
-
init_auth();
|
|
1072
|
-
init_constants();
|
|
1073
|
-
|
|
1074
|
-
// src/forward.js
|
|
1075
|
-
import fs9 from "node:fs";
|
|
1076
|
-
|
|
1077
|
-
// src/docs.js
|
|
1078
|
-
import fs7 from "node:fs";
|
|
1079
|
-
import path6 from "node:path";
|
|
1080
|
-
var DOC_WORKSPACES_KEY = "agentDocWorkspaces";
|
|
1081
|
-
function pathApi2(platform) {
|
|
1082
|
-
return platform === "win32" ? path6.win32 : path6.posix;
|
|
1083
|
-
}
|
|
1084
|
-
function commandWritesAgentDocs(args = []) {
|
|
1085
|
-
return args[0] === "init";
|
|
1086
|
-
}
|
|
1087
|
-
function initOutputDir(args = []) {
|
|
1088
|
-
for (let index = 1; index < args.length; index += 1) {
|
|
1089
|
-
const arg = args[index];
|
|
1090
|
-
if (arg === "--output-dir" || arg === "-o") {
|
|
1091
|
-
return args[index + 1] || ".";
|
|
1092
|
-
}
|
|
1093
|
-
if (arg.startsWith("--output-dir=")) {
|
|
1094
|
-
return arg.slice("--output-dir=".length) || ".";
|
|
1095
|
-
}
|
|
1096
|
-
}
|
|
1097
|
-
return ".";
|
|
1098
|
-
}
|
|
1099
|
-
function resolveWorkspacePath(outputDir, { cwd = process.cwd(), platform = process.platform } = {}) {
|
|
1100
|
-
const api = pathApi2(platform);
|
|
1101
|
-
const raw = outputDir || ".";
|
|
1102
|
-
return api.isAbsolute(raw) ? api.normalize(raw) : api.resolve(cwd, raw);
|
|
1103
|
-
}
|
|
1104
|
-
function recordAgentDocsWorkspace(outputDir, {
|
|
1105
|
-
env = process.env,
|
|
1106
|
-
platform = process.platform,
|
|
1107
|
-
cwd = process.cwd(),
|
|
1108
|
-
now = /* @__PURE__ */ new Date()
|
|
1109
|
-
} = {}) {
|
|
1110
|
-
const workspacePath = resolveWorkspacePath(outputDir, { cwd, platform });
|
|
1111
|
-
const config = loadConfig(env, platform);
|
|
1112
|
-
const existing = Array.isArray(config[DOC_WORKSPACES_KEY]) ? config[DOC_WORKSPACES_KEY] : [];
|
|
1113
|
-
const workspaces = [workspacePath, ...existing.filter((entry) => entry !== workspacePath)];
|
|
1114
|
-
const next = {
|
|
1115
|
-
...config,
|
|
1116
|
-
[DOC_WORKSPACES_KEY]: workspaces,
|
|
1117
|
-
lastAgentDocsInitAt: now.toISOString()
|
|
1118
|
-
};
|
|
1119
|
-
saveConfig(next, env, platform);
|
|
1120
|
-
return next;
|
|
1121
|
-
}
|
|
1122
|
-
function recordForwardedDocsCommand(args, code, options = {}) {
|
|
1123
|
-
if (code !== 0 || !commandWritesAgentDocs(args)) {
|
|
1124
|
-
return false;
|
|
1125
|
-
}
|
|
1126
|
-
recordAgentDocsWorkspace(initOutputDir(args), options);
|
|
1127
|
-
return true;
|
|
1128
|
-
}
|
|
1129
|
-
function refreshAgentDocsWorkspaces({
|
|
1130
|
-
config,
|
|
1131
|
-
venvPath,
|
|
1132
|
-
env = process.env,
|
|
1133
|
-
platform = process.platform,
|
|
1134
|
-
run: run2 = runInherit,
|
|
1135
|
-
now = /* @__PURE__ */ new Date()
|
|
1136
|
-
} = {}) {
|
|
1137
|
-
const currentConfig = config || loadConfig(env, platform);
|
|
1138
|
-
const workspaces = Array.isArray(currentConfig[DOC_WORKSPACES_KEY]) ? [...new Set(currentConfig[DOC_WORKSPACES_KEY].filter((entry) => typeof entry === "string" && entry.trim()))] : [];
|
|
1139
|
-
if (workspaces.length === 0) {
|
|
1140
|
-
saveConfig(currentConfig, env, platform);
|
|
1141
|
-
return { config: currentConfig, refreshed: 0, failed: 0 };
|
|
1142
|
-
}
|
|
1143
|
-
const vendianPath = venvVendian(venvPath, platform);
|
|
1144
|
-
let refreshed = 0;
|
|
1145
|
-
let failed = 0;
|
|
1146
|
-
for (const workspace of workspaces) {
|
|
1147
|
-
if (!fs7.existsSync(workspace)) {
|
|
1269
|
+
}
|
|
1270
|
+
}
|
|
1271
|
+
if (depth >= maxDepth) {
|
|
1148
1272
|
continue;
|
|
1149
1273
|
}
|
|
1150
|
-
|
|
1151
|
-
|
|
1152
|
-
|
|
1153
|
-
|
|
1154
|
-
|
|
1155
|
-
console.error(`[vendian] Could not refresh SDK docs in ${workspace}`);
|
|
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 });
|
|
1156
1279
|
}
|
|
1157
1280
|
}
|
|
1158
|
-
|
|
1159
|
-
...currentConfig,
|
|
1160
|
-
[DOC_WORKSPACES_KEY]: workspaces,
|
|
1161
|
-
lastAgentDocsRefreshAt: now.toISOString()
|
|
1162
|
-
};
|
|
1163
|
-
saveConfig(next, env, platform);
|
|
1164
|
-
return { config: next, refreshed, failed };
|
|
1281
|
+
return manifests;
|
|
1165
1282
|
}
|
|
1166
|
-
|
|
1167
|
-
|
|
1168
|
-
|
|
1169
|
-
|
|
1170
|
-
|
|
1171
|
-
|
|
1172
|
-
nonInteractive = false,
|
|
1173
|
-
backend = void 0,
|
|
1174
|
-
apiUrl = void 0,
|
|
1175
|
-
noBrowser = false,
|
|
1176
|
-
forceAuth = false,
|
|
1177
|
-
env = process.env,
|
|
1178
|
-
platform = process.platform
|
|
1179
|
-
} = {}) {
|
|
1180
|
-
const existing = loadConfig(env, platform);
|
|
1181
|
-
const registry = registryConfig(existing, env, platform);
|
|
1182
|
-
const auth = cloudAuthStatus({ backend, apiUrl, env, platform });
|
|
1183
|
-
const next = {
|
|
1184
|
-
...existing,
|
|
1185
|
-
gitlabHost: registry.gitlabHost || DEFAULT_GITLAB_HOST,
|
|
1186
|
-
gitlabUsername: registry.username || "__token__",
|
|
1187
|
-
sdkPublicProjectId: registry.sdkProjectId || DEFAULT_SDK_PUBLIC_PROJECT_ID,
|
|
1188
|
-
sdkRuntimeProjectId: registry.runtimeProjectId || DEFAULT_SDK_RUNTIME_PROJECT_ID
|
|
1189
|
-
};
|
|
1190
|
-
let cloudAuthApiUrl = auth.authenticated ? auth.apiUrl : void 0;
|
|
1191
|
-
console.log("Vendian login");
|
|
1192
|
-
console.log("This signs in to Vendian and prepares the local runtime.");
|
|
1193
|
-
console.log("Agent requirements.txt files stay reserved for agent-owned dependencies.");
|
|
1194
|
-
console.log("");
|
|
1195
|
-
if ((!auth.authenticated || forceAuth) && !nonInteractive) {
|
|
1196
|
-
console.log(`Opening Vendian sign-in for ${auth.apiUrl}...`);
|
|
1197
|
-
const login = await loginWithVendianOAuth({ backend, apiUrl, noBrowser, env });
|
|
1198
|
-
saveCloudToken(login, { env, platform });
|
|
1199
|
-
cloudAuthApiUrl = login.apiUrl;
|
|
1200
|
-
const packageCredentials = login.packageCredentials;
|
|
1201
|
-
if (!registry.token && !packageCredentials?.token) {
|
|
1202
|
-
throw new Error("Vendian login succeeded, but the backend did not return package credentials. Configure CLI_PACKAGE_REGISTRY_TOKEN on the backend.");
|
|
1203
|
-
}
|
|
1204
|
-
if (packageCredentials) {
|
|
1205
|
-
savePackageCredentials(next, packageCredentials, { platform });
|
|
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;
|
|
1206
1289
|
}
|
|
1207
|
-
|
|
1208
|
-
|
|
1209
|
-
|
|
1210
|
-
const refreshed = await refreshPackageAccessFromCloudAuth({ config: next, auth, env, platform });
|
|
1211
|
-
Object.assign(next, refreshed.config);
|
|
1212
|
-
} else if (registry.token) {
|
|
1213
|
-
if (registry.tokenSource !== "secret-store") {
|
|
1214
|
-
next.gitlabToken = registry.token;
|
|
1290
|
+
const parent = path4.dirname(current);
|
|
1291
|
+
if (parent === current) {
|
|
1292
|
+
break;
|
|
1215
1293
|
}
|
|
1216
|
-
|
|
1217
|
-
}
|
|
1218
|
-
const installRegistry = registryConfig(next, env, platform);
|
|
1219
|
-
if (!installRegistry.token) {
|
|
1220
|
-
throw new Error("Package access is missing. Run interactive `vendian login` to sign in.");
|
|
1221
|
-
}
|
|
1222
|
-
const python = findPython(platform);
|
|
1223
|
-
if (!python) {
|
|
1224
|
-
throw new Error("Python 3.11+ was not found. Install Python 3.11 or newer, then rerun `vendian login`.");
|
|
1225
|
-
}
|
|
1226
|
-
const venvPath = managedVenvPath(env, platform);
|
|
1227
|
-
console.log(`Managed environment: ${venvPath}`);
|
|
1228
|
-
const pythonPath = ensureVenv(venvPath, python, platform);
|
|
1229
|
-
console.log(`Python: ${pythonVersion(pythonPath) || pythonPath}`);
|
|
1230
|
-
saveConfig(next, env, platform);
|
|
1231
|
-
installVendianPackages({ pythonPath, venvPath, config: next, env, platform });
|
|
1232
|
-
const updated = { ...next, lastManagedUpdateAt: (/* @__PURE__ */ new Date()).toISOString() };
|
|
1233
|
-
refreshAgentDocsWorkspaces({ config: updated, venvPath, env, platform });
|
|
1234
|
-
if (!verifyVendianImports(pythonPath)) {
|
|
1235
|
-
throw new Error("Vendian packages installed, but import verification failed.");
|
|
1236
|
-
}
|
|
1237
|
-
const vendianPath = venvVendian(venvPath, platform);
|
|
1238
|
-
if (!fs8.existsSync(vendianPath)) {
|
|
1239
|
-
throw new Error("Vendian executable was not found after install.");
|
|
1240
|
-
}
|
|
1241
|
-
console.log("");
|
|
1242
|
-
for (const line of setupCompletionLines({ cloudAuthApiUrl, backend, apiUrl })) {
|
|
1243
|
-
console.log(line);
|
|
1294
|
+
current = parent;
|
|
1244
1295
|
}
|
|
1296
|
+
return null;
|
|
1245
1297
|
}
|
|
1246
|
-
function
|
|
1247
|
-
|
|
1248
|
-
return false;
|
|
1249
|
-
}
|
|
1250
|
-
config.gitlabHost = packageCredentials.gitlabHost || config.gitlabHost;
|
|
1251
|
-
config.gitlabUsername = packageCredentials.username || config.gitlabUsername;
|
|
1252
|
-
config.sdkPublicProjectId = packageCredentials.sdkProjectId || config.sdkPublicProjectId;
|
|
1253
|
-
config.sdkRuntimeProjectId = packageCredentials.runtimeProjectId || config.sdkRuntimeProjectId;
|
|
1254
|
-
config.vendianAgentsVersion = packageCredentials.vendianAgentsVersion || config.vendianAgentsVersion;
|
|
1255
|
-
config.vendianAgentsRuntimeVersion = packageCredentials.vendianAgentsRuntimeVersion || config.vendianAgentsRuntimeVersion;
|
|
1256
|
-
const secret = savePackageTokenSecret(packageCredentials.token, config, platform);
|
|
1257
|
-
if (secret.ok) {
|
|
1258
|
-
Object.assign(config, secret.config || {});
|
|
1259
|
-
delete config.gitlabToken;
|
|
1260
|
-
console.log("Package access saved.");
|
|
1261
|
-
} else {
|
|
1262
|
-
config.gitlabToken = packageCredentials.token;
|
|
1263
|
-
console.log("Package access saved in the local Vendian CLI config file.");
|
|
1264
|
-
}
|
|
1265
|
-
return true;
|
|
1298
|
+
function hasDirectManifest(root) {
|
|
1299
|
+
return [...MANIFEST_NAMES].some((name) => fs5.existsSync(path4.join(root, name)));
|
|
1266
1300
|
}
|
|
1267
|
-
|
|
1268
|
-
|
|
1269
|
-
|
|
1270
|
-
|
|
1271
|
-
platform = process.platform,
|
|
1272
|
-
save = true
|
|
1273
|
-
} = {}) {
|
|
1274
|
-
const currentConfig = config || loadConfig(env, platform);
|
|
1275
|
-
const registry = registryConfig(currentConfig, env, platform);
|
|
1276
|
-
if (registry.token) {
|
|
1277
|
-
return { config: currentConfig, refreshed: false };
|
|
1278
|
-
}
|
|
1279
|
-
const status = auth || activeCloudAuthStatus({ env, platform });
|
|
1280
|
-
if (!status.authenticated || !status.apiUrl || !status.profile?.access_token) {
|
|
1281
|
-
return { config: currentConfig, refreshed: false };
|
|
1301
|
+
function displayPath(target, cwd) {
|
|
1302
|
+
const relative = path4.relative(cwd, target);
|
|
1303
|
+
if (!relative) {
|
|
1304
|
+
return ".";
|
|
1282
1305
|
}
|
|
1283
|
-
|
|
1284
|
-
|
|
1285
|
-
accessToken: status.profile.access_token
|
|
1286
|
-
});
|
|
1287
|
-
if (!packageCredentials?.token) {
|
|
1288
|
-
return { config: currentConfig, refreshed: false };
|
|
1306
|
+
if (relative.startsWith("..") || path4.isAbsolute(relative)) {
|
|
1307
|
+
return target;
|
|
1289
1308
|
}
|
|
1290
|
-
|
|
1291
|
-
|
|
1292
|
-
|
|
1293
|
-
|
|
1309
|
+
return relative.startsWith(".") ? relative : `.${path4.sep}${relative}`;
|
|
1310
|
+
}
|
|
1311
|
+
function commonSearchRoots(home) {
|
|
1312
|
+
if (!home) {
|
|
1313
|
+
return [];
|
|
1294
1314
|
}
|
|
1295
|
-
return
|
|
1315
|
+
return [
|
|
1316
|
+
"agents",
|
|
1317
|
+
"Projects",
|
|
1318
|
+
"Code",
|
|
1319
|
+
"Developer",
|
|
1320
|
+
"Documents",
|
|
1321
|
+
"Desktop"
|
|
1322
|
+
].map((name) => path4.join(home, name));
|
|
1296
1323
|
}
|
|
1297
|
-
function
|
|
1298
|
-
const
|
|
1299
|
-
|
|
1300
|
-
|
|
1301
|
-
|
|
1302
|
-
|
|
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);
|
|
1303
1341
|
}
|
|
1304
|
-
|
|
1305
|
-
lines.push("Then: vendian cloud local serve --agents-dir ./agents");
|
|
1306
|
-
return lines;
|
|
1342
|
+
return result;
|
|
1307
1343
|
}
|
|
1308
|
-
function
|
|
1309
|
-
|
|
1310
|
-
|
|
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;
|
|
1311
1354
|
}
|
|
1312
|
-
|
|
1313
|
-
|
|
1355
|
+
return parents;
|
|
1356
|
+
}
|
|
1357
|
+
function safeResolve(candidate) {
|
|
1358
|
+
try {
|
|
1359
|
+
return path4.resolve(candidate);
|
|
1360
|
+
} catch {
|
|
1361
|
+
return null;
|
|
1314
1362
|
}
|
|
1315
|
-
return "vendian cloud auth login";
|
|
1316
1363
|
}
|
|
1317
1364
|
|
|
1365
|
+
// src/dev-server.js
|
|
1366
|
+
init_auth();
|
|
1367
|
+
init_constants();
|
|
1368
|
+
init_config();
|
|
1369
|
+
|
|
1318
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";
|
|
1319
1378
|
var AUTO_UPDATE_INTERVAL_MS = 24 * 60 * 60 * 1e3;
|
|
1320
1379
|
async function forwardToPythonVendian(args, { env = process.env, platform = process.platform } = {}) {
|
|
1321
1380
|
const invocation = await preparePythonVendianInvocation(args, { env, platform });
|
|
@@ -1446,7 +1505,12 @@ function reportProgress(onProgress, message) {
|
|
|
1446
1505
|
}
|
|
1447
1506
|
}
|
|
1448
1507
|
|
|
1508
|
+
// src/dev-server.js
|
|
1509
|
+
init_process();
|
|
1510
|
+
init_paths();
|
|
1511
|
+
|
|
1449
1512
|
// src/workspaces.js
|
|
1513
|
+
init_process();
|
|
1450
1514
|
async function listCloudWorkspaces({
|
|
1451
1515
|
env = process.env,
|
|
1452
1516
|
platform = process.platform,
|
|
@@ -2296,6 +2360,7 @@ function formatSeconds(value) {
|
|
|
2296
2360
|
}
|
|
2297
2361
|
|
|
2298
2362
|
// src/serve-log-store.js
|
|
2363
|
+
init_paths();
|
|
2299
2364
|
import crypto2 from "node:crypto";
|
|
2300
2365
|
import fs10 from "node:fs";
|
|
2301
2366
|
import path7 from "node:path";
|
|
@@ -2435,7 +2500,7 @@ function buildLocalServeEventStreamArgs({ agentsDir: agentsDir2 = "./agents", co
|
|
|
2435
2500
|
}
|
|
2436
2501
|
|
|
2437
2502
|
// src/version.js
|
|
2438
|
-
var CLI_VERSION = true ? "0.0.
|
|
2503
|
+
var CLI_VERSION = true ? "0.0.43" : process.env.npm_package_version || "0.0.0-dev";
|
|
2439
2504
|
|
|
2440
2505
|
// src/dev-server.js
|
|
2441
2506
|
var __dirname = path8.dirname(fileURLToPath(import.meta.url));
|
|
@@ -2448,6 +2513,7 @@ var logStore = null;
|
|
|
2448
2513
|
var agentsDir = "";
|
|
2449
2514
|
var activeServeAgentsDir = "";
|
|
2450
2515
|
var collectionId = "";
|
|
2516
|
+
var activeAuthLogin = null;
|
|
2451
2517
|
async function startDevServer({
|
|
2452
2518
|
port = 3859,
|
|
2453
2519
|
agents = "",
|
|
@@ -2572,6 +2638,8 @@ async function handleApi(req, res, pathname, parsed) {
|
|
|
2572
2638
|
return apiServeStop(req, res);
|
|
2573
2639
|
case "/api/create":
|
|
2574
2640
|
return await apiCreate(req, res, body);
|
|
2641
|
+
case "/api/auth/login":
|
|
2642
|
+
return await apiAuthLogin(req, res, body);
|
|
2575
2643
|
case "/api/auth/switch":
|
|
2576
2644
|
return await apiAuthSwitch(req, res, body);
|
|
2577
2645
|
default:
|
|
@@ -2738,6 +2806,55 @@ async function apiAuthSwitch(req, res, body) {
|
|
|
2738
2806
|
jsonResponse(res, { ok: false, error: err.message });
|
|
2739
2807
|
}
|
|
2740
2808
|
}
|
|
2809
|
+
async function loginOrActivateBackend({ backend, env, setupFn, statusFn, activateFn } = {}) {
|
|
2810
|
+
if (!backend || !BACKEND_TARGETS[backend]) {
|
|
2811
|
+
return { ok: false, error: `Unknown backend: ${backend}` };
|
|
2812
|
+
}
|
|
2813
|
+
const authEnv = { ...env || process.env, VENDIAN_API_URL: "" };
|
|
2814
|
+
const readStatus = statusFn || cloudAuthStatus;
|
|
2815
|
+
const activate = activateFn || activateCloudProfile;
|
|
2816
|
+
const status = readStatus({ backend, env: authEnv });
|
|
2817
|
+
if (status.authenticated) {
|
|
2818
|
+
const result = activate({ backend, env: authEnv });
|
|
2819
|
+
if (result?.activated) {
|
|
2820
|
+
return { ok: true, switched: true, backend };
|
|
2821
|
+
}
|
|
2822
|
+
}
|
|
2823
|
+
const runSetup = setupFn || (async (options) => {
|
|
2824
|
+
const { setup: setup2 } = await Promise.resolve().then(() => (init_setup(), setup_exports));
|
|
2825
|
+
return setup2(options);
|
|
2826
|
+
});
|
|
2827
|
+
await runSetup({ backend, forceAuth: true });
|
|
2828
|
+
return { ok: true, loggedIn: true, backend };
|
|
2829
|
+
}
|
|
2830
|
+
async function apiAuthLogin(req, res, body) {
|
|
2831
|
+
try {
|
|
2832
|
+
const result = await runSingleAuthLogin({ backend: body.backend });
|
|
2833
|
+
const status = result.ok ? 200 : 400;
|
|
2834
|
+
jsonResponse(res, result, status);
|
|
2835
|
+
} catch (err) {
|
|
2836
|
+
jsonResponse(res, { ok: false, error: err.message || "Login failed" }, 500);
|
|
2837
|
+
}
|
|
2838
|
+
}
|
|
2839
|
+
async function runSingleAuthLogin({ backend, loginFn } = {}) {
|
|
2840
|
+
if (activeAuthLogin?.promise) {
|
|
2841
|
+
if (activeAuthLogin.backend !== backend) {
|
|
2842
|
+
return {
|
|
2843
|
+
ok: false,
|
|
2844
|
+
error: `A Vendian sign-in is already in progress for ${activeAuthLogin.backend}. Finish that browser flow or wait for it to close, then try ${backend} again.`
|
|
2845
|
+
};
|
|
2846
|
+
}
|
|
2847
|
+
return activeAuthLogin.promise;
|
|
2848
|
+
}
|
|
2849
|
+
const executeLogin = loginFn || loginOrActivateBackend;
|
|
2850
|
+
const promise = Promise.resolve().then(() => executeLogin({ backend })).finally(() => {
|
|
2851
|
+
if (activeAuthLogin?.promise === promise) {
|
|
2852
|
+
activeAuthLogin = null;
|
|
2853
|
+
}
|
|
2854
|
+
});
|
|
2855
|
+
activeAuthLogin = { backend, promise };
|
|
2856
|
+
return promise;
|
|
2857
|
+
}
|
|
2741
2858
|
async function apiValidate(req, res, body) {
|
|
2742
2859
|
const targetPath = body.path || agentsDir;
|
|
2743
2860
|
const invocation = await preparePythonVendianInvocation(
|
|
@@ -3026,16 +3143,27 @@ function openUrl(url) {
|
|
|
3026
3143
|
spawn3(shell || cmd, shell ? args : [url], { detached: true, stdio: "ignore" }).unref();
|
|
3027
3144
|
}
|
|
3028
3145
|
|
|
3146
|
+
// src/main.js
|
|
3147
|
+
init_setup();
|
|
3148
|
+
|
|
3029
3149
|
// src/tui.js
|
|
3030
3150
|
init_constants();
|
|
3031
3151
|
init_auth();
|
|
3152
|
+
init_config();
|
|
3032
3153
|
import fs12 from "node:fs";
|
|
3033
3154
|
import path9 from "node:path";
|
|
3034
3155
|
import readline from "node:readline";
|
|
3156
|
+
init_install();
|
|
3035
3157
|
|
|
3036
3158
|
// src/npm-update.js
|
|
3159
|
+
init_config();
|
|
3037
3160
|
var NPM_CHECK_INTERVAL_MS = 30 * 60 * 1e3;
|
|
3038
3161
|
|
|
3162
|
+
// src/tui.js
|
|
3163
|
+
init_paths();
|
|
3164
|
+
init_setup();
|
|
3165
|
+
init_process();
|
|
3166
|
+
|
|
3039
3167
|
// src/ui/figures.js
|
|
3040
3168
|
var supportsUnicode = process.platform !== "win32" || Boolean(
|
|
3041
3169
|
process.env.WT_SESSION || // Windows Terminal
|