@vendian/cli 0.0.33 → 0.0.35
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 +1817 -1159
- 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"] },
|
|
@@ -408,421 +754,348 @@ function doctor({ env = process.env, platform = process.platform } = {}) {
|
|
|
408
754
|
const vendianPath = venvVendian(venvPath, platform);
|
|
409
755
|
const registry = registryConfig(config, env, platform);
|
|
410
756
|
console.log("Vendian doctor");
|
|
411
|
-
console.log(`Home: ${venvPath}`);
|
|
412
|
-
console.log(`System Python 3.11+: ${findPython(platform) ? "yes" : "no"}`);
|
|
413
|
-
console.log(`uv: ${hasUv() ? "yes" : "no, will use pip fallback"}`);
|
|
414
|
-
console.log(`Managed Python: ${fs4.existsSync(pythonPath) ? pythonVersion(pythonPath) || "present" : "missing"}`);
|
|
415
|
-
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;
|
|
757
|
+
console.log(`Home: ${venvPath}`);
|
|
758
|
+
console.log(`System Python 3.11+: ${findPython(platform) ? "yes" : "no"}`);
|
|
759
|
+
console.log(`uv: ${hasUv() ? "yes" : "no, will use pip fallback"}`);
|
|
760
|
+
console.log(`Managed Python: ${fs4.existsSync(pythonPath) ? pythonVersion(pythonPath) || "present" : "missing"}`);
|
|
761
|
+
console.log(`Vendian executable: ${fs4.existsSync(vendianPath) ? vendianPath : "missing"}`);
|
|
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
|
-
|
|
764
|
-
|
|
765
|
-
|
|
766
|
-
|
|
767
|
-
|
|
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;
|
|
768
987
|
}
|
|
769
|
-
|
|
988
|
+
return parents;
|
|
770
989
|
}
|
|
771
|
-
function
|
|
772
|
-
|
|
990
|
+
function safeResolve(candidate) {
|
|
991
|
+
try {
|
|
992
|
+
return path4.resolve(candidate);
|
|
993
|
+
} catch {
|
|
994
|
+
return null;
|
|
995
|
+
}
|
|
773
996
|
}
|
|
774
|
-
|
|
775
|
-
|
|
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;
|
|
776
1011
|
}
|
|
777
|
-
function
|
|
778
|
-
return ["
|
|
1012
|
+
function commandWritesAgentDocs(args = []) {
|
|
1013
|
+
return args[0] === "init";
|
|
779
1014
|
}
|
|
780
|
-
function
|
|
781
|
-
|
|
782
|
-
|
|
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
|
+
}
|
|
783
1024
|
}
|
|
784
|
-
|
|
785
|
-
|
|
786
|
-
|
|
1025
|
+
return ".";
|
|
1026
|
+
}
|
|
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;
|
|
787
1053
|
}
|
|
788
|
-
|
|
789
|
-
return
|
|
1054
|
+
recordAgentDocsWorkspace(initOutputDir(args), options);
|
|
1055
|
+
return true;
|
|
790
1056
|
}
|
|
791
|
-
function
|
|
792
|
-
|
|
793
|
-
|
|
794
|
-
|
|
795
|
-
|
|
796
|
-
|
|
797
|
-
|
|
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 };
|
|
1070
|
+
}
|
|
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,
|
|
@@ -868,474 +1141,290 @@ async function setup({
|
|
|
868
1141
|
if (registry.tokenSource !== "secret-store") {
|
|
869
1142
|
next.gitlabToken = registry.token;
|
|
870
1143
|
}
|
|
871
|
-
console.log("Using saved package access.");
|
|
872
|
-
}
|
|
873
|
-
const installRegistry = registryConfig(next, env, platform);
|
|
874
|
-
if (!installRegistry.token) {
|
|
875
|
-
throw new Error("Package access is missing. Run interactive `vendian login` to sign in.");
|
|
876
|
-
}
|
|
877
|
-
const python = findPython(platform);
|
|
878
|
-
if (!python) {
|
|
879
|
-
throw new Error("Python 3.11+ was not found. Install Python 3.11 or newer, then rerun `vendian login`.");
|
|
880
|
-
}
|
|
881
|
-
const venvPath = managedVenvPath(env, platform);
|
|
882
|
-
console.log(`Managed environment: ${venvPath}`);
|
|
883
|
-
const pythonPath = ensureVenv(venvPath, python, platform);
|
|
884
|
-
console.log(`Python: ${pythonVersion(pythonPath) || pythonPath}`);
|
|
885
|
-
saveConfig(next, env, platform);
|
|
886
|
-
installVendianPackages({ pythonPath, venvPath, config: next, env, platform });
|
|
887
|
-
const updated = { ...next, lastManagedUpdateAt: (/* @__PURE__ */ new Date()).toISOString() };
|
|
888
|
-
refreshAgentDocsWorkspaces({ config: updated, venvPath, env, platform });
|
|
889
|
-
if (!verifyVendianImports(pythonPath)) {
|
|
890
|
-
throw new Error("Vendian packages installed, but import verification failed.");
|
|
891
|
-
}
|
|
892
|
-
const vendianPath = venvVendian(venvPath, platform);
|
|
893
|
-
if (!fs7.existsSync(vendianPath)) {
|
|
894
|
-
throw new Error("Vendian executable was not found after install.");
|
|
895
|
-
}
|
|
896
|
-
console.log("");
|
|
897
|
-
for (const line of setupCompletionLines({ cloudAuthApiUrl, backend, apiUrl })) {
|
|
898
|
-
console.log(line);
|
|
899
|
-
}
|
|
900
|
-
}
|
|
901
|
-
function savePackageCredentials(config, packageCredentials, { platform = process.platform } = {}) {
|
|
902
|
-
if (!packageCredentials?.token) {
|
|
903
|
-
return false;
|
|
904
|
-
}
|
|
905
|
-
config.gitlabHost = packageCredentials.gitlabHost || config.gitlabHost;
|
|
906
|
-
config.gitlabUsername = packageCredentials.username || config.gitlabUsername;
|
|
907
|
-
config.sdkPublicProjectId = packageCredentials.sdkProjectId || config.sdkPublicProjectId;
|
|
908
|
-
config.sdkRuntimeProjectId = packageCredentials.runtimeProjectId || config.sdkRuntimeProjectId;
|
|
909
|
-
config.vendianAgentsVersion = packageCredentials.vendianAgentsVersion || config.vendianAgentsVersion;
|
|
910
|
-
config.vendianAgentsRuntimeVersion = packageCredentials.vendianAgentsRuntimeVersion || config.vendianAgentsRuntimeVersion;
|
|
911
|
-
const secret = savePackageTokenSecret(packageCredentials.token, config, platform);
|
|
912
|
-
if (secret.ok) {
|
|
913
|
-
Object.assign(config, secret.config || {});
|
|
914
|
-
delete config.gitlabToken;
|
|
915
|
-
console.log("Package access saved.");
|
|
916
|
-
} else {
|
|
917
|
-
config.gitlabToken = packageCredentials.token;
|
|
918
|
-
console.log("Package access saved in the local Vendian CLI config file.");
|
|
919
|
-
}
|
|
920
|
-
return true;
|
|
921
|
-
}
|
|
922
|
-
async function refreshPackageAccessFromCloudAuth({
|
|
923
|
-
config,
|
|
924
|
-
auth,
|
|
925
|
-
env = process.env,
|
|
926
|
-
platform = process.platform,
|
|
927
|
-
save = true
|
|
928
|
-
} = {}) {
|
|
929
|
-
const currentConfig = config || loadConfig(env, platform);
|
|
930
|
-
const registry = registryConfig(currentConfig, env, platform);
|
|
931
|
-
if (registry.token) {
|
|
932
|
-
return { config: currentConfig, refreshed: false };
|
|
933
|
-
}
|
|
934
|
-
const status = auth || activeCloudAuthStatus({ env, platform });
|
|
935
|
-
if (!status.authenticated || !status.apiUrl || !status.profile?.access_token) {
|
|
936
|
-
return { config: currentConfig, refreshed: false };
|
|
937
|
-
}
|
|
938
|
-
const packageCredentials = await fetchPackageCredentials({
|
|
939
|
-
apiUrl: status.apiUrl,
|
|
940
|
-
accessToken: status.profile.access_token
|
|
941
|
-
});
|
|
942
|
-
if (!packageCredentials?.token) {
|
|
943
|
-
return { config: currentConfig, refreshed: false };
|
|
944
|
-
}
|
|
945
|
-
const next = { ...currentConfig };
|
|
946
|
-
const stored = savePackageCredentials(next, packageCredentials, { platform });
|
|
947
|
-
if (stored && save) {
|
|
948
|
-
saveConfig(next, env, platform);
|
|
949
|
-
}
|
|
950
|
-
return { config: next, refreshed: stored };
|
|
951
|
-
}
|
|
952
|
-
function setupCompletionLines({ cloudAuthApiUrl, backend, apiUrl } = {}) {
|
|
953
|
-
const lines = ["Vendian login complete."];
|
|
954
|
-
if (cloudAuthApiUrl) {
|
|
955
|
-
lines.push(`Connected to ${cloudAuthApiUrl}.`);
|
|
956
|
-
lines.push("Next: vendian cloud local serve --agents-dir ./agents");
|
|
957
|
-
return lines;
|
|
958
|
-
}
|
|
959
|
-
lines.push(`Next: ${cloudAuthLoginCommand({ backend, apiUrl })}`);
|
|
960
|
-
lines.push("Then: vendian cloud local serve --agents-dir ./agents");
|
|
961
|
-
return lines;
|
|
962
|
-
}
|
|
963
|
-
function cloudAuthLoginCommand({ backend, apiUrl } = {}) {
|
|
964
|
-
if (apiUrl) {
|
|
965
|
-
return `vendian cloud auth login --api-url ${apiUrl}`;
|
|
966
|
-
}
|
|
967
|
-
if (backend) {
|
|
968
|
-
return `vendian cloud auth login --backend ${backend}`;
|
|
969
|
-
}
|
|
970
|
-
return "vendian cloud auth login";
|
|
971
|
-
}
|
|
972
|
-
|
|
973
|
-
// src/forward.js
|
|
974
|
-
var AUTO_UPDATE_INTERVAL_MS = 24 * 60 * 60 * 1e3;
|
|
975
|
-
async function forwardToPythonVendian(args, { env = process.env, platform = process.platform } = {}) {
|
|
976
|
-
const invocation = await preparePythonVendianInvocation(args, { env, platform });
|
|
977
|
-
const code = await spawnForward(invocation.command, invocation.args, {
|
|
978
|
-
env: invocation.env
|
|
979
|
-
});
|
|
980
|
-
recordForwardedDocsCommand(args, code, { env, platform });
|
|
981
|
-
process.exitCode = code;
|
|
982
|
-
}
|
|
983
|
-
async function preparePythonVendianInvocation(args, {
|
|
984
|
-
env = process.env,
|
|
985
|
-
platform = process.platform,
|
|
986
|
-
onProgress = null,
|
|
987
|
-
skipAutoUpdate = false
|
|
988
|
-
} = {}) {
|
|
989
|
-
const venvPath = managedVenvPath(env, platform);
|
|
990
|
-
const vendianPath = venvVendian(venvPath, platform);
|
|
991
|
-
reportProgress(onProgress, "Checking managed Python runtime");
|
|
992
|
-
if (!fs8.existsSync(vendianPath)) {
|
|
993
|
-
throw new Error("Vendian is not set up yet. Run `vendian login` first.");
|
|
994
|
-
}
|
|
995
|
-
const loadedConfig = loadConfig(env, platform);
|
|
996
|
-
const requiresPackageAccess = commandNeedsPackageAccess(args);
|
|
997
|
-
if (requiresPackageAccess) {
|
|
998
|
-
reportProgress(onProgress, "Refreshing package access");
|
|
999
|
-
}
|
|
1000
|
-
const refreshed = requiresPackageAccess ? await refreshPackageAccessFromCloudAuth({ config: loadedConfig, env, platform }) : { config: loadedConfig };
|
|
1001
|
-
if (!skipAutoUpdate) {
|
|
1002
|
-
maybeAutoUpdateManagedEnv({ env, platform, venvPath, onProgress });
|
|
1003
|
-
}
|
|
1004
|
-
const config = refreshed.config;
|
|
1005
|
-
reportProgress(onProgress, "Preparing package indexes");
|
|
1006
|
-
const registry = registryConfig(config, env, platform);
|
|
1007
|
-
if (requiresPackageAccess && !registry.token) {
|
|
1008
|
-
throw new Error("Package access is missing. Run `vendian login` or `vendian update` before starting local agents.");
|
|
1009
|
-
}
|
|
1010
|
-
reportProgress(onProgress, "Launching local serve daemon");
|
|
1011
|
-
return {
|
|
1012
|
-
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;
|
|
1144
|
+
console.log("Using saved package access.");
|
|
1064
1145
|
}
|
|
1065
|
-
const
|
|
1066
|
-
|
|
1067
|
-
|
|
1068
|
-
return false;
|
|
1146
|
+
const installRegistry = registryConfig(next, env, platform);
|
|
1147
|
+
if (!installRegistry.token) {
|
|
1148
|
+
throw new Error("Package access is missing. Run interactive `vendian login` to sign in.");
|
|
1069
1149
|
}
|
|
1070
|
-
const
|
|
1071
|
-
if (!
|
|
1072
|
-
|
|
1150
|
+
const python = findPython(platform);
|
|
1151
|
+
if (!python) {
|
|
1152
|
+
throw new Error("Python 3.11+ was not found. Install Python 3.11 or newer, then rerun `vendian login`.");
|
|
1073
1153
|
}
|
|
1074
|
-
const
|
|
1075
|
-
|
|
1076
|
-
|
|
1154
|
+
const venvPath = managedVenvPath(env, platform);
|
|
1155
|
+
console.log(`Managed environment: ${venvPath}`);
|
|
1156
|
+
const pythonPath = ensureVenv(venvPath, python, platform);
|
|
1157
|
+
console.log(`Python: ${pythonVersion(pythonPath) || pythonPath}`);
|
|
1158
|
+
saveConfig(next, env, platform);
|
|
1159
|
+
installVendianPackages({ pythonPath, venvPath, config: next, env, platform });
|
|
1160
|
+
const updated = { ...next, lastManagedUpdateAt: (/* @__PURE__ */ new Date()).toISOString() };
|
|
1161
|
+
refreshAgentDocsWorkspaces({ config: updated, venvPath, env, platform });
|
|
1162
|
+
if (!verifyVendianImports(pythonPath)) {
|
|
1163
|
+
throw new Error("Vendian packages installed, but import verification failed.");
|
|
1077
1164
|
}
|
|
1078
|
-
|
|
1079
|
-
|
|
1080
|
-
|
|
1081
|
-
|
|
1082
|
-
|
|
1083
|
-
|
|
1084
|
-
|
|
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;
|
|
1165
|
+
const vendianPath = venvVendian(venvPath, platform);
|
|
1166
|
+
if (!fs8.existsSync(vendianPath)) {
|
|
1167
|
+
throw new Error("Vendian executable was not found after install.");
|
|
1168
|
+
}
|
|
1169
|
+
console.log("");
|
|
1170
|
+
for (const line of setupCompletionLines({ cloudAuthApiUrl, backend, apiUrl })) {
|
|
1171
|
+
console.log(line);
|
|
1096
1172
|
}
|
|
1097
1173
|
}
|
|
1098
|
-
function
|
|
1099
|
-
if (
|
|
1100
|
-
|
|
1174
|
+
function savePackageCredentials(config, packageCredentials, { platform = process.platform } = {}) {
|
|
1175
|
+
if (!packageCredentials?.token) {
|
|
1176
|
+
return false;
|
|
1177
|
+
}
|
|
1178
|
+
config.gitlabHost = packageCredentials.gitlabHost || config.gitlabHost;
|
|
1179
|
+
config.gitlabUsername = packageCredentials.username || config.gitlabUsername;
|
|
1180
|
+
config.sdkPublicProjectId = packageCredentials.sdkProjectId || config.sdkPublicProjectId;
|
|
1181
|
+
config.sdkRuntimeProjectId = packageCredentials.runtimeProjectId || config.sdkRuntimeProjectId;
|
|
1182
|
+
config.vendianAgentsVersion = packageCredentials.vendianAgentsVersion || config.vendianAgentsVersion;
|
|
1183
|
+
config.vendianAgentsRuntimeVersion = packageCredentials.vendianAgentsRuntimeVersion || config.vendianAgentsRuntimeVersion;
|
|
1184
|
+
const secret = savePackageTokenSecret(packageCredentials.token, config, platform);
|
|
1185
|
+
if (secret.ok) {
|
|
1186
|
+
Object.assign(config, secret.config || {});
|
|
1187
|
+
delete config.gitlabToken;
|
|
1188
|
+
console.log("Package access saved.");
|
|
1189
|
+
} else {
|
|
1190
|
+
config.gitlabToken = packageCredentials.token;
|
|
1191
|
+
console.log("Package access saved in the local Vendian CLI config file.");
|
|
1101
1192
|
}
|
|
1193
|
+
return true;
|
|
1102
1194
|
}
|
|
1103
|
-
|
|
1104
|
-
|
|
1105
|
-
|
|
1106
|
-
|
|
1107
|
-
|
|
1108
|
-
|
|
1109
|
-
// src/version.js
|
|
1110
|
-
var CLI_VERSION = true ? "0.0.33" : 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
|
|
1195
|
+
async function refreshPackageAccessFromCloudAuth({
|
|
1196
|
+
config,
|
|
1197
|
+
auth,
|
|
1198
|
+
env = process.env,
|
|
1199
|
+
platform = process.platform,
|
|
1200
|
+
save = true
|
|
1141
1201
|
} = {}) {
|
|
1142
|
-
const
|
|
1143
|
-
const
|
|
1144
|
-
|
|
1145
|
-
|
|
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
|
-
});
|
|
1202
|
+
const currentConfig = config || loadConfig(env, platform);
|
|
1203
|
+
const registry = registryConfig(currentConfig, env, platform);
|
|
1204
|
+
if (registry.token) {
|
|
1205
|
+
return { config: currentConfig, refreshed: false };
|
|
1160
1206
|
}
|
|
1161
|
-
|
|
1162
|
-
if (
|
|
1163
|
-
|
|
1207
|
+
const status = auth || activeCloudAuthStatus({ env, platform });
|
|
1208
|
+
if (!status.authenticated || !status.apiUrl || !status.profile?.access_token) {
|
|
1209
|
+
return { config: currentConfig, refreshed: false };
|
|
1164
1210
|
}
|
|
1165
|
-
|
|
1166
|
-
|
|
1211
|
+
const packageCredentials = await fetchPackageCredentials({
|
|
1212
|
+
apiUrl: status.apiUrl,
|
|
1213
|
+
accessToken: status.profile.access_token
|
|
1214
|
+
});
|
|
1215
|
+
if (!packageCredentials?.token) {
|
|
1216
|
+
return { config: currentConfig, refreshed: false };
|
|
1167
1217
|
}
|
|
1168
|
-
const
|
|
1169
|
-
|
|
1170
|
-
|
|
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
|
-
}
|
|
1218
|
+
const next = { ...currentConfig };
|
|
1219
|
+
const stored = savePackageCredentials(next, packageCredentials, { platform });
|
|
1220
|
+
if (stored && save) {
|
|
1221
|
+
saveConfig(next, env, platform);
|
|
1180
1222
|
}
|
|
1181
|
-
return
|
|
1223
|
+
return { config: next, refreshed: stored };
|
|
1182
1224
|
}
|
|
1183
|
-
function
|
|
1184
|
-
|
|
1185
|
-
|
|
1186
|
-
|
|
1187
|
-
|
|
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
|
-
});
|
|
1225
|
+
function setupCompletionLines({ cloudAuthApiUrl, backend, apiUrl } = {}) {
|
|
1226
|
+
const lines = ["Vendian login complete."];
|
|
1227
|
+
if (cloudAuthApiUrl) {
|
|
1228
|
+
lines.push(`Connected to ${cloudAuthApiUrl}.`);
|
|
1229
|
+
lines.push("Next: vendian cloud local serve --agents-dir ./agents");
|
|
1230
|
+
return lines;
|
|
1208
1231
|
}
|
|
1209
|
-
|
|
1232
|
+
lines.push(`Next: ${cloudAuthLoginCommand({ backend, apiUrl })}`);
|
|
1233
|
+
lines.push("Then: vendian cloud local serve --agents-dir ./agents");
|
|
1234
|
+
return lines;
|
|
1210
1235
|
}
|
|
1211
|
-
function
|
|
1212
|
-
|
|
1213
|
-
|
|
1214
|
-
count += 1;
|
|
1215
|
-
if (count >= limit) {
|
|
1216
|
-
return count;
|
|
1217
|
-
}
|
|
1236
|
+
function cloudAuthLoginCommand({ backend, apiUrl } = {}) {
|
|
1237
|
+
if (apiUrl) {
|
|
1238
|
+
return `vendian cloud auth login --api-url ${apiUrl}`;
|
|
1218
1239
|
}
|
|
1219
|
-
|
|
1220
|
-
}
|
|
1221
|
-
function findManifestFiles(root, { maxDepth, maxDirs, limit }) {
|
|
1222
|
-
const manifests = [];
|
|
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
|
-
}
|
|
1240
|
+
if (backend) {
|
|
1241
|
+
return `vendian cloud auth login --backend ${backend}`;
|
|
1255
1242
|
}
|
|
1256
|
-
return
|
|
1243
|
+
return "vendian cloud auth login";
|
|
1244
|
+
}
|
|
1245
|
+
|
|
1246
|
+
// src/forward.js
|
|
1247
|
+
var AUTO_UPDATE_INTERVAL_MS = 24 * 60 * 60 * 1e3;
|
|
1248
|
+
async function forwardToPythonVendian(args, { env = process.env, platform = process.platform } = {}) {
|
|
1249
|
+
const invocation = await preparePythonVendianInvocation(args, { env, platform });
|
|
1250
|
+
const code = await spawnForward(invocation.command, invocation.args, {
|
|
1251
|
+
env: invocation.env
|
|
1252
|
+
});
|
|
1253
|
+
recordForwardedDocsCommand(args, code, { env, platform });
|
|
1254
|
+
process.exitCode = code;
|
|
1257
1255
|
}
|
|
1258
|
-
function
|
|
1259
|
-
|
|
1260
|
-
|
|
1261
|
-
|
|
1262
|
-
|
|
1263
|
-
|
|
1264
|
-
|
|
1265
|
-
|
|
1266
|
-
|
|
1267
|
-
|
|
1256
|
+
async function preparePythonVendianInvocation(args, {
|
|
1257
|
+
env = process.env,
|
|
1258
|
+
platform = process.platform,
|
|
1259
|
+
onProgress = null,
|
|
1260
|
+
skipAutoUpdate = false
|
|
1261
|
+
} = {}) {
|
|
1262
|
+
const venvPath = managedVenvPath(env, platform);
|
|
1263
|
+
const vendianPath = venvVendian(venvPath, platform);
|
|
1264
|
+
reportProgress(onProgress, "Checking managed Python runtime");
|
|
1265
|
+
if (!fs9.existsSync(vendianPath)) {
|
|
1266
|
+
throw new Error("Vendian is not set up yet. Run `vendian login` first.");
|
|
1267
|
+
}
|
|
1268
|
+
const loadedConfig = loadConfig(env, platform);
|
|
1269
|
+
const requiresPackageAccess = commandNeedsPackageAccess(args);
|
|
1270
|
+
if (requiresPackageAccess) {
|
|
1271
|
+
reportProgress(onProgress, "Refreshing package access");
|
|
1272
|
+
}
|
|
1273
|
+
const refreshed = requiresPackageAccess ? await refreshPackageAccessFromCloudAuth({ config: loadedConfig, env, platform }) : { config: loadedConfig };
|
|
1274
|
+
if (!skipAutoUpdate) {
|
|
1275
|
+
maybeAutoUpdateManagedEnv({ env, platform, venvPath, onProgress });
|
|
1276
|
+
}
|
|
1277
|
+
const config = refreshed.config;
|
|
1278
|
+
reportProgress(onProgress, "Preparing package indexes");
|
|
1279
|
+
const registry = registryConfig(config, env, platform);
|
|
1280
|
+
if (requiresPackageAccess && !registry.token) {
|
|
1281
|
+
throw new Error("Package access is missing. Run `vendian login` or `vendian update` before starting local agents.");
|
|
1282
|
+
}
|
|
1283
|
+
reportProgress(onProgress, "Launching local serve daemon");
|
|
1284
|
+
return {
|
|
1285
|
+
command: vendianPath,
|
|
1286
|
+
args,
|
|
1287
|
+
env: {
|
|
1288
|
+
...env,
|
|
1289
|
+
...packageIndexEnv(config, env, platform)
|
|
1268
1290
|
}
|
|
1269
|
-
|
|
1291
|
+
};
|
|
1292
|
+
}
|
|
1293
|
+
function commandNeedsPackageAccess(args = []) {
|
|
1294
|
+
return args[0] === "cloud" && args[1] === "local" && ["serve", "run"].includes(args[2]);
|
|
1295
|
+
}
|
|
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) {
|
|
@@ -2080,59 +2199,28 @@ function processExitMessage(event) {
|
|
|
2080
2199
|
return `Agent serve exited with code ${event.code}`;
|
|
2081
2200
|
}
|
|
2082
2201
|
if (event?.signal) {
|
|
2083
|
-
return `Agent serve exited from ${event.signal}`;
|
|
2084
|
-
}
|
|
2085
|
-
return "Agent serve exited";
|
|
2086
|
-
}
|
|
2087
|
-
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);
|
|
2202
|
+
return `Agent serve exited from ${event.signal}`;
|
|
2134
2203
|
}
|
|
2135
|
-
return
|
|
2204
|
+
return "Agent serve exited";
|
|
2205
|
+
}
|
|
2206
|
+
function agentPrepareLogMessage(event) {
|
|
2207
|
+
if (event.type === "agent_prepare_started") {
|
|
2208
|
+
return "Preparing agent";
|
|
2209
|
+
}
|
|
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,58 +2331,561 @@ 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.35" : 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
|
+
hasAgentPy: fs11.existsSync(path8.join(folder.absolutePath, "agent.py")),
|
|
2608
|
+
hasRequirements: fs11.existsSync(path8.join(folder.absolutePath, "requirements.txt")),
|
|
2609
|
+
runtimeStatus: runtime.status
|
|
2610
|
+
};
|
|
2611
|
+
});
|
|
2612
|
+
jsonResponse(res, { agents, agentsDir });
|
|
2613
|
+
}
|
|
2614
|
+
function apiLogs(req, res, parsed) {
|
|
2615
|
+
const since = parseInt(parsed.searchParams.get("since") || "0", 10);
|
|
2616
|
+
const lines = serveLogs.slice(since);
|
|
2617
|
+
jsonResponse(res, { logs: lines, total: serveLogs.length, since });
|
|
2618
|
+
}
|
|
2619
|
+
function apiAuth(req, res) {
|
|
2620
|
+
const active = activeCloudAuthStatus();
|
|
2621
|
+
const backends = Object.entries(BACKEND_TARGETS).filter(([key]) => !["localhost", "production"].includes(key)).map(([key, url]) => {
|
|
2622
|
+
const status = cloudAuthStatus({ backend: key });
|
|
2623
|
+
return {
|
|
2624
|
+
key,
|
|
2625
|
+
url,
|
|
2626
|
+
authenticated: status.authenticated,
|
|
2627
|
+
email: status.email || null,
|
|
2628
|
+
active: status.apiUrl === active.apiUrl && active.authenticated
|
|
2271
2629
|
};
|
|
2630
|
+
});
|
|
2631
|
+
jsonResponse(res, {
|
|
2632
|
+
authenticated: active.authenticated,
|
|
2633
|
+
email: active.email || null,
|
|
2634
|
+
apiUrl: active.apiUrl || null,
|
|
2635
|
+
backends
|
|
2636
|
+
});
|
|
2637
|
+
}
|
|
2638
|
+
async function apiWorkspaces(req, res) {
|
|
2639
|
+
const result = await listCloudWorkspaces({ timeoutMs: 15e3 });
|
|
2640
|
+
jsonResponse(res, result);
|
|
2641
|
+
}
|
|
2642
|
+
async function apiAuthSwitch(req, res, body) {
|
|
2643
|
+
const backend = body.backend;
|
|
2644
|
+
if (!backend || !BACKEND_TARGETS[backend]) {
|
|
2645
|
+
return jsonResponse(res, { ok: false, error: `Unknown backend: ${backend}` }, 400);
|
|
2272
2646
|
}
|
|
2273
2647
|
try {
|
|
2274
|
-
|
|
2275
|
-
|
|
2276
|
-
|
|
2277
|
-
|
|
2648
|
+
const { activateCloudProfile: activateCloudProfile2 } = await Promise.resolve().then(() => (init_auth(), auth_exports));
|
|
2649
|
+
const result = activateCloudProfile2({ backend });
|
|
2650
|
+
if (result?.activated) {
|
|
2651
|
+
jsonResponse(res, { ok: true, switched: true, backend });
|
|
2652
|
+
} else {
|
|
2653
|
+
jsonResponse(res, { ok: false, needsLogin: true, backend, url: BACKEND_TARGETS[backend] });
|
|
2654
|
+
}
|
|
2655
|
+
} catch (err) {
|
|
2656
|
+
jsonResponse(res, { ok: false, error: err.message });
|
|
2278
2657
|
}
|
|
2279
2658
|
}
|
|
2280
|
-
function
|
|
2281
|
-
const
|
|
2282
|
-
const
|
|
2283
|
-
|
|
2284
|
-
|
|
2285
|
-
|
|
2286
|
-
|
|
2287
|
-
|
|
2659
|
+
async function apiValidate(req, res, body) {
|
|
2660
|
+
const targetPath = body.path || agentsDir;
|
|
2661
|
+
const invocation = await preparePythonVendianInvocation(
|
|
2662
|
+
["validate", targetPath, "--json", "--no-runtime"],
|
|
2663
|
+
{ skipAutoUpdate: true }
|
|
2664
|
+
);
|
|
2665
|
+
const result = await runCaptureAsync(invocation.command, invocation.args, { env: invocation.env });
|
|
2666
|
+
try {
|
|
2667
|
+
const parsed = JSON.parse(result.stdout);
|
|
2668
|
+
jsonResponse(res, parsed);
|
|
2669
|
+
} catch {
|
|
2670
|
+
jsonResponse(res, {
|
|
2671
|
+
valid: false,
|
|
2672
|
+
errors: result.stderr ? [result.stderr.trim()] : ["Validation failed"],
|
|
2673
|
+
stdout: result.stdout
|
|
2674
|
+
});
|
|
2675
|
+
}
|
|
2288
2676
|
}
|
|
2289
|
-
function
|
|
2290
|
-
|
|
2291
|
-
|
|
2292
|
-
|
|
2677
|
+
async function apiServeStart(req, res, body) {
|
|
2678
|
+
if (serveChild && !serveChild.killed) {
|
|
2679
|
+
return jsonResponse(res, { ok: false, error: "Already serving" });
|
|
2680
|
+
}
|
|
2681
|
+
const targetDir = body.agentsDir || agentsDir;
|
|
2682
|
+
const targetCollection = body.collectionId || collectionId;
|
|
2683
|
+
try {
|
|
2684
|
+
const invocation = await preparePythonVendianInvocation(
|
|
2685
|
+
buildLocalServeEventStreamArgs({ agentsDir: targetDir, collectionId: targetCollection }),
|
|
2686
|
+
{ onProgress: null }
|
|
2687
|
+
);
|
|
2688
|
+
serveState = initialServeState();
|
|
2689
|
+
serveLogs = [];
|
|
2690
|
+
logStore = createServeLogStore({ agentsDir: targetDir, collectionId: targetCollection });
|
|
2691
|
+
serveChild = spawn3(invocation.command, invocation.args, {
|
|
2692
|
+
env: invocation.env,
|
|
2693
|
+
stdio: ["ignore", "pipe", "pipe"],
|
|
2694
|
+
shell: false
|
|
2695
|
+
});
|
|
2696
|
+
let buffer = "";
|
|
2697
|
+
serveChild.stdout.setEncoding("utf8");
|
|
2698
|
+
serveChild.stdout.on("data", (chunk) => {
|
|
2699
|
+
buffer += chunk;
|
|
2700
|
+
const lines = buffer.split(/\r?\n/);
|
|
2701
|
+
buffer = lines.pop() || "";
|
|
2702
|
+
for (const line of lines) {
|
|
2703
|
+
serveLogs.push(line);
|
|
2704
|
+
if (serveLogs.length > 5e3) serveLogs.shift();
|
|
2705
|
+
try {
|
|
2706
|
+
const event = parseServeEventLine(line);
|
|
2707
|
+
if (event) {
|
|
2708
|
+
serveState = applyServeEvent(serveState, event);
|
|
2709
|
+
try {
|
|
2710
|
+
logStore?.append(event);
|
|
2711
|
+
} catch {
|
|
2712
|
+
}
|
|
2713
|
+
}
|
|
2714
|
+
} catch {
|
|
2715
|
+
}
|
|
2716
|
+
}
|
|
2717
|
+
});
|
|
2718
|
+
serveChild.stderr.setEncoding("utf8");
|
|
2719
|
+
serveChild.stderr.on("data", (chunk) => {
|
|
2720
|
+
const text = chunk.trim();
|
|
2721
|
+
if (text) {
|
|
2722
|
+
serveLogs.push(`[stderr] ${text}`);
|
|
2723
|
+
if (serveLogs.length > 5e3) serveLogs.shift();
|
|
2724
|
+
}
|
|
2725
|
+
});
|
|
2726
|
+
serveChild.on("exit", (code, signal) => {
|
|
2727
|
+
serveState = { ...serveState, stopped: true, connected: false };
|
|
2728
|
+
serveLogs.push(`[process] Exited code=${code} signal=${signal || "none"}`);
|
|
2729
|
+
serveChild = null;
|
|
2730
|
+
try {
|
|
2731
|
+
logStore?.compact();
|
|
2732
|
+
} catch {
|
|
2733
|
+
}
|
|
2734
|
+
});
|
|
2735
|
+
jsonResponse(res, { ok: true, pid: serveChild.pid });
|
|
2736
|
+
} catch (err) {
|
|
2737
|
+
jsonResponse(res, { ok: false, error: err.message });
|
|
2738
|
+
}
|
|
2293
2739
|
}
|
|
2294
|
-
function
|
|
2295
|
-
|
|
2740
|
+
function apiServeStop(req, res) {
|
|
2741
|
+
if (!serveChild || serveChild.killed) {
|
|
2742
|
+
return jsonResponse(res, { ok: true, wasRunning: false });
|
|
2743
|
+
}
|
|
2744
|
+
serveChild.kill("SIGTERM");
|
|
2745
|
+
setTimeout(() => {
|
|
2746
|
+
if (serveChild && !serveChild.killed) {
|
|
2747
|
+
serveChild.kill("SIGKILL");
|
|
2748
|
+
}
|
|
2749
|
+
}, 3e3);
|
|
2750
|
+
jsonResponse(res, { ok: true, wasRunning: true });
|
|
2751
|
+
}
|
|
2752
|
+
async function apiCreate(req, res, body) {
|
|
2753
|
+
const name = (body.name || "").trim();
|
|
2754
|
+
if (!name) {
|
|
2755
|
+
return jsonResponse(res, { ok: false, error: "Name is required" }, 400);
|
|
2756
|
+
}
|
|
2757
|
+
try {
|
|
2758
|
+
const invocation = await preparePythonVendianInvocation(
|
|
2759
|
+
["create", name, "--output-dir", agentsDir],
|
|
2760
|
+
{ skipAutoUpdate: true }
|
|
2761
|
+
);
|
|
2762
|
+
const result = await runCaptureAsync(invocation.command, invocation.args, { env: invocation.env });
|
|
2763
|
+
if (result.code === 0) {
|
|
2764
|
+
jsonResponse(res, { ok: true, name, output: result.stdout });
|
|
2765
|
+
} else {
|
|
2766
|
+
jsonResponse(res, { ok: false, error: result.stderr || result.stdout || "Create failed" });
|
|
2767
|
+
}
|
|
2768
|
+
} catch (err) {
|
|
2769
|
+
jsonResponse(res, { ok: false, error: err.message });
|
|
2770
|
+
}
|
|
2771
|
+
}
|
|
2772
|
+
function jsonResponse(res, data, status = 200) {
|
|
2773
|
+
const body = JSON.stringify(data);
|
|
2774
|
+
res.writeHead(status, { "Content-Type": "application/json" });
|
|
2775
|
+
res.end(body);
|
|
2776
|
+
}
|
|
2777
|
+
function readBody(req) {
|
|
2778
|
+
return new Promise((resolve) => {
|
|
2779
|
+
const chunks = [];
|
|
2780
|
+
req.on("data", (chunk) => chunks.push(chunk));
|
|
2781
|
+
req.on("end", () => {
|
|
2782
|
+
const raw = Buffer.concat(chunks).toString("utf8");
|
|
2783
|
+
try {
|
|
2784
|
+
resolve(JSON.parse(raw));
|
|
2785
|
+
} catch {
|
|
2786
|
+
resolve({});
|
|
2787
|
+
}
|
|
2788
|
+
});
|
|
2789
|
+
});
|
|
2790
|
+
}
|
|
2791
|
+
function runCaptureAsync(command, args, options = {}) {
|
|
2792
|
+
return new Promise((resolve) => {
|
|
2793
|
+
const child = spawn3(command, args, {
|
|
2794
|
+
stdio: ["ignore", "pipe", "pipe"],
|
|
2795
|
+
shell: false,
|
|
2796
|
+
...options
|
|
2797
|
+
});
|
|
2798
|
+
let stdout = "";
|
|
2799
|
+
let stderr = "";
|
|
2800
|
+
child.stdout.on("data", (d) => {
|
|
2801
|
+
stdout += d;
|
|
2802
|
+
});
|
|
2803
|
+
child.stderr.on("data", (d) => {
|
|
2804
|
+
stderr += d;
|
|
2805
|
+
});
|
|
2806
|
+
child.on("exit", (code) => {
|
|
2807
|
+
resolve({ code: code ?? 1, stdout, stderr });
|
|
2808
|
+
});
|
|
2809
|
+
child.on("error", (err) => {
|
|
2810
|
+
resolve({ code: 1, stdout, stderr: err.message });
|
|
2811
|
+
});
|
|
2812
|
+
});
|
|
2813
|
+
}
|
|
2814
|
+
function parseManifestBasic(manifestPath) {
|
|
2815
|
+
try {
|
|
2816
|
+
const content = fs11.readFileSync(manifestPath, "utf8");
|
|
2817
|
+
const get = (key) => {
|
|
2818
|
+
const m = content.match(new RegExp(`^${key}\\s*:\\s*['"]?([^'"\\n#]+?)['"]?\\s*$`, "m"));
|
|
2819
|
+
return m ? m[1].trim() : "";
|
|
2820
|
+
};
|
|
2821
|
+
const getList = (key) => {
|
|
2822
|
+
const m = content.match(new RegExp(`^${key}\\s*:`, "m"));
|
|
2823
|
+
if (!m) return [];
|
|
2824
|
+
const after = content.slice(m.index + m[0].length);
|
|
2825
|
+
const lines = after.split("\n");
|
|
2826
|
+
const items = [];
|
|
2827
|
+
for (const line of lines) {
|
|
2828
|
+
if (/^\s+-\s/.test(line) || /^\s+\w+\s*:/.test(line)) {
|
|
2829
|
+
const val = line.replace(/^\s+[-]?\s*/, "").replace(/:.*$/, "").trim();
|
|
2830
|
+
if (val) items.push(val);
|
|
2831
|
+
} else if (/^\S/.test(line) && items.length > 0) {
|
|
2832
|
+
break;
|
|
2833
|
+
}
|
|
2834
|
+
}
|
|
2835
|
+
return items;
|
|
2836
|
+
};
|
|
2837
|
+
return {
|
|
2838
|
+
id: get("id"),
|
|
2839
|
+
name: get("name"),
|
|
2840
|
+
description: get("description"),
|
|
2841
|
+
version: get("version"),
|
|
2842
|
+
triggers: getList("triggers"),
|
|
2843
|
+
credentials: getList("credentials")
|
|
2844
|
+
};
|
|
2845
|
+
} catch {
|
|
2846
|
+
return {};
|
|
2847
|
+
}
|
|
2848
|
+
}
|
|
2849
|
+
function formatDaemonEvent(event) {
|
|
2850
|
+
switch (event.type) {
|
|
2851
|
+
case "daemon_registered":
|
|
2852
|
+
return "Connected to backend";
|
|
2853
|
+
case "daemon_event_stream_connected":
|
|
2854
|
+
return "Event stream connected";
|
|
2855
|
+
case "daemon_event_stream_lost":
|
|
2856
|
+
return `Event stream lost: ${event.error || "unknown"}`;
|
|
2857
|
+
case "inventory_synced":
|
|
2858
|
+
return `Inventory synced (${event.agentCount || 0} agents)`;
|
|
2859
|
+
case "agent_discovery_completed":
|
|
2860
|
+
return `Discovered ${event.agentCount || 0} agents`;
|
|
2861
|
+
case "backend_connection_lost":
|
|
2862
|
+
return `Connection lost: ${event.error || event.activity || "unknown"}`;
|
|
2863
|
+
case "backend_connection_restored":
|
|
2864
|
+
return "Connection restored";
|
|
2865
|
+
case "retry_scheduled":
|
|
2866
|
+
return `Retrying ${event.activity || "operation"} in ${event.delaySeconds || "?"}s`;
|
|
2867
|
+
default:
|
|
2868
|
+
return event.type;
|
|
2869
|
+
}
|
|
2870
|
+
}
|
|
2871
|
+
function openUrl(url) {
|
|
2872
|
+
const cmd = process.platform === "win32" ? "start" : process.platform === "darwin" ? "open" : "xdg-open";
|
|
2873
|
+
const args = process.platform === "win32" ? ["/c", "start", "", url] : [url];
|
|
2874
|
+
const shell = process.platform === "win32" ? "cmd.exe" : void 0;
|
|
2875
|
+
spawn3(shell || cmd, shell ? args : [url], { detached: true, stdio: "ignore" }).unref();
|
|
2296
2876
|
}
|
|
2297
2877
|
|
|
2878
|
+
// src/tui.js
|
|
2879
|
+
init_constants();
|
|
2880
|
+
init_auth();
|
|
2881
|
+
import { spawnSync as spawnSync4 } from "node:child_process";
|
|
2882
|
+
import fs12 from "node:fs";
|
|
2883
|
+
import path9 from "node:path";
|
|
2884
|
+
import readline from "node:readline";
|
|
2885
|
+
|
|
2886
|
+
// src/npm-update.js
|
|
2887
|
+
var NPM_CHECK_INTERVAL_MS = 30 * 60 * 1e3;
|
|
2888
|
+
|
|
2298
2889
|
// src/ui/figures.js
|
|
2299
2890
|
var supportsUnicode = process.platform !== "win32" || Boolean(
|
|
2300
2891
|
process.env.WT_SESSION || // Windows Terminal
|
|
@@ -2602,10 +3193,16 @@ var COMMAND_GROUPS = [
|
|
|
2602
3193
|
{ cmd: "vendian models", desc: "List available AI models" }
|
|
2603
3194
|
] },
|
|
2604
3195
|
{ section: "Running agents", commands: [
|
|
3196
|
+
{ cmd: "vendian dev", desc: "Open the dev UI in your browser" },
|
|
2605
3197
|
{ cmd: "vendian cloud local run --collection-id ID --path . --input-json '{}'", desc: "Run one agent" },
|
|
2606
3198
|
{ cmd: "vendian cloud local serve --agents-dir .", desc: "Start agents" },
|
|
2607
3199
|
{ cmd: "vendian login --backend staging", desc: "Sign in to staging" }
|
|
2608
3200
|
] },
|
|
3201
|
+
{ section: "Syncing & deploying", commands: [
|
|
3202
|
+
{ cmd: "vendian cloud local push --collection-id ID --project-id PID", desc: "Push code to project repo" },
|
|
3203
|
+
{ cmd: "vendian cloud local push --branch feature/x --from dev", desc: "Push to a new branch" },
|
|
3204
|
+
{ cmd: "vendian cloud local deploy --collection-id ID --project-id PID", desc: "Deploy to cloud" }
|
|
3205
|
+
] },
|
|
2609
3206
|
{ section: "Maintenance", commands: [
|
|
2610
3207
|
{ cmd: "vendian doctor", desc: "Check if everything is set up" },
|
|
2611
3208
|
{ cmd: "vendian update", desc: "Update to the latest version" }
|
|
@@ -2649,7 +3246,7 @@ async function pickProject({ env, platform, candidates }) {
|
|
|
2649
3246
|
print(col.bold(" Which project do you want to run?"));
|
|
2650
3247
|
print("");
|
|
2651
3248
|
const items = candidates.map((candidate) => {
|
|
2652
|
-
const name = readManifestName(candidate.absolutePath) || friendlyName(
|
|
3249
|
+
const name = readManifestName(candidate.absolutePath) || friendlyName(path9.basename(candidate.absolutePath) || candidate.path);
|
|
2653
3250
|
return `${name.padEnd(32)} ${col.gray(`${candidate.agentCount} agent${candidate.agentCount === 1 ? "" : "s"}`)}`;
|
|
2654
3251
|
});
|
|
2655
3252
|
const idx = await pickMenu(items, { allowBack: true });
|
|
@@ -2663,7 +3260,7 @@ async function pickAgentsFromFolder({ env, platform, candidate }) {
|
|
|
2663
3260
|
if (folders.length <= 1) return { agentsDir: candidate.path };
|
|
2664
3261
|
const named = folders.map((f) => ({
|
|
2665
3262
|
...f,
|
|
2666
|
-
displayName: readManifestName(f.absolutePath) || friendlyName(f.name ||
|
|
3263
|
+
displayName: readManifestName(f.absolutePath) || friendlyName(f.name || path9.basename(f.path))
|
|
2667
3264
|
}));
|
|
2668
3265
|
clearScreen();
|
|
2669
3266
|
printHeader({ env, platform });
|
|
@@ -2686,19 +3283,19 @@ async function pickSingleAgentToRun({ env, platform, candidates }) {
|
|
|
2686
3283
|
print(col.yellow(" Couldn't find any agents."));
|
|
2687
3284
|
const input = await ask("Agent folder", "./agents/my-agent");
|
|
2688
3285
|
if (!input) return null;
|
|
2689
|
-
return { path: input, displayName: friendlyName(
|
|
3286
|
+
return { path: input, displayName: friendlyName(path9.basename(input) || "Agent") };
|
|
2690
3287
|
}
|
|
2691
3288
|
const agents = candidates.flatMap((candidate) => {
|
|
2692
3289
|
const folders = findAgentFolders(candidate.absolutePath);
|
|
2693
3290
|
if (folders.length > 0) {
|
|
2694
3291
|
return folders.map((folder) => ({
|
|
2695
3292
|
path: folder.path,
|
|
2696
|
-
displayName: readManifestName(folder.absolutePath) || friendlyName(folder.name ||
|
|
3293
|
+
displayName: readManifestName(folder.absolutePath) || friendlyName(folder.name || path9.basename(folder.path))
|
|
2697
3294
|
}));
|
|
2698
3295
|
}
|
|
2699
3296
|
return [{
|
|
2700
3297
|
path: candidate.path,
|
|
2701
|
-
displayName: readManifestName(candidate.absolutePath) || friendlyName(
|
|
3298
|
+
displayName: readManifestName(candidate.absolutePath) || friendlyName(path9.basename(candidate.absolutePath) || candidate.path)
|
|
2702
3299
|
}];
|
|
2703
3300
|
});
|
|
2704
3301
|
if (agents.length === 1) return agents[0];
|
|
@@ -2798,7 +3395,7 @@ async function showServe({ env, platform }) {
|
|
|
2798
3395
|
const candidates = findAgentDirectoryCandidates();
|
|
2799
3396
|
const picked = await pickAgentsToRun({ env, platform, candidates });
|
|
2800
3397
|
if (!picked) return "home";
|
|
2801
|
-
const { agentsDir } = picked;
|
|
3398
|
+
const { agentsDir: agentsDir2 } = picked;
|
|
2802
3399
|
const workspace = await chooseCloudWorkspace({
|
|
2803
3400
|
env,
|
|
2804
3401
|
platform,
|
|
@@ -2806,9 +3403,9 @@ async function showServe({ env, platform }) {
|
|
|
2806
3403
|
pickerTitle: "Which project do you want to run?"
|
|
2807
3404
|
});
|
|
2808
3405
|
if (!workspace) return "home";
|
|
2809
|
-
return await runServeDashboard({ env, platform, agentsDir, collectionId: workspace.id, wsLabel: workspace.label });
|
|
3406
|
+
return await runServeDashboard({ env, platform, agentsDir: agentsDir2, collectionId: workspace.id, wsLabel: workspace.label });
|
|
2810
3407
|
}
|
|
2811
|
-
async function runServeDashboard({ env, platform, agentsDir, collectionId, wsLabel = "" }) {
|
|
3408
|
+
async function runServeDashboard({ env, platform, agentsDir: agentsDir2, collectionId: collectionId2, wsLabel = "" }) {
|
|
2812
3409
|
let state = initialServeState();
|
|
2813
3410
|
let child = null;
|
|
2814
3411
|
let ctrlCArmed = false;
|
|
@@ -2817,7 +3414,7 @@ async function runServeDashboard({ env, platform, agentsDir, collectionId, wsLab
|
|
|
2817
3414
|
let stopSignalAttempt = 0;
|
|
2818
3415
|
let stopTimer = null;
|
|
2819
3416
|
let dashboardClosed = false;
|
|
2820
|
-
let
|
|
3417
|
+
let logStore2 = null;
|
|
2821
3418
|
let lastAgentOnlinePrint = 0;
|
|
2822
3419
|
let agentsSeenOnline = false;
|
|
2823
3420
|
let initialSetupDone = false;
|
|
@@ -2827,8 +3424,8 @@ async function runServeDashboard({ env, platform, agentsDir, collectionId, wsLab
|
|
|
2827
3424
|
print("");
|
|
2828
3425
|
print(col.gray("\u2550".repeat(W)));
|
|
2829
3426
|
print(` ${col.cyan(col.bold("\u2191 VENDIAN"))} ${col.bold("Serving agents")}`);
|
|
2830
|
-
print(` ${col.gray("Dir:")} ${
|
|
2831
|
-
print(` ${col.gray("Workspace:")} ${wsLabel ||
|
|
3427
|
+
print(` ${col.gray("Dir:")} ${agentsDir2}`);
|
|
3428
|
+
print(` ${col.gray("Workspace:")} ${wsLabel || collectionId2 || "\u2014"}`);
|
|
2832
3429
|
print(col.gray(hr(W)));
|
|
2833
3430
|
print(col.gray(" S stop gracefully | ^c ^c exit"));
|
|
2834
3431
|
print(col.gray(hr(W)));
|
|
@@ -2855,7 +3452,7 @@ async function runServeDashboard({ env, platform, agentsDir, collectionId, wsLab
|
|
|
2855
3452
|
if (!event) return;
|
|
2856
3453
|
const type = String(event.type || "");
|
|
2857
3454
|
const agentPath = String(event.relativePath || event.path || "");
|
|
2858
|
-
const agentName = agentPath ? friendlyName(
|
|
3455
|
+
const agentName = agentPath ? friendlyName(path9.basename(agentPath)) || agentPath : ".";
|
|
2859
3456
|
if (type === "daemon_wake_received") return;
|
|
2860
3457
|
if (type === "lease_claim_no_job") return;
|
|
2861
3458
|
if (type === "dispatch_job_received" || type === "dispatch_job_acked") return;
|
|
@@ -2866,7 +3463,7 @@ async function runServeDashboard({ env, platform, agentsDir, collectionId, wsLab
|
|
|
2866
3463
|
const now = Date.now();
|
|
2867
3464
|
if (!hasProblems && allOnline && agentsSeenOnline && now - lastAgentOnlinePrint < 12e4) return;
|
|
2868
3465
|
for (const a of agents) {
|
|
2869
|
-
const name = friendlyName(
|
|
3466
|
+
const name = friendlyName(path9.basename(a.relativePath || ".")) || a.manifestName || a.relativePath || "?";
|
|
2870
3467
|
if (a.status === "error") {
|
|
2871
3468
|
printAgent(name, col.red(`\u2716 Error: ${a.errorMessage || ""}`));
|
|
2872
3469
|
continue;
|
|
@@ -2945,15 +3542,15 @@ async function runServeDashboard({ env, platform, agentsDir, collectionId, wsLab
|
|
|
2945
3542
|
}
|
|
2946
3543
|
}
|
|
2947
3544
|
try {
|
|
2948
|
-
|
|
2949
|
-
const historicalLogs =
|
|
3545
|
+
logStore2 = createServeLogStore({ agentsDir: agentsDir2, collectionId: collectionId2, env, platform });
|
|
3546
|
+
const historicalLogs = logStore2.load();
|
|
2950
3547
|
if (historicalLogs.length > 0) {
|
|
2951
3548
|
const agentLogs = mergeAgentLogRecords(state.agentLogs, historicalLogs);
|
|
2952
3549
|
state = { ...state, agentLogs, agentRunState: { ...agentRunStateFromLogs(agentLogs, { includeRunning: false }), ...state.agentRunState } };
|
|
2953
3550
|
}
|
|
2954
3551
|
child = await spawnLocalServeEventStream({
|
|
2955
|
-
agentsDir,
|
|
2956
|
-
collectionId,
|
|
3552
|
+
agentsDir: agentsDir2,
|
|
3553
|
+
collectionId: collectionId2,
|
|
2957
3554
|
env,
|
|
2958
3555
|
platform,
|
|
2959
3556
|
onProgress: (msg) => {
|
|
@@ -2962,7 +3559,7 @@ async function runServeDashboard({ env, platform, agentsDir, collectionId, wsLab
|
|
|
2962
3559
|
}
|
|
2963
3560
|
});
|
|
2964
3561
|
process.stdout.write("\r" + " ".repeat(80) + "\r");
|
|
2965
|
-
printDaemon("serve_started", col.gray(`dir=${
|
|
3562
|
+
printDaemon("serve_started", col.gray(`dir=${agentsDir2}`));
|
|
2966
3563
|
} catch (error) {
|
|
2967
3564
|
setCursorVisible(true);
|
|
2968
3565
|
print(col.red(` ${fig.cross} Failed to start: ${errMsg(error)}`));
|
|
@@ -2991,9 +3588,13 @@ async function runServeDashboard({ env, platform, agentsDir, collectionId, wsLab
|
|
|
2991
3588
|
if (!stopRequested || dashboardClosed || !childIsRunning()) return;
|
|
2992
3589
|
stopSignalAttempt += 1;
|
|
2993
3590
|
const signal = stopSignalForAttempt(stopSignalAttempt);
|
|
2994
|
-
|
|
2995
|
-
child
|
|
2996
|
-
}
|
|
3591
|
+
if (process.platform === "win32") {
|
|
3592
|
+
killProcessTree(child);
|
|
3593
|
+
} else {
|
|
3594
|
+
try {
|
|
3595
|
+
child.kill(signal);
|
|
3596
|
+
} catch {
|
|
3597
|
+
}
|
|
2997
3598
|
}
|
|
2998
3599
|
if (signal !== "SIGKILL") scheduleStopEscalation();
|
|
2999
3600
|
}, 4e3);
|
|
@@ -3009,18 +3610,25 @@ async function runServeDashboard({ env, platform, agentsDir, collectionId, wsLab
|
|
|
3009
3610
|
print(col.yellow("\n Stopping your agents\u2026"));
|
|
3010
3611
|
clearTimeout(ctrlCArmTimer);
|
|
3011
3612
|
ctrlCArmed = false;
|
|
3012
|
-
|
|
3013
|
-
child
|
|
3014
|
-
}
|
|
3613
|
+
if (process.platform === "win32") {
|
|
3614
|
+
killProcessTree(child);
|
|
3615
|
+
} else {
|
|
3616
|
+
try {
|
|
3617
|
+
child.kill(stopSignalForAttempt(stopSignalAttempt));
|
|
3618
|
+
} catch {
|
|
3619
|
+
}
|
|
3620
|
+
scheduleStopEscalation();
|
|
3015
3621
|
}
|
|
3016
|
-
scheduleStopEscalation();
|
|
3017
3622
|
}
|
|
3018
3623
|
if (process.stdin.isTTY) {
|
|
3019
3624
|
let onKeypress = function(str, key) {
|
|
3020
3625
|
if (!key) return;
|
|
3021
3626
|
if (key.ctrl && key.name === "c") {
|
|
3022
3627
|
if (ctrlCArmed) {
|
|
3023
|
-
|
|
3628
|
+
try {
|
|
3629
|
+
child?.kill("SIGINT");
|
|
3630
|
+
} catch (error) {
|
|
3631
|
+
}
|
|
3024
3632
|
finish("exit");
|
|
3025
3633
|
return;
|
|
3026
3634
|
}
|
|
@@ -3060,7 +3668,7 @@ async function runServeDashboard({ env, platform, agentsDir, collectionId, wsLab
|
|
|
3060
3668
|
const event = parseServeEventLine(line);
|
|
3061
3669
|
if (event) {
|
|
3062
3670
|
try {
|
|
3063
|
-
|
|
3671
|
+
logStore2?.append(event);
|
|
3064
3672
|
} catch {
|
|
3065
3673
|
}
|
|
3066
3674
|
state = applyServeEvent(state, event);
|
|
@@ -3102,7 +3710,7 @@ async function runServeDashboard({ env, platform, agentsDir, collectionId, wsLab
|
|
|
3102
3710
|
print(col.gray("\n Agents stopped."));
|
|
3103
3711
|
}
|
|
3104
3712
|
try {
|
|
3105
|
-
|
|
3713
|
+
logStore2?.compact();
|
|
3106
3714
|
} catch {
|
|
3107
3715
|
}
|
|
3108
3716
|
removeKeypress();
|
|
@@ -3238,7 +3846,7 @@ function endpointErrorStatus(error) {
|
|
|
3238
3846
|
}
|
|
3239
3847
|
function buildLocalRunArgs({
|
|
3240
3848
|
agentPath = ".",
|
|
3241
|
-
collectionId = "",
|
|
3849
|
+
collectionId: collectionId2 = "",
|
|
3242
3850
|
inputJson = "{}"
|
|
3243
3851
|
} = {}) {
|
|
3244
3852
|
return [
|
|
@@ -3246,7 +3854,7 @@ function buildLocalRunArgs({
|
|
|
3246
3854
|
"local",
|
|
3247
3855
|
"run",
|
|
3248
3856
|
"--collection-id",
|
|
3249
|
-
|
|
3857
|
+
collectionId2,
|
|
3250
3858
|
"--path",
|
|
3251
3859
|
agentPath || ".",
|
|
3252
3860
|
"--input-json",
|
|
@@ -3258,31 +3866,51 @@ function stopSignalForAttempt(attempt = 0) {
|
|
|
3258
3866
|
if (attempt === 1) return "SIGTERM";
|
|
3259
3867
|
return "SIGKILL";
|
|
3260
3868
|
}
|
|
3869
|
+
function killProcessTree(child) {
|
|
3870
|
+
if (!child || child.exitCode != null || child.signalCode != null) return;
|
|
3871
|
+
if (process.platform === "win32") {
|
|
3872
|
+
try {
|
|
3873
|
+
spawnSync4("taskkill", ["/F", "/T", "/PID", String(child.pid)], { stdio: "ignore" });
|
|
3874
|
+
} catch {
|
|
3875
|
+
}
|
|
3876
|
+
} else {
|
|
3877
|
+
try {
|
|
3878
|
+
child.kill("SIGTERM");
|
|
3879
|
+
} catch {
|
|
3880
|
+
}
|
|
3881
|
+
}
|
|
3882
|
+
}
|
|
3261
3883
|
function helpText() {
|
|
3262
3884
|
return [
|
|
3263
3885
|
"",
|
|
3264
3886
|
` \u2191 VENDIAN CLI v${CLI_VERSION}`,
|
|
3265
3887
|
"",
|
|
3266
3888
|
" Usage:",
|
|
3267
|
-
" vendian Open the
|
|
3889
|
+
" vendian Open the dev UI in your browser",
|
|
3890
|
+
" vendian terminal Open the interactive terminal shell",
|
|
3268
3891
|
" vendian login Sign in and prepare the local runtime",
|
|
3269
3892
|
" vendian doctor Check local bootstrap health",
|
|
3270
3893
|
" vendian update Update the managed runtime",
|
|
3271
3894
|
" vendian init Write SDK agent docs into a workspace",
|
|
3272
3895
|
' vendian create "My Agent" Scaffold a new agent from templates',
|
|
3896
|
+
" vendian dev Open the dev UI (alias for bare vendian)",
|
|
3273
3897
|
" vendian <command> Run a managed Python SDK/cloud command",
|
|
3274
3898
|
"",
|
|
3275
3899
|
" Examples:",
|
|
3900
|
+
" vendian",
|
|
3901
|
+
" vendian terminal",
|
|
3276
3902
|
" vendian login",
|
|
3277
3903
|
" vendian init --output-dir ./agents",
|
|
3278
3904
|
' vendian create "My Agent" --output-dir ./agents',
|
|
3279
3905
|
" vendian validate ./agents/my-agent --runtime",
|
|
3280
3906
|
" vendian models",
|
|
3281
3907
|
" vendian cloud local serve --agents-dir ./agents",
|
|
3908
|
+
" vendian cloud local push --collection-id ID --project-id PID --branch dev",
|
|
3909
|
+
" vendian cloud local push --branch feature/x --from dev",
|
|
3282
3910
|
" vendian doctor",
|
|
3283
3911
|
" vendian update",
|
|
3284
3912
|
"",
|
|
3285
|
-
` Run \u203A vendian \u203A
|
|
3913
|
+
` Run \u203A vendian terminal \u203A for the interactive terminal shell`,
|
|
3286
3914
|
""
|
|
3287
3915
|
].join("\n");
|
|
3288
3916
|
}
|
|
@@ -3293,7 +3921,7 @@ function readManifestName(absoluteFolderPath) {
|
|
|
3293
3921
|
if (!absoluteFolderPath) return null;
|
|
3294
3922
|
for (const fname of ["manifest.yaml", "manifest.yml"]) {
|
|
3295
3923
|
try {
|
|
3296
|
-
const content =
|
|
3924
|
+
const content = fs12.readFileSync(path9.join(absoluteFolderPath, fname), "utf8");
|
|
3297
3925
|
const m = content.match(/^name\s*:\s*['"]?([^'"\n#]+?)['"]?\s*$/m);
|
|
3298
3926
|
if (m?.[1]) return m[1].trim();
|
|
3299
3927
|
} catch {
|
|
@@ -3341,11 +3969,7 @@ Environment:
|
|
|
3341
3969
|
async function main(argv) {
|
|
3342
3970
|
const [command, ...rest] = argv;
|
|
3343
3971
|
if (!command) {
|
|
3344
|
-
|
|
3345
|
-
await runTui();
|
|
3346
|
-
} else {
|
|
3347
|
-
printHelp();
|
|
3348
|
-
}
|
|
3972
|
+
await startDevServer(parseDevOptions([]));
|
|
3349
3973
|
return;
|
|
3350
3974
|
}
|
|
3351
3975
|
if (command === "--help" || command === "-h") {
|
|
@@ -3356,10 +3980,22 @@ async function main(argv) {
|
|
|
3356
3980
|
console.log(CLI_VERSION);
|
|
3357
3981
|
return;
|
|
3358
3982
|
}
|
|
3983
|
+
if (command === "terminal" || command === "tui") {
|
|
3984
|
+
if (process.stdin.isTTY && process.stdout.isTTY) {
|
|
3985
|
+
await runTui();
|
|
3986
|
+
} else {
|
|
3987
|
+
printHelp();
|
|
3988
|
+
}
|
|
3989
|
+
return;
|
|
3990
|
+
}
|
|
3359
3991
|
if (isBootstrapCommand(command)) {
|
|
3360
3992
|
await setup(parseSetupOptions(rest));
|
|
3361
3993
|
return;
|
|
3362
3994
|
}
|
|
3995
|
+
if (command === "dev") {
|
|
3996
|
+
await startDevServer(parseDevOptions(rest));
|
|
3997
|
+
return;
|
|
3998
|
+
}
|
|
3363
3999
|
if (command === "doctor") {
|
|
3364
4000
|
doctor();
|
|
3365
4001
|
return;
|
|
@@ -3368,11 +4004,33 @@ async function main(argv) {
|
|
|
3368
4004
|
await setup({ nonInteractive: true });
|
|
3369
4005
|
return;
|
|
3370
4006
|
}
|
|
4007
|
+
if (command.startsWith("--port") || command.startsWith("-p") || command === "--no-open" || command === "--agents-dir" || command === "--agents") {
|
|
4008
|
+
await startDevServer(parseDevOptions(argv));
|
|
4009
|
+
return;
|
|
4010
|
+
}
|
|
3371
4011
|
await forwardToPythonVendian(argv);
|
|
3372
4012
|
}
|
|
3373
4013
|
function isBootstrapCommand(command) {
|
|
3374
4014
|
return command === "login" || command === "setup";
|
|
3375
4015
|
}
|
|
4016
|
+
function parseDevOptions(args) {
|
|
4017
|
+
const options = {};
|
|
4018
|
+
for (let i = 0; i < args.length; i++) {
|
|
4019
|
+
const arg = args[i];
|
|
4020
|
+
if (arg === "--port" || arg === "-p") {
|
|
4021
|
+
options.port = parseInt(args[++i], 10) || 3859;
|
|
4022
|
+
} else if (arg.startsWith("--port=")) {
|
|
4023
|
+
options.port = parseInt(arg.slice(7), 10) || 3859;
|
|
4024
|
+
} else if (arg === "--agents-dir" || arg === "--agents") {
|
|
4025
|
+
options.agents = args[++i];
|
|
4026
|
+
} else if (arg.startsWith("--agents-dir=")) {
|
|
4027
|
+
options.agents = arg.slice(13);
|
|
4028
|
+
} else if (arg === "--no-open") {
|
|
4029
|
+
options.noOpen = true;
|
|
4030
|
+
}
|
|
4031
|
+
}
|
|
4032
|
+
return options;
|
|
4033
|
+
}
|
|
3376
4034
|
function parseSetupOptions(args) {
|
|
3377
4035
|
const options = {
|
|
3378
4036
|
nonInteractive: args.includes("--yes") || args.includes("--non-interactive"),
|