@vendian/cli 0.0.34 → 0.0.36
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 +1722 -1026
- package/package.json +1 -1
package/cli-wrapper.mjs
CHANGED
|
@@ -2,152 +2,498 @@
|
|
|
2
2
|
import { createRequire as __vendianCreateRequire } from 'node:module';
|
|
3
3
|
const require = __vendianCreateRequire(import.meta.url);
|
|
4
4
|
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
5
|
+
var __defProp = Object.defineProperty;
|
|
6
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
7
|
+
var __esm = (fn, res) => function __init() {
|
|
8
|
+
return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res;
|
|
9
|
+
};
|
|
10
|
+
var __export = (target, all) => {
|
|
11
|
+
for (var name in all)
|
|
12
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
13
|
+
};
|
|
12
14
|
|
|
13
15
|
// src/constants.js
|
|
14
|
-
var DEFAULT_GITLAB_HOST
|
|
15
|
-
var
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
16
|
+
var DEFAULT_GITLAB_HOST, DEFAULT_SDK_PUBLIC_PROJECT_ID, DEFAULT_SDK_RUNTIME_PROJECT_ID, PUBLIC_PACKAGE, RUNTIME_PACKAGE, CONFIG_VERSION, BACKEND_TARGETS;
|
|
17
|
+
var init_constants = __esm({
|
|
18
|
+
"src/constants.js"() {
|
|
19
|
+
DEFAULT_GITLAB_HOST = "gitlab.com";
|
|
20
|
+
DEFAULT_SDK_PUBLIC_PROJECT_ID = "77150592";
|
|
21
|
+
DEFAULT_SDK_RUNTIME_PROJECT_ID = "79410876";
|
|
22
|
+
PUBLIC_PACKAGE = "vendian-agents[full]";
|
|
23
|
+
RUNTIME_PACKAGE = "vendian-agents-runtime";
|
|
24
|
+
CONFIG_VERSION = 1;
|
|
25
|
+
BACKEND_TARGETS = {
|
|
26
|
+
local: "http://localhost:3000",
|
|
27
|
+
localhost: "http://localhost:3000",
|
|
28
|
+
dev: "https://api.dev.vendian.ai",
|
|
29
|
+
staging: "https://api.staging.vendian.ai",
|
|
30
|
+
prod: "https://api.vendian.ai",
|
|
31
|
+
production: "https://api.vendian.ai"
|
|
32
|
+
};
|
|
33
|
+
}
|
|
34
|
+
});
|
|
28
35
|
|
|
29
|
-
// src/
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
36
|
+
// src/auth.js
|
|
37
|
+
var auth_exports = {};
|
|
38
|
+
__export(auth_exports, {
|
|
39
|
+
activateCloudProfile: () => activateCloudProfile,
|
|
40
|
+
activeCloudAuthStatus: () => activeCloudAuthStatus,
|
|
41
|
+
buildLinuxOpenCommand: () => buildLinuxOpenCommand,
|
|
42
|
+
buildMacOpenCommand: () => buildMacOpenCommand,
|
|
43
|
+
buildWindowsOpenCommand: () => buildWindowsOpenCommand,
|
|
44
|
+
cloudAuthStatus: () => cloudAuthStatus,
|
|
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(/\/$/, "");
|
|
38
60
|
}
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
function vendianHome(env = process.env, platform = process.platform) {
|
|
42
|
-
if (env.VENDIAN_CLI_HOME) {
|
|
43
|
-
return pathApi(platform).resolve(env.VENDIAN_CLI_HOME);
|
|
61
|
+
if (env.VENDIAN_API_URL) {
|
|
62
|
+
return env.VENDIAN_API_URL.replace(/\/$/, "");
|
|
44
63
|
}
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
64
|
+
const target = backend || "prod";
|
|
65
|
+
const resolved = BACKEND_TARGETS[target];
|
|
66
|
+
if (!resolved) {
|
|
67
|
+
throw new Error(`Unknown Vendian backend '${target}'. Use local, dev, staging, prod, or --api-url.`);
|
|
48
68
|
}
|
|
49
|
-
return
|
|
69
|
+
return resolved;
|
|
50
70
|
}
|
|
51
|
-
function
|
|
52
|
-
|
|
53
|
-
|
|
71
|
+
async function loginWithVendianOAuth({ backend, apiUrl, noBrowser = false, env = process.env } = {}) {
|
|
72
|
+
const resolvedApiUrl = resolveApiUrl({ backend, apiUrl, env });
|
|
73
|
+
const callback = await createCallbackServer(env);
|
|
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();
|
|
54
96
|
}
|
|
55
|
-
return pathApi(platform).join(vendianHome(env, platform), "config.json");
|
|
56
97
|
}
|
|
57
|
-
function
|
|
58
|
-
if (
|
|
59
|
-
return
|
|
98
|
+
async function fetchPackageCredentials({ apiUrl, accessToken }) {
|
|
99
|
+
if (!apiUrl || !accessToken) {
|
|
100
|
+
return void 0;
|
|
60
101
|
}
|
|
61
|
-
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
return
|
|
65
|
-
}
|
|
66
|
-
function venvVendian(venvPath, platform = process.platform) {
|
|
67
|
-
return platform === "win32" ? path.win32.join(venvPath, "Scripts", "vendian.exe") : path.posix.join(venvPath, "bin", "vendian");
|
|
102
|
+
const payload = await getJson(`${String(apiUrl).replace(/\/$/, "")}/api/v1/cli/package-credentials`, {
|
|
103
|
+
Authorization: `Bearer ${accessToken}`
|
|
104
|
+
});
|
|
105
|
+
return payload.packageCredentials;
|
|
68
106
|
}
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
function loadConfig(env = process.env, platform = process.platform) {
|
|
72
|
-
const file = configPath(env, platform);
|
|
107
|
+
function loadCloudConfig(env = process.env, platform = process.platform) {
|
|
108
|
+
const file = cloudConfigPath(env, platform);
|
|
73
109
|
try {
|
|
74
|
-
const parsed = JSON.parse(
|
|
75
|
-
return typeof parsed === "object"
|
|
76
|
-
} catch
|
|
77
|
-
if (error && error.code === "ENOENT") {
|
|
78
|
-
return {};
|
|
79
|
-
}
|
|
110
|
+
const parsed = JSON.parse(fs6.readFileSync(file, "utf8"));
|
|
111
|
+
return parsed && typeof parsed === "object" ? parsed : {};
|
|
112
|
+
} catch {
|
|
80
113
|
return {};
|
|
81
114
|
}
|
|
82
115
|
}
|
|
83
|
-
function
|
|
84
|
-
const
|
|
85
|
-
|
|
86
|
-
const
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
116
|
+
function cloudAuthStatus({ backend, apiUrl, env = process.env, platform = process.platform } = {}) {
|
|
117
|
+
const targetApiUrl = resolveApiUrl({ backend, apiUrl, env });
|
|
118
|
+
const config = loadCloudConfig(env, platform);
|
|
119
|
+
const profiles = config.profiles && typeof config.profiles === "object" ? config.profiles : {};
|
|
120
|
+
const profile = profiles[targetApiUrl];
|
|
121
|
+
return {
|
|
122
|
+
apiUrl: targetApiUrl,
|
|
123
|
+
activeApiUrl: typeof config.active_api_url === "string" ? config.active_api_url : void 0,
|
|
124
|
+
profile: profile && typeof profile === "object" ? profile : void 0,
|
|
125
|
+
authenticated: Boolean(profile?.access_token),
|
|
126
|
+
email: typeof profile?.email === "string" ? profile.email : void 0,
|
|
127
|
+
expiresAt: typeof profile?.expires_at === "string" ? profile.expires_at : void 0,
|
|
128
|
+
profiles
|
|
129
|
+
};
|
|
94
130
|
}
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
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
|
+
};
|
|
105
145
|
}
|
|
106
|
-
function
|
|
107
|
-
const
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
146
|
+
function activateCloudProfile({ backend, apiUrl, env = process.env, platform = process.platform } = {}) {
|
|
147
|
+
const targetApiUrl = resolveApiUrl({ backend, apiUrl, env });
|
|
148
|
+
const config = loadCloudConfig(env, platform);
|
|
149
|
+
const profiles = config.profiles && typeof config.profiles === "object" ? config.profiles : {};
|
|
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
|
+
};
|
|
159
|
+
}
|
|
160
|
+
const next = {
|
|
161
|
+
...config,
|
|
162
|
+
version: config.version || 2,
|
|
163
|
+
profiles,
|
|
164
|
+
active_api_url: targetApiUrl
|
|
165
|
+
};
|
|
166
|
+
writeCloudConfig(next, { env, platform });
|
|
113
167
|
return {
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
168
|
+
apiUrl: targetApiUrl,
|
|
169
|
+
activeApiUrl: targetApiUrl,
|
|
170
|
+
profile,
|
|
171
|
+
authenticated: true,
|
|
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
|
|
118
176
|
};
|
|
119
177
|
}
|
|
120
|
-
function
|
|
121
|
-
const
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
178
|
+
async function getOAuthConfig(apiUrl, redirectUri) {
|
|
179
|
+
const url = new URL(`${apiUrl}/api/v1/cli/auth/oauth/config`);
|
|
180
|
+
url.searchParams.set("redirectUri", redirectUri);
|
|
181
|
+
return getJson(url);
|
|
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
|
|
125
201
|
});
|
|
126
|
-
|
|
127
|
-
|
|
202
|
+
const payload = await postForm(String(config.tokenUrl), body);
|
|
203
|
+
const token = payload.access_token || payload.id_token;
|
|
204
|
+
if (!token) {
|
|
205
|
+
throw new Error("OAuth token exchange did not return a Clerk token");
|
|
128
206
|
}
|
|
129
|
-
return
|
|
207
|
+
return String(token);
|
|
130
208
|
}
|
|
131
|
-
function
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
stdio: "inherit",
|
|
135
|
-
shell: false,
|
|
136
|
-
...options
|
|
137
|
-
});
|
|
138
|
-
child.on("error", reject);
|
|
139
|
-
child.on("exit", (code, signal) => {
|
|
140
|
-
if (signal) {
|
|
141
|
-
reject(new Error(`command terminated by ${signal}`));
|
|
142
|
-
return;
|
|
143
|
-
}
|
|
144
|
-
resolve(code ?? 1);
|
|
145
|
-
});
|
|
146
|
-
});
|
|
209
|
+
async function getJson(url, headers = {}) {
|
|
210
|
+
const response = await fetch(url, { headers: { Accept: "application/json", ...headers } });
|
|
211
|
+
return decodeResponse(response);
|
|
147
212
|
}
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
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;
|
|
476
|
+
}
|
|
477
|
+
function spawnForward(command, args, options = {}) {
|
|
478
|
+
return new Promise((resolve, reject) => {
|
|
479
|
+
const child = spawn(command, args, {
|
|
480
|
+
stdio: "inherit",
|
|
481
|
+
shell: false,
|
|
482
|
+
...options
|
|
483
|
+
});
|
|
484
|
+
child.on("error", reject);
|
|
485
|
+
child.on("exit", (code, signal) => {
|
|
486
|
+
if (signal) {
|
|
487
|
+
reject(new Error(`command terminated by ${signal}`));
|
|
488
|
+
return;
|
|
489
|
+
}
|
|
490
|
+
resolve(code ?? 1);
|
|
491
|
+
});
|
|
492
|
+
});
|
|
493
|
+
}
|
|
494
|
+
|
|
495
|
+
// src/python.js
|
|
496
|
+
import fs2 from "node:fs";
|
|
151
497
|
function findPython(platform = process.platform) {
|
|
152
498
|
const candidates = platform === "win32" ? [
|
|
153
499
|
{ command: "py", args: ["-3.11"] },
|
|
@@ -413,416 +759,343 @@ function doctor({ env = process.env, platform = process.platform } = {}) {
|
|
|
413
759
|
console.log(`uv: ${hasUv() ? "yes" : "no, will use pip fallback"}`);
|
|
414
760
|
console.log(`Managed Python: ${fs4.existsSync(pythonPath) ? pythonVersion(pythonPath) || "present" : "missing"}`);
|
|
415
761
|
console.log(`Vendian executable: ${fs4.existsSync(vendianPath) ? vendianPath : "missing"}`);
|
|
416
|
-
console.log(`Vendian imports: ${fs4.existsSync(pythonPath) && verifyVendianImports(pythonPath) ? "ok" : "missing"}`);
|
|
417
|
-
console.log(`Package access: ${registry.token ? `configured (${registry.tokenSource})` : "missing"}`);
|
|
418
|
-
}
|
|
419
|
-
|
|
420
|
-
// src/forward.js
|
|
421
|
-
import fs8 from "node:fs";
|
|
422
|
-
|
|
423
|
-
// src/docs.js
|
|
424
|
-
import fs5 from "node:fs";
|
|
425
|
-
import path4 from "node:path";
|
|
426
|
-
var DOC_WORKSPACES_KEY = "agentDocWorkspaces";
|
|
427
|
-
function pathApi2(platform) {
|
|
428
|
-
return platform === "win32" ? path4.win32 : path4.posix;
|
|
429
|
-
}
|
|
430
|
-
function commandWritesAgentDocs(args = []) {
|
|
431
|
-
return args[0] === "init";
|
|
432
|
-
}
|
|
433
|
-
function initOutputDir(args = []) {
|
|
434
|
-
for (let index = 1; index < args.length; index += 1) {
|
|
435
|
-
const arg = args[index];
|
|
436
|
-
if (arg === "--output-dir" || arg === "-o") {
|
|
437
|
-
return args[index + 1] || ".";
|
|
438
|
-
}
|
|
439
|
-
if (arg.startsWith("--output-dir=")) {
|
|
440
|
-
return arg.slice("--output-dir=".length) || ".";
|
|
441
|
-
}
|
|
442
|
-
}
|
|
443
|
-
return ".";
|
|
444
|
-
}
|
|
445
|
-
function resolveWorkspacePath(outputDir, { cwd = process.cwd(), platform = process.platform } = {}) {
|
|
446
|
-
const api = pathApi2(platform);
|
|
447
|
-
const raw = outputDir || ".";
|
|
448
|
-
return api.isAbsolute(raw) ? api.normalize(raw) : api.resolve(cwd, raw);
|
|
449
|
-
}
|
|
450
|
-
function recordAgentDocsWorkspace(outputDir, {
|
|
451
|
-
env = process.env,
|
|
452
|
-
platform = process.platform,
|
|
453
|
-
cwd = process.cwd(),
|
|
454
|
-
now = /* @__PURE__ */ new Date()
|
|
455
|
-
} = {}) {
|
|
456
|
-
const workspacePath = resolveWorkspacePath(outputDir, { cwd, platform });
|
|
457
|
-
const config = loadConfig(env, platform);
|
|
458
|
-
const existing = Array.isArray(config[DOC_WORKSPACES_KEY]) ? config[DOC_WORKSPACES_KEY] : [];
|
|
459
|
-
const workspaces = [workspacePath, ...existing.filter((entry) => entry !== workspacePath)];
|
|
460
|
-
const next = {
|
|
461
|
-
...config,
|
|
462
|
-
[DOC_WORKSPACES_KEY]: workspaces,
|
|
463
|
-
lastAgentDocsInitAt: now.toISOString()
|
|
464
|
-
};
|
|
465
|
-
saveConfig(next, env, platform);
|
|
466
|
-
return next;
|
|
467
|
-
}
|
|
468
|
-
function recordForwardedDocsCommand(args, code, options = {}) {
|
|
469
|
-
if (code !== 0 || !commandWritesAgentDocs(args)) {
|
|
470
|
-
return false;
|
|
471
|
-
}
|
|
472
|
-
recordAgentDocsWorkspace(initOutputDir(args), options);
|
|
473
|
-
return true;
|
|
762
|
+
console.log(`Vendian imports: ${fs4.existsSync(pythonPath) && verifyVendianImports(pythonPath) ? "ok" : "missing"}`);
|
|
763
|
+
console.log(`Package access: ${registry.token ? `configured (${registry.tokenSource})` : "missing"}`);
|
|
474
764
|
}
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
765
|
+
|
|
766
|
+
// src/dev-server.js
|
|
767
|
+
import http2 from "node:http";
|
|
768
|
+
import fs11 from "node:fs";
|
|
769
|
+
import path8 from "node:path";
|
|
770
|
+
import { fileURLToPath } from "node:url";
|
|
771
|
+
import { spawn as spawn3 } from "node:child_process";
|
|
772
|
+
|
|
773
|
+
// src/agent-discovery.js
|
|
774
|
+
import fs5 from "node:fs";
|
|
775
|
+
import os2 from "node:os";
|
|
776
|
+
import path4 from "node:path";
|
|
777
|
+
var SKIP_DIRS = /* @__PURE__ */ new Set([
|
|
778
|
+
".agents",
|
|
779
|
+
".git",
|
|
780
|
+
".hg",
|
|
781
|
+
".mypy_cache",
|
|
782
|
+
".pytest_cache",
|
|
783
|
+
".ruff_cache",
|
|
784
|
+
".svn",
|
|
785
|
+
".venv",
|
|
786
|
+
"__pycache__",
|
|
787
|
+
"build",
|
|
788
|
+
"dist",
|
|
789
|
+
"node_modules",
|
|
790
|
+
"venv"
|
|
791
|
+
]);
|
|
792
|
+
var MANIFEST_NAMES = /* @__PURE__ */ new Set(["manifest.yaml", "manifest.yml"]);
|
|
793
|
+
function findAgentDirectoryCandidates({
|
|
794
|
+
cwd = process.cwd(),
|
|
795
|
+
home = os2.homedir(),
|
|
796
|
+
maxDepth = 6,
|
|
797
|
+
maxDirs = 1e3,
|
|
798
|
+
limit = 12
|
|
482
799
|
} = {}) {
|
|
483
|
-
const
|
|
484
|
-
const
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
let refreshed = 0;
|
|
491
|
-
let failed = 0;
|
|
492
|
-
for (const workspace of workspaces) {
|
|
493
|
-
if (!fs5.existsSync(workspace)) {
|
|
494
|
-
continue;
|
|
800
|
+
const start = path4.resolve(cwd);
|
|
801
|
+
const candidates = [];
|
|
802
|
+
const seen = /* @__PURE__ */ new Set();
|
|
803
|
+
function addCandidate(candidatePath) {
|
|
804
|
+
const resolved = safeResolve(candidatePath);
|
|
805
|
+
if (!resolved || seen.has(resolved)) {
|
|
806
|
+
return;
|
|
495
807
|
}
|
|
496
|
-
const
|
|
497
|
-
if (
|
|
498
|
-
|
|
499
|
-
} else {
|
|
500
|
-
failed += 1;
|
|
501
|
-
console.error(`[vendian] Could not refresh SDK docs in ${workspace}`);
|
|
808
|
+
const count = countAgentManifests(resolved, { maxDepth, maxDirs, limit: 100 });
|
|
809
|
+
if (count < 1) {
|
|
810
|
+
return;
|
|
502
811
|
}
|
|
812
|
+
seen.add(resolved);
|
|
813
|
+
candidates.push({
|
|
814
|
+
path: displayPath(resolved, start),
|
|
815
|
+
absolutePath: resolved,
|
|
816
|
+
agentCount: count
|
|
817
|
+
});
|
|
503
818
|
}
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
lastAgentDocsRefreshAt: now.toISOString()
|
|
508
|
-
};
|
|
509
|
-
saveConfig(next, env, platform);
|
|
510
|
-
return { config: next, refreshed, failed };
|
|
511
|
-
}
|
|
512
|
-
|
|
513
|
-
// src/setup.js
|
|
514
|
-
import fs7 from "node:fs";
|
|
515
|
-
|
|
516
|
-
// src/auth.js
|
|
517
|
-
import crypto from "node:crypto";
|
|
518
|
-
import http from "node:http";
|
|
519
|
-
import fs6 from "node:fs";
|
|
520
|
-
import path5 from "node:path";
|
|
521
|
-
import { spawnSync as spawnSync3 } from "node:child_process";
|
|
522
|
-
var DEFAULT_OAUTH_REDIRECT_PORT = 8765;
|
|
523
|
-
function resolveApiUrl({ backend, apiUrl, env = process.env } = {}) {
|
|
524
|
-
if (apiUrl) {
|
|
525
|
-
return apiUrl.replace(/\/$/, "");
|
|
819
|
+
addCandidate(path4.join(start, "agents"));
|
|
820
|
+
if (hasDirectManifest(start)) {
|
|
821
|
+
addCandidate(start);
|
|
526
822
|
}
|
|
527
|
-
|
|
528
|
-
|
|
823
|
+
for (const parent of parentDirs(start, 3)) {
|
|
824
|
+
addCandidate(path4.join(parent, "agents"));
|
|
529
825
|
}
|
|
530
|
-
const
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
826
|
+
const roots = uniqueExistingDirs([
|
|
827
|
+
start,
|
|
828
|
+
...commonSearchRoots(home)
|
|
829
|
+
]);
|
|
830
|
+
for (const root of roots) {
|
|
831
|
+
for (const manifest of findManifestFiles(root, { maxDepth, maxDirs, limit: 250 })) {
|
|
832
|
+
const workspace = nearestAgentsAncestor(manifest, root);
|
|
833
|
+
addCandidate(workspace || path4.dirname(manifest));
|
|
834
|
+
if (candidates.length >= limit) {
|
|
835
|
+
return candidates.slice(0, limit);
|
|
836
|
+
}
|
|
837
|
+
}
|
|
534
838
|
}
|
|
535
|
-
return
|
|
839
|
+
return candidates.slice(0, limit);
|
|
536
840
|
}
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
const
|
|
552
|
-
|
|
553
|
-
|
|
841
|
+
function findAgentFolders(root, {
|
|
842
|
+
cwd = process.cwd(),
|
|
843
|
+
maxDepth = 6,
|
|
844
|
+
maxDirs = 1e3,
|
|
845
|
+
limit = 100
|
|
846
|
+
} = {}) {
|
|
847
|
+
const start = path4.resolve(cwd);
|
|
848
|
+
const resolvedRoot = safeResolve(root || ".");
|
|
849
|
+
if (!resolvedRoot) {
|
|
850
|
+
return [];
|
|
851
|
+
}
|
|
852
|
+
const seen = /* @__PURE__ */ new Set();
|
|
853
|
+
const folders = [];
|
|
854
|
+
for (const manifest of findManifestFiles(resolvedRoot, { maxDepth, maxDirs, limit })) {
|
|
855
|
+
const folder = path4.dirname(manifest);
|
|
856
|
+
const resolved = safeResolve(folder);
|
|
857
|
+
if (!resolved || seen.has(resolved)) {
|
|
858
|
+
continue;
|
|
554
859
|
}
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
860
|
+
seen.add(resolved);
|
|
861
|
+
folders.push({
|
|
862
|
+
path: displayPath(resolved, start),
|
|
863
|
+
absolutePath: resolved,
|
|
864
|
+
name: path4.basename(resolved) || displayPath(resolved, start)
|
|
558
865
|
});
|
|
559
|
-
return { apiUrl: resolvedApiUrl, ...exchange };
|
|
560
|
-
} finally {
|
|
561
|
-
await callback.close();
|
|
562
|
-
}
|
|
563
|
-
}
|
|
564
|
-
async function fetchPackageCredentials({ apiUrl, accessToken }) {
|
|
565
|
-
if (!apiUrl || !accessToken) {
|
|
566
|
-
return void 0;
|
|
567
866
|
}
|
|
568
|
-
|
|
569
|
-
Authorization: `Bearer ${accessToken}`
|
|
570
|
-
});
|
|
571
|
-
return payload.packageCredentials;
|
|
867
|
+
return folders.sort((a, b) => a.path.localeCompare(b.path)).slice(0, limit);
|
|
572
868
|
}
|
|
573
|
-
function
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
869
|
+
function countAgentManifests(root, { maxDepth = 6, maxDirs = 1e3, limit = 100 } = {}) {
|
|
870
|
+
let count = 0;
|
|
871
|
+
for (const _manifest of findManifestFiles(root, { maxDepth, maxDirs, limit })) {
|
|
872
|
+
count += 1;
|
|
873
|
+
if (count >= limit) {
|
|
874
|
+
return count;
|
|
875
|
+
}
|
|
580
876
|
}
|
|
877
|
+
return count;
|
|
581
878
|
}
|
|
582
|
-
function
|
|
583
|
-
const
|
|
584
|
-
const
|
|
585
|
-
const
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
}
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
const profile = profiles[targetApiUrl];
|
|
617
|
-
if (!profile || typeof profile !== "object" || !profile.access_token) {
|
|
618
|
-
return {
|
|
619
|
-
apiUrl: targetApiUrl,
|
|
620
|
-
activeApiUrl: typeof config.active_api_url === "string" ? config.active_api_url : void 0,
|
|
621
|
-
authenticated: false,
|
|
622
|
-
activated: false,
|
|
623
|
-
profiles
|
|
624
|
-
};
|
|
879
|
+
function findManifestFiles(root, { maxDepth, maxDirs, limit }) {
|
|
880
|
+
const manifests = [];
|
|
881
|
+
const stack = [{ dir: root, depth: 0 }];
|
|
882
|
+
const seen = /* @__PURE__ */ new Set();
|
|
883
|
+
while (stack.length && manifests.length < limit && seen.size < maxDirs) {
|
|
884
|
+
const { dir, depth } = stack.pop();
|
|
885
|
+
const resolved = safeResolve(dir);
|
|
886
|
+
if (!resolved || seen.has(resolved)) {
|
|
887
|
+
continue;
|
|
888
|
+
}
|
|
889
|
+
seen.add(resolved);
|
|
890
|
+
let entries;
|
|
891
|
+
try {
|
|
892
|
+
entries = fs5.readdirSync(resolved, { withFileTypes: true });
|
|
893
|
+
} catch {
|
|
894
|
+
continue;
|
|
895
|
+
}
|
|
896
|
+
for (const entry of entries) {
|
|
897
|
+
if (entry.isFile() && MANIFEST_NAMES.has(entry.name)) {
|
|
898
|
+
manifests.push(path4.join(resolved, entry.name));
|
|
899
|
+
if (manifests.length >= limit) {
|
|
900
|
+
break;
|
|
901
|
+
}
|
|
902
|
+
}
|
|
903
|
+
}
|
|
904
|
+
if (depth >= maxDepth) {
|
|
905
|
+
continue;
|
|
906
|
+
}
|
|
907
|
+
for (const entry of entries) {
|
|
908
|
+
if (!entry.isDirectory() || SKIP_DIRS.has(entry.name)) {
|
|
909
|
+
continue;
|
|
910
|
+
}
|
|
911
|
+
stack.push({ dir: path4.join(resolved, entry.name), depth: depth + 1 });
|
|
912
|
+
}
|
|
625
913
|
}
|
|
626
|
-
|
|
627
|
-
...config,
|
|
628
|
-
version: config.version || 2,
|
|
629
|
-
profiles,
|
|
630
|
-
active_api_url: targetApiUrl
|
|
631
|
-
};
|
|
632
|
-
writeCloudConfig(next, { env, platform });
|
|
633
|
-
return {
|
|
634
|
-
apiUrl: targetApiUrl,
|
|
635
|
-
activeApiUrl: targetApiUrl,
|
|
636
|
-
profile,
|
|
637
|
-
authenticated: true,
|
|
638
|
-
activated: true,
|
|
639
|
-
email: typeof profile.email === "string" ? profile.email : void 0,
|
|
640
|
-
expiresAt: typeof profile.expires_at === "string" ? profile.expires_at : void 0,
|
|
641
|
-
profiles
|
|
642
|
-
};
|
|
643
|
-
}
|
|
644
|
-
async function getOAuthConfig(apiUrl, redirectUri) {
|
|
645
|
-
const url = new URL(`${apiUrl}/api/v1/cli/auth/oauth/config`);
|
|
646
|
-
url.searchParams.set("redirectUri", redirectUri);
|
|
647
|
-
return getJson(url);
|
|
648
|
-
}
|
|
649
|
-
function authorizationUrlFor(config, { state, codeChallenge }) {
|
|
650
|
-
const url = new URL(String(config.authorizationUrl));
|
|
651
|
-
url.searchParams.set("response_type", "code");
|
|
652
|
-
url.searchParams.set("client_id", String(config.clientId));
|
|
653
|
-
url.searchParams.set("redirect_uri", String(config.redirectUri));
|
|
654
|
-
url.searchParams.set("scope", String(config.scopes || "email profile"));
|
|
655
|
-
url.searchParams.set("state", state);
|
|
656
|
-
url.searchParams.set("code_challenge", codeChallenge);
|
|
657
|
-
url.searchParams.set("code_challenge_method", "S256");
|
|
658
|
-
return url.toString();
|
|
914
|
+
return manifests;
|
|
659
915
|
}
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
|
|
916
|
+
function nearestAgentsAncestor(manifestPath, root) {
|
|
917
|
+
let current = path4.dirname(manifestPath);
|
|
918
|
+
const stop = path4.resolve(root);
|
|
919
|
+
while (current.startsWith(stop)) {
|
|
920
|
+
if (path4.basename(current).toLowerCase() === "agents") {
|
|
921
|
+
return current;
|
|
922
|
+
}
|
|
923
|
+
const parent = path4.dirname(current);
|
|
924
|
+
if (parent === current) {
|
|
925
|
+
break;
|
|
926
|
+
}
|
|
927
|
+
current = parent;
|
|
672
928
|
}
|
|
673
|
-
return
|
|
929
|
+
return null;
|
|
674
930
|
}
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
return decodeResponse(response);
|
|
931
|
+
function hasDirectManifest(root) {
|
|
932
|
+
return [...MANIFEST_NAMES].some((name) => fs5.existsSync(path4.join(root, name)));
|
|
678
933
|
}
|
|
679
|
-
|
|
680
|
-
const
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
934
|
+
function displayPath(target, cwd) {
|
|
935
|
+
const relative = path4.relative(cwd, target);
|
|
936
|
+
if (!relative) {
|
|
937
|
+
return ".";
|
|
938
|
+
}
|
|
939
|
+
if (relative.startsWith("..") || path4.isAbsolute(relative)) {
|
|
940
|
+
return target;
|
|
941
|
+
}
|
|
942
|
+
return relative.startsWith(".") ? relative : `.${path4.sep}${relative}`;
|
|
686
943
|
}
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
|
|
944
|
+
function commonSearchRoots(home) {
|
|
945
|
+
if (!home) {
|
|
946
|
+
return [];
|
|
947
|
+
}
|
|
948
|
+
return [
|
|
949
|
+
"agents",
|
|
950
|
+
"Projects",
|
|
951
|
+
"Code",
|
|
952
|
+
"Developer",
|
|
953
|
+
"Documents",
|
|
954
|
+
"Desktop"
|
|
955
|
+
].map((name) => path4.join(home, name));
|
|
694
956
|
}
|
|
695
|
-
|
|
696
|
-
const
|
|
697
|
-
|
|
698
|
-
|
|
957
|
+
function uniqueExistingDirs(paths) {
|
|
958
|
+
const result = [];
|
|
959
|
+
const seen = /* @__PURE__ */ new Set();
|
|
960
|
+
for (const item of paths) {
|
|
961
|
+
const resolved = safeResolve(item);
|
|
962
|
+
if (!resolved || seen.has(resolved)) {
|
|
963
|
+
continue;
|
|
964
|
+
}
|
|
699
965
|
try {
|
|
700
|
-
|
|
966
|
+
if (!fs5.statSync(resolved).isDirectory()) {
|
|
967
|
+
continue;
|
|
968
|
+
}
|
|
701
969
|
} catch {
|
|
702
|
-
|
|
970
|
+
continue;
|
|
703
971
|
}
|
|
972
|
+
seen.add(resolved);
|
|
973
|
+
result.push(resolved);
|
|
704
974
|
}
|
|
705
|
-
|
|
706
|
-
throw new Error(payload.message || payload.error || `HTTP ${response.status}`);
|
|
707
|
-
}
|
|
708
|
-
return payload;
|
|
709
|
-
}
|
|
710
|
-
async function createCallbackServer(env) {
|
|
711
|
-
const port = Number(env.VENDIAN_CLI_OAUTH_REDIRECT_PORT || DEFAULT_OAUTH_REDIRECT_PORT);
|
|
712
|
-
let server;
|
|
713
|
-
let settled = false;
|
|
714
|
-
let timeout;
|
|
715
|
-
const waitPromise = new Promise((resolve, reject) => {
|
|
716
|
-
timeout = setTimeout(() => {
|
|
717
|
-
settled = true;
|
|
718
|
-
reject(new Error("OAuth login timed out before callback completed"));
|
|
719
|
-
}, 3e5);
|
|
720
|
-
server = http.createServer((req, res) => {
|
|
721
|
-
const url = new URL(req.url || "/", `http://${req.headers.host || "127.0.0.1"}`);
|
|
722
|
-
const code = url.searchParams.get("code");
|
|
723
|
-
const state = url.searchParams.get("state");
|
|
724
|
-
const error = url.searchParams.get("error");
|
|
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
|
-
reject(new Error(`OAuth login failed: ${error}`));
|
|
738
|
-
return;
|
|
739
|
-
}
|
|
740
|
-
if (!code) {
|
|
741
|
-
reject(new Error("OAuth callback did not include an authorization code"));
|
|
742
|
-
return;
|
|
743
|
-
}
|
|
744
|
-
resolve({ code, state });
|
|
745
|
-
});
|
|
746
|
-
server.on("error", reject);
|
|
747
|
-
server.listen(port, "127.0.0.1");
|
|
748
|
-
});
|
|
749
|
-
await new Promise((resolve, reject) => {
|
|
750
|
-
server.once("listening", resolve);
|
|
751
|
-
server.once("error", reject);
|
|
752
|
-
});
|
|
753
|
-
return {
|
|
754
|
-
redirectUri: `http://127.0.0.1:${server.address().port}/callback`,
|
|
755
|
-
wait: () => waitPromise,
|
|
756
|
-
close: () => new Promise((resolve) => server.close(resolve))
|
|
757
|
-
};
|
|
975
|
+
return result;
|
|
758
976
|
}
|
|
759
|
-
function
|
|
760
|
-
const
|
|
761
|
-
|
|
762
|
-
|
|
763
|
-
|
|
977
|
+
function parentDirs(start, limit) {
|
|
978
|
+
const parents = [];
|
|
979
|
+
let current = path4.resolve(start);
|
|
980
|
+
for (let index = 0; index < limit; index += 1) {
|
|
981
|
+
const parent = path4.dirname(current);
|
|
982
|
+
if (!parent || parent === current) {
|
|
983
|
+
break;
|
|
984
|
+
}
|
|
985
|
+
parents.push(parent);
|
|
986
|
+
current = parent;
|
|
764
987
|
}
|
|
765
|
-
|
|
766
|
-
|
|
767
|
-
|
|
988
|
+
return parents;
|
|
989
|
+
}
|
|
990
|
+
function safeResolve(candidate) {
|
|
991
|
+
try {
|
|
992
|
+
return path4.resolve(candidate);
|
|
993
|
+
} catch {
|
|
994
|
+
return null;
|
|
768
995
|
}
|
|
769
|
-
spawnSync3(...buildLinuxOpenCommand(url), { stdio: "ignore", shell: false });
|
|
770
996
|
}
|
|
771
|
-
|
|
772
|
-
|
|
997
|
+
|
|
998
|
+
// src/dev-server.js
|
|
999
|
+
init_auth();
|
|
1000
|
+
init_constants();
|
|
1001
|
+
|
|
1002
|
+
// src/forward.js
|
|
1003
|
+
import fs9 from "node:fs";
|
|
1004
|
+
|
|
1005
|
+
// src/docs.js
|
|
1006
|
+
import fs7 from "node:fs";
|
|
1007
|
+
import path6 from "node:path";
|
|
1008
|
+
var DOC_WORKSPACES_KEY = "agentDocWorkspaces";
|
|
1009
|
+
function pathApi2(platform) {
|
|
1010
|
+
return platform === "win32" ? path6.win32 : path6.posix;
|
|
773
1011
|
}
|
|
774
|
-
function
|
|
775
|
-
return ["
|
|
1012
|
+
function commandWritesAgentDocs(args = []) {
|
|
1013
|
+
return args[0] === "init";
|
|
776
1014
|
}
|
|
777
|
-
function
|
|
778
|
-
|
|
1015
|
+
function initOutputDir(args = []) {
|
|
1016
|
+
for (let index = 1; index < args.length; index += 1) {
|
|
1017
|
+
const arg = args[index];
|
|
1018
|
+
if (arg === "--output-dir" || arg === "-o") {
|
|
1019
|
+
return args[index + 1] || ".";
|
|
1020
|
+
}
|
|
1021
|
+
if (arg.startsWith("--output-dir=")) {
|
|
1022
|
+
return arg.slice("--output-dir=".length) || ".";
|
|
1023
|
+
}
|
|
1024
|
+
}
|
|
1025
|
+
return ".";
|
|
779
1026
|
}
|
|
780
|
-
function
|
|
781
|
-
|
|
782
|
-
|
|
1027
|
+
function resolveWorkspacePath(outputDir, { cwd = process.cwd(), platform = process.platform } = {}) {
|
|
1028
|
+
const api = pathApi2(platform);
|
|
1029
|
+
const raw = outputDir || ".";
|
|
1030
|
+
return api.isAbsolute(raw) ? api.normalize(raw) : api.resolve(cwd, raw);
|
|
1031
|
+
}
|
|
1032
|
+
function recordAgentDocsWorkspace(outputDir, {
|
|
1033
|
+
env = process.env,
|
|
1034
|
+
platform = process.platform,
|
|
1035
|
+
cwd = process.cwd(),
|
|
1036
|
+
now = /* @__PURE__ */ new Date()
|
|
1037
|
+
} = {}) {
|
|
1038
|
+
const workspacePath = resolveWorkspacePath(outputDir, { cwd, platform });
|
|
1039
|
+
const config = loadConfig(env, platform);
|
|
1040
|
+
const existing = Array.isArray(config[DOC_WORKSPACES_KEY]) ? config[DOC_WORKSPACES_KEY] : [];
|
|
1041
|
+
const workspaces = [workspacePath, ...existing.filter((entry) => entry !== workspacePath)];
|
|
1042
|
+
const next = {
|
|
1043
|
+
...config,
|
|
1044
|
+
[DOC_WORKSPACES_KEY]: workspaces,
|
|
1045
|
+
lastAgentDocsInitAt: now.toISOString()
|
|
1046
|
+
};
|
|
1047
|
+
saveConfig(next, env, platform);
|
|
1048
|
+
return next;
|
|
1049
|
+
}
|
|
1050
|
+
function recordForwardedDocsCommand(args, code, options = {}) {
|
|
1051
|
+
if (code !== 0 || !commandWritesAgentDocs(args)) {
|
|
1052
|
+
return false;
|
|
783
1053
|
}
|
|
784
|
-
|
|
785
|
-
|
|
786
|
-
|
|
1054
|
+
recordAgentDocsWorkspace(initOutputDir(args), options);
|
|
1055
|
+
return true;
|
|
1056
|
+
}
|
|
1057
|
+
function refreshAgentDocsWorkspaces({
|
|
1058
|
+
config,
|
|
1059
|
+
venvPath,
|
|
1060
|
+
env = process.env,
|
|
1061
|
+
platform = process.platform,
|
|
1062
|
+
run: run2 = runInherit,
|
|
1063
|
+
now = /* @__PURE__ */ new Date()
|
|
1064
|
+
} = {}) {
|
|
1065
|
+
const currentConfig = config || loadConfig(env, platform);
|
|
1066
|
+
const workspaces = Array.isArray(currentConfig[DOC_WORKSPACES_KEY]) ? [...new Set(currentConfig[DOC_WORKSPACES_KEY].filter((entry) => typeof entry === "string" && entry.trim()))] : [];
|
|
1067
|
+
if (workspaces.length === 0) {
|
|
1068
|
+
saveConfig(currentConfig, env, platform);
|
|
1069
|
+
return { config: currentConfig, refreshed: 0, failed: 0 };
|
|
787
1070
|
}
|
|
788
|
-
const
|
|
789
|
-
|
|
790
|
-
|
|
791
|
-
|
|
792
|
-
|
|
793
|
-
|
|
794
|
-
|
|
795
|
-
const
|
|
796
|
-
if (
|
|
797
|
-
|
|
1071
|
+
const vendianPath = venvVendian(venvPath, platform);
|
|
1072
|
+
let refreshed = 0;
|
|
1073
|
+
let failed = 0;
|
|
1074
|
+
for (const workspace of workspaces) {
|
|
1075
|
+
if (!fs7.existsSync(workspace)) {
|
|
1076
|
+
continue;
|
|
1077
|
+
}
|
|
1078
|
+
const status = run2(vendianPath, ["init", "--output-dir", workspace, "--yes"]);
|
|
1079
|
+
if (status === 0) {
|
|
1080
|
+
refreshed += 1;
|
|
1081
|
+
} else {
|
|
1082
|
+
failed += 1;
|
|
1083
|
+
console.error(`[vendian] Could not refresh SDK docs in ${workspace}`);
|
|
798
1084
|
}
|
|
799
|
-
} catch {
|
|
800
1085
|
}
|
|
801
|
-
|
|
802
|
-
|
|
803
|
-
|
|
804
|
-
|
|
805
|
-
user_id: token.userId,
|
|
806
|
-
email: token.email,
|
|
807
|
-
expires_at: token.expiresAt,
|
|
808
|
-
scopes: token.scopes,
|
|
809
|
-
tooling_eligible: token.toolingEligible
|
|
1086
|
+
const next = {
|
|
1087
|
+
...currentConfig,
|
|
1088
|
+
[DOC_WORKSPACES_KEY]: workspaces,
|
|
1089
|
+
lastAgentDocsRefreshAt: now.toISOString()
|
|
810
1090
|
};
|
|
811
|
-
|
|
812
|
-
}
|
|
813
|
-
function writeCloudConfig(raw, { env = process.env, platform = process.platform } = {}) {
|
|
814
|
-
const file = cloudConfigPath(env, platform);
|
|
815
|
-
fs6.mkdirSync(path5.dirname(file), { recursive: true });
|
|
816
|
-
fs6.writeFileSync(file, `${JSON.stringify(raw, null, 2)}
|
|
817
|
-
`, { encoding: "utf8", mode: 384 });
|
|
818
|
-
try {
|
|
819
|
-
fs6.chmodSync(file, 384);
|
|
820
|
-
} catch {
|
|
821
|
-
}
|
|
822
|
-
return file;
|
|
1091
|
+
saveConfig(next, env, platform);
|
|
1092
|
+
return { config: next, refreshed, failed };
|
|
823
1093
|
}
|
|
824
1094
|
|
|
825
1095
|
// src/setup.js
|
|
1096
|
+
init_auth();
|
|
1097
|
+
init_constants();
|
|
1098
|
+
import fs8 from "node:fs";
|
|
826
1099
|
async function setup({
|
|
827
1100
|
nonInteractive = false,
|
|
828
1101
|
backend = void 0,
|
|
@@ -890,7 +1163,7 @@ async function setup({
|
|
|
890
1163
|
throw new Error("Vendian packages installed, but import verification failed.");
|
|
891
1164
|
}
|
|
892
1165
|
const vendianPath = venvVendian(venvPath, platform);
|
|
893
|
-
if (!
|
|
1166
|
+
if (!fs8.existsSync(vendianPath)) {
|
|
894
1167
|
throw new Error("Vendian executable was not found after install.");
|
|
895
1168
|
}
|
|
896
1169
|
console.log("");
|
|
@@ -989,7 +1262,7 @@ async function preparePythonVendianInvocation(args, {
|
|
|
989
1262
|
const venvPath = managedVenvPath(env, platform);
|
|
990
1263
|
const vendianPath = venvVendian(venvPath, platform);
|
|
991
1264
|
reportProgress(onProgress, "Checking managed Python runtime");
|
|
992
|
-
if (!
|
|
1265
|
+
if (!fs9.existsSync(vendianPath)) {
|
|
993
1266
|
throw new Error("Vendian is not set up yet. Run `vendian login` first.");
|
|
994
1267
|
}
|
|
995
1268
|
const loadedConfig = loadConfig(env, platform);
|
|
@@ -1010,332 +1283,148 @@ async function preparePythonVendianInvocation(args, {
|
|
|
1010
1283
|
reportProgress(onProgress, "Launching local serve daemon");
|
|
1011
1284
|
return {
|
|
1012
1285
|
command: vendianPath,
|
|
1013
|
-
args,
|
|
1014
|
-
env: {
|
|
1015
|
-
...env,
|
|
1016
|
-
...packageIndexEnv(config, env, platform)
|
|
1017
|
-
}
|
|
1018
|
-
};
|
|
1019
|
-
}
|
|
1020
|
-
function commandNeedsPackageAccess(args = []) {
|
|
1021
|
-
return args[0] === "cloud" && args[1] === "local" && ["serve", "run"].includes(args[2]);
|
|
1022
|
-
}
|
|
1023
|
-
function packageIndexEnv(config = {}, env = process.env, platform = process.platform) {
|
|
1024
|
-
const registry = registryConfig(config, env, platform);
|
|
1025
|
-
const indexes = buildIndexUrls(registry);
|
|
1026
|
-
if (!indexes) {
|
|
1027
|
-
return {};
|
|
1028
|
-
}
|
|
1029
|
-
const extraIndexUrls = joinIndexUrls([
|
|
1030
|
-
indexes.runtimeIndexUrl,
|
|
1031
|
-
"https://pypi.org/simple",
|
|
1032
|
-
env.VENDIAN_PIP_EXTRA_INDEX_URL
|
|
1033
|
-
]);
|
|
1034
|
-
const uvExtraIndexUrls = joinIndexUrls([
|
|
1035
|
-
indexes.runtimeIndexUrl,
|
|
1036
|
-
"https://pypi.org/simple",
|
|
1037
|
-
env.VENDIAN_UV_EXTRA_INDEX_URL
|
|
1038
|
-
]);
|
|
1039
|
-
return {
|
|
1040
|
-
VENDIAN_PIP_INDEX_URL: indexes.sdkIndexUrl,
|
|
1041
|
-
VENDIAN_PIP_EXTRA_INDEX_URL: extraIndexUrls,
|
|
1042
|
-
VENDIAN_UV_INDEX_URL: indexes.sdkIndexUrl,
|
|
1043
|
-
VENDIAN_UV_EXTRA_INDEX_URL: uvExtraIndexUrls
|
|
1044
|
-
};
|
|
1045
|
-
}
|
|
1046
|
-
function joinIndexUrls(urls) {
|
|
1047
|
-
return urls.flatMap((value) => String(value || "").split(/\s+/)).map((value) => value.trim()).filter(Boolean).filter((value, index, all) => all.indexOf(value) === index).join(" ");
|
|
1048
|
-
}
|
|
1049
|
-
function maybeAutoUpdateManagedEnv({
|
|
1050
|
-
env = process.env,
|
|
1051
|
-
platform = process.platform,
|
|
1052
|
-
venvPath = managedVenvPath(env, platform),
|
|
1053
|
-
force = false,
|
|
1054
|
-
log = true,
|
|
1055
|
-
load = loadConfig,
|
|
1056
|
-
save = saveConfig,
|
|
1057
|
-
installPackages = installVendianPackages,
|
|
1058
|
-
refreshDocs = refreshAgentDocsWorkspaces,
|
|
1059
|
-
now = Date.now(),
|
|
1060
|
-
onProgress = null
|
|
1061
|
-
} = {}) {
|
|
1062
|
-
if (env.VENDIAN_SKIP_AUTO_UPDATE === "1") {
|
|
1063
|
-
return false;
|
|
1064
|
-
}
|
|
1065
|
-
const config = load(env, platform);
|
|
1066
|
-
const lastUpdate = Date.parse(config.lastManagedUpdateAt || "");
|
|
1067
|
-
if (!force && Number.isFinite(lastUpdate) && now - lastUpdate < AUTO_UPDATE_INTERVAL_MS) {
|
|
1068
|
-
return false;
|
|
1069
|
-
}
|
|
1070
|
-
const registry = registryConfig(config, env, platform);
|
|
1071
|
-
if (!registry.token) {
|
|
1072
|
-
return false;
|
|
1073
|
-
}
|
|
1074
|
-
const pythonPath = venvPython(venvPath, platform);
|
|
1075
|
-
if (!fs8.existsSync(pythonPath)) {
|
|
1076
|
-
return false;
|
|
1077
|
-
}
|
|
1078
|
-
try {
|
|
1079
|
-
if (log) {
|
|
1080
|
-
console.error("[vendian] Checking managed CLI/runtime updates...");
|
|
1081
|
-
}
|
|
1082
|
-
reportProgress(onProgress, "Installing managed CLI/runtime updates");
|
|
1083
|
-
installPackages({ pythonPath, venvPath, config, env, platform });
|
|
1084
|
-
const next = { ...config, lastManagedUpdateAt: new Date(now).toISOString() };
|
|
1085
|
-
save(next, env, platform);
|
|
1086
|
-
reportProgress(onProgress, "Refreshing generated agent docs");
|
|
1087
|
-
refreshDocs({ config: next, venvPath, env, platform });
|
|
1088
|
-
reportProgress(onProgress, "Managed CLI/runtime ready");
|
|
1089
|
-
return true;
|
|
1090
|
-
} catch (error) {
|
|
1091
|
-
const message = error && typeof error.message === "string" ? error.message : String(error);
|
|
1092
|
-
if (log) {
|
|
1093
|
-
console.error(`[vendian] Update check failed; continuing with installed CLI. ${message}`);
|
|
1094
|
-
}
|
|
1095
|
-
return false;
|
|
1096
|
-
}
|
|
1097
|
-
}
|
|
1098
|
-
function reportProgress(onProgress, message) {
|
|
1099
|
-
if (typeof onProgress === "function") {
|
|
1100
|
-
onProgress(message);
|
|
1101
|
-
}
|
|
1102
|
-
}
|
|
1103
|
-
|
|
1104
|
-
// src/tui.js
|
|
1105
|
-
import fs11 from "node:fs";
|
|
1106
|
-
import path8 from "node:path";
|
|
1107
|
-
import readline from "node:readline";
|
|
1108
|
-
|
|
1109
|
-
// src/version.js
|
|
1110
|
-
var CLI_VERSION = true ? "0.0.34" : process.env.npm_package_version || "0.0.0-dev";
|
|
1111
|
-
|
|
1112
|
-
// src/npm-update.js
|
|
1113
|
-
var NPM_CHECK_INTERVAL_MS = 30 * 60 * 1e3;
|
|
1114
|
-
|
|
1115
|
-
// src/agent-discovery.js
|
|
1116
|
-
import fs9 from "node:fs";
|
|
1117
|
-
import os2 from "node:os";
|
|
1118
|
-
import path6 from "node:path";
|
|
1119
|
-
var SKIP_DIRS = /* @__PURE__ */ new Set([
|
|
1120
|
-
".agents",
|
|
1121
|
-
".git",
|
|
1122
|
-
".hg",
|
|
1123
|
-
".mypy_cache",
|
|
1124
|
-
".pytest_cache",
|
|
1125
|
-
".ruff_cache",
|
|
1126
|
-
".svn",
|
|
1127
|
-
".venv",
|
|
1128
|
-
"__pycache__",
|
|
1129
|
-
"build",
|
|
1130
|
-
"dist",
|
|
1131
|
-
"node_modules",
|
|
1132
|
-
"venv"
|
|
1133
|
-
]);
|
|
1134
|
-
var MANIFEST_NAMES = /* @__PURE__ */ new Set(["manifest.yaml", "manifest.yml"]);
|
|
1135
|
-
function findAgentDirectoryCandidates({
|
|
1136
|
-
cwd = process.cwd(),
|
|
1137
|
-
home = os2.homedir(),
|
|
1138
|
-
maxDepth = 6,
|
|
1139
|
-
maxDirs = 1e3,
|
|
1140
|
-
limit = 12
|
|
1141
|
-
} = {}) {
|
|
1142
|
-
const start = path6.resolve(cwd);
|
|
1143
|
-
const candidates = [];
|
|
1144
|
-
const seen = /* @__PURE__ */ new Set();
|
|
1145
|
-
function addCandidate(candidatePath) {
|
|
1146
|
-
const resolved = safeResolve(candidatePath);
|
|
1147
|
-
if (!resolved || seen.has(resolved)) {
|
|
1148
|
-
return;
|
|
1149
|
-
}
|
|
1150
|
-
const count = countAgentManifests(resolved, { maxDepth, maxDirs, limit: 100 });
|
|
1151
|
-
if (count < 1) {
|
|
1152
|
-
return;
|
|
1153
|
-
}
|
|
1154
|
-
seen.add(resolved);
|
|
1155
|
-
candidates.push({
|
|
1156
|
-
path: displayPath(resolved, start),
|
|
1157
|
-
absolutePath: resolved,
|
|
1158
|
-
agentCount: count
|
|
1159
|
-
});
|
|
1160
|
-
}
|
|
1161
|
-
addCandidate(path6.join(start, "agents"));
|
|
1162
|
-
if (hasDirectManifest(start)) {
|
|
1163
|
-
addCandidate(start);
|
|
1164
|
-
}
|
|
1165
|
-
for (const parent of parentDirs(start, 3)) {
|
|
1166
|
-
addCandidate(path6.join(parent, "agents"));
|
|
1167
|
-
}
|
|
1168
|
-
const roots = uniqueExistingDirs([
|
|
1169
|
-
start,
|
|
1170
|
-
...commonSearchRoots(home)
|
|
1171
|
-
]);
|
|
1172
|
-
for (const root of roots) {
|
|
1173
|
-
for (const manifest of findManifestFiles(root, { maxDepth, maxDirs, limit: 250 })) {
|
|
1174
|
-
const workspace = nearestAgentsAncestor(manifest, root);
|
|
1175
|
-
addCandidate(workspace || path6.dirname(manifest));
|
|
1176
|
-
if (candidates.length >= limit) {
|
|
1177
|
-
return candidates.slice(0, limit);
|
|
1178
|
-
}
|
|
1179
|
-
}
|
|
1180
|
-
}
|
|
1181
|
-
return candidates.slice(0, limit);
|
|
1182
|
-
}
|
|
1183
|
-
function findAgentFolders(root, {
|
|
1184
|
-
cwd = process.cwd(),
|
|
1185
|
-
maxDepth = 6,
|
|
1186
|
-
maxDirs = 1e3,
|
|
1187
|
-
limit = 100
|
|
1188
|
-
} = {}) {
|
|
1189
|
-
const start = path6.resolve(cwd);
|
|
1190
|
-
const resolvedRoot = safeResolve(root || ".");
|
|
1191
|
-
if (!resolvedRoot) {
|
|
1192
|
-
return [];
|
|
1193
|
-
}
|
|
1194
|
-
const seen = /* @__PURE__ */ new Set();
|
|
1195
|
-
const folders = [];
|
|
1196
|
-
for (const manifest of findManifestFiles(resolvedRoot, { maxDepth, maxDirs, limit })) {
|
|
1197
|
-
const folder = path6.dirname(manifest);
|
|
1198
|
-
const resolved = safeResolve(folder);
|
|
1199
|
-
if (!resolved || seen.has(resolved)) {
|
|
1200
|
-
continue;
|
|
1201
|
-
}
|
|
1202
|
-
seen.add(resolved);
|
|
1203
|
-
folders.push({
|
|
1204
|
-
path: displayPath(resolved, start),
|
|
1205
|
-
absolutePath: resolved,
|
|
1206
|
-
name: path6.basename(resolved) || displayPath(resolved, start)
|
|
1207
|
-
});
|
|
1208
|
-
}
|
|
1209
|
-
return folders.sort((a, b) => a.path.localeCompare(b.path)).slice(0, limit);
|
|
1210
|
-
}
|
|
1211
|
-
function countAgentManifests(root, { maxDepth = 6, maxDirs = 1e3, limit = 100 } = {}) {
|
|
1212
|
-
let count = 0;
|
|
1213
|
-
for (const _manifest of findManifestFiles(root, { maxDepth, maxDirs, limit })) {
|
|
1214
|
-
count += 1;
|
|
1215
|
-
if (count >= limit) {
|
|
1216
|
-
return count;
|
|
1286
|
+
args,
|
|
1287
|
+
env: {
|
|
1288
|
+
...env,
|
|
1289
|
+
...packageIndexEnv(config, env, platform)
|
|
1217
1290
|
}
|
|
1218
|
-
}
|
|
1219
|
-
return count;
|
|
1291
|
+
};
|
|
1220
1292
|
}
|
|
1221
|
-
function
|
|
1222
|
-
|
|
1223
|
-
const stack = [{ dir: root, depth: 0 }];
|
|
1224
|
-
const seen = /* @__PURE__ */ new Set();
|
|
1225
|
-
while (stack.length && manifests.length < limit && seen.size < maxDirs) {
|
|
1226
|
-
const { dir, depth } = stack.pop();
|
|
1227
|
-
const resolved = safeResolve(dir);
|
|
1228
|
-
if (!resolved || seen.has(resolved)) {
|
|
1229
|
-
continue;
|
|
1230
|
-
}
|
|
1231
|
-
seen.add(resolved);
|
|
1232
|
-
let entries;
|
|
1233
|
-
try {
|
|
1234
|
-
entries = fs9.readdirSync(resolved, { withFileTypes: true });
|
|
1235
|
-
} catch {
|
|
1236
|
-
continue;
|
|
1237
|
-
}
|
|
1238
|
-
for (const entry of entries) {
|
|
1239
|
-
if (entry.isFile() && MANIFEST_NAMES.has(entry.name)) {
|
|
1240
|
-
manifests.push(path6.join(resolved, entry.name));
|
|
1241
|
-
if (manifests.length >= limit) {
|
|
1242
|
-
break;
|
|
1243
|
-
}
|
|
1244
|
-
}
|
|
1245
|
-
}
|
|
1246
|
-
if (depth >= maxDepth) {
|
|
1247
|
-
continue;
|
|
1248
|
-
}
|
|
1249
|
-
for (const entry of entries) {
|
|
1250
|
-
if (!entry.isDirectory() || SKIP_DIRS.has(entry.name)) {
|
|
1251
|
-
continue;
|
|
1252
|
-
}
|
|
1253
|
-
stack.push({ dir: path6.join(resolved, entry.name), depth: depth + 1 });
|
|
1254
|
-
}
|
|
1255
|
-
}
|
|
1256
|
-
return manifests;
|
|
1293
|
+
function commandNeedsPackageAccess(args = []) {
|
|
1294
|
+
return args[0] === "cloud" && args[1] === "local" && ["serve", "run"].includes(args[2]);
|
|
1257
1295
|
}
|
|
1258
|
-
function
|
|
1259
|
-
|
|
1260
|
-
const
|
|
1261
|
-
|
|
1262
|
-
|
|
1263
|
-
return current;
|
|
1264
|
-
}
|
|
1265
|
-
const parent = path6.dirname(current);
|
|
1266
|
-
if (parent === current) {
|
|
1267
|
-
break;
|
|
1268
|
-
}
|
|
1269
|
-
current = parent;
|
|
1296
|
+
function packageIndexEnv(config = {}, env = process.env, platform = process.platform) {
|
|
1297
|
+
const registry = registryConfig(config, env, platform);
|
|
1298
|
+
const indexes = buildIndexUrls(registry);
|
|
1299
|
+
if (!indexes) {
|
|
1300
|
+
return {};
|
|
1270
1301
|
}
|
|
1271
|
-
|
|
1302
|
+
const extraIndexUrls = joinIndexUrls([
|
|
1303
|
+
indexes.runtimeIndexUrl,
|
|
1304
|
+
"https://pypi.org/simple",
|
|
1305
|
+
env.VENDIAN_PIP_EXTRA_INDEX_URL
|
|
1306
|
+
]);
|
|
1307
|
+
const uvExtraIndexUrls = joinIndexUrls([
|
|
1308
|
+
indexes.runtimeIndexUrl,
|
|
1309
|
+
"https://pypi.org/simple",
|
|
1310
|
+
env.VENDIAN_UV_EXTRA_INDEX_URL
|
|
1311
|
+
]);
|
|
1312
|
+
return {
|
|
1313
|
+
VENDIAN_PIP_INDEX_URL: indexes.sdkIndexUrl,
|
|
1314
|
+
VENDIAN_PIP_EXTRA_INDEX_URL: extraIndexUrls,
|
|
1315
|
+
VENDIAN_UV_INDEX_URL: indexes.sdkIndexUrl,
|
|
1316
|
+
VENDIAN_UV_EXTRA_INDEX_URL: uvExtraIndexUrls
|
|
1317
|
+
};
|
|
1272
1318
|
}
|
|
1273
|
-
function
|
|
1274
|
-
return
|
|
1319
|
+
function joinIndexUrls(urls) {
|
|
1320
|
+
return urls.flatMap((value) => String(value || "").split(/\s+/)).map((value) => value.trim()).filter(Boolean).filter((value, index, all) => all.indexOf(value) === index).join(" ");
|
|
1275
1321
|
}
|
|
1276
|
-
function
|
|
1277
|
-
|
|
1278
|
-
|
|
1279
|
-
|
|
1322
|
+
function maybeAutoUpdateManagedEnv({
|
|
1323
|
+
env = process.env,
|
|
1324
|
+
platform = process.platform,
|
|
1325
|
+
venvPath = managedVenvPath(env, platform),
|
|
1326
|
+
force = false,
|
|
1327
|
+
log = true,
|
|
1328
|
+
load = loadConfig,
|
|
1329
|
+
save = saveConfig,
|
|
1330
|
+
installPackages = installVendianPackages,
|
|
1331
|
+
refreshDocs = refreshAgentDocsWorkspaces,
|
|
1332
|
+
now = Date.now(),
|
|
1333
|
+
onProgress = null
|
|
1334
|
+
} = {}) {
|
|
1335
|
+
if (env.VENDIAN_SKIP_AUTO_UPDATE === "1") {
|
|
1336
|
+
return false;
|
|
1280
1337
|
}
|
|
1281
|
-
|
|
1282
|
-
|
|
1338
|
+
const config = load(env, platform);
|
|
1339
|
+
const lastUpdate = Date.parse(config.lastManagedUpdateAt || "");
|
|
1340
|
+
if (!force && Number.isFinite(lastUpdate) && now - lastUpdate < AUTO_UPDATE_INTERVAL_MS) {
|
|
1341
|
+
return false;
|
|
1283
1342
|
}
|
|
1284
|
-
|
|
1285
|
-
|
|
1286
|
-
|
|
1287
|
-
if (!home) {
|
|
1288
|
-
return [];
|
|
1343
|
+
const registry = registryConfig(config, env, platform);
|
|
1344
|
+
if (!registry.token) {
|
|
1345
|
+
return false;
|
|
1289
1346
|
}
|
|
1290
|
-
|
|
1291
|
-
|
|
1292
|
-
|
|
1293
|
-
|
|
1294
|
-
|
|
1295
|
-
|
|
1296
|
-
|
|
1297
|
-
].map((name) => path6.join(home, name));
|
|
1298
|
-
}
|
|
1299
|
-
function uniqueExistingDirs(paths) {
|
|
1300
|
-
const result = [];
|
|
1301
|
-
const seen = /* @__PURE__ */ new Set();
|
|
1302
|
-
for (const item of paths) {
|
|
1303
|
-
const resolved = safeResolve(item);
|
|
1304
|
-
if (!resolved || seen.has(resolved)) {
|
|
1305
|
-
continue;
|
|
1347
|
+
const pythonPath = venvPython(venvPath, platform);
|
|
1348
|
+
if (!fs9.existsSync(pythonPath)) {
|
|
1349
|
+
return false;
|
|
1350
|
+
}
|
|
1351
|
+
try {
|
|
1352
|
+
if (log) {
|
|
1353
|
+
console.error("[vendian] Checking managed CLI/runtime updates...");
|
|
1306
1354
|
}
|
|
1307
|
-
|
|
1308
|
-
|
|
1309
|
-
|
|
1310
|
-
|
|
1311
|
-
|
|
1312
|
-
|
|
1355
|
+
reportProgress(onProgress, "Installing managed CLI/runtime updates");
|
|
1356
|
+
installPackages({ pythonPath, venvPath, config, env, platform });
|
|
1357
|
+
const next = { ...config, lastManagedUpdateAt: new Date(now).toISOString() };
|
|
1358
|
+
save(next, env, platform);
|
|
1359
|
+
reportProgress(onProgress, "Refreshing generated agent docs");
|
|
1360
|
+
refreshDocs({ config: next, venvPath, env, platform });
|
|
1361
|
+
reportProgress(onProgress, "Managed CLI/runtime ready");
|
|
1362
|
+
return true;
|
|
1363
|
+
} catch (error) {
|
|
1364
|
+
const message = error && typeof error.message === "string" ? error.message : String(error);
|
|
1365
|
+
if (log) {
|
|
1366
|
+
console.error(`[vendian] Update check failed; continuing with installed CLI. ${message}`);
|
|
1313
1367
|
}
|
|
1314
|
-
|
|
1315
|
-
result.push(resolved);
|
|
1368
|
+
return false;
|
|
1316
1369
|
}
|
|
1317
|
-
return result;
|
|
1318
1370
|
}
|
|
1319
|
-
function
|
|
1320
|
-
|
|
1321
|
-
|
|
1322
|
-
for (let index = 0; index < limit; index += 1) {
|
|
1323
|
-
const parent = path6.dirname(current);
|
|
1324
|
-
if (!parent || parent === current) {
|
|
1325
|
-
break;
|
|
1326
|
-
}
|
|
1327
|
-
parents.push(parent);
|
|
1328
|
-
current = parent;
|
|
1371
|
+
function reportProgress(onProgress, message) {
|
|
1372
|
+
if (typeof onProgress === "function") {
|
|
1373
|
+
onProgress(message);
|
|
1329
1374
|
}
|
|
1330
|
-
return parents;
|
|
1331
1375
|
}
|
|
1332
|
-
|
|
1376
|
+
|
|
1377
|
+
// src/workspaces.js
|
|
1378
|
+
async function listCloudWorkspaces({
|
|
1379
|
+
env = process.env,
|
|
1380
|
+
platform = process.platform,
|
|
1381
|
+
prepareInvocation = preparePythonVendianInvocation,
|
|
1382
|
+
run: run2 = runCapture,
|
|
1383
|
+
onProgress = null,
|
|
1384
|
+
timeoutMs = 2e4
|
|
1385
|
+
} = {}) {
|
|
1386
|
+
const progress = typeof onProgress === "function" ? onProgress : () => {
|
|
1387
|
+
};
|
|
1388
|
+
progress("Checking managed Python runtime");
|
|
1389
|
+
const invocation = await prepareInvocation(["cloud", "collections", "list", "--json"], {
|
|
1390
|
+
env,
|
|
1391
|
+
platform,
|
|
1392
|
+
onProgress: progress,
|
|
1393
|
+
skipAutoUpdate: true
|
|
1394
|
+
});
|
|
1395
|
+
progress("Loading workspaces from Vendian");
|
|
1396
|
+
const result = run2(invocation.command, invocation.args, { env: invocation.env, timeout: timeoutMs });
|
|
1397
|
+
if (!result.ok) {
|
|
1398
|
+
return {
|
|
1399
|
+
ok: false,
|
|
1400
|
+
workspaces: [],
|
|
1401
|
+
error: (result.stderr || result.stdout || `workspace list exited with code ${result.status}`).trim()
|
|
1402
|
+
};
|
|
1403
|
+
}
|
|
1333
1404
|
try {
|
|
1334
|
-
return
|
|
1335
|
-
} catch {
|
|
1336
|
-
|
|
1405
|
+
return { ok: true, workspaces: normalizeWorkspaceList(JSON.parse(result.stdout)), error: "" };
|
|
1406
|
+
} catch (error) {
|
|
1407
|
+
const message = error && typeof error.message === "string" ? error.message : String(error);
|
|
1408
|
+
return { ok: false, workspaces: [], error: `Could not parse workspace list: ${message}` };
|
|
1337
1409
|
}
|
|
1338
1410
|
}
|
|
1411
|
+
function normalizeWorkspaceList(payload) {
|
|
1412
|
+
const data = payload && typeof payload === "object" && "data" in payload ? payload.data : payload;
|
|
1413
|
+
const raw = Array.isArray(data) ? data : data && typeof data === "object" && Array.isArray(data.data) ? data.data : data && typeof data === "object" && Array.isArray(data.collections) ? data.collections : [];
|
|
1414
|
+
return raw.filter((item) => item && typeof item === "object").map((item) => ({
|
|
1415
|
+
id: stringValue(item.id),
|
|
1416
|
+
name: stringValue(item.name || item.slug || item.id || "Unnamed workspace"),
|
|
1417
|
+
slug: stringValue(item.slug)
|
|
1418
|
+
})).filter((item) => item.id);
|
|
1419
|
+
}
|
|
1420
|
+
function workspaceLabel(workspace) {
|
|
1421
|
+
const name = workspace?.name || workspace?.slug || workspace?.id || "Unnamed workspace";
|
|
1422
|
+
const slug = workspace?.slug && workspace.slug !== name ? ` (${workspace.slug})` : "";
|
|
1423
|
+
return `${name}${slug}`;
|
|
1424
|
+
}
|
|
1425
|
+
function stringValue(value) {
|
|
1426
|
+
return value == null ? "" : String(value).trim();
|
|
1427
|
+
}
|
|
1339
1428
|
|
|
1340
1429
|
// src/serve-events.js
|
|
1341
1430
|
function initialServeState() {
|
|
@@ -1392,14 +1481,14 @@ function applyServeEvent(state, event) {
|
|
|
1392
1481
|
logs: appendLog(state.logs, event)
|
|
1393
1482
|
};
|
|
1394
1483
|
if (event.type === "workspace_resolved") {
|
|
1395
|
-
return { ...next, collectionId:
|
|
1484
|
+
return { ...next, collectionId: stringValue2(event.collectionId), activity: "Workspace resolved" };
|
|
1396
1485
|
}
|
|
1397
1486
|
if (event.type === "daemon_registered") {
|
|
1398
1487
|
return {
|
|
1399
1488
|
...next,
|
|
1400
1489
|
connected: true,
|
|
1401
|
-
daemonId:
|
|
1402
|
-
cliSessionId:
|
|
1490
|
+
daemonId: stringValue2(event.daemonId),
|
|
1491
|
+
cliSessionId: stringValue2(event.cliSessionId),
|
|
1403
1492
|
activity: "Daemon registered"
|
|
1404
1493
|
};
|
|
1405
1494
|
}
|
|
@@ -1419,7 +1508,7 @@ function applyServeEvent(state, event) {
|
|
|
1419
1508
|
};
|
|
1420
1509
|
}
|
|
1421
1510
|
if (event.type === "daemon_wake_received") {
|
|
1422
|
-
const sourceType =
|
|
1511
|
+
const sourceType = stringValue2(event.sourceEventType || "backend wake");
|
|
1423
1512
|
return {
|
|
1424
1513
|
...next,
|
|
1425
1514
|
activity: `Wake received: ${sourceType}`
|
|
@@ -1434,17 +1523,17 @@ function applyServeEvent(state, event) {
|
|
|
1434
1523
|
if (event.type === "dispatch_job_received") {
|
|
1435
1524
|
return {
|
|
1436
1525
|
...next,
|
|
1437
|
-
activity: `Dispatch received for run ${
|
|
1526
|
+
activity: `Dispatch received for run ${stringValue2(event.runId || event.sessionId || "unknown")}`
|
|
1438
1527
|
};
|
|
1439
1528
|
}
|
|
1440
1529
|
if (event.type === "dispatch_job_acked") {
|
|
1441
1530
|
return {
|
|
1442
1531
|
...next,
|
|
1443
|
-
activity: `Dispatch acknowledged for run ${
|
|
1532
|
+
activity: `Dispatch acknowledged for run ${stringValue2(event.runId || event.sessionId || "unknown")}`
|
|
1444
1533
|
};
|
|
1445
1534
|
}
|
|
1446
1535
|
if (event.type === "daemon_stderr") {
|
|
1447
|
-
const message =
|
|
1536
|
+
const message = stringValue2(event.message || event.stderr || "Daemon wrote to stderr");
|
|
1448
1537
|
return {
|
|
1449
1538
|
...next,
|
|
1450
1539
|
activity: `Daemon stderr: ${message}`,
|
|
@@ -1489,14 +1578,14 @@ function applyServeEvent(state, event) {
|
|
|
1489
1578
|
if (event.type === "agent_prepare_progress") {
|
|
1490
1579
|
const relativePath = agentLabel(event);
|
|
1491
1580
|
const timestamp = event.timestamp || (/* @__PURE__ */ new Date()).toISOString();
|
|
1492
|
-
const message =
|
|
1581
|
+
const message = stringValue2(event.message || event.stage || "Preparing agent");
|
|
1493
1582
|
return {
|
|
1494
1583
|
...next,
|
|
1495
1584
|
agentLogs: appendAgentLog(state.agentLogs, event),
|
|
1496
1585
|
agentRunState: setAgentRunState(state.agentRunState, relativePath, {
|
|
1497
1586
|
status: "preparing",
|
|
1498
1587
|
lastEventAt: timestamp,
|
|
1499
|
-
progressStage:
|
|
1588
|
+
progressStage: stringValue2(event.stage),
|
|
1500
1589
|
progressMessage: message
|
|
1501
1590
|
}),
|
|
1502
1591
|
activity: `${relativePath}: ${message}`
|
|
@@ -1508,11 +1597,11 @@ function applyServeEvent(state, event) {
|
|
|
1508
1597
|
const runState = isError ? setAgentRunState(state.agentRunState, agentLabel(event), {
|
|
1509
1598
|
status: "error",
|
|
1510
1599
|
lastEventAt: event.timestamp || (/* @__PURE__ */ new Date()).toISOString(),
|
|
1511
|
-
errorMessage:
|
|
1600
|
+
errorMessage: stringValue2(event.error || event.errorMessage || "Agent setup failed")
|
|
1512
1601
|
}) : isDisabled ? setAgentRunState(state.agentRunState, agentLabel(event), {
|
|
1513
1602
|
status: "disabled",
|
|
1514
1603
|
lastEventAt: event.timestamp || (/* @__PURE__ */ new Date()).toISOString(),
|
|
1515
|
-
disabledReason:
|
|
1604
|
+
disabledReason: stringValue2(event.disabledReason || event.warning || "System dependency not met"),
|
|
1516
1605
|
resolutionHints: Array.isArray(event.resolutionHints) ? event.resolutionHints : []
|
|
1517
1606
|
}) : setAgentRunState(state.agentRunState, agentLabel(event), {
|
|
1518
1607
|
status: "ready",
|
|
@@ -1548,8 +1637,8 @@ function applyServeEvent(state, event) {
|
|
|
1548
1637
|
agentLogs: appendAgentLog(state.agentLogs, event),
|
|
1549
1638
|
agentRunState: setAgentRunState(state.agentRunState, relativePath, {
|
|
1550
1639
|
status: "running",
|
|
1551
|
-
runId:
|
|
1552
|
-
jobType:
|
|
1640
|
+
runId: stringValue2(event.runId || event.deployRequestId),
|
|
1641
|
+
jobType: stringValue2(event.jobType || "run"),
|
|
1553
1642
|
startedAt: timestamp,
|
|
1554
1643
|
completedAt: null,
|
|
1555
1644
|
lastEventAt: timestamp,
|
|
@@ -1563,7 +1652,7 @@ function applyServeEvent(state, event) {
|
|
|
1563
1652
|
const relativePath = agentLabel(event);
|
|
1564
1653
|
const timestamp = event.timestamp || (/* @__PURE__ */ new Date()).toISOString();
|
|
1565
1654
|
const success = event.success !== false;
|
|
1566
|
-
const runId =
|
|
1655
|
+
const runId = stringValue2(event.runId || event.deployRequestId);
|
|
1567
1656
|
const previous = (state.agentRunState || {})[relativePath];
|
|
1568
1657
|
const effectiveSuccess = success && !(previous?.status === "error" && previous?.runId === runId);
|
|
1569
1658
|
return {
|
|
@@ -1572,10 +1661,10 @@ function applyServeEvent(state, event) {
|
|
|
1572
1661
|
agentRunState: setAgentRunState(state.agentRunState, relativePath, {
|
|
1573
1662
|
status: effectiveSuccess ? "completed" : "error",
|
|
1574
1663
|
runId,
|
|
1575
|
-
jobType:
|
|
1664
|
+
jobType: stringValue2(event.jobType || "run"),
|
|
1576
1665
|
completedAt: timestamp,
|
|
1577
1666
|
lastEventAt: timestamp,
|
|
1578
|
-
errorMessage: effectiveSuccess ? null : previous?.errorMessage ||
|
|
1667
|
+
errorMessage: effectiveSuccess ? null : previous?.errorMessage || stringValue2(event.error || "Job failed")
|
|
1579
1668
|
}),
|
|
1580
1669
|
jobsRun: state.jobsRun + 1,
|
|
1581
1670
|
currentJob: null,
|
|
@@ -1583,13 +1672,13 @@ function applyServeEvent(state, event) {
|
|
|
1583
1672
|
};
|
|
1584
1673
|
}
|
|
1585
1674
|
if (event.type === "backend_connection_lost") {
|
|
1586
|
-
return { ...next, connected: false, activity: `Connection lost during ${
|
|
1675
|
+
return { ...next, connected: false, activity: `Connection lost during ${stringValue2(event.activity)}`, retry: event };
|
|
1587
1676
|
}
|
|
1588
1677
|
if (event.type === "backend_connection_restored") {
|
|
1589
1678
|
return { ...next, connected: true, activity: "Backend connection restored", retry: null };
|
|
1590
1679
|
}
|
|
1591
1680
|
if (event.type === "retry_scheduled") {
|
|
1592
|
-
return { ...next, retry: event, activity: `Retrying ${
|
|
1681
|
+
return { ...next, retry: event, activity: `Retrying ${stringValue2(event.activity)} in ${formatSeconds(event.delaySeconds)}` };
|
|
1593
1682
|
}
|
|
1594
1683
|
if (event.type === "error") {
|
|
1595
1684
|
const relativePath = event.relativePath || event.path ? agentLabel(event) : "";
|
|
@@ -1600,11 +1689,11 @@ function applyServeEvent(state, event) {
|
|
|
1600
1689
|
agentLogs: hasAgentScope ? appendAgentLog(state.agentLogs, event) : state.agentLogs,
|
|
1601
1690
|
agentRunState: hasAgentScope ? setAgentRunState(state.agentRunState, relativePath, {
|
|
1602
1691
|
status: "error",
|
|
1603
|
-
runId:
|
|
1604
|
-
jobType:
|
|
1692
|
+
runId: stringValue2(event.runId || event.deployRequestId),
|
|
1693
|
+
jobType: stringValue2(event.jobType || "run"),
|
|
1605
1694
|
completedAt: timestamp,
|
|
1606
1695
|
lastEventAt: timestamp,
|
|
1607
|
-
errorMessage:
|
|
1696
|
+
errorMessage: stringValue2(event.error || event.code || "Unknown error")
|
|
1608
1697
|
}) : state.agentRunState,
|
|
1609
1698
|
activity: "Error",
|
|
1610
1699
|
errors: appendError(state.errors, jobLabel(event), event.error || event.code || "Unknown error")
|
|
@@ -1622,8 +1711,8 @@ function applyServeEvent(state, event) {
|
|
|
1622
1711
|
}
|
|
1623
1712
|
if (event.type === "process_exit" || event.type === "daemon_exit") {
|
|
1624
1713
|
const timestamp = event.timestamp || (/* @__PURE__ */ new Date()).toISOString();
|
|
1625
|
-
const message =
|
|
1626
|
-
const relativePath =
|
|
1714
|
+
const message = stringValue2(event.message || processExitMessage(event));
|
|
1715
|
+
const relativePath = stringValue2(event.relativePath || "");
|
|
1627
1716
|
const runState = failRunningAgents(state.agentRunState, message, timestamp, relativePath);
|
|
1628
1717
|
return {
|
|
1629
1718
|
...next,
|
|
@@ -1678,7 +1767,7 @@ function mergeAgentLogRecords(agentLogs, records = []) {
|
|
|
1678
1767
|
let next = agentLogs || {};
|
|
1679
1768
|
for (const record of records) {
|
|
1680
1769
|
if (!record || typeof record !== "object") continue;
|
|
1681
|
-
const relativePath =
|
|
1770
|
+
const relativePath = stringValue2(record.relativePath || ".");
|
|
1682
1771
|
const entry = record.entry && typeof record.entry === "object" ? record.entry : record;
|
|
1683
1772
|
const existing = next[relativePath] || [];
|
|
1684
1773
|
next = {
|
|
@@ -1693,13 +1782,13 @@ function agentRunStateFromLogs(agentLogs, { includeRunning = true } = {}) {
|
|
|
1693
1782
|
for (const [relativePath, entries] of Object.entries(agentLogs || {})) {
|
|
1694
1783
|
for (const entry of entries || []) {
|
|
1695
1784
|
const timestamp = entry.timestamp || (/* @__PURE__ */ new Date()).toISOString();
|
|
1696
|
-
const runId =
|
|
1785
|
+
const runId = stringValue2(entry.runId);
|
|
1697
1786
|
const current = next[relativePath] || {};
|
|
1698
1787
|
if (entry.eventType === "job_started") {
|
|
1699
1788
|
next = setAgentRunState(next, relativePath, {
|
|
1700
1789
|
status: includeRunning ? "running" : current.status,
|
|
1701
1790
|
runId,
|
|
1702
|
-
jobType: entry.jobType ?
|
|
1791
|
+
jobType: entry.jobType ? stringValue2(entry.jobType) : current.jobType,
|
|
1703
1792
|
startedAt: timestamp,
|
|
1704
1793
|
lastEventAt: timestamp
|
|
1705
1794
|
});
|
|
@@ -1710,10 +1799,10 @@ function agentRunStateFromLogs(agentLogs, { includeRunning = true } = {}) {
|
|
|
1710
1799
|
next = setAgentRunState(next, relativePath, {
|
|
1711
1800
|
status: failed ? "error" : "completed",
|
|
1712
1801
|
runId,
|
|
1713
|
-
jobType: entry.jobType ?
|
|
1802
|
+
jobType: entry.jobType ? stringValue2(entry.jobType) : current.jobType,
|
|
1714
1803
|
completedAt: timestamp,
|
|
1715
1804
|
lastEventAt: timestamp,
|
|
1716
|
-
errorMessage: failed ? formatErrorMessage(entry.error) || current.errorMessage ||
|
|
1805
|
+
errorMessage: failed ? formatErrorMessage(entry.error) || current.errorMessage || stringValue2(entry.message || "Job failed") : null
|
|
1717
1806
|
});
|
|
1718
1807
|
continue;
|
|
1719
1808
|
}
|
|
@@ -1721,10 +1810,10 @@ function agentRunStateFromLogs(agentLogs, { includeRunning = true } = {}) {
|
|
|
1721
1810
|
next = setAgentRunState(next, relativePath, {
|
|
1722
1811
|
status: "error",
|
|
1723
1812
|
runId,
|
|
1724
|
-
jobType: entry.jobType ?
|
|
1813
|
+
jobType: entry.jobType ? stringValue2(entry.jobType) : current.jobType,
|
|
1725
1814
|
completedAt: timestamp,
|
|
1726
1815
|
lastEventAt: timestamp,
|
|
1727
|
-
errorMessage: formatErrorMessage(entry.error) ||
|
|
1816
|
+
errorMessage: formatErrorMessage(entry.error) || stringValue2(entry.message || "Job failed")
|
|
1728
1817
|
});
|
|
1729
1818
|
continue;
|
|
1730
1819
|
}
|
|
@@ -1742,34 +1831,34 @@ function serveEventAgentLogEntry(event) {
|
|
|
1742
1831
|
if (!event || typeof event !== "object") {
|
|
1743
1832
|
return null;
|
|
1744
1833
|
}
|
|
1745
|
-
const type =
|
|
1746
|
-
const relativePath =
|
|
1834
|
+
const type = stringValue2(event.type);
|
|
1835
|
+
const relativePath = stringValue2(event.relativePath || event.path || ".");
|
|
1747
1836
|
const timestamp = event.timestamp || (/* @__PURE__ */ new Date()).toISOString();
|
|
1748
1837
|
if (type === "run_log") {
|
|
1749
1838
|
return {
|
|
1750
1839
|
relativePath,
|
|
1751
1840
|
entry: {
|
|
1752
1841
|
timestamp,
|
|
1753
|
-
runId:
|
|
1754
|
-
eventType:
|
|
1755
|
-
level:
|
|
1756
|
-
message:
|
|
1757
|
-
stepId: event.stepId ?
|
|
1842
|
+
runId: stringValue2(event.runId),
|
|
1843
|
+
eventType: stringValue2(event.eventType || "log"),
|
|
1844
|
+
level: stringValue2(event.level || "info"),
|
|
1845
|
+
message: stringValue2(event.message),
|
|
1846
|
+
stepId: event.stepId ? stringValue2(event.stepId) : null,
|
|
1758
1847
|
current: event.current ?? null,
|
|
1759
1848
|
total: event.total ?? null,
|
|
1760
1849
|
success: event.success ?? null,
|
|
1761
1850
|
error: event.error ?? null,
|
|
1762
|
-
jobType: event.jobType ?
|
|
1851
|
+
jobType: event.jobType ? stringValue2(event.jobType) : null
|
|
1763
1852
|
}
|
|
1764
1853
|
};
|
|
1765
1854
|
}
|
|
1766
1855
|
if (type === "job_started") {
|
|
1767
|
-
const jobType =
|
|
1856
|
+
const jobType = stringValue2(event.jobType || "run");
|
|
1768
1857
|
return {
|
|
1769
1858
|
relativePath,
|
|
1770
1859
|
entry: {
|
|
1771
1860
|
timestamp,
|
|
1772
|
-
runId:
|
|
1861
|
+
runId: stringValue2(event.runId || event.deployRequestId),
|
|
1773
1862
|
eventType: "job_started",
|
|
1774
1863
|
level: "info",
|
|
1775
1864
|
message: `Started ${jobType}`,
|
|
@@ -1784,15 +1873,15 @@ function serveEventAgentLogEntry(event) {
|
|
|
1784
1873
|
}
|
|
1785
1874
|
if (type === "job_completed") {
|
|
1786
1875
|
const success = event.success !== false;
|
|
1787
|
-
const jobType =
|
|
1876
|
+
const jobType = stringValue2(event.jobType || "run");
|
|
1788
1877
|
return {
|
|
1789
1878
|
relativePath,
|
|
1790
1879
|
entry: {
|
|
1791
1880
|
timestamp,
|
|
1792
|
-
runId:
|
|
1881
|
+
runId: stringValue2(event.runId || event.deployRequestId),
|
|
1793
1882
|
eventType: "job_completed",
|
|
1794
1883
|
level: success ? "info" : "error",
|
|
1795
|
-
message: success ? "Completed successfully" :
|
|
1884
|
+
message: success ? "Completed successfully" : stringValue2(event.error || "Failed"),
|
|
1796
1885
|
stepId: null,
|
|
1797
1886
|
current: null,
|
|
1798
1887
|
total: null,
|
|
@@ -1807,16 +1896,16 @@ function serveEventAgentLogEntry(event) {
|
|
|
1807
1896
|
relativePath,
|
|
1808
1897
|
entry: {
|
|
1809
1898
|
timestamp,
|
|
1810
|
-
runId:
|
|
1899
|
+
runId: stringValue2(event.runId || event.deployRequestId),
|
|
1811
1900
|
eventType: "error",
|
|
1812
1901
|
level: "error",
|
|
1813
|
-
message:
|
|
1902
|
+
message: stringValue2(event.error || event.code || "Unknown error"),
|
|
1814
1903
|
stepId: null,
|
|
1815
1904
|
current: null,
|
|
1816
1905
|
total: null,
|
|
1817
1906
|
success: false,
|
|
1818
1907
|
error: event.error ?? event.code ?? null,
|
|
1819
|
-
jobType: event.jobType ?
|
|
1908
|
+
jobType: event.jobType ? stringValue2(event.jobType) : null
|
|
1820
1909
|
}
|
|
1821
1910
|
};
|
|
1822
1911
|
}
|
|
@@ -1830,7 +1919,7 @@ function serveEventAgentLogEntry(event) {
|
|
|
1830
1919
|
eventType: type,
|
|
1831
1920
|
level: event.status === "error" ? "error" : "info",
|
|
1832
1921
|
message: agentPrepareLogMessage(event),
|
|
1833
|
-
stepId:
|
|
1922
|
+
stepId: stringValue2(event.stage || type),
|
|
1834
1923
|
current: null,
|
|
1835
1924
|
total: null,
|
|
1836
1925
|
success,
|
|
@@ -1840,12 +1929,12 @@ function serveEventAgentLogEntry(event) {
|
|
|
1840
1929
|
};
|
|
1841
1930
|
}
|
|
1842
1931
|
if (type === "process_exit" || type === "daemon_exit") {
|
|
1843
|
-
const message =
|
|
1932
|
+
const message = stringValue2(event.message || processExitMessage(event));
|
|
1844
1933
|
return {
|
|
1845
1934
|
relativePath,
|
|
1846
1935
|
entry: {
|
|
1847
1936
|
timestamp,
|
|
1848
|
-
runId:
|
|
1937
|
+
runId: stringValue2(event.runId || event.sessionId || "daemon"),
|
|
1849
1938
|
eventType: type,
|
|
1850
1939
|
level: "error",
|
|
1851
1940
|
message,
|
|
@@ -1859,35 +1948,65 @@ function serveEventAgentLogEntry(event) {
|
|
|
1859
1948
|
signal: event.signal ?? null,
|
|
1860
1949
|
stderrTail: event.stderrTail ?? null
|
|
1861
1950
|
},
|
|
1862
|
-
jobType: event.jobType ?
|
|
1951
|
+
jobType: event.jobType ? stringValue2(event.jobType) : null
|
|
1863
1952
|
}
|
|
1864
1953
|
};
|
|
1865
1954
|
}
|
|
1866
1955
|
return null;
|
|
1867
1956
|
}
|
|
1957
|
+
function agentRuntimeStatus(agent, agentRunState = {}) {
|
|
1958
|
+
const path10 = stringValue2(agent?.relativePath || ".");
|
|
1959
|
+
const run2 = (agentRunState || {})[path10];
|
|
1960
|
+
const inventoryStatus = stringValue2(agent?.status);
|
|
1961
|
+
if (run2?.status === "running") {
|
|
1962
|
+
return { status: "running", label: "running", run: run2 };
|
|
1963
|
+
}
|
|
1964
|
+
if (run2?.status === "preparing") {
|
|
1965
|
+
return { status: "preparing", label: "preparing", run: run2 };
|
|
1966
|
+
}
|
|
1967
|
+
if (run2?.status === "error" || inventoryStatus === "error") {
|
|
1968
|
+
return { status: "error", label: "error", run: run2 };
|
|
1969
|
+
}
|
|
1970
|
+
if (run2?.status === "disabled" || inventoryStatus === "disabled") {
|
|
1971
|
+
return {
|
|
1972
|
+
status: "disabled",
|
|
1973
|
+
label: "disabled",
|
|
1974
|
+
run: run2,
|
|
1975
|
+
disabledReason: run2?.disabledReason || agent?.disabledReason || "System dependency not met locally",
|
|
1976
|
+
resolutionHints: run2?.resolutionHints || agent?.resolutionHints || []
|
|
1977
|
+
};
|
|
1978
|
+
}
|
|
1979
|
+
if (inventoryStatus === "online") {
|
|
1980
|
+
return { status: run2?.status === "completed" ? "completed" : "ready", label: "ready", run: run2 };
|
|
1981
|
+
}
|
|
1982
|
+
if (run2?.status === "completed") {
|
|
1983
|
+
return { status: "completed", label: "ready", run: run2 };
|
|
1984
|
+
}
|
|
1985
|
+
return { status: inventoryStatus || "unknown", label: inventoryStatus || "unknown", run: run2 };
|
|
1986
|
+
}
|
|
1868
1987
|
function appendError(errors, scope, message) {
|
|
1869
|
-
return [...errors, { scope, message:
|
|
1988
|
+
return [...errors, { scope, message: stringValue2(message) }].slice(-20);
|
|
1870
1989
|
}
|
|
1871
1990
|
function serveDebugEntry(event) {
|
|
1872
1991
|
if (!event || typeof event !== "object") return null;
|
|
1873
|
-
const type =
|
|
1992
|
+
const type = stringValue2(event.type);
|
|
1874
1993
|
const timestamp = event.timestamp || event.occurredAt || (/* @__PURE__ */ new Date()).toISOString();
|
|
1875
1994
|
const base = { timestamp, eventType: type, level: debugLevel(event) };
|
|
1876
1995
|
switch (type) {
|
|
1877
1996
|
case "workspace_resolved":
|
|
1878
|
-
return { ...base, message: `workspace resolved collection=${
|
|
1997
|
+
return { ...base, message: `workspace resolved collection=${stringValue2(event.collectionId || "unknown")}` };
|
|
1879
1998
|
case "daemon_registered":
|
|
1880
1999
|
return { ...base, message: `daemon registered daemon=${shortId(event.daemonId)} cli=${shortId(event.cliSessionId)}` };
|
|
1881
2000
|
case "daemon_event_stream_connected":
|
|
1882
2001
|
return { ...base, message: `daemon event stream connected daemon=${shortId(event.daemonId)}` };
|
|
1883
2002
|
case "daemon_event_stream_lost":
|
|
1884
|
-
return { ...base, level: "warn", message: `daemon event stream lost retry=${formatSeconds(event.retryInSeconds)} error=${
|
|
2003
|
+
return { ...base, level: "warn", message: `daemon event stream lost retry=${formatSeconds(event.retryInSeconds)} error=${stringValue2(event.error || "unknown")}` };
|
|
1885
2004
|
case "daemon_wake_received":
|
|
1886
2005
|
return {
|
|
1887
2006
|
...base,
|
|
1888
2007
|
message: [
|
|
1889
|
-
`wake received source=${
|
|
1890
|
-
event.eventType ? `event=${
|
|
2008
|
+
`wake received source=${stringValue2(event.sourceEventType || "backend")}`,
|
|
2009
|
+
event.eventType ? `event=${stringValue2(event.eventType)}` : "",
|
|
1891
2010
|
event.sessionId ? `session=${shortId(event.sessionId)}` : "",
|
|
1892
2011
|
event.runId ? `run=${shortId(event.runId)}` : "",
|
|
1893
2012
|
event.localAgentRowId ? `agentRow=${shortId(event.localAgentRowId)}` : ""
|
|
@@ -1910,7 +2029,7 @@ function serveDebugEntry(event) {
|
|
|
1910
2029
|
message: `dispatch acked delivery=${shortId(event.deliveryId)} run=${shortId(event.runId || event.sessionId)} attempt=${shortId(event.attemptId)}`
|
|
1911
2030
|
};
|
|
1912
2031
|
case "job_started":
|
|
1913
|
-
return { ...base, message: `job started type=${
|
|
2032
|
+
return { ...base, message: `job started type=${stringValue2(event.jobType || "run")} run=${shortId(event.runId || event.sessionId || event.deployRequestId || event.testRunId)}` };
|
|
1914
2033
|
case "job_completed":
|
|
1915
2034
|
return {
|
|
1916
2035
|
...base,
|
|
@@ -1918,18 +2037,18 @@ function serveDebugEntry(event) {
|
|
|
1918
2037
|
message: `job completed success=${event.success !== false} run=${shortId(event.runId || event.sessionId || event.deployRequestId || event.testRunId)}${event.error ? ` error=${formatErrorMessage(event.error)}` : ""}`
|
|
1919
2038
|
};
|
|
1920
2039
|
case "backend_connection_lost":
|
|
1921
|
-
return { ...base, level: "warn", message: `backend connection lost activity=${
|
|
2040
|
+
return { ...base, level: "warn", message: `backend connection lost activity=${stringValue2(event.activity)} error=${stringValue2(event.error)}` };
|
|
1922
2041
|
case "backend_connection_restored":
|
|
1923
|
-
return { ...base, message: `backend connection restored activity=${
|
|
2042
|
+
return { ...base, message: `backend connection restored activity=${stringValue2(event.activity)}` };
|
|
1924
2043
|
case "retry_scheduled":
|
|
1925
|
-
return { ...base, level: "warn", message: `retry scheduled activity=${
|
|
2044
|
+
return { ...base, level: "warn", message: `retry scheduled activity=${stringValue2(event.activity)} delay=${formatSeconds(event.delaySeconds)}` };
|
|
1926
2045
|
case "daemon_stderr":
|
|
1927
|
-
return { ...base, level: looksLikeError(event.message || event.stderr) ? "error" : "warn", message: `stderr ${
|
|
2046
|
+
return { ...base, level: looksLikeError(event.message || event.stderr) ? "error" : "warn", message: `stderr ${stringValue2(event.message || event.stderr)}` };
|
|
1928
2047
|
case "error":
|
|
1929
|
-
return { ...base, level: "error", message: `error scope=${
|
|
2048
|
+
return { ...base, level: "error", message: `error scope=${stringValue2(event.relativePath || event.path || "daemon")} ${stringValue2(event.error || event.code || "unknown")}` };
|
|
1930
2049
|
case "process_exit":
|
|
1931
2050
|
case "daemon_exit":
|
|
1932
|
-
return { ...base, level: "error", message:
|
|
2051
|
+
return { ...base, level: "error", message: stringValue2(event.message || processExitMessage(event)) };
|
|
1933
2052
|
case "stopped":
|
|
1934
2053
|
return { ...base, message: `stopped jobsRun=${Number(event.jobsRun ?? 0)}` };
|
|
1935
2054
|
default:
|
|
@@ -1937,7 +2056,7 @@ function serveDebugEntry(event) {
|
|
|
1937
2056
|
}
|
|
1938
2057
|
}
|
|
1939
2058
|
function debugLevel(event) {
|
|
1940
|
-
if (event?.level) return
|
|
2059
|
+
if (event?.level) return stringValue2(event.level);
|
|
1941
2060
|
if (event?.type === "error" || event?.type === "process_exit" || event?.type === "daemon_exit") return "error";
|
|
1942
2061
|
return "info";
|
|
1943
2062
|
}
|
|
@@ -1945,14 +2064,14 @@ function boolText(value) {
|
|
|
1945
2064
|
return value ? "yes" : "no";
|
|
1946
2065
|
}
|
|
1947
2066
|
function shortId(value) {
|
|
1948
|
-
const text =
|
|
2067
|
+
const text = stringValue2(value);
|
|
1949
2068
|
return text ? text.slice(0, 8) : "unknown";
|
|
1950
2069
|
}
|
|
1951
2070
|
function looksLikeError(value) {
|
|
1952
|
-
return /(error|exception|traceback|failed|fatal|denied|unauthorized|timeout)/i.test(
|
|
2071
|
+
return /(error|exception|traceback|failed|fatal|denied|unauthorized|timeout)/i.test(stringValue2(value));
|
|
1953
2072
|
}
|
|
1954
2073
|
function setAgentRunState(agentRunState, relativePath, patch) {
|
|
1955
|
-
const key =
|
|
2074
|
+
const key = stringValue2(relativePath || ".");
|
|
1956
2075
|
return {
|
|
1957
2076
|
...agentRunState || {},
|
|
1958
2077
|
[key]: {
|
|
@@ -1963,9 +2082,9 @@ function setAgentRunState(agentRunState, relativePath, patch) {
|
|
|
1963
2082
|
}
|
|
1964
2083
|
function failRunningAgents(agentRunState, message, timestamp, relativePath = "") {
|
|
1965
2084
|
let next = agentRunState || {};
|
|
1966
|
-
for (const [
|
|
2085
|
+
for (const [path10, run2] of Object.entries(agentRunState || {})) {
|
|
1967
2086
|
if (run2?.status !== "running") continue;
|
|
1968
|
-
next = setAgentRunState(next,
|
|
2087
|
+
next = setAgentRunState(next, path10, {
|
|
1969
2088
|
status: "error",
|
|
1970
2089
|
completedAt: timestamp,
|
|
1971
2090
|
lastEventAt: timestamp,
|
|
@@ -1987,40 +2106,40 @@ function appendProcessExitAgentLogs(agentLogs, event, agentRunState, relativePat
|
|
|
1987
2106
|
if (relativePath) {
|
|
1988
2107
|
paths.add(relativePath);
|
|
1989
2108
|
}
|
|
1990
|
-
for (const [
|
|
2109
|
+
for (const [path10, run2] of Object.entries(agentRunState || {})) {
|
|
1991
2110
|
if (run2?.status === "running") {
|
|
1992
|
-
paths.add(
|
|
2111
|
+
paths.add(path10);
|
|
1993
2112
|
}
|
|
1994
2113
|
}
|
|
1995
2114
|
if (paths.size === 0) {
|
|
1996
2115
|
return appendAgentLog(agentLogs, event);
|
|
1997
2116
|
}
|
|
1998
2117
|
let next = agentLogs || {};
|
|
1999
|
-
for (const
|
|
2000
|
-
next = appendAgentLog(next, { ...event, relativePath:
|
|
2118
|
+
for (const path10 of paths) {
|
|
2119
|
+
next = appendAgentLog(next, { ...event, relativePath: path10 });
|
|
2001
2120
|
}
|
|
2002
2121
|
return next;
|
|
2003
2122
|
}
|
|
2004
2123
|
function reconcileInventoryRunState(agentRunState, agents, timestamp) {
|
|
2005
2124
|
let next = agentRunState || {};
|
|
2006
2125
|
for (const agent of agents || []) {
|
|
2007
|
-
const
|
|
2008
|
-
const current = next[
|
|
2126
|
+
const path10 = stringValue2(agent?.relativePath || ".");
|
|
2127
|
+
const current = next[path10];
|
|
2009
2128
|
if (agent?.status === "error") {
|
|
2010
|
-
next = setAgentRunState(next,
|
|
2129
|
+
next = setAgentRunState(next, path10, {
|
|
2011
2130
|
status: "error",
|
|
2012
2131
|
lastEventAt: timestamp || (/* @__PURE__ */ new Date()).toISOString(),
|
|
2013
|
-
errorMessage:
|
|
2132
|
+
errorMessage: stringValue2(agent.errorMessage || agent.error || "Agent setup failed")
|
|
2014
2133
|
});
|
|
2015
2134
|
} else if (agent?.status === "disabled") {
|
|
2016
|
-
next = setAgentRunState(next,
|
|
2135
|
+
next = setAgentRunState(next, path10, {
|
|
2017
2136
|
status: "disabled",
|
|
2018
2137
|
lastEventAt: timestamp || (/* @__PURE__ */ new Date()).toISOString(),
|
|
2019
|
-
disabledReason:
|
|
2138
|
+
disabledReason: stringValue2(agent.disabledReason || "System dependency not met locally"),
|
|
2020
2139
|
resolutionHints: Array.isArray(agent.resolutionHints) ? agent.resolutionHints : []
|
|
2021
2140
|
});
|
|
2022
2141
|
} else if (agent?.status === "online" && ["preparing", "error", "disabled"].includes(current?.status) && !current?.runId) {
|
|
2023
|
-
next = setAgentRunState(next,
|
|
2142
|
+
next = setAgentRunState(next, path10, {
|
|
2024
2143
|
status: "ready",
|
|
2025
2144
|
lastEventAt: timestamp || (/* @__PURE__ */ new Date()).toISOString(),
|
|
2026
2145
|
progressMessage: null,
|
|
@@ -2036,22 +2155,22 @@ function reconcileInventoryRunState(agentRunState, agents, timestamp) {
|
|
|
2036
2155
|
function normalizeStoredAgentLogEntry(entry) {
|
|
2037
2156
|
return {
|
|
2038
2157
|
timestamp: entry.timestamp || (/* @__PURE__ */ new Date()).toISOString(),
|
|
2039
|
-
runId:
|
|
2040
|
-
eventType:
|
|
2041
|
-
level:
|
|
2042
|
-
message:
|
|
2043
|
-
stepId: entry.stepId ?
|
|
2158
|
+
runId: stringValue2(entry.runId),
|
|
2159
|
+
eventType: stringValue2(entry.eventType || "log"),
|
|
2160
|
+
level: stringValue2(entry.level || "info"),
|
|
2161
|
+
message: stringValue2(entry.message),
|
|
2162
|
+
stepId: entry.stepId ? stringValue2(entry.stepId) : null,
|
|
2044
2163
|
current: entry.current ?? null,
|
|
2045
2164
|
total: entry.total ?? null,
|
|
2046
2165
|
success: entry.success ?? null,
|
|
2047
2166
|
error: entry.error ?? null,
|
|
2048
|
-
jobType: entry.jobType ?
|
|
2167
|
+
jobType: entry.jobType ? stringValue2(entry.jobType) : null
|
|
2049
2168
|
};
|
|
2050
2169
|
}
|
|
2051
2170
|
function formatErrorMessage(error) {
|
|
2052
2171
|
if (!error) return null;
|
|
2053
|
-
if (typeof error === "object") return
|
|
2054
|
-
return
|
|
2172
|
+
if (typeof error === "object") return stringValue2(error.message || "error");
|
|
2173
|
+
return stringValue2(error);
|
|
2055
2174
|
}
|
|
2056
2175
|
function findNewAgents(previous, next) {
|
|
2057
2176
|
const seen = new Set(previous.map(agentKey).filter(Boolean));
|
|
@@ -2064,15 +2183,15 @@ function agentKey(agent) {
|
|
|
2064
2183
|
if (!agent || typeof agent !== "object") {
|
|
2065
2184
|
return "";
|
|
2066
2185
|
}
|
|
2067
|
-
return
|
|
2186
|
+
return stringValue2(agent.localAgentId || agent.relativePath || agent.path || agent.manifestName || agent.id);
|
|
2068
2187
|
}
|
|
2069
2188
|
function agentLabel(event) {
|
|
2070
|
-
return
|
|
2189
|
+
return stringValue2(event.relativePath || event.path || ".");
|
|
2071
2190
|
}
|
|
2072
2191
|
function jobLabel(event) {
|
|
2073
|
-
return
|
|
2192
|
+
return stringValue2(event.runId || event.deployRequestId || event.relativePath || event.jobType || "job");
|
|
2074
2193
|
}
|
|
2075
|
-
function
|
|
2194
|
+
function stringValue2(value) {
|
|
2076
2195
|
return value == null ? "" : String(value);
|
|
2077
2196
|
}
|
|
2078
2197
|
function processExitMessage(event) {
|
|
@@ -2085,54 +2204,23 @@ function processExitMessage(event) {
|
|
|
2085
2204
|
return "Agent serve exited";
|
|
2086
2205
|
}
|
|
2087
2206
|
function agentPrepareLogMessage(event) {
|
|
2088
|
-
if (event.type === "agent_prepare_started") {
|
|
2089
|
-
return "Preparing agent";
|
|
2090
|
-
}
|
|
2091
|
-
if (event.type === "agent_prepare_completed") {
|
|
2092
|
-
if (event.status === "error") return stringValue(event.error || event.errorMessage || "Agent setup failed");
|
|
2093
|
-
if (event.status === "disabled") return stringValue(event.warning || event.disabledReason || "Agent disabled locally");
|
|
2094
|
-
return "Agent ready";
|
|
2095
|
-
}
|
|
2096
|
-
return stringValue(event.message || event.stage || "Preparing agent");
|
|
2097
|
-
}
|
|
2098
|
-
function textTail(value, limit = 800) {
|
|
2099
|
-
const text = String(value || "").trim().replace(/\s+/g, " ");
|
|
2100
|
-
return text.length > limit ? text.slice(-limit) : text;
|
|
2101
|
-
}
|
|
2102
|
-
function formatSeconds(value) {
|
|
2103
|
-
const numeric = Number(value);
|
|
2104
|
-
return Number.isFinite(numeric) ? `${numeric.toFixed(1)}s` : "";
|
|
2105
|
-
}
|
|
2106
|
-
|
|
2107
|
-
// src/serve-process.js
|
|
2108
|
-
import { spawn as spawn2 } from "node:child_process";
|
|
2109
|
-
async function spawnLocalServeEventStream({
|
|
2110
|
-
agentsDir = "./agents",
|
|
2111
|
-
collectionId = "",
|
|
2112
|
-
env = process.env,
|
|
2113
|
-
platform = process.platform,
|
|
2114
|
-
onProgress = null
|
|
2115
|
-
} = {}) {
|
|
2116
|
-
const invocation = await preparePythonVendianInvocation(buildLocalServeEventStreamArgs({ agentsDir, collectionId }), { env, platform, onProgress });
|
|
2117
|
-
return spawn2(invocation.command, invocation.args, {
|
|
2118
|
-
env: invocation.env,
|
|
2119
|
-
stdio: ["ignore", "pipe", "pipe"],
|
|
2120
|
-
shell: false
|
|
2121
|
-
});
|
|
2122
|
-
}
|
|
2123
|
-
function buildLocalServeEventStreamArgs({ agentsDir = "./agents", collectionId = "" } = {}) {
|
|
2124
|
-
const args = [
|
|
2125
|
-
"cloud",
|
|
2126
|
-
"local",
|
|
2127
|
-
"serve",
|
|
2128
|
-
"--agents-dir",
|
|
2129
|
-
agentsDir || "./agents",
|
|
2130
|
-
"--event-stream"
|
|
2131
|
-
];
|
|
2132
|
-
if (collectionId) {
|
|
2133
|
-
args.push("--collection-id", collectionId);
|
|
2207
|
+
if (event.type === "agent_prepare_started") {
|
|
2208
|
+
return "Preparing agent";
|
|
2134
2209
|
}
|
|
2135
|
-
|
|
2210
|
+
if (event.type === "agent_prepare_completed") {
|
|
2211
|
+
if (event.status === "error") return stringValue2(event.error || event.errorMessage || "Agent setup failed");
|
|
2212
|
+
if (event.status === "disabled") return stringValue2(event.warning || event.disabledReason || "Agent disabled locally");
|
|
2213
|
+
return "Agent ready";
|
|
2214
|
+
}
|
|
2215
|
+
return stringValue2(event.message || event.stage || "Preparing agent");
|
|
2216
|
+
}
|
|
2217
|
+
function textTail(value, limit = 800) {
|
|
2218
|
+
const text = String(value || "").trim().replace(/\s+/g, " ");
|
|
2219
|
+
return text.length > limit ? text.slice(-limit) : text;
|
|
2220
|
+
}
|
|
2221
|
+
function formatSeconds(value) {
|
|
2222
|
+
const numeric = Number(value);
|
|
2223
|
+
return Number.isFinite(numeric) ? `${numeric.toFixed(1)}s` : "";
|
|
2136
2224
|
}
|
|
2137
2225
|
|
|
2138
2226
|
// src/serve-log-store.js
|
|
@@ -2142,12 +2230,12 @@ import path7 from "node:path";
|
|
|
2142
2230
|
var MAX_EVENTS = 5e3;
|
|
2143
2231
|
var MAX_RUNS = 50;
|
|
2144
2232
|
function createServeLogStore({
|
|
2145
|
-
agentsDir = "./agents",
|
|
2146
|
-
collectionId = "",
|
|
2233
|
+
agentsDir: agentsDir2 = "./agents",
|
|
2234
|
+
collectionId: collectionId2 = "",
|
|
2147
2235
|
env = process.env,
|
|
2148
2236
|
platform = process.platform
|
|
2149
2237
|
} = {}) {
|
|
2150
|
-
const file = serveLogFilePath({ agentsDir, collectionId, env, platform });
|
|
2238
|
+
const file = serveLogFilePath({ agentsDir: agentsDir2, collectionId: collectionId2, env, platform });
|
|
2151
2239
|
let appendCount = 0;
|
|
2152
2240
|
return {
|
|
2153
2241
|
file,
|
|
@@ -2162,8 +2250,8 @@ function createServeLogStore({
|
|
|
2162
2250
|
fs10.mkdirSync(path7.dirname(file), { recursive: true });
|
|
2163
2251
|
const record = {
|
|
2164
2252
|
version: 1,
|
|
2165
|
-
collectionId:
|
|
2166
|
-
agentsDir: String(
|
|
2253
|
+
collectionId: collectionId2 || "",
|
|
2254
|
+
agentsDir: String(agentsDir2 || "./agents"),
|
|
2167
2255
|
relativePath: normalized.relativePath,
|
|
2168
2256
|
entry: normalized.entry
|
|
2169
2257
|
};
|
|
@@ -2181,15 +2269,15 @@ function createServeLogStore({
|
|
|
2181
2269
|
};
|
|
2182
2270
|
}
|
|
2183
2271
|
function serveLogFilePath({
|
|
2184
|
-
agentsDir = "./agents",
|
|
2185
|
-
collectionId = "",
|
|
2272
|
+
agentsDir: agentsDir2 = "./agents",
|
|
2273
|
+
collectionId: collectionId2 = "",
|
|
2186
2274
|
env = process.env,
|
|
2187
2275
|
platform = process.platform
|
|
2188
2276
|
} = {}) {
|
|
2189
2277
|
const root = vendianHome(env, platform);
|
|
2190
|
-
const resolvedAgentsDir = path7.resolve(String(
|
|
2191
|
-
const hash = crypto2.createHash("sha256").update(`${
|
|
2192
|
-
return path7.join(root, "serve-logs", `${safePathPart(
|
|
2278
|
+
const resolvedAgentsDir = path7.resolve(String(agentsDir2 || "./agents"));
|
|
2279
|
+
const hash = crypto2.createHash("sha256").update(`${collectionId2 || "default"}\0${resolvedAgentsDir}`).digest("hex").slice(0, 16);
|
|
2280
|
+
return path7.join(root, "serve-logs", `${safePathPart(collectionId2 || "default")}-${hash}.jsonl`);
|
|
2193
2281
|
}
|
|
2194
2282
|
function readServeLogRecords(file) {
|
|
2195
2283
|
if (!file || !fs10.existsSync(file)) {
|
|
@@ -2243,57 +2331,605 @@ function safePathPart(value) {
|
|
|
2243
2331
|
return String(value || "default").replace(/[^a-zA-Z0-9_.-]+/g, "-").replace(/^-+|-+$/g, "").slice(0, 80) || "default";
|
|
2244
2332
|
}
|
|
2245
2333
|
|
|
2246
|
-
// src/
|
|
2247
|
-
|
|
2334
|
+
// src/serve-process.js
|
|
2335
|
+
import { spawn as spawn2 } from "node:child_process";
|
|
2336
|
+
async function spawnLocalServeEventStream({
|
|
2337
|
+
agentsDir: agentsDir2 = "./agents",
|
|
2338
|
+
collectionId: collectionId2 = "",
|
|
2248
2339
|
env = process.env,
|
|
2249
2340
|
platform = process.platform,
|
|
2250
|
-
|
|
2251
|
-
run: run2 = runCapture,
|
|
2252
|
-
onProgress = null,
|
|
2253
|
-
timeoutMs = 2e4
|
|
2341
|
+
onProgress = null
|
|
2254
2342
|
} = {}) {
|
|
2255
|
-
const
|
|
2256
|
-
|
|
2257
|
-
|
|
2258
|
-
|
|
2259
|
-
|
|
2260
|
-
platform,
|
|
2261
|
-
onProgress: progress,
|
|
2262
|
-
skipAutoUpdate: true
|
|
2343
|
+
const invocation = await preparePythonVendianInvocation(buildLocalServeEventStreamArgs({ agentsDir: agentsDir2, collectionId: collectionId2 }), { env, platform, onProgress });
|
|
2344
|
+
return spawn2(invocation.command, invocation.args, {
|
|
2345
|
+
env: invocation.env,
|
|
2346
|
+
stdio: ["ignore", "pipe", "pipe"],
|
|
2347
|
+
shell: false
|
|
2263
2348
|
});
|
|
2264
|
-
|
|
2265
|
-
|
|
2266
|
-
|
|
2349
|
+
}
|
|
2350
|
+
function buildLocalServeEventStreamArgs({ agentsDir: agentsDir2 = "./agents", collectionId: collectionId2 = "" } = {}) {
|
|
2351
|
+
const args = [
|
|
2352
|
+
"cloud",
|
|
2353
|
+
"local",
|
|
2354
|
+
"serve",
|
|
2355
|
+
"--agents-dir",
|
|
2356
|
+
agentsDir2 || "./agents",
|
|
2357
|
+
"--event-stream"
|
|
2358
|
+
];
|
|
2359
|
+
if (collectionId2) {
|
|
2360
|
+
args.push("--collection-id", collectionId2);
|
|
2361
|
+
}
|
|
2362
|
+
return args;
|
|
2363
|
+
}
|
|
2364
|
+
|
|
2365
|
+
// src/version.js
|
|
2366
|
+
var CLI_VERSION = true ? "0.0.36" : process.env.npm_package_version || "0.0.0-dev";
|
|
2367
|
+
|
|
2368
|
+
// src/dev-server.js
|
|
2369
|
+
var __dirname = path8.dirname(fileURLToPath(import.meta.url));
|
|
2370
|
+
var UI_DIR = fs11.existsSync(path8.join(__dirname, "dev-ui")) ? path8.join(__dirname, "dev-ui") : fs11.existsSync(path8.join(__dirname, "src", "dev-ui")) ? path8.join(__dirname, "src", "dev-ui") : path8.join(__dirname, "dev-ui");
|
|
2371
|
+
var serveChild = null;
|
|
2372
|
+
var serveState = initialServeState();
|
|
2373
|
+
var serveLogs = [];
|
|
2374
|
+
var logStore = null;
|
|
2375
|
+
var agentsDir = "";
|
|
2376
|
+
var collectionId = "";
|
|
2377
|
+
async function startDevServer({
|
|
2378
|
+
port = 3859,
|
|
2379
|
+
agents = "",
|
|
2380
|
+
noOpen = false,
|
|
2381
|
+
env = process.env,
|
|
2382
|
+
platform = process.platform
|
|
2383
|
+
} = {}) {
|
|
2384
|
+
if (agents) {
|
|
2385
|
+
agentsDir = path8.resolve(agents);
|
|
2386
|
+
} else {
|
|
2387
|
+
const candidates = findAgentDirectoryCandidates();
|
|
2388
|
+
agentsDir = candidates[0]?.absolutePath || path8.resolve("./agents");
|
|
2389
|
+
}
|
|
2390
|
+
const server = http2.createServer(handleRequest);
|
|
2391
|
+
server.listen(port, "127.0.0.1", () => {
|
|
2392
|
+
const url = `http://localhost:${port}`;
|
|
2393
|
+
console.log("");
|
|
2394
|
+
console.log(" \x1B[1;36m\u26A1 Vendian Dev UI\x1B[0m");
|
|
2395
|
+
console.log(` \x1B[90m${"\u2500".repeat(44)}\x1B[0m`);
|
|
2396
|
+
console.log(` \x1B[1mLocal:\x1B[0m ${url}`);
|
|
2397
|
+
console.log(` \x1B[1mAgents:\x1B[0m ${agentsDir}`);
|
|
2398
|
+
console.log(` \x1B[1mVersion:\x1B[0m ${CLI_VERSION}`);
|
|
2399
|
+
console.log(` \x1B[90m${"\u2500".repeat(44)}\x1B[0m`);
|
|
2400
|
+
console.log(" \x1B[90mPress Ctrl+C to stop.\x1B[0m");
|
|
2401
|
+
console.log("");
|
|
2402
|
+
if (!noOpen) {
|
|
2403
|
+
setTimeout(() => openUrl(url), 400);
|
|
2404
|
+
}
|
|
2405
|
+
});
|
|
2406
|
+
server.on("error", (err) => {
|
|
2407
|
+
if (err.code === "EADDRINUSE") {
|
|
2408
|
+
console.error(`
|
|
2409
|
+
\x1B[31mPort ${port} is already in use.\x1B[0m Try: vendian dev --port ${port + 1}
|
|
2410
|
+
`);
|
|
2411
|
+
} else {
|
|
2412
|
+
console.error(`
|
|
2413
|
+
\x1B[31m${err.message}\x1B[0m
|
|
2414
|
+
`);
|
|
2415
|
+
}
|
|
2416
|
+
process.exit(1);
|
|
2417
|
+
});
|
|
2418
|
+
process.on("SIGINT", () => shutdown(server));
|
|
2419
|
+
process.on("SIGTERM", () => shutdown(server));
|
|
2420
|
+
}
|
|
2421
|
+
function shutdown(server) {
|
|
2422
|
+
console.log("\n \x1B[90mShutting down...\x1B[0m");
|
|
2423
|
+
if (serveChild && !serveChild.killed) {
|
|
2424
|
+
serveChild.kill("SIGTERM");
|
|
2425
|
+
}
|
|
2426
|
+
server.close();
|
|
2427
|
+
process.exit(0);
|
|
2428
|
+
}
|
|
2429
|
+
function handleRequest(req, res) {
|
|
2430
|
+
const parsed = new URL(req.url, `http://${req.headers.host}`);
|
|
2431
|
+
const pathname = parsed.pathname;
|
|
2432
|
+
res.setHeader("Access-Control-Allow-Origin", "*");
|
|
2433
|
+
res.setHeader("Access-Control-Allow-Methods", "GET, POST, OPTIONS");
|
|
2434
|
+
res.setHeader("Access-Control-Allow-Headers", "Content-Type");
|
|
2435
|
+
if (req.method === "OPTIONS") {
|
|
2436
|
+
res.writeHead(204);
|
|
2437
|
+
res.end();
|
|
2438
|
+
return;
|
|
2439
|
+
}
|
|
2440
|
+
if (pathname.startsWith("/api/")) {
|
|
2441
|
+
return handleApi(req, res, pathname, parsed);
|
|
2442
|
+
}
|
|
2443
|
+
serveStatic(req, res, pathname);
|
|
2444
|
+
}
|
|
2445
|
+
var MIME_TYPES = {
|
|
2446
|
+
".html": "text/html",
|
|
2447
|
+
".css": "text/css",
|
|
2448
|
+
".js": "application/javascript",
|
|
2449
|
+
".json": "application/json",
|
|
2450
|
+
".svg": "image/svg+xml",
|
|
2451
|
+
".png": "image/png",
|
|
2452
|
+
".ico": "image/x-icon"
|
|
2453
|
+
};
|
|
2454
|
+
function serveStatic(req, res, pathname) {
|
|
2455
|
+
let filePath = path8.join(UI_DIR, pathname === "/" ? "index.html" : pathname);
|
|
2456
|
+
if (!fs11.existsSync(filePath) || fs11.statSync(filePath).isDirectory()) {
|
|
2457
|
+
filePath = path8.join(UI_DIR, "index.html");
|
|
2458
|
+
}
|
|
2459
|
+
if (!fs11.existsSync(filePath)) {
|
|
2460
|
+
res.writeHead(404);
|
|
2461
|
+
res.end("Not Found");
|
|
2462
|
+
return;
|
|
2463
|
+
}
|
|
2464
|
+
const ext = path8.extname(filePath);
|
|
2465
|
+
const contentType = MIME_TYPES[ext] || "application/octet-stream";
|
|
2466
|
+
const content = fs11.readFileSync(filePath);
|
|
2467
|
+
res.writeHead(200, { "Content-Type": contentType });
|
|
2468
|
+
res.end(content);
|
|
2469
|
+
}
|
|
2470
|
+
async function handleApi(req, res, pathname, parsed) {
|
|
2471
|
+
try {
|
|
2472
|
+
if (req.method === "GET") {
|
|
2473
|
+
switch (pathname) {
|
|
2474
|
+
case "/api/status":
|
|
2475
|
+
return apiStatus(req, res);
|
|
2476
|
+
case "/api/agents":
|
|
2477
|
+
return apiAgents(req, res);
|
|
2478
|
+
case "/api/logs":
|
|
2479
|
+
return apiLogs(req, res, parsed);
|
|
2480
|
+
case "/api/serve/state":
|
|
2481
|
+
return apiServeState(req, res);
|
|
2482
|
+
case "/api/auth":
|
|
2483
|
+
return apiAuth(req, res);
|
|
2484
|
+
case "/api/workspaces":
|
|
2485
|
+
return await apiWorkspaces(req, res);
|
|
2486
|
+
default:
|
|
2487
|
+
return jsonResponse(res, { error: "not found" }, 404);
|
|
2488
|
+
}
|
|
2489
|
+
}
|
|
2490
|
+
if (req.method === "POST") {
|
|
2491
|
+
const body = await readBody(req);
|
|
2492
|
+
switch (pathname) {
|
|
2493
|
+
case "/api/validate":
|
|
2494
|
+
return await apiValidate(req, res, body);
|
|
2495
|
+
case "/api/serve/start":
|
|
2496
|
+
return await apiServeStart(req, res, body);
|
|
2497
|
+
case "/api/serve/stop":
|
|
2498
|
+
return apiServeStop(req, res);
|
|
2499
|
+
case "/api/create":
|
|
2500
|
+
return await apiCreate(req, res, body);
|
|
2501
|
+
case "/api/auth/switch":
|
|
2502
|
+
return await apiAuthSwitch(req, res, body);
|
|
2503
|
+
default:
|
|
2504
|
+
return jsonResponse(res, { error: "not found" }, 404);
|
|
2505
|
+
}
|
|
2506
|
+
}
|
|
2507
|
+
jsonResponse(res, { error: "method not allowed" }, 405);
|
|
2508
|
+
} catch (err) {
|
|
2509
|
+
jsonResponse(res, { error: err.message || "Internal error" }, 500);
|
|
2510
|
+
}
|
|
2511
|
+
}
|
|
2512
|
+
function apiStatus(req, res) {
|
|
2513
|
+
const serving = serveChild !== null && !serveChild.killed;
|
|
2514
|
+
jsonResponse(res, {
|
|
2515
|
+
serving,
|
|
2516
|
+
agentsDir,
|
|
2517
|
+
collectionId,
|
|
2518
|
+
connected: serveState.connected,
|
|
2519
|
+
activity: serveState.activity,
|
|
2520
|
+
agentCount: serveState.agents.length,
|
|
2521
|
+
jobsRun: serveState.jobsRun,
|
|
2522
|
+
version: CLI_VERSION
|
|
2523
|
+
});
|
|
2524
|
+
}
|
|
2525
|
+
function apiServeState(req, res) {
|
|
2526
|
+
const serving = serveChild !== null && !serveChild.killed;
|
|
2527
|
+
const agentStatuses = serveState.agents.map((agent) => {
|
|
2528
|
+
const relPath = agent.relativePath || ".";
|
|
2529
|
+
const runtime = agentRuntimeStatus(agent, serveState.agentRunState);
|
|
2530
|
+
const runState = (serveState.agentRunState || {})[relPath] || {};
|
|
2267
2531
|
return {
|
|
2268
|
-
|
|
2269
|
-
|
|
2270
|
-
|
|
2532
|
+
name: agent.manifestName || agent.relativePath || "Agent",
|
|
2533
|
+
relativePath: relPath,
|
|
2534
|
+
status: runtime.status,
|
|
2535
|
+
errorMessage: agent.errorMessage || runState.errorMessage || null,
|
|
2536
|
+
progressMessage: runState.progressMessage || null,
|
|
2537
|
+
lastEventAt: runState.lastEventAt || null,
|
|
2538
|
+
runId: runState.runId || null,
|
|
2539
|
+
jobType: runState.jobType || null
|
|
2540
|
+
};
|
|
2541
|
+
});
|
|
2542
|
+
const activity = [];
|
|
2543
|
+
for (const [agentPath, entries] of Object.entries(serveState.agentLogs || {})) {
|
|
2544
|
+
const agent = serveState.agents.find((a) => (a.relativePath || ".") === agentPath);
|
|
2545
|
+
const name = agent?.manifestName || agentPath;
|
|
2546
|
+
for (const e of (entries || []).slice(-10)) {
|
|
2547
|
+
activity.push({ ...e, _agentName: name, _agentPath: agentPath });
|
|
2548
|
+
}
|
|
2549
|
+
}
|
|
2550
|
+
for (const event of (serveState.logs || []).slice(-30)) {
|
|
2551
|
+
if (!event || !event.type) continue;
|
|
2552
|
+
const ts = event.timestamp || "";
|
|
2553
|
+
const type = event.type;
|
|
2554
|
+
if ([
|
|
2555
|
+
"daemon_registered",
|
|
2556
|
+
"daemon_event_stream_connected",
|
|
2557
|
+
"inventory_synced",
|
|
2558
|
+
"agent_discovery_completed",
|
|
2559
|
+
"backend_connection_lost",
|
|
2560
|
+
"backend_connection_restored",
|
|
2561
|
+
"retry_scheduled",
|
|
2562
|
+
"daemon_event_stream_lost"
|
|
2563
|
+
].includes(type)) {
|
|
2564
|
+
activity.push({
|
|
2565
|
+
timestamp: ts,
|
|
2566
|
+
eventType: type,
|
|
2567
|
+
level: type.includes("lost") || type.includes("retry") ? "warn" : "info",
|
|
2568
|
+
message: formatDaemonEvent(event),
|
|
2569
|
+
_agentName: "System",
|
|
2570
|
+
_agentPath: ""
|
|
2571
|
+
});
|
|
2572
|
+
}
|
|
2573
|
+
}
|
|
2574
|
+
activity.sort((a, b) => (a.timestamp || "").localeCompare(b.timestamp || ""));
|
|
2575
|
+
jsonResponse(res, {
|
|
2576
|
+
serving,
|
|
2577
|
+
connected: serveState.connected,
|
|
2578
|
+
stopped: serveState.stopped,
|
|
2579
|
+
activity: serveState.activity,
|
|
2580
|
+
agentCount: serveState.agents.length,
|
|
2581
|
+
jobsRun: serveState.jobsRun,
|
|
2582
|
+
agents: agentStatuses,
|
|
2583
|
+
recentActivity: activity.slice(-40),
|
|
2584
|
+
errors: serveState.errors || []
|
|
2585
|
+
});
|
|
2586
|
+
}
|
|
2587
|
+
function apiAgents(req, res) {
|
|
2588
|
+
const folders = findAgentFolders(agentsDir);
|
|
2589
|
+
const agents = folders.map((folder) => {
|
|
2590
|
+
const manifestPath = path8.join(folder.absolutePath, "manifest.yaml");
|
|
2591
|
+
const info = parseManifestBasic(manifestPath);
|
|
2592
|
+
const relPath = path8.relative(agentsDir, folder.absolutePath).replace(/\\/g, "/") || folder.name;
|
|
2593
|
+
const inventoryAgent = serveState.agents.find(
|
|
2594
|
+
(a) => (a.relativePath || ".") === relPath || a.relativePath === folder.name
|
|
2595
|
+
);
|
|
2596
|
+
const runtime = inventoryAgent ? agentRuntimeStatus(inventoryAgent, serveState.agentRunState) : { status: "unknown" };
|
|
2597
|
+
return {
|
|
2598
|
+
path: folder.absolutePath,
|
|
2599
|
+
relativePath: relPath,
|
|
2600
|
+
name: folder.name,
|
|
2601
|
+
displayName: info.name || folder.name,
|
|
2602
|
+
id: info.id || "",
|
|
2603
|
+
description: info.description || "",
|
|
2604
|
+
version: info.version || "",
|
|
2605
|
+
triggers: info.triggers || [],
|
|
2606
|
+
credentials: info.credentials || [],
|
|
2607
|
+
triggerCount: info.triggerCount || (info.triggers || []).length,
|
|
2608
|
+
credentialCount: info.credentialCount || (info.credentials || []).length,
|
|
2609
|
+
hasAgentPy: fs11.existsSync(path8.join(folder.absolutePath, "agent.py")),
|
|
2610
|
+
hasRequirements: fs11.existsSync(path8.join(folder.absolutePath, "requirements.txt")),
|
|
2611
|
+
runtimeStatus: runtime.status
|
|
2612
|
+
};
|
|
2613
|
+
});
|
|
2614
|
+
jsonResponse(res, { agents, agentsDir });
|
|
2615
|
+
}
|
|
2616
|
+
function apiLogs(req, res, parsed) {
|
|
2617
|
+
const since = parseInt(parsed.searchParams.get("since") || "0", 10);
|
|
2618
|
+
const lines = serveLogs.slice(since);
|
|
2619
|
+
jsonResponse(res, { logs: lines, total: serveLogs.length, since });
|
|
2620
|
+
}
|
|
2621
|
+
function apiAuth(req, res) {
|
|
2622
|
+
const active = activeCloudAuthStatus();
|
|
2623
|
+
const activeUrl = (active.activeApiUrl || active.apiUrl || "").replace(/\/$/, "");
|
|
2624
|
+
const backends = Object.entries(BACKEND_TARGETS).filter(([key]) => !["localhost", "production"].includes(key)).map(([key, url]) => {
|
|
2625
|
+
const canonicalUrl = url.replace(/\/$/, "");
|
|
2626
|
+
const status = cloudAuthStatus({ backend: key, env: { ...process.env, VENDIAN_API_URL: "" } });
|
|
2627
|
+
const isActive = Boolean(activeUrl && canonicalUrl === activeUrl && active.authenticated);
|
|
2628
|
+
return {
|
|
2629
|
+
key,
|
|
2630
|
+
url,
|
|
2631
|
+
authenticated: status.authenticated,
|
|
2632
|
+
email: status.email || null,
|
|
2633
|
+
active: isActive
|
|
2271
2634
|
};
|
|
2635
|
+
});
|
|
2636
|
+
jsonResponse(res, {
|
|
2637
|
+
authenticated: active.authenticated,
|
|
2638
|
+
email: active.email || null,
|
|
2639
|
+
apiUrl: active.apiUrl || null,
|
|
2640
|
+
backends
|
|
2641
|
+
});
|
|
2642
|
+
}
|
|
2643
|
+
async function apiWorkspaces(req, res) {
|
|
2644
|
+
const result = await listCloudWorkspaces({ timeoutMs: 15e3 });
|
|
2645
|
+
jsonResponse(res, result);
|
|
2646
|
+
}
|
|
2647
|
+
async function apiAuthSwitch(req, res, body) {
|
|
2648
|
+
const backend = body.backend;
|
|
2649
|
+
if (!backend || !BACKEND_TARGETS[backend]) {
|
|
2650
|
+
return jsonResponse(res, { ok: false, error: `Unknown backend: ${backend}` }, 400);
|
|
2272
2651
|
}
|
|
2273
2652
|
try {
|
|
2274
|
-
|
|
2275
|
-
|
|
2276
|
-
|
|
2277
|
-
|
|
2653
|
+
const { activateCloudProfile: activateCloudProfile2 } = await Promise.resolve().then(() => (init_auth(), auth_exports));
|
|
2654
|
+
const result = activateCloudProfile2({ backend });
|
|
2655
|
+
if (result?.activated) {
|
|
2656
|
+
jsonResponse(res, { ok: true, switched: true, backend });
|
|
2657
|
+
} else {
|
|
2658
|
+
jsonResponse(res, { ok: false, needsLogin: true, backend, url: BACKEND_TARGETS[backend] });
|
|
2659
|
+
}
|
|
2660
|
+
} catch (err) {
|
|
2661
|
+
jsonResponse(res, { ok: false, error: err.message });
|
|
2278
2662
|
}
|
|
2279
2663
|
}
|
|
2280
|
-
function
|
|
2281
|
-
const
|
|
2282
|
-
const
|
|
2283
|
-
|
|
2284
|
-
|
|
2285
|
-
|
|
2286
|
-
|
|
2287
|
-
|
|
2664
|
+
async function apiValidate(req, res, body) {
|
|
2665
|
+
const targetPath = body.path || agentsDir;
|
|
2666
|
+
const invocation = await preparePythonVendianInvocation(
|
|
2667
|
+
["validate", targetPath, "--json", "--no-runtime"],
|
|
2668
|
+
{ skipAutoUpdate: true }
|
|
2669
|
+
);
|
|
2670
|
+
const result = await runCaptureAsync(invocation.command, invocation.args, { env: invocation.env });
|
|
2671
|
+
try {
|
|
2672
|
+
const parsed = JSON.parse(result.stdout);
|
|
2673
|
+
jsonResponse(res, parsed);
|
|
2674
|
+
} catch {
|
|
2675
|
+
jsonResponse(res, {
|
|
2676
|
+
valid: false,
|
|
2677
|
+
errors: result.stderr ? [result.stderr.trim()] : ["Validation failed"],
|
|
2678
|
+
stdout: result.stdout
|
|
2679
|
+
});
|
|
2680
|
+
}
|
|
2288
2681
|
}
|
|
2289
|
-
function
|
|
2290
|
-
|
|
2291
|
-
|
|
2292
|
-
|
|
2682
|
+
async function apiServeStart(req, res, body) {
|
|
2683
|
+
if (serveChild && !serveChild.killed) {
|
|
2684
|
+
return jsonResponse(res, { ok: false, error: "Already serving" });
|
|
2685
|
+
}
|
|
2686
|
+
const targetDir = body.agentsDir || agentsDir;
|
|
2687
|
+
const targetCollection = body.collectionId || collectionId;
|
|
2688
|
+
try {
|
|
2689
|
+
const invocation = await preparePythonVendianInvocation(
|
|
2690
|
+
buildLocalServeEventStreamArgs({ agentsDir: targetDir, collectionId: targetCollection }),
|
|
2691
|
+
{ onProgress: null }
|
|
2692
|
+
);
|
|
2693
|
+
serveState = initialServeState();
|
|
2694
|
+
serveLogs = [];
|
|
2695
|
+
logStore = createServeLogStore({ agentsDir: targetDir, collectionId: targetCollection });
|
|
2696
|
+
serveChild = spawn3(invocation.command, invocation.args, {
|
|
2697
|
+
env: invocation.env,
|
|
2698
|
+
stdio: ["ignore", "pipe", "pipe"],
|
|
2699
|
+
shell: false
|
|
2700
|
+
});
|
|
2701
|
+
let buffer = "";
|
|
2702
|
+
serveChild.stdout.setEncoding("utf8");
|
|
2703
|
+
serveChild.stdout.on("data", (chunk) => {
|
|
2704
|
+
buffer += chunk;
|
|
2705
|
+
const lines = buffer.split(/\r?\n/);
|
|
2706
|
+
buffer = lines.pop() || "";
|
|
2707
|
+
for (const line of lines) {
|
|
2708
|
+
serveLogs.push(line);
|
|
2709
|
+
if (serveLogs.length > 5e3) serveLogs.shift();
|
|
2710
|
+
try {
|
|
2711
|
+
const event = parseServeEventLine(line);
|
|
2712
|
+
if (event) {
|
|
2713
|
+
serveState = applyServeEvent(serveState, event);
|
|
2714
|
+
try {
|
|
2715
|
+
logStore?.append(event);
|
|
2716
|
+
} catch {
|
|
2717
|
+
}
|
|
2718
|
+
}
|
|
2719
|
+
} catch {
|
|
2720
|
+
}
|
|
2721
|
+
}
|
|
2722
|
+
});
|
|
2723
|
+
serveChild.stderr.setEncoding("utf8");
|
|
2724
|
+
serveChild.stderr.on("data", (chunk) => {
|
|
2725
|
+
const text = chunk.trim();
|
|
2726
|
+
if (text) {
|
|
2727
|
+
serveLogs.push(`[stderr] ${text}`);
|
|
2728
|
+
if (serveLogs.length > 5e3) serveLogs.shift();
|
|
2729
|
+
}
|
|
2730
|
+
});
|
|
2731
|
+
serveChild.on("exit", (code, signal) => {
|
|
2732
|
+
serveState = { ...serveState, stopped: true, connected: false };
|
|
2733
|
+
serveLogs.push(`[process] Exited code=${code} signal=${signal || "none"}`);
|
|
2734
|
+
serveChild = null;
|
|
2735
|
+
try {
|
|
2736
|
+
logStore?.compact();
|
|
2737
|
+
} catch {
|
|
2738
|
+
}
|
|
2739
|
+
});
|
|
2740
|
+
jsonResponse(res, { ok: true, pid: serveChild.pid });
|
|
2741
|
+
} catch (err) {
|
|
2742
|
+
jsonResponse(res, { ok: false, error: err.message });
|
|
2743
|
+
}
|
|
2293
2744
|
}
|
|
2294
|
-
function
|
|
2295
|
-
|
|
2745
|
+
function apiServeStop(req, res) {
|
|
2746
|
+
if (!serveChild || serveChild.killed) {
|
|
2747
|
+
return jsonResponse(res, { ok: true, wasRunning: false });
|
|
2748
|
+
}
|
|
2749
|
+
serveChild.kill("SIGTERM");
|
|
2750
|
+
setTimeout(() => {
|
|
2751
|
+
if (serveChild && !serveChild.killed) {
|
|
2752
|
+
serveChild.kill("SIGKILL");
|
|
2753
|
+
}
|
|
2754
|
+
}, 3e3);
|
|
2755
|
+
jsonResponse(res, { ok: true, wasRunning: true });
|
|
2756
|
+
}
|
|
2757
|
+
async function apiCreate(req, res, body) {
|
|
2758
|
+
const name = (body.name || "").trim();
|
|
2759
|
+
if (!name) {
|
|
2760
|
+
return jsonResponse(res, { ok: false, error: "Name is required" }, 400);
|
|
2761
|
+
}
|
|
2762
|
+
try {
|
|
2763
|
+
const invocation = await preparePythonVendianInvocation(
|
|
2764
|
+
["create", name, "--output-dir", agentsDir],
|
|
2765
|
+
{ skipAutoUpdate: true }
|
|
2766
|
+
);
|
|
2767
|
+
const result = await runCaptureAsync(invocation.command, invocation.args, { env: invocation.env });
|
|
2768
|
+
if (result.code === 0) {
|
|
2769
|
+
jsonResponse(res, { ok: true, name, output: result.stdout });
|
|
2770
|
+
} else {
|
|
2771
|
+
jsonResponse(res, { ok: false, error: result.stderr || result.stdout || "Create failed" });
|
|
2772
|
+
}
|
|
2773
|
+
} catch (err) {
|
|
2774
|
+
jsonResponse(res, { ok: false, error: err.message });
|
|
2775
|
+
}
|
|
2776
|
+
}
|
|
2777
|
+
function jsonResponse(res, data, status = 200) {
|
|
2778
|
+
const body = JSON.stringify(data);
|
|
2779
|
+
res.writeHead(status, { "Content-Type": "application/json" });
|
|
2780
|
+
res.end(body);
|
|
2781
|
+
}
|
|
2782
|
+
function readBody(req) {
|
|
2783
|
+
return new Promise((resolve) => {
|
|
2784
|
+
const chunks = [];
|
|
2785
|
+
req.on("data", (chunk) => chunks.push(chunk));
|
|
2786
|
+
req.on("end", () => {
|
|
2787
|
+
const raw = Buffer.concat(chunks).toString("utf8");
|
|
2788
|
+
try {
|
|
2789
|
+
resolve(JSON.parse(raw));
|
|
2790
|
+
} catch {
|
|
2791
|
+
resolve({});
|
|
2792
|
+
}
|
|
2793
|
+
});
|
|
2794
|
+
});
|
|
2795
|
+
}
|
|
2796
|
+
function runCaptureAsync(command, args, options = {}) {
|
|
2797
|
+
return new Promise((resolve) => {
|
|
2798
|
+
const child = spawn3(command, args, {
|
|
2799
|
+
stdio: ["ignore", "pipe", "pipe"],
|
|
2800
|
+
shell: false,
|
|
2801
|
+
...options
|
|
2802
|
+
});
|
|
2803
|
+
let stdout = "";
|
|
2804
|
+
let stderr = "";
|
|
2805
|
+
child.stdout.on("data", (d) => {
|
|
2806
|
+
stdout += d;
|
|
2807
|
+
});
|
|
2808
|
+
child.stderr.on("data", (d) => {
|
|
2809
|
+
stderr += d;
|
|
2810
|
+
});
|
|
2811
|
+
child.on("exit", (code) => {
|
|
2812
|
+
resolve({ code: code ?? 1, stdout, stderr });
|
|
2813
|
+
});
|
|
2814
|
+
child.on("error", (err) => {
|
|
2815
|
+
resolve({ code: 1, stdout, stderr: err.message });
|
|
2816
|
+
});
|
|
2817
|
+
});
|
|
2818
|
+
}
|
|
2819
|
+
function parseManifestBasic(manifestPath) {
|
|
2820
|
+
try {
|
|
2821
|
+
const content = fs11.readFileSync(manifestPath, "utf8");
|
|
2822
|
+
const get = (key) => {
|
|
2823
|
+
const m = content.match(new RegExp(`^${key}\\s*:\\s*['"]?([^'"\\n#]+?)['"]?\\s*$`, "m"));
|
|
2824
|
+
return m ? m[1].trim() : "";
|
|
2825
|
+
};
|
|
2826
|
+
const countListItems = (key) => {
|
|
2827
|
+
const m = content.match(new RegExp(`^${key}\\s*:`, "m"));
|
|
2828
|
+
if (!m) return 0;
|
|
2829
|
+
const after = content.slice(m.index + m[0].length);
|
|
2830
|
+
const lines = after.split("\n");
|
|
2831
|
+
let count = 0;
|
|
2832
|
+
let firstIndent = -1;
|
|
2833
|
+
for (const line of lines) {
|
|
2834
|
+
if (/^\S/.test(line) && count > 0) break;
|
|
2835
|
+
const listMatch = line.match(/^(\s+)-\s/);
|
|
2836
|
+
if (listMatch) {
|
|
2837
|
+
const indent = listMatch[1].length;
|
|
2838
|
+
if (firstIndent < 0) firstIndent = indent;
|
|
2839
|
+
if (indent === firstIndent) count++;
|
|
2840
|
+
}
|
|
2841
|
+
}
|
|
2842
|
+
return count;
|
|
2843
|
+
};
|
|
2844
|
+
const getListLabels = (key) => {
|
|
2845
|
+
const m = content.match(new RegExp(`^${key}\\s*:`, "m"));
|
|
2846
|
+
if (!m) return [];
|
|
2847
|
+
const after = content.slice(m.index + m[0].length);
|
|
2848
|
+
const lines = after.split("\n");
|
|
2849
|
+
const items = [];
|
|
2850
|
+
let currentItem = null;
|
|
2851
|
+
let firstIndent = -1;
|
|
2852
|
+
for (const line of lines) {
|
|
2853
|
+
if (/^\S/.test(line) && items.length + (currentItem ? 1 : 0) > 0) break;
|
|
2854
|
+
const listMatch = line.match(/^(\s+)-\s*(.*)/);
|
|
2855
|
+
if (listMatch) {
|
|
2856
|
+
const indent = listMatch[1].length;
|
|
2857
|
+
if (firstIndent < 0) firstIndent = indent;
|
|
2858
|
+
if (indent === firstIndent) {
|
|
2859
|
+
if (currentItem) items.push(currentItem);
|
|
2860
|
+
const inlineValue = listMatch[2].trim();
|
|
2861
|
+
if (inlineValue && !inlineValue.includes(":")) {
|
|
2862
|
+
currentItem = { label: inlineValue };
|
|
2863
|
+
} else {
|
|
2864
|
+
currentItem = {};
|
|
2865
|
+
const kvMatch = inlineValue.match(/^(\w+)\s*:\s*(.+)/);
|
|
2866
|
+
if (kvMatch) currentItem[kvMatch[1]] = kvMatch[2].replace(/['"]*/g, "").trim();
|
|
2867
|
+
}
|
|
2868
|
+
} else if (currentItem) {
|
|
2869
|
+
const kvMatch = line.match(/^\s+(\w+)\s*:\s*(.+)/);
|
|
2870
|
+
if (kvMatch) currentItem[kvMatch[1]] = kvMatch[2].replace(/['"]*/g, "").trim();
|
|
2871
|
+
}
|
|
2872
|
+
} else if (currentItem && /^\s+\w+\s*:/.test(line)) {
|
|
2873
|
+
const kvMatch = line.match(/^\s+(\w+)\s*:\s*(.+)/);
|
|
2874
|
+
if (kvMatch) currentItem[kvMatch[1]] = kvMatch[2].replace(/['"]*/g, "").trim();
|
|
2875
|
+
}
|
|
2876
|
+
}
|
|
2877
|
+
if (currentItem) items.push(currentItem);
|
|
2878
|
+
return items.map((item) => item.label || item.type || item.id || Object.values(item)[0] || "").filter(Boolean);
|
|
2879
|
+
};
|
|
2880
|
+
return {
|
|
2881
|
+
id: get("id"),
|
|
2882
|
+
name: get("name"),
|
|
2883
|
+
description: get("description"),
|
|
2884
|
+
version: get("version"),
|
|
2885
|
+
triggers: getListLabels("triggers"),
|
|
2886
|
+
credentials: getListLabels("credentials"),
|
|
2887
|
+
triggerCount: countListItems("triggers"),
|
|
2888
|
+
credentialCount: countListItems("credentials")
|
|
2889
|
+
};
|
|
2890
|
+
} catch {
|
|
2891
|
+
return {};
|
|
2892
|
+
}
|
|
2893
|
+
}
|
|
2894
|
+
function formatDaemonEvent(event) {
|
|
2895
|
+
switch (event.type) {
|
|
2896
|
+
case "daemon_registered":
|
|
2897
|
+
return "Connected to backend";
|
|
2898
|
+
case "daemon_event_stream_connected":
|
|
2899
|
+
return "Event stream connected";
|
|
2900
|
+
case "daemon_event_stream_lost":
|
|
2901
|
+
return `Event stream lost: ${event.error || "unknown"}`;
|
|
2902
|
+
case "inventory_synced":
|
|
2903
|
+
return `Inventory synced (${event.agentCount || 0} agents)`;
|
|
2904
|
+
case "agent_discovery_completed":
|
|
2905
|
+
return `Discovered ${event.agentCount || 0} agents`;
|
|
2906
|
+
case "backend_connection_lost":
|
|
2907
|
+
return `Connection lost: ${event.error || event.activity || "unknown"}`;
|
|
2908
|
+
case "backend_connection_restored":
|
|
2909
|
+
return "Connection restored";
|
|
2910
|
+
case "retry_scheduled":
|
|
2911
|
+
return `Retrying ${event.activity || "operation"} in ${event.delaySeconds || "?"}s`;
|
|
2912
|
+
default:
|
|
2913
|
+
return event.type;
|
|
2914
|
+
}
|
|
2296
2915
|
}
|
|
2916
|
+
function openUrl(url) {
|
|
2917
|
+
const cmd = process.platform === "win32" ? "start" : process.platform === "darwin" ? "open" : "xdg-open";
|
|
2918
|
+
const args = process.platform === "win32" ? ["/c", "start", "", url] : [url];
|
|
2919
|
+
const shell = process.platform === "win32" ? "cmd.exe" : void 0;
|
|
2920
|
+
spawn3(shell || cmd, shell ? args : [url], { detached: true, stdio: "ignore" }).unref();
|
|
2921
|
+
}
|
|
2922
|
+
|
|
2923
|
+
// src/tui.js
|
|
2924
|
+
init_constants();
|
|
2925
|
+
init_auth();
|
|
2926
|
+
import { spawnSync as spawnSync4 } from "node:child_process";
|
|
2927
|
+
import fs12 from "node:fs";
|
|
2928
|
+
import path9 from "node:path";
|
|
2929
|
+
import readline from "node:readline";
|
|
2930
|
+
|
|
2931
|
+
// src/npm-update.js
|
|
2932
|
+
var NPM_CHECK_INTERVAL_MS = 30 * 60 * 1e3;
|
|
2297
2933
|
|
|
2298
2934
|
// src/ui/figures.js
|
|
2299
2935
|
var supportsUnicode = process.platform !== "win32" || Boolean(
|
|
@@ -2602,6 +3238,7 @@ var COMMAND_GROUPS = [
|
|
|
2602
3238
|
{ cmd: "vendian models", desc: "List available AI models" }
|
|
2603
3239
|
] },
|
|
2604
3240
|
{ section: "Running agents", commands: [
|
|
3241
|
+
{ cmd: "vendian dev", desc: "Open the dev UI in your browser" },
|
|
2605
3242
|
{ cmd: "vendian cloud local run --collection-id ID --path . --input-json '{}'", desc: "Run one agent" },
|
|
2606
3243
|
{ cmd: "vendian cloud local serve --agents-dir .", desc: "Start agents" },
|
|
2607
3244
|
{ cmd: "vendian login --backend staging", desc: "Sign in to staging" }
|
|
@@ -2654,7 +3291,7 @@ async function pickProject({ env, platform, candidates }) {
|
|
|
2654
3291
|
print(col.bold(" Which project do you want to run?"));
|
|
2655
3292
|
print("");
|
|
2656
3293
|
const items = candidates.map((candidate) => {
|
|
2657
|
-
const name = readManifestName(candidate.absolutePath) || friendlyName(
|
|
3294
|
+
const name = readManifestName(candidate.absolutePath) || friendlyName(path9.basename(candidate.absolutePath) || candidate.path);
|
|
2658
3295
|
return `${name.padEnd(32)} ${col.gray(`${candidate.agentCount} agent${candidate.agentCount === 1 ? "" : "s"}`)}`;
|
|
2659
3296
|
});
|
|
2660
3297
|
const idx = await pickMenu(items, { allowBack: true });
|
|
@@ -2668,7 +3305,7 @@ async function pickAgentsFromFolder({ env, platform, candidate }) {
|
|
|
2668
3305
|
if (folders.length <= 1) return { agentsDir: candidate.path };
|
|
2669
3306
|
const named = folders.map((f) => ({
|
|
2670
3307
|
...f,
|
|
2671
|
-
displayName: readManifestName(f.absolutePath) || friendlyName(f.name ||
|
|
3308
|
+
displayName: readManifestName(f.absolutePath) || friendlyName(f.name || path9.basename(f.path))
|
|
2672
3309
|
}));
|
|
2673
3310
|
clearScreen();
|
|
2674
3311
|
printHeader({ env, platform });
|
|
@@ -2691,19 +3328,19 @@ async function pickSingleAgentToRun({ env, platform, candidates }) {
|
|
|
2691
3328
|
print(col.yellow(" Couldn't find any agents."));
|
|
2692
3329
|
const input = await ask("Agent folder", "./agents/my-agent");
|
|
2693
3330
|
if (!input) return null;
|
|
2694
|
-
return { path: input, displayName: friendlyName(
|
|
3331
|
+
return { path: input, displayName: friendlyName(path9.basename(input) || "Agent") };
|
|
2695
3332
|
}
|
|
2696
3333
|
const agents = candidates.flatMap((candidate) => {
|
|
2697
3334
|
const folders = findAgentFolders(candidate.absolutePath);
|
|
2698
3335
|
if (folders.length > 0) {
|
|
2699
3336
|
return folders.map((folder) => ({
|
|
2700
3337
|
path: folder.path,
|
|
2701
|
-
displayName: readManifestName(folder.absolutePath) || friendlyName(folder.name ||
|
|
3338
|
+
displayName: readManifestName(folder.absolutePath) || friendlyName(folder.name || path9.basename(folder.path))
|
|
2702
3339
|
}));
|
|
2703
3340
|
}
|
|
2704
3341
|
return [{
|
|
2705
3342
|
path: candidate.path,
|
|
2706
|
-
displayName: readManifestName(candidate.absolutePath) || friendlyName(
|
|
3343
|
+
displayName: readManifestName(candidate.absolutePath) || friendlyName(path9.basename(candidate.absolutePath) || candidate.path)
|
|
2707
3344
|
}];
|
|
2708
3345
|
});
|
|
2709
3346
|
if (agents.length === 1) return agents[0];
|
|
@@ -2803,7 +3440,7 @@ async function showServe({ env, platform }) {
|
|
|
2803
3440
|
const candidates = findAgentDirectoryCandidates();
|
|
2804
3441
|
const picked = await pickAgentsToRun({ env, platform, candidates });
|
|
2805
3442
|
if (!picked) return "home";
|
|
2806
|
-
const { agentsDir } = picked;
|
|
3443
|
+
const { agentsDir: agentsDir2 } = picked;
|
|
2807
3444
|
const workspace = await chooseCloudWorkspace({
|
|
2808
3445
|
env,
|
|
2809
3446
|
platform,
|
|
@@ -2811,9 +3448,9 @@ async function showServe({ env, platform }) {
|
|
|
2811
3448
|
pickerTitle: "Which project do you want to run?"
|
|
2812
3449
|
});
|
|
2813
3450
|
if (!workspace) return "home";
|
|
2814
|
-
return await runServeDashboard({ env, platform, agentsDir, collectionId: workspace.id, wsLabel: workspace.label });
|
|
3451
|
+
return await runServeDashboard({ env, platform, agentsDir: agentsDir2, collectionId: workspace.id, wsLabel: workspace.label });
|
|
2815
3452
|
}
|
|
2816
|
-
async function runServeDashboard({ env, platform, agentsDir, collectionId, wsLabel = "" }) {
|
|
3453
|
+
async function runServeDashboard({ env, platform, agentsDir: agentsDir2, collectionId: collectionId2, wsLabel = "" }) {
|
|
2817
3454
|
let state = initialServeState();
|
|
2818
3455
|
let child = null;
|
|
2819
3456
|
let ctrlCArmed = false;
|
|
@@ -2822,7 +3459,7 @@ async function runServeDashboard({ env, platform, agentsDir, collectionId, wsLab
|
|
|
2822
3459
|
let stopSignalAttempt = 0;
|
|
2823
3460
|
let stopTimer = null;
|
|
2824
3461
|
let dashboardClosed = false;
|
|
2825
|
-
let
|
|
3462
|
+
let logStore2 = null;
|
|
2826
3463
|
let lastAgentOnlinePrint = 0;
|
|
2827
3464
|
let agentsSeenOnline = false;
|
|
2828
3465
|
let initialSetupDone = false;
|
|
@@ -2832,8 +3469,8 @@ async function runServeDashboard({ env, platform, agentsDir, collectionId, wsLab
|
|
|
2832
3469
|
print("");
|
|
2833
3470
|
print(col.gray("\u2550".repeat(W)));
|
|
2834
3471
|
print(` ${col.cyan(col.bold("\u2191 VENDIAN"))} ${col.bold("Serving agents")}`);
|
|
2835
|
-
print(` ${col.gray("Dir:")} ${
|
|
2836
|
-
print(` ${col.gray("Workspace:")} ${wsLabel ||
|
|
3472
|
+
print(` ${col.gray("Dir:")} ${agentsDir2}`);
|
|
3473
|
+
print(` ${col.gray("Workspace:")} ${wsLabel || collectionId2 || "\u2014"}`);
|
|
2837
3474
|
print(col.gray(hr(W)));
|
|
2838
3475
|
print(col.gray(" S stop gracefully | ^c ^c exit"));
|
|
2839
3476
|
print(col.gray(hr(W)));
|
|
@@ -2860,7 +3497,7 @@ async function runServeDashboard({ env, platform, agentsDir, collectionId, wsLab
|
|
|
2860
3497
|
if (!event) return;
|
|
2861
3498
|
const type = String(event.type || "");
|
|
2862
3499
|
const agentPath = String(event.relativePath || event.path || "");
|
|
2863
|
-
const agentName = agentPath ? friendlyName(
|
|
3500
|
+
const agentName = agentPath ? friendlyName(path9.basename(agentPath)) || agentPath : ".";
|
|
2864
3501
|
if (type === "daemon_wake_received") return;
|
|
2865
3502
|
if (type === "lease_claim_no_job") return;
|
|
2866
3503
|
if (type === "dispatch_job_received" || type === "dispatch_job_acked") return;
|
|
@@ -2871,7 +3508,7 @@ async function runServeDashboard({ env, platform, agentsDir, collectionId, wsLab
|
|
|
2871
3508
|
const now = Date.now();
|
|
2872
3509
|
if (!hasProblems && allOnline && agentsSeenOnline && now - lastAgentOnlinePrint < 12e4) return;
|
|
2873
3510
|
for (const a of agents) {
|
|
2874
|
-
const name = friendlyName(
|
|
3511
|
+
const name = friendlyName(path9.basename(a.relativePath || ".")) || a.manifestName || a.relativePath || "?";
|
|
2875
3512
|
if (a.status === "error") {
|
|
2876
3513
|
printAgent(name, col.red(`\u2716 Error: ${a.errorMessage || ""}`));
|
|
2877
3514
|
continue;
|
|
@@ -2950,15 +3587,15 @@ async function runServeDashboard({ env, platform, agentsDir, collectionId, wsLab
|
|
|
2950
3587
|
}
|
|
2951
3588
|
}
|
|
2952
3589
|
try {
|
|
2953
|
-
|
|
2954
|
-
const historicalLogs =
|
|
3590
|
+
logStore2 = createServeLogStore({ agentsDir: agentsDir2, collectionId: collectionId2, env, platform });
|
|
3591
|
+
const historicalLogs = logStore2.load();
|
|
2955
3592
|
if (historicalLogs.length > 0) {
|
|
2956
3593
|
const agentLogs = mergeAgentLogRecords(state.agentLogs, historicalLogs);
|
|
2957
3594
|
state = { ...state, agentLogs, agentRunState: { ...agentRunStateFromLogs(agentLogs, { includeRunning: false }), ...state.agentRunState } };
|
|
2958
3595
|
}
|
|
2959
3596
|
child = await spawnLocalServeEventStream({
|
|
2960
|
-
agentsDir,
|
|
2961
|
-
collectionId,
|
|
3597
|
+
agentsDir: agentsDir2,
|
|
3598
|
+
collectionId: collectionId2,
|
|
2962
3599
|
env,
|
|
2963
3600
|
platform,
|
|
2964
3601
|
onProgress: (msg) => {
|
|
@@ -2967,7 +3604,7 @@ async function runServeDashboard({ env, platform, agentsDir, collectionId, wsLab
|
|
|
2967
3604
|
}
|
|
2968
3605
|
});
|
|
2969
3606
|
process.stdout.write("\r" + " ".repeat(80) + "\r");
|
|
2970
|
-
printDaemon("serve_started", col.gray(`dir=${
|
|
3607
|
+
printDaemon("serve_started", col.gray(`dir=${agentsDir2}`));
|
|
2971
3608
|
} catch (error) {
|
|
2972
3609
|
setCursorVisible(true);
|
|
2973
3610
|
print(col.red(` ${fig.cross} Failed to start: ${errMsg(error)}`));
|
|
@@ -2996,9 +3633,13 @@ async function runServeDashboard({ env, platform, agentsDir, collectionId, wsLab
|
|
|
2996
3633
|
if (!stopRequested || dashboardClosed || !childIsRunning()) return;
|
|
2997
3634
|
stopSignalAttempt += 1;
|
|
2998
3635
|
const signal = stopSignalForAttempt(stopSignalAttempt);
|
|
2999
|
-
|
|
3000
|
-
child
|
|
3001
|
-
}
|
|
3636
|
+
if (process.platform === "win32") {
|
|
3637
|
+
killProcessTree(child);
|
|
3638
|
+
} else {
|
|
3639
|
+
try {
|
|
3640
|
+
child.kill(signal);
|
|
3641
|
+
} catch {
|
|
3642
|
+
}
|
|
3002
3643
|
}
|
|
3003
3644
|
if (signal !== "SIGKILL") scheduleStopEscalation();
|
|
3004
3645
|
}, 4e3);
|
|
@@ -3014,18 +3655,25 @@ async function runServeDashboard({ env, platform, agentsDir, collectionId, wsLab
|
|
|
3014
3655
|
print(col.yellow("\n Stopping your agents\u2026"));
|
|
3015
3656
|
clearTimeout(ctrlCArmTimer);
|
|
3016
3657
|
ctrlCArmed = false;
|
|
3017
|
-
|
|
3018
|
-
child
|
|
3019
|
-
}
|
|
3658
|
+
if (process.platform === "win32") {
|
|
3659
|
+
killProcessTree(child);
|
|
3660
|
+
} else {
|
|
3661
|
+
try {
|
|
3662
|
+
child.kill(stopSignalForAttempt(stopSignalAttempt));
|
|
3663
|
+
} catch {
|
|
3664
|
+
}
|
|
3665
|
+
scheduleStopEscalation();
|
|
3020
3666
|
}
|
|
3021
|
-
scheduleStopEscalation();
|
|
3022
3667
|
}
|
|
3023
3668
|
if (process.stdin.isTTY) {
|
|
3024
3669
|
let onKeypress = function(str, key) {
|
|
3025
3670
|
if (!key) return;
|
|
3026
3671
|
if (key.ctrl && key.name === "c") {
|
|
3027
3672
|
if (ctrlCArmed) {
|
|
3028
|
-
|
|
3673
|
+
try {
|
|
3674
|
+
child?.kill("SIGINT");
|
|
3675
|
+
} catch (error) {
|
|
3676
|
+
}
|
|
3029
3677
|
finish("exit");
|
|
3030
3678
|
return;
|
|
3031
3679
|
}
|
|
@@ -3065,7 +3713,7 @@ async function runServeDashboard({ env, platform, agentsDir, collectionId, wsLab
|
|
|
3065
3713
|
const event = parseServeEventLine(line);
|
|
3066
3714
|
if (event) {
|
|
3067
3715
|
try {
|
|
3068
|
-
|
|
3716
|
+
logStore2?.append(event);
|
|
3069
3717
|
} catch {
|
|
3070
3718
|
}
|
|
3071
3719
|
state = applyServeEvent(state, event);
|
|
@@ -3107,7 +3755,7 @@ async function runServeDashboard({ env, platform, agentsDir, collectionId, wsLab
|
|
|
3107
3755
|
print(col.gray("\n Agents stopped."));
|
|
3108
3756
|
}
|
|
3109
3757
|
try {
|
|
3110
|
-
|
|
3758
|
+
logStore2?.compact();
|
|
3111
3759
|
} catch {
|
|
3112
3760
|
}
|
|
3113
3761
|
removeKeypress();
|
|
@@ -3243,7 +3891,7 @@ function endpointErrorStatus(error) {
|
|
|
3243
3891
|
}
|
|
3244
3892
|
function buildLocalRunArgs({
|
|
3245
3893
|
agentPath = ".",
|
|
3246
|
-
collectionId = "",
|
|
3894
|
+
collectionId: collectionId2 = "",
|
|
3247
3895
|
inputJson = "{}"
|
|
3248
3896
|
} = {}) {
|
|
3249
3897
|
return [
|
|
@@ -3251,7 +3899,7 @@ function buildLocalRunArgs({
|
|
|
3251
3899
|
"local",
|
|
3252
3900
|
"run",
|
|
3253
3901
|
"--collection-id",
|
|
3254
|
-
|
|
3902
|
+
collectionId2,
|
|
3255
3903
|
"--path",
|
|
3256
3904
|
agentPath || ".",
|
|
3257
3905
|
"--input-json",
|
|
@@ -3263,21 +3911,39 @@ function stopSignalForAttempt(attempt = 0) {
|
|
|
3263
3911
|
if (attempt === 1) return "SIGTERM";
|
|
3264
3912
|
return "SIGKILL";
|
|
3265
3913
|
}
|
|
3914
|
+
function killProcessTree(child) {
|
|
3915
|
+
if (!child || child.exitCode != null || child.signalCode != null) return;
|
|
3916
|
+
if (process.platform === "win32") {
|
|
3917
|
+
try {
|
|
3918
|
+
spawnSync4("taskkill", ["/F", "/T", "/PID", String(child.pid)], { stdio: "ignore" });
|
|
3919
|
+
} catch {
|
|
3920
|
+
}
|
|
3921
|
+
} else {
|
|
3922
|
+
try {
|
|
3923
|
+
child.kill("SIGTERM");
|
|
3924
|
+
} catch {
|
|
3925
|
+
}
|
|
3926
|
+
}
|
|
3927
|
+
}
|
|
3266
3928
|
function helpText() {
|
|
3267
3929
|
return [
|
|
3268
3930
|
"",
|
|
3269
3931
|
` \u2191 VENDIAN CLI v${CLI_VERSION}`,
|
|
3270
3932
|
"",
|
|
3271
3933
|
" Usage:",
|
|
3272
|
-
" vendian Open the
|
|
3934
|
+
" vendian Open the dev UI in your browser",
|
|
3935
|
+
" vendian terminal Open the interactive terminal shell",
|
|
3273
3936
|
" vendian login Sign in and prepare the local runtime",
|
|
3274
3937
|
" vendian doctor Check local bootstrap health",
|
|
3275
3938
|
" vendian update Update the managed runtime",
|
|
3276
3939
|
" vendian init Write SDK agent docs into a workspace",
|
|
3277
3940
|
' vendian create "My Agent" Scaffold a new agent from templates',
|
|
3941
|
+
" vendian dev Open the dev UI (alias for bare vendian)",
|
|
3278
3942
|
" vendian <command> Run a managed Python SDK/cloud command",
|
|
3279
3943
|
"",
|
|
3280
3944
|
" Examples:",
|
|
3945
|
+
" vendian",
|
|
3946
|
+
" vendian terminal",
|
|
3281
3947
|
" vendian login",
|
|
3282
3948
|
" vendian init --output-dir ./agents",
|
|
3283
3949
|
' vendian create "My Agent" --output-dir ./agents',
|
|
@@ -3289,7 +3955,7 @@ function helpText() {
|
|
|
3289
3955
|
" vendian doctor",
|
|
3290
3956
|
" vendian update",
|
|
3291
3957
|
"",
|
|
3292
|
-
` Run \u203A vendian \u203A
|
|
3958
|
+
` Run \u203A vendian terminal \u203A for the interactive terminal shell`,
|
|
3293
3959
|
""
|
|
3294
3960
|
].join("\n");
|
|
3295
3961
|
}
|
|
@@ -3300,7 +3966,7 @@ function readManifestName(absoluteFolderPath) {
|
|
|
3300
3966
|
if (!absoluteFolderPath) return null;
|
|
3301
3967
|
for (const fname of ["manifest.yaml", "manifest.yml"]) {
|
|
3302
3968
|
try {
|
|
3303
|
-
const content =
|
|
3969
|
+
const content = fs12.readFileSync(path9.join(absoluteFolderPath, fname), "utf8");
|
|
3304
3970
|
const m = content.match(/^name\s*:\s*['"]?([^'"\n#]+?)['"]?\s*$/m);
|
|
3305
3971
|
if (m?.[1]) return m[1].trim();
|
|
3306
3972
|
} catch {
|
|
@@ -3348,11 +4014,7 @@ Environment:
|
|
|
3348
4014
|
async function main(argv) {
|
|
3349
4015
|
const [command, ...rest] = argv;
|
|
3350
4016
|
if (!command) {
|
|
3351
|
-
|
|
3352
|
-
await runTui();
|
|
3353
|
-
} else {
|
|
3354
|
-
printHelp();
|
|
3355
|
-
}
|
|
4017
|
+
await startDevServer(parseDevOptions([]));
|
|
3356
4018
|
return;
|
|
3357
4019
|
}
|
|
3358
4020
|
if (command === "--help" || command === "-h") {
|
|
@@ -3363,10 +4025,22 @@ async function main(argv) {
|
|
|
3363
4025
|
console.log(CLI_VERSION);
|
|
3364
4026
|
return;
|
|
3365
4027
|
}
|
|
4028
|
+
if (command === "terminal" || command === "tui") {
|
|
4029
|
+
if (process.stdin.isTTY && process.stdout.isTTY) {
|
|
4030
|
+
await runTui();
|
|
4031
|
+
} else {
|
|
4032
|
+
printHelp();
|
|
4033
|
+
}
|
|
4034
|
+
return;
|
|
4035
|
+
}
|
|
3366
4036
|
if (isBootstrapCommand(command)) {
|
|
3367
4037
|
await setup(parseSetupOptions(rest));
|
|
3368
4038
|
return;
|
|
3369
4039
|
}
|
|
4040
|
+
if (command === "dev") {
|
|
4041
|
+
await startDevServer(parseDevOptions(rest));
|
|
4042
|
+
return;
|
|
4043
|
+
}
|
|
3370
4044
|
if (command === "doctor") {
|
|
3371
4045
|
doctor();
|
|
3372
4046
|
return;
|
|
@@ -3375,11 +4049,33 @@ async function main(argv) {
|
|
|
3375
4049
|
await setup({ nonInteractive: true });
|
|
3376
4050
|
return;
|
|
3377
4051
|
}
|
|
4052
|
+
if (command.startsWith("--port") || command.startsWith("-p") || command === "--no-open" || command === "--agents-dir" || command === "--agents") {
|
|
4053
|
+
await startDevServer(parseDevOptions(argv));
|
|
4054
|
+
return;
|
|
4055
|
+
}
|
|
3378
4056
|
await forwardToPythonVendian(argv);
|
|
3379
4057
|
}
|
|
3380
4058
|
function isBootstrapCommand(command) {
|
|
3381
4059
|
return command === "login" || command === "setup";
|
|
3382
4060
|
}
|
|
4061
|
+
function parseDevOptions(args) {
|
|
4062
|
+
const options = {};
|
|
4063
|
+
for (let i = 0; i < args.length; i++) {
|
|
4064
|
+
const arg = args[i];
|
|
4065
|
+
if (arg === "--port" || arg === "-p") {
|
|
4066
|
+
options.port = parseInt(args[++i], 10) || 3859;
|
|
4067
|
+
} else if (arg.startsWith("--port=")) {
|
|
4068
|
+
options.port = parseInt(arg.slice(7), 10) || 3859;
|
|
4069
|
+
} else if (arg === "--agents-dir" || arg === "--agents") {
|
|
4070
|
+
options.agents = args[++i];
|
|
4071
|
+
} else if (arg.startsWith("--agents-dir=")) {
|
|
4072
|
+
options.agents = arg.slice(13);
|
|
4073
|
+
} else if (arg === "--no-open") {
|
|
4074
|
+
options.noOpen = true;
|
|
4075
|
+
}
|
|
4076
|
+
}
|
|
4077
|
+
return options;
|
|
4078
|
+
}
|
|
3383
4079
|
function parseSetupOptions(args) {
|
|
3384
4080
|
const options = {
|
|
3385
4081
|
nonInteractive: args.includes("--yes") || args.includes("--non-interactive"),
|