@vm0/cli 9.244.5 → 9.244.7
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/{agent-NQVS6VCY.js → agent-2TIFNP32.js} +7 -7
- package/{banking-U5U2G6QH.js → banking-UR7XJI3L.js} +3 -3
- package/{chat-BGF7IB3O.js → chat-5N4C3FDV.js} +4 -4
- package/{chunk-UEGRDQR3.js → chunk-26BGLMA3.js} +4 -32
- package/chunk-26BGLMA3.js.map +1 -0
- package/{chunk-ZMTKJ4VW.js → chunk-5QJVQEHO.js} +69 -6
- package/chunk-5QJVQEHO.js.map +1 -0
- package/{chunk-SBH7L4UT.js → chunk-7KSTVBNN.js} +4 -4
- package/{chunk-624QMD56.js → chunk-BIJC6U3G.js} +2 -2
- package/{chunk-KS62N6VR.js → chunk-E32I3TNQ.js} +26 -129
- package/chunk-E32I3TNQ.js.map +1 -0
- package/{chunk-4V373IPM.js → chunk-JWDLLCI3.js} +2 -2
- package/{chunk-PBFD5MP4.js → chunk-M6GLCPCJ.js} +3 -3
- package/{chunk-VUUGKMER.js → chunk-NR6ANGRK.js} +41 -245
- package/chunk-NR6ANGRK.js.map +1 -0
- package/{chunk-NOUED6XP.js → chunk-QCG2H3VD.js} +2 -2
- package/{chunk-MKPBFN7W.js → chunk-STMJYQTH.js} +2 -2
- package/chunk-ZUSDQG2O.js +50 -0
- package/chunk-ZUSDQG2O.js.map +1 -0
- package/{computer-use-F4VFLO6X.js → computer-use-3WUKJYHK.js} +3 -3
- package/{connector-SWU3H7YL.js → connector-PDEOFNTE.js} +9 -21
- package/connector-PDEOFNTE.js.map +1 -0
- package/{credit-ZMXGA5CQ.js → credit-ET4RS32X.js} +4 -4
- package/{developer-support-IOTX3JWR.js → developer-support-3DJEJT2A.js} +3 -3
- package/{doctor-DTMFWDXL.js → doctor-F7OWYXXH.js} +3 -3
- package/{generate-IQUCOIYW.js → generate-4GKWSL6P.js} +4 -4
- package/{github-AJT3E56L.js → github-S5HFK2IC.js} +3 -3
- package/{goal-NCK5T57P.js → goal-ISJWIT2Q.js} +3 -3
- package/{host-BRKGWZLP.js → host-T52XQ2SZ.js} +4 -5
- package/{host-BRKGWZLP.js.map → host-T52XQ2SZ.js.map} +1 -1
- package/index.js +12 -2040
- package/index.js.map +1 -1
- package/{logs-WZVT4BS7.js → logs-D4IL5OLR.js} +8 -8
- package/{mail-QS4HABXH.js → mail-JXO3GW72.js} +5 -5
- package/{maps-3X6V34JQ.js → maps-T3YFBRLP.js} +3 -3
- package/{model-6ETRDASM.js → model-GJRTV6E5.js} +4 -4
- package/{model-provider-XEKOLWZY.js → model-provider-T4VN4HNU.js} +4 -4
- package/{org-DKGPME4L.js → org-GNNU77Y5.js} +3 -3
- package/package.json +1 -1
- package/{phone-6455TFON.js → phone-EQ4R2GBO.js} +3 -3
- package/{preference-2XMANS52.js → preference-3FHEQPKG.js} +3 -3
- package/{chunk-CK6ATTWW.js → resource-EOO2VATC.js} +133 -4
- package/resource-EOO2VATC.js.map +1 -0
- package/{scrape-CIP2FZED.js → scrape-TZCFJY6X.js} +3 -3
- package/{search-GCQ55FYS.js → search-RXP4DMUC.js} +7 -7
- package/{secret-NVLEVYXQ.js → secret-S4ZCVY2X.js} +3 -3
- package/{slack-EGYW76W2.js → slack-7YJFUJE6.js} +3 -3
- package/{teams-2D63XX2R.js → teams-45MMUROF.js} +3 -3
- package/{telegram-GQARG3MD.js → telegram-7ZCSI24W.js} +3 -3
- package/{variable-WMKLX7TF.js → variable-O33TDP4J.js} +3 -3
- package/{video-NAUNUYGP.js → video-D5LSABFJ.js} +2 -2
- package/{web-7U3YS3KP.js → web-JW7AH2UT.js} +3 -3
- package/{web-search-EIO3GVKH.js → web-search-MHFQFZS6.js} +3 -3
- package/{whoami-QZJAGTDW.js → whoami-5DJLOQUZ.js} +5 -5
- package/{workflow-RMJIYOUB.js → workflow-TXG3RBJR.js} +3 -3
- package/zero.js +35 -35
- package/chunk-CK6ATTWW.js.map +0 -1
- package/chunk-KS62N6VR.js.map +0 -1
- package/chunk-T6TZMDU6.js +0 -132
- package/chunk-T6TZMDU6.js.map +0 -1
- package/chunk-UEGRDQR3.js.map +0 -1
- package/chunk-VUUGKMER.js.map +0 -1
- package/chunk-ZMTKJ4VW.js.map +0 -1
- package/connector-SWU3H7YL.js.map +0 -1
- package/resource-MIFLTTK6.js +0 -143
- package/resource-MIFLTTK6.js.map +0 -1
- /package/{agent-NQVS6VCY.js.map → agent-2TIFNP32.js.map} +0 -0
- /package/{banking-U5U2G6QH.js.map → banking-UR7XJI3L.js.map} +0 -0
- /package/{chat-BGF7IB3O.js.map → chat-5N4C3FDV.js.map} +0 -0
- /package/{chunk-SBH7L4UT.js.map → chunk-7KSTVBNN.js.map} +0 -0
- /package/{chunk-624QMD56.js.map → chunk-BIJC6U3G.js.map} +0 -0
- /package/{chunk-4V373IPM.js.map → chunk-JWDLLCI3.js.map} +0 -0
- /package/{chunk-PBFD5MP4.js.map → chunk-M6GLCPCJ.js.map} +0 -0
- /package/{chunk-NOUED6XP.js.map → chunk-QCG2H3VD.js.map} +0 -0
- /package/{chunk-MKPBFN7W.js.map → chunk-STMJYQTH.js.map} +0 -0
- /package/{computer-use-F4VFLO6X.js.map → computer-use-3WUKJYHK.js.map} +0 -0
- /package/{credit-ZMXGA5CQ.js.map → credit-ET4RS32X.js.map} +0 -0
- /package/{developer-support-IOTX3JWR.js.map → developer-support-3DJEJT2A.js.map} +0 -0
- /package/{doctor-DTMFWDXL.js.map → doctor-F7OWYXXH.js.map} +0 -0
- /package/{generate-IQUCOIYW.js.map → generate-4GKWSL6P.js.map} +0 -0
- /package/{github-AJT3E56L.js.map → github-S5HFK2IC.js.map} +0 -0
- /package/{goal-NCK5T57P.js.map → goal-ISJWIT2Q.js.map} +0 -0
- /package/{logs-WZVT4BS7.js.map → logs-D4IL5OLR.js.map} +0 -0
- /package/{mail-QS4HABXH.js.map → mail-JXO3GW72.js.map} +0 -0
- /package/{maps-3X6V34JQ.js.map → maps-T3YFBRLP.js.map} +0 -0
- /package/{model-6ETRDASM.js.map → model-GJRTV6E5.js.map} +0 -0
- /package/{model-provider-XEKOLWZY.js.map → model-provider-T4VN4HNU.js.map} +0 -0
- /package/{org-DKGPME4L.js.map → org-GNNU77Y5.js.map} +0 -0
- /package/{phone-6455TFON.js.map → phone-EQ4R2GBO.js.map} +0 -0
- /package/{preference-2XMANS52.js.map → preference-3FHEQPKG.js.map} +0 -0
- /package/{scrape-CIP2FZED.js.map → scrape-TZCFJY6X.js.map} +0 -0
- /package/{search-GCQ55FYS.js.map → search-RXP4DMUC.js.map} +0 -0
- /package/{secret-NVLEVYXQ.js.map → secret-S4ZCVY2X.js.map} +0 -0
- /package/{slack-EGYW76W2.js.map → slack-7YJFUJE6.js.map} +0 -0
- /package/{teams-2D63XX2R.js.map → teams-45MMUROF.js.map} +0 -0
- /package/{telegram-GQARG3MD.js.map → telegram-7ZCSI24W.js.map} +0 -0
- /package/{variable-WMKLX7TF.js.map → variable-O33TDP4J.js.map} +0 -0
- /package/{video-NAUNUYGP.js.map → video-D5LSABFJ.js.map} +0 -0
- /package/{web-7U3YS3KP.js.map → web-JW7AH2UT.js.map} +0 -0
- /package/{web-search-EIO3GVKH.js.map → web-search-MHFQFZS6.js.map} +0 -0
- /package/{whoami-QZJAGTDW.js.map → whoami-5DJLOQUZ.js.map} +0 -0
- /package/{workflow-RMJIYOUB.js.map → workflow-TXG3RBJR.js.map} +0 -0
package/index.js
CHANGED
|
@@ -2,84 +2,41 @@
|
|
|
2
2
|
import { createRequire as __createRequire } from "node:module";
|
|
3
3
|
const require = __createRequire(import.meta.url);
|
|
4
4
|
import {
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
formatBytes,
|
|
8
|
-
formatRelativeTime,
|
|
9
|
-
listTarFiles,
|
|
10
|
-
removeExtraFiles
|
|
11
|
-
} from "./chunk-T6TZMDU6.js";
|
|
5
|
+
formatRelativeTime
|
|
6
|
+
} from "./chunk-ZUSDQG2O.js";
|
|
12
7
|
import {
|
|
13
|
-
EventRenderer,
|
|
14
|
-
EventStreamNormalizer,
|
|
15
8
|
collectArtifacts,
|
|
16
9
|
collectKeyValue,
|
|
17
|
-
collectLogItems,
|
|
18
10
|
collectMounts,
|
|
19
11
|
collectVolumeVersions,
|
|
20
|
-
extractAndGroupVariables,
|
|
21
12
|
extractSecretNames,
|
|
22
13
|
extractVarNames,
|
|
23
|
-
formatIsoTimestamp,
|
|
24
|
-
getInstructionsFilename,
|
|
25
|
-
getInstructionsStorageName,
|
|
26
|
-
isSupportedFramework,
|
|
27
14
|
isUUID,
|
|
28
15
|
loadValues,
|
|
29
|
-
parseBoundedLogCount,
|
|
30
16
|
parseIdentifier,
|
|
31
17
|
parsePermissionPolicies,
|
|
32
|
-
parsePositiveLogCount,
|
|
33
|
-
parseSearchQuery,
|
|
34
18
|
parseTime,
|
|
35
19
|
pollEvents,
|
|
36
20
|
renderRunCreated,
|
|
37
|
-
require_dist,
|
|
38
21
|
showNextSteps
|
|
39
|
-
} from "./chunk-
|
|
40
|
-
import {
|
|
41
|
-
Qn,
|
|
42
|
-
So
|
|
43
|
-
} from "./chunk-CK6ATTWW.js";
|
|
22
|
+
} from "./chunk-E32I3TNQ.js";
|
|
44
23
|
import "./chunk-DWWE7STW.js";
|
|
45
|
-
import {
|
|
46
|
-
isInteractive,
|
|
47
|
-
promptText
|
|
48
|
-
} from "./chunk-JFY6GK7Y.js";
|
|
49
24
|
import {
|
|
50
25
|
ALL_RUN_STATUSES,
|
|
51
|
-
agentDefinitionSchema,
|
|
52
26
|
cancelRun,
|
|
53
|
-
commitStorage,
|
|
54
|
-
createOrUpdateCompose,
|
|
55
27
|
createRun,
|
|
56
28
|
directRunModelProviderTypeSchema,
|
|
57
|
-
getAgentEvents,
|
|
58
29
|
getCheckpoint,
|
|
59
30
|
getComposeById,
|
|
60
31
|
getComposeByName,
|
|
61
32
|
getComposeVersion,
|
|
62
|
-
getMetrics,
|
|
63
|
-
getNetworkLogs,
|
|
64
33
|
getRunQueue,
|
|
65
34
|
getSession,
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
guaranteedConnectorProvidedBindingNames,
|
|
69
|
-
listRuns,
|
|
70
|
-
listStorages,
|
|
71
|
-
listZeroConnectors,
|
|
72
|
-
listZeroSecrets,
|
|
73
|
-
listZeroVariables,
|
|
74
|
-
prepareStorage,
|
|
75
|
-
searchLogs,
|
|
76
|
-
volumeConfigSchema
|
|
77
|
-
} from "./chunk-VUUGKMER.js";
|
|
35
|
+
listRuns
|
|
36
|
+
} from "./chunk-NR6ANGRK.js";
|
|
78
37
|
import "./chunk-LTDQISD7.js";
|
|
79
38
|
import {
|
|
80
|
-
ApiRequestError,
|
|
81
39
|
clearConfig,
|
|
82
|
-
external_exports,
|
|
83
40
|
getActiveOrg,
|
|
84
41
|
getApiUrl,
|
|
85
42
|
getToken,
|
|
@@ -88,17 +45,16 @@ import {
|
|
|
88
45
|
saveConfig,
|
|
89
46
|
source_default,
|
|
90
47
|
withErrorHandler
|
|
91
|
-
} from "./chunk-
|
|
48
|
+
} from "./chunk-26BGLMA3.js";
|
|
92
49
|
import {
|
|
93
50
|
configureGlobalProxyFromEnv
|
|
94
|
-
} from "./chunk-
|
|
51
|
+
} from "./chunk-M6GLCPCJ.js";
|
|
95
52
|
import "./chunk-LRHXR7JT.js";
|
|
96
53
|
import {
|
|
97
54
|
Command,
|
|
98
55
|
Option
|
|
99
56
|
} from "./chunk-B3W2RUBP.js";
|
|
100
57
|
import {
|
|
101
|
-
__toESM,
|
|
102
58
|
init_esm_shims
|
|
103
59
|
} from "./chunk-DIRAZ3YX.js";
|
|
104
60
|
|
|
@@ -277,485 +233,11 @@ var setupTokenCommand = new Command().name("setup-token").description("Output au
|
|
|
277
233
|
// src/commands/auth/index.ts
|
|
278
234
|
var authCommand = new Command().name("auth").description("Authenticate vm0").addCommand(loginCommand).addCommand(logoutCommand).addCommand(statusCommand).addCommand(setupTokenCommand);
|
|
279
235
|
|
|
280
|
-
// src/commands/
|
|
281
|
-
init_esm_shims();
|
|
282
|
-
var import_yaml = __toESM(require_dist(), 1);
|
|
283
|
-
import { readFile as readFile2 } from "fs/promises";
|
|
284
|
-
import { existsSync as existsSync2 } from "fs";
|
|
285
|
-
import { dirname } from "path";
|
|
286
|
-
|
|
287
|
-
// src/lib/domain/yaml-validator.ts
|
|
288
|
-
init_esm_shims();
|
|
289
|
-
var cliAgentNameSchema = external_exports.string().min(3, "Agent name must be at least 3 characters").max(64, "Agent name must be 64 characters or less").regex(
|
|
290
|
-
/^[a-zA-Z0-9]([a-zA-Z0-9-]{0,62}[a-zA-Z0-9])?$/,
|
|
291
|
-
"Agent name must start and end with letter or number, and contain only letters, numbers, and hyphens"
|
|
292
|
-
);
|
|
293
|
-
var cliComposeSchema = external_exports.object({
|
|
294
|
-
version: external_exports.string().min(1, "Missing config.version"),
|
|
295
|
-
agents: external_exports.record(cliAgentNameSchema, agentDefinitionSchema),
|
|
296
|
-
volumes: external_exports.record(external_exports.string(), volumeConfigSchema).optional()
|
|
297
|
-
}).superRefine((config, ctx) => {
|
|
298
|
-
const agentKeys = Object.keys(config.agents);
|
|
299
|
-
if (agentKeys.length === 0) {
|
|
300
|
-
ctx.addIssue({
|
|
301
|
-
code: external_exports.ZodIssueCode.custom,
|
|
302
|
-
message: "agents must have at least one agent defined",
|
|
303
|
-
path: ["agents"]
|
|
304
|
-
});
|
|
305
|
-
return;
|
|
306
|
-
}
|
|
307
|
-
if (agentKeys.length > 1) {
|
|
308
|
-
ctx.addIssue({
|
|
309
|
-
code: external_exports.ZodIssueCode.custom,
|
|
310
|
-
message: "Multiple agents not supported yet. Only one agent allowed.",
|
|
311
|
-
path: ["agents"]
|
|
312
|
-
});
|
|
313
|
-
return;
|
|
314
|
-
}
|
|
315
|
-
const agentName = agentKeys[0];
|
|
316
|
-
const agent = config.agents[agentName];
|
|
317
|
-
const agentVolumes = agent?.volumes;
|
|
318
|
-
if (agentVolumes && agentVolumes.length > 0) {
|
|
319
|
-
if (!config.volumes) {
|
|
320
|
-
ctx.addIssue({
|
|
321
|
-
code: external_exports.ZodIssueCode.custom,
|
|
322
|
-
message: "Agent references volumes but no volumes section defined. Each volume must have explicit name and version.",
|
|
323
|
-
path: ["volumes"]
|
|
324
|
-
});
|
|
325
|
-
return;
|
|
326
|
-
}
|
|
327
|
-
for (const volDeclaration of agentVolumes) {
|
|
328
|
-
const parts = volDeclaration.split(":");
|
|
329
|
-
if (parts.length !== 2) {
|
|
330
|
-
ctx.addIssue({
|
|
331
|
-
code: external_exports.ZodIssueCode.custom,
|
|
332
|
-
message: `Invalid volume declaration: ${volDeclaration}. Expected format: volume-key:/mount/path`,
|
|
333
|
-
path: ["agents", agentName, "volumes"]
|
|
334
|
-
});
|
|
335
|
-
continue;
|
|
336
|
-
}
|
|
337
|
-
const volumeKey = parts[0].trim();
|
|
338
|
-
if (!config.volumes[volumeKey]) {
|
|
339
|
-
ctx.addIssue({
|
|
340
|
-
code: external_exports.ZodIssueCode.custom,
|
|
341
|
-
message: `Volume "${volumeKey}" is not defined in volumes section. Each volume must have explicit name and version.`,
|
|
342
|
-
path: ["volumes", volumeKey]
|
|
343
|
-
});
|
|
344
|
-
}
|
|
345
|
-
}
|
|
346
|
-
}
|
|
347
|
-
});
|
|
348
|
-
function formatInvalidTypeIssue(path9, issue) {
|
|
349
|
-
const runtimeReceived = Reflect.get(issue, "received");
|
|
350
|
-
const received = typeof runtimeReceived === "string" ? runtimeReceived : void 0;
|
|
351
|
-
const isMissing = received === "undefined" || issue.message.includes("received undefined") || issue.message === "Required";
|
|
352
|
-
if (path9 === "version" && isMissing) {
|
|
353
|
-
return "Missing config.version";
|
|
354
|
-
}
|
|
355
|
-
if (path9 === "agents" && isMissing) {
|
|
356
|
-
return "Missing agents object in config";
|
|
357
|
-
}
|
|
358
|
-
if (path9.startsWith("volumes.") && path9.endsWith(".name")) {
|
|
359
|
-
const volumeKey = path9.split(".")[1];
|
|
360
|
-
return `Volume "${volumeKey}" must have a 'name' field (string)`;
|
|
361
|
-
}
|
|
362
|
-
if (path9.startsWith("volumes.") && path9.endsWith(".version")) {
|
|
363
|
-
const volumeKey = path9.split(".")[1];
|
|
364
|
-
return `Volume "${volumeKey}" must have a 'version' field (string)`;
|
|
365
|
-
}
|
|
366
|
-
if (issue.expected === "array") {
|
|
367
|
-
const fieldName = path9.replace(/^agents\.[^.]+\./, "agent.");
|
|
368
|
-
return `${fieldName} must be an array`;
|
|
369
|
-
}
|
|
370
|
-
if (issue.expected === "string" && received === "number") {
|
|
371
|
-
const fieldName = path9.replace(/^agents\.[^.]+\./, "agent.");
|
|
372
|
-
const match = fieldName.match(/^(agent\.[^.]+)\.\d+$/);
|
|
373
|
-
if (match) {
|
|
374
|
-
return `Each entry in ${match[1]?.replace("agent.", "")} must be a string`;
|
|
375
|
-
}
|
|
376
|
-
}
|
|
377
|
-
return null;
|
|
378
|
-
}
|
|
379
|
-
function formatZodError(error) {
|
|
380
|
-
const issue = error.issues[0];
|
|
381
|
-
if (!issue) return "Validation failed";
|
|
382
|
-
const path9 = issue.path.join(".");
|
|
383
|
-
const message = issue.message;
|
|
384
|
-
if (!path9) return message;
|
|
385
|
-
if (issue.code === "invalid_type") {
|
|
386
|
-
const formatted = formatInvalidTypeIssue(path9, issue);
|
|
387
|
-
if (formatted) return formatted;
|
|
388
|
-
}
|
|
389
|
-
if (issue.code === "invalid_key" && path9.startsWith("agents.")) {
|
|
390
|
-
return "Invalid agent name format. Must be 3-64 characters, letters, numbers, and hyphens only. Must start and end with letter or number.";
|
|
391
|
-
}
|
|
392
|
-
if (message === "Invalid key in record" && path9.startsWith("agents.")) {
|
|
393
|
-
return "Invalid agent name format. Must be 3-64 characters, letters, numbers, and hyphens only. Must start and end with letter or number.";
|
|
394
|
-
}
|
|
395
|
-
if (issue.code === "custom") {
|
|
396
|
-
return message;
|
|
397
|
-
}
|
|
398
|
-
if (path9.startsWith("agents.")) {
|
|
399
|
-
const cleanPath = path9.replace(/^agents\.[^.]+\./, "agent.");
|
|
400
|
-
if (message.startsWith("Invalid input:")) {
|
|
401
|
-
const match = message.match(/expected (\w+), received (\w+)/);
|
|
402
|
-
if (match && match[1] === "string" && match[2] === "number") {
|
|
403
|
-
const fieldMatch = cleanPath.match(/^(agent\.[^.]+)\.\d+$/);
|
|
404
|
-
if (fieldMatch) {
|
|
405
|
-
return `Each entry in ${fieldMatch[1]?.replace("agent.", "")} must be a string`;
|
|
406
|
-
}
|
|
407
|
-
}
|
|
408
|
-
}
|
|
409
|
-
return `${cleanPath}: ${message}`;
|
|
410
|
-
}
|
|
411
|
-
return `${path9}: ${message}`;
|
|
412
|
-
}
|
|
413
|
-
var KNOWN_AGENT_FIELDS = Object.keys(agentDefinitionSchema.shape);
|
|
414
|
-
function levenshteinDistance(a, b) {
|
|
415
|
-
if (a === b) return 0;
|
|
416
|
-
if (a.length === 0) return b.length;
|
|
417
|
-
if (b.length === 0) return a.length;
|
|
418
|
-
if (a.length > b.length) [a, b] = [b, a];
|
|
419
|
-
const row = Array.from({ length: a.length + 1 }, (_, i) => {
|
|
420
|
-
return i;
|
|
421
|
-
});
|
|
422
|
-
for (let j = 1; j <= b.length; j++) {
|
|
423
|
-
let prev = j - 1;
|
|
424
|
-
row[0] = j;
|
|
425
|
-
for (let i = 1; i <= a.length; i++) {
|
|
426
|
-
const cost = a[i - 1] === b[j - 1] ? 0 : 1;
|
|
427
|
-
const current = Math.min(
|
|
428
|
-
row[i] + 1,
|
|
429
|
-
// deletion
|
|
430
|
-
row[i - 1] + 1,
|
|
431
|
-
// insertion
|
|
432
|
-
prev + cost
|
|
433
|
-
// substitution
|
|
434
|
-
);
|
|
435
|
-
prev = row[i];
|
|
436
|
-
row[i] = current;
|
|
437
|
-
}
|
|
438
|
-
}
|
|
439
|
-
return row[a.length];
|
|
440
|
-
}
|
|
441
|
-
function findSimilarField(unknown, knownFields) {
|
|
442
|
-
let bestMatch = null;
|
|
443
|
-
let bestDistance = Infinity;
|
|
444
|
-
for (const known of knownFields) {
|
|
445
|
-
if (unknown === known) continue;
|
|
446
|
-
const distance = levenshteinDistance(unknown, known);
|
|
447
|
-
if (distance <= 2 && distance < bestDistance) {
|
|
448
|
-
bestDistance = distance;
|
|
449
|
-
bestMatch = known;
|
|
450
|
-
}
|
|
451
|
-
if (unknown.length >= 3 && known.startsWith(unknown) && !bestMatch) {
|
|
452
|
-
bestMatch = known;
|
|
453
|
-
}
|
|
454
|
-
}
|
|
455
|
-
return bestMatch;
|
|
456
|
-
}
|
|
457
|
-
function extractAgentEntries(config) {
|
|
458
|
-
if (!config || typeof config !== "object") return null;
|
|
459
|
-
const cfg = config;
|
|
460
|
-
const agents = cfg.agents;
|
|
461
|
-
if (!agents || typeof agents !== "object" || Array.isArray(agents))
|
|
462
|
-
return null;
|
|
463
|
-
return agents;
|
|
464
|
-
}
|
|
465
|
-
function checkForFieldTypos(config) {
|
|
466
|
-
const agents = extractAgentEntries(config);
|
|
467
|
-
if (!agents) return null;
|
|
468
|
-
const errors = [];
|
|
469
|
-
for (const [agentName, agent] of Object.entries(agents)) {
|
|
470
|
-
if (!agent || typeof agent !== "object" || Array.isArray(agent)) continue;
|
|
471
|
-
for (const field of Object.keys(agent)) {
|
|
472
|
-
if (KNOWN_AGENT_FIELDS.includes(field)) continue;
|
|
473
|
-
const suggestion = findSimilarField(field, KNOWN_AGENT_FIELDS);
|
|
474
|
-
if (suggestion) {
|
|
475
|
-
errors.push(
|
|
476
|
-
`Unknown field "${field}" in agent "${agentName}". Did you mean "${suggestion}"?`
|
|
477
|
-
);
|
|
478
|
-
}
|
|
479
|
-
}
|
|
480
|
-
}
|
|
481
|
-
return errors.length > 0 ? errors.join("\n") : null;
|
|
482
|
-
}
|
|
483
|
-
function checkForDeprecatedProvider(config) {
|
|
484
|
-
const agents = extractAgentEntries(config);
|
|
485
|
-
if (!agents) return null;
|
|
486
|
-
for (const agent of Object.values(agents)) {
|
|
487
|
-
if (agent && typeof agent === "object" && !Array.isArray(agent)) {
|
|
488
|
-
if ("provider" in agent && !("framework" in agent)) {
|
|
489
|
-
const providerValue = agent.provider;
|
|
490
|
-
return `'provider' field is deprecated. Use 'framework' instead.
|
|
491
|
-
|
|
492
|
-
Change in your vm0.yaml:
|
|
493
|
-
- provider: ${providerValue}
|
|
494
|
-
+ framework: ${providerValue}`;
|
|
495
|
-
}
|
|
496
|
-
}
|
|
497
|
-
}
|
|
498
|
-
return null;
|
|
499
|
-
}
|
|
500
|
-
function validateAgentCompose(config) {
|
|
501
|
-
const deprecationError = checkForDeprecatedProvider(config);
|
|
502
|
-
if (deprecationError) {
|
|
503
|
-
return { valid: false, error: deprecationError };
|
|
504
|
-
}
|
|
505
|
-
const typoError = checkForFieldTypos(config);
|
|
506
|
-
if (typoError) {
|
|
507
|
-
return { valid: false, error: typoError };
|
|
508
|
-
}
|
|
509
|
-
if (config && typeof config === "object" && Array.isArray(config.agents)) {
|
|
510
|
-
return {
|
|
511
|
-
valid: false,
|
|
512
|
-
error: "agents must be an object, not an array. Use format: agents: { agent-name: { ... } }"
|
|
513
|
-
};
|
|
514
|
-
}
|
|
515
|
-
if (!config || typeof config !== "object") {
|
|
516
|
-
return { valid: false, error: "Config must be an object" };
|
|
517
|
-
}
|
|
518
|
-
const result = cliComposeSchema.safeParse(config);
|
|
519
|
-
if (!result.success) {
|
|
520
|
-
return { valid: false, error: formatZodError(result.error) };
|
|
521
|
-
}
|
|
522
|
-
return { valid: true };
|
|
523
|
-
}
|
|
524
|
-
|
|
525
|
-
// src/lib/storage/system-storage.ts
|
|
236
|
+
// src/commands/run/index.ts
|
|
526
237
|
init_esm_shims();
|
|
527
|
-
import * as fs2 from "fs/promises";
|
|
528
|
-
import * as path2 from "path";
|
|
529
|
-
import * as os2 from "os";
|
|
530
238
|
|
|
531
|
-
// src/
|
|
239
|
+
// src/commands/run/run.ts
|
|
532
240
|
init_esm_shims();
|
|
533
|
-
import { createHash } from "crypto";
|
|
534
|
-
import * as fs from "fs";
|
|
535
|
-
import * as path from "path";
|
|
536
|
-
import * as os from "os";
|
|
537
|
-
async function hashFileStream(filePath) {
|
|
538
|
-
return new Promise((resolve, reject) => {
|
|
539
|
-
const hash = createHash("sha256");
|
|
540
|
-
const stream = fs.createReadStream(filePath);
|
|
541
|
-
stream.on("data", (chunk) => {
|
|
542
|
-
return hash.update(chunk);
|
|
543
|
-
});
|
|
544
|
-
stream.on("end", () => {
|
|
545
|
-
return resolve(hash.digest("hex"));
|
|
546
|
-
});
|
|
547
|
-
stream.on("error", reject);
|
|
548
|
-
});
|
|
549
|
-
}
|
|
550
|
-
async function getAllFiles(dirPath, baseDir = dirPath) {
|
|
551
|
-
const files = [];
|
|
552
|
-
const entries = await fs.promises.readdir(dirPath, { withFileTypes: true });
|
|
553
|
-
for (const entry of entries) {
|
|
554
|
-
const fullPath = path.join(dirPath, entry.name);
|
|
555
|
-
const relativePath = path.relative(baseDir, fullPath);
|
|
556
|
-
if (relativePath.startsWith(".vm0")) {
|
|
557
|
-
continue;
|
|
558
|
-
}
|
|
559
|
-
if (entry.isDirectory()) {
|
|
560
|
-
const subFiles = await getAllFiles(fullPath, baseDir);
|
|
561
|
-
files.push(...subFiles);
|
|
562
|
-
} else {
|
|
563
|
-
files.push(fullPath);
|
|
564
|
-
}
|
|
565
|
-
}
|
|
566
|
-
return files;
|
|
567
|
-
}
|
|
568
|
-
async function collectFileMetadata(cwd, files, onProgress) {
|
|
569
|
-
const fileEntries = [];
|
|
570
|
-
for (let i = 0; i < files.length; i++) {
|
|
571
|
-
const file = files[i];
|
|
572
|
-
const relativePath = path.relative(cwd, file);
|
|
573
|
-
const [hash, stats] = await Promise.all([
|
|
574
|
-
hashFileStream(file),
|
|
575
|
-
fs.promises.stat(file)
|
|
576
|
-
]);
|
|
577
|
-
fileEntries.push({
|
|
578
|
-
path: relativePath,
|
|
579
|
-
hash,
|
|
580
|
-
size: stats.size
|
|
581
|
-
});
|
|
582
|
-
if (onProgress && (i + 1) % 100 === 0) {
|
|
583
|
-
onProgress(`Hashing files... ${i + 1}/${files.length}`);
|
|
584
|
-
}
|
|
585
|
-
}
|
|
586
|
-
return fileEntries;
|
|
587
|
-
}
|
|
588
|
-
async function createArchive(cwd, files) {
|
|
589
|
-
const tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), "vm0-"));
|
|
590
|
-
const tarPath = path.join(tmpDir, "archive.tar.gz");
|
|
591
|
-
try {
|
|
592
|
-
const relativePaths = files.map((file) => {
|
|
593
|
-
return path.relative(cwd, file);
|
|
594
|
-
});
|
|
595
|
-
if (relativePaths.length > 0) {
|
|
596
|
-
await Qn(
|
|
597
|
-
{
|
|
598
|
-
gzip: true,
|
|
599
|
-
file: tarPath,
|
|
600
|
-
cwd
|
|
601
|
-
},
|
|
602
|
-
relativePaths
|
|
603
|
-
);
|
|
604
|
-
} else {
|
|
605
|
-
await Qn(
|
|
606
|
-
{
|
|
607
|
-
gzip: true,
|
|
608
|
-
file: tarPath,
|
|
609
|
-
cwd,
|
|
610
|
-
filter: excludeVm0Filter
|
|
611
|
-
},
|
|
612
|
-
["."]
|
|
613
|
-
);
|
|
614
|
-
}
|
|
615
|
-
const tarBuffer = await fs.promises.readFile(tarPath);
|
|
616
|
-
return tarBuffer;
|
|
617
|
-
} finally {
|
|
618
|
-
if (fs.existsSync(tarPath)) {
|
|
619
|
-
await fs.promises.unlink(tarPath);
|
|
620
|
-
}
|
|
621
|
-
await fs.promises.rmdir(tmpDir);
|
|
622
|
-
}
|
|
623
|
-
}
|
|
624
|
-
function createManifest(files) {
|
|
625
|
-
const manifest = {
|
|
626
|
-
version: 1,
|
|
627
|
-
files,
|
|
628
|
-
createdAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
629
|
-
};
|
|
630
|
-
return Buffer.from(JSON.stringify(manifest, null, 2));
|
|
631
|
-
}
|
|
632
|
-
function sleep(ms) {
|
|
633
|
-
return new Promise((resolve) => {
|
|
634
|
-
return setTimeout(resolve, ms);
|
|
635
|
-
});
|
|
636
|
-
}
|
|
637
|
-
async function uploadToPresignedUrl(presignedUrl, data, contentType, maxRetries = 3) {
|
|
638
|
-
let lastError = null;
|
|
639
|
-
for (let attempt = 1; attempt <= maxRetries; attempt++) {
|
|
640
|
-
try {
|
|
641
|
-
const response = await fetch(presignedUrl, {
|
|
642
|
-
method: "PUT",
|
|
643
|
-
body: data,
|
|
644
|
-
headers: {
|
|
645
|
-
"Content-Type": contentType
|
|
646
|
-
}
|
|
647
|
-
});
|
|
648
|
-
if (response.ok) {
|
|
649
|
-
return;
|
|
650
|
-
}
|
|
651
|
-
if (response.status >= 400 && response.status < 500) {
|
|
652
|
-
const text2 = await response.text();
|
|
653
|
-
throw new Error(`S3 upload failed: ${response.status} - ${text2}`);
|
|
654
|
-
}
|
|
655
|
-
const text = await response.text();
|
|
656
|
-
lastError = new Error(`S3 upload failed: ${response.status} - ${text}`);
|
|
657
|
-
} catch (error) {
|
|
658
|
-
lastError = error instanceof Error ? error : new Error("Unknown upload error");
|
|
659
|
-
if (lastError.message.includes("400") || lastError.message.includes("403")) {
|
|
660
|
-
throw lastError;
|
|
661
|
-
}
|
|
662
|
-
}
|
|
663
|
-
if (attempt < maxRetries) {
|
|
664
|
-
const backoffMs = Math.pow(2, attempt - 1) * 1e3;
|
|
665
|
-
await sleep(backoffMs);
|
|
666
|
-
}
|
|
667
|
-
}
|
|
668
|
-
throw lastError || new Error("S3 upload failed after retries");
|
|
669
|
-
}
|
|
670
|
-
async function directUpload(storageName, storageType, cwd, options) {
|
|
671
|
-
const { onProgress, force } = options || {};
|
|
672
|
-
onProgress?.("Collecting files...");
|
|
673
|
-
const files = await getAllFiles(cwd);
|
|
674
|
-
onProgress?.("Computing file hashes...");
|
|
675
|
-
const fileEntries = await collectFileMetadata(cwd, files, onProgress);
|
|
676
|
-
onProgress?.("Preparing upload...");
|
|
677
|
-
const prepareResult = await prepareStorage({
|
|
678
|
-
storageName,
|
|
679
|
-
storageType,
|
|
680
|
-
files: fileEntries,
|
|
681
|
-
force
|
|
682
|
-
});
|
|
683
|
-
if (prepareResult.existing) {
|
|
684
|
-
onProgress?.("Version exists, updating HEAD...");
|
|
685
|
-
const commitResult2 = await commitStorage({
|
|
686
|
-
storageName,
|
|
687
|
-
storageType,
|
|
688
|
-
versionId: prepareResult.versionId,
|
|
689
|
-
files: fileEntries
|
|
690
|
-
});
|
|
691
|
-
return {
|
|
692
|
-
versionId: commitResult2.versionId,
|
|
693
|
-
size: commitResult2.size,
|
|
694
|
-
fileCount: commitResult2.fileCount,
|
|
695
|
-
deduplicated: true,
|
|
696
|
-
empty: commitResult2.fileCount === 0
|
|
697
|
-
};
|
|
698
|
-
}
|
|
699
|
-
if (files.length > 0) {
|
|
700
|
-
onProgress?.("Compressing files...");
|
|
701
|
-
const archiveBuffer = await createArchive(cwd, files);
|
|
702
|
-
onProgress?.("Uploading archive to S3...");
|
|
703
|
-
if (!prepareResult.uploads) {
|
|
704
|
-
throw new Error("No upload URLs received from prepare endpoint");
|
|
705
|
-
}
|
|
706
|
-
await uploadToPresignedUrl(
|
|
707
|
-
prepareResult.uploads.archive.presignedUrl,
|
|
708
|
-
archiveBuffer,
|
|
709
|
-
"application/gzip"
|
|
710
|
-
);
|
|
711
|
-
}
|
|
712
|
-
onProgress?.("Uploading manifest...");
|
|
713
|
-
if (!prepareResult.uploads) {
|
|
714
|
-
throw new Error("No upload URLs received from prepare endpoint");
|
|
715
|
-
}
|
|
716
|
-
const manifestBuffer = createManifest(fileEntries);
|
|
717
|
-
await uploadToPresignedUrl(
|
|
718
|
-
prepareResult.uploads.manifest.presignedUrl,
|
|
719
|
-
manifestBuffer,
|
|
720
|
-
"application/json"
|
|
721
|
-
);
|
|
722
|
-
onProgress?.("Committing...");
|
|
723
|
-
const commitResult = await commitStorage({
|
|
724
|
-
storageName,
|
|
725
|
-
storageType,
|
|
726
|
-
versionId: prepareResult.versionId,
|
|
727
|
-
files: fileEntries
|
|
728
|
-
});
|
|
729
|
-
return {
|
|
730
|
-
versionId: commitResult.versionId,
|
|
731
|
-
size: commitResult.size,
|
|
732
|
-
fileCount: commitResult.fileCount,
|
|
733
|
-
deduplicated: commitResult.deduplicated || false,
|
|
734
|
-
empty: commitResult.fileCount === 0
|
|
735
|
-
};
|
|
736
|
-
}
|
|
737
|
-
|
|
738
|
-
// src/lib/storage/system-storage.ts
|
|
739
|
-
async function uploadInstructions(agentName, instructionsFilePath, basePath, framework) {
|
|
740
|
-
const storageName = getInstructionsStorageName(agentName.toLowerCase());
|
|
741
|
-
const absolutePath = path2.isAbsolute(instructionsFilePath) ? instructionsFilePath : path2.join(basePath, instructionsFilePath);
|
|
742
|
-
const content = await fs2.readFile(absolutePath, "utf8");
|
|
743
|
-
const tmpDir = await fs2.mkdtemp(path2.join(os2.tmpdir(), "vm0-instructions-"));
|
|
744
|
-
const instructionsDir = path2.join(tmpDir, "instructions");
|
|
745
|
-
await fs2.mkdir(instructionsDir);
|
|
746
|
-
const filename = getInstructionsFilename(framework);
|
|
747
|
-
await fs2.writeFile(path2.join(instructionsDir, filename), content);
|
|
748
|
-
try {
|
|
749
|
-
const result = await directUpload(storageName, "volume", instructionsDir);
|
|
750
|
-
return {
|
|
751
|
-
name: storageName,
|
|
752
|
-
versionId: result.versionId,
|
|
753
|
-
action: result.deduplicated ? "deduplicated" : "created"
|
|
754
|
-
};
|
|
755
|
-
} finally {
|
|
756
|
-
await fs2.rm(tmpDir, { recursive: true, force: true });
|
|
757
|
-
}
|
|
758
|
-
}
|
|
759
241
|
|
|
760
242
|
// src/lib/utils/update-checker.ts
|
|
761
243
|
init_esm_shims();
|
|
@@ -884,228 +366,7 @@ async function waitForSilentUpgrade(timeout = TIMEOUT_MS) {
|
|
|
884
366
|
}
|
|
885
367
|
}
|
|
886
368
|
|
|
887
|
-
// src/commands/compose/index.ts
|
|
888
|
-
var DEFAULT_CONFIG_FILE = "vm0.yaml";
|
|
889
|
-
function getSecretsFromComposeContent(content) {
|
|
890
|
-
const grouped = extractAndGroupVariables(content);
|
|
891
|
-
return new Set(
|
|
892
|
-
grouped.secrets.map((r) => {
|
|
893
|
-
return r.name;
|
|
894
|
-
})
|
|
895
|
-
);
|
|
896
|
-
}
|
|
897
|
-
function getVarsFromComposeContent(content) {
|
|
898
|
-
const grouped = extractAndGroupVariables(content);
|
|
899
|
-
return new Set(
|
|
900
|
-
grouped.vars.map((r) => {
|
|
901
|
-
return r.name;
|
|
902
|
-
})
|
|
903
|
-
);
|
|
904
|
-
}
|
|
905
|
-
async function loadAndValidateConfig(configFile) {
|
|
906
|
-
if (!existsSync2(configFile)) {
|
|
907
|
-
throw new Error(`Config file not found: ${configFile}`);
|
|
908
|
-
}
|
|
909
|
-
const content = await readFile2(configFile, "utf8");
|
|
910
|
-
let config;
|
|
911
|
-
try {
|
|
912
|
-
config = (0, import_yaml.parse)(content);
|
|
913
|
-
} catch (error) {
|
|
914
|
-
const message = error instanceof Error ? error.message : "Unknown error";
|
|
915
|
-
throw new Error(`Invalid YAML format: ${message}`);
|
|
916
|
-
}
|
|
917
|
-
const validation = validateAgentCompose(config);
|
|
918
|
-
if (!validation.valid) {
|
|
919
|
-
throw new Error(validation.error);
|
|
920
|
-
}
|
|
921
|
-
const cfg = config;
|
|
922
|
-
const agentsConfig = cfg.agents;
|
|
923
|
-
const agentName = Object.keys(agentsConfig)[0];
|
|
924
|
-
const agent = agentsConfig[agentName];
|
|
925
|
-
const basePath = dirname(configFile);
|
|
926
|
-
return { config, agentName, agent, basePath };
|
|
927
|
-
}
|
|
928
|
-
async function uploadInstructionsIfPresent(agentName, agent, basePath, jsonMode) {
|
|
929
|
-
if (!agent.instructions) return;
|
|
930
|
-
if (!jsonMode) {
|
|
931
|
-
console.log(`Uploading instructions: ${agent.instructions}`);
|
|
932
|
-
}
|
|
933
|
-
const result = await uploadInstructions(
|
|
934
|
-
agentName,
|
|
935
|
-
agent.instructions,
|
|
936
|
-
basePath,
|
|
937
|
-
agent.framework
|
|
938
|
-
);
|
|
939
|
-
if (!jsonMode) {
|
|
940
|
-
console.log(
|
|
941
|
-
source_default.green(
|
|
942
|
-
`\u2713 Instructions ${result.action === "deduplicated" ? "(unchanged)" : "uploaded"}: ${result.versionId.slice(0, 8)}`
|
|
943
|
-
)
|
|
944
|
-
);
|
|
945
|
-
}
|
|
946
|
-
}
|
|
947
|
-
function stripSkillsFromAgents(config) {
|
|
948
|
-
if (!config || typeof config !== "object") return config;
|
|
949
|
-
const cfg = structuredClone(config);
|
|
950
|
-
const agents = cfg.agents;
|
|
951
|
-
if (agents && typeof agents === "object" && !Array.isArray(agents)) {
|
|
952
|
-
for (const agent of Object.values(agents)) {
|
|
953
|
-
if (agent && typeof agent === "object" && !Array.isArray(agent)) {
|
|
954
|
-
delete agent.skills;
|
|
955
|
-
}
|
|
956
|
-
}
|
|
957
|
-
}
|
|
958
|
-
return cfg;
|
|
959
|
-
}
|
|
960
|
-
async function checkAndPromptMissingItems(config, options) {
|
|
961
|
-
const requiredSecrets = getSecretsFromComposeContent(config);
|
|
962
|
-
const requiredVars = getVarsFromComposeContent(config);
|
|
963
|
-
if (requiredSecrets.size === 0 && requiredVars.size === 0) {
|
|
964
|
-
return { missingSecrets: [], missingVars: [] };
|
|
965
|
-
}
|
|
966
|
-
const [secretsResponse, variablesResponse, connectorsResponse] = await Promise.all([
|
|
967
|
-
requiredSecrets.size > 0 ? listZeroSecrets() : { secrets: [] },
|
|
968
|
-
requiredVars.size > 0 ? listZeroVariables() : { variables: [] },
|
|
969
|
-
listZeroConnectors()
|
|
970
|
-
]);
|
|
971
|
-
const existingSecretNames = new Set(
|
|
972
|
-
secretsResponse.secrets.map((s) => {
|
|
973
|
-
return s.name;
|
|
974
|
-
})
|
|
975
|
-
);
|
|
976
|
-
const existingVarNames = new Set(
|
|
977
|
-
variablesResponse.variables.map((v) => {
|
|
978
|
-
return v.name;
|
|
979
|
-
})
|
|
980
|
-
);
|
|
981
|
-
const guaranteedConnectorProvidedSecretNames = guaranteedConnectorProvidedBindingNames({
|
|
982
|
-
bindings: connectorsResponse.connectorProvidedBindings,
|
|
983
|
-
namespace: "secrets"
|
|
984
|
-
});
|
|
985
|
-
const guaranteedConnectorProvidedVarNames = guaranteedConnectorProvidedBindingNames({
|
|
986
|
-
bindings: connectorsResponse.connectorProvidedBindings,
|
|
987
|
-
namespace: "vars"
|
|
988
|
-
});
|
|
989
|
-
const missingSecrets = [...requiredSecrets].filter((name) => {
|
|
990
|
-
return !existingSecretNames.has(name) && !guaranteedConnectorProvidedSecretNames.has(name);
|
|
991
|
-
});
|
|
992
|
-
const missingVars = [...requiredVars].filter((name) => {
|
|
993
|
-
return !existingVarNames.has(name) && !guaranteedConnectorProvidedVarNames.has(name);
|
|
994
|
-
});
|
|
995
|
-
if (missingSecrets.length === 0 && missingVars.length === 0) {
|
|
996
|
-
return { missingSecrets: [], missingVars: [] };
|
|
997
|
-
}
|
|
998
|
-
if (!options.json) {
|
|
999
|
-
console.log();
|
|
1000
|
-
console.log(source_default.yellow("\u26A0 Missing secrets/variables detected:"));
|
|
1001
|
-
if (missingSecrets.length > 0) {
|
|
1002
|
-
console.log(source_default.yellow(` Secrets: ${missingSecrets.join(", ")}`));
|
|
1003
|
-
}
|
|
1004
|
-
if (missingVars.length > 0) {
|
|
1005
|
-
console.log(source_default.yellow(` Variables: ${missingVars.join(", ")}`));
|
|
1006
|
-
}
|
|
1007
|
-
console.log();
|
|
1008
|
-
}
|
|
1009
|
-
return { missingSecrets, missingVars };
|
|
1010
|
-
}
|
|
1011
|
-
async function finalizeCompose(config, agent, options) {
|
|
1012
|
-
if (!options.json) {
|
|
1013
|
-
console.log("Uploading compose...");
|
|
1014
|
-
}
|
|
1015
|
-
const contentToPost = stripSkillsFromAgents(config);
|
|
1016
|
-
const response = await createOrUpdateCompose({ content: contentToPost });
|
|
1017
|
-
const shortVersionId = response.versionId.slice(0, 8);
|
|
1018
|
-
const displayName = response.name;
|
|
1019
|
-
const result = {
|
|
1020
|
-
composeId: response.composeId,
|
|
1021
|
-
composeName: response.name,
|
|
1022
|
-
versionId: response.versionId,
|
|
1023
|
-
action: response.action,
|
|
1024
|
-
displayName
|
|
1025
|
-
};
|
|
1026
|
-
if (!options.json) {
|
|
1027
|
-
const missingItems = await checkAndPromptMissingItems(config, options);
|
|
1028
|
-
if (missingItems.missingSecrets.length > 0 || missingItems.missingVars.length > 0) {
|
|
1029
|
-
result.missingSecrets = missingItems.missingSecrets;
|
|
1030
|
-
result.missingVars = missingItems.missingVars;
|
|
1031
|
-
}
|
|
1032
|
-
}
|
|
1033
|
-
if (!options.json) {
|
|
1034
|
-
if (response.action === "created") {
|
|
1035
|
-
console.log(source_default.green(`\u2713 Compose created: ${displayName}`));
|
|
1036
|
-
} else {
|
|
1037
|
-
console.log(source_default.green(`\u2713 Compose version exists: ${displayName}`));
|
|
1038
|
-
}
|
|
1039
|
-
console.log(source_default.dim(` Version: ${shortVersionId}`));
|
|
1040
|
-
console.log();
|
|
1041
|
-
console.log(" Run your agent:");
|
|
1042
|
-
console.log(
|
|
1043
|
-
source_default.cyan(
|
|
1044
|
-
` vm0 run ${displayName}:${shortVersionId} --artifact <artifact> "your prompt"`
|
|
1045
|
-
)
|
|
1046
|
-
);
|
|
1047
|
-
}
|
|
1048
|
-
if (options.autoUpdate !== false) {
|
|
1049
|
-
await waitForSilentUpgrade();
|
|
1050
|
-
}
|
|
1051
|
-
return result;
|
|
1052
|
-
}
|
|
1053
|
-
var composeCommand = new Command().name("compose").description("Create or update agent compose (e.g., vm0.yaml)").argument(
|
|
1054
|
-
"[agent-yaml]",
|
|
1055
|
-
`Path to agent YAML file (default: ${DEFAULT_CONFIG_FILE})`
|
|
1056
|
-
).option("-y, --yes", "Skip confirmation prompts").option(
|
|
1057
|
-
"--experimental-shared-compose",
|
|
1058
|
-
"[deprecated] No longer required, kept for backward compatibility"
|
|
1059
|
-
).option("--json", "Output JSON for scripts (suppresses interactive output)").option(
|
|
1060
|
-
"--porcelain",
|
|
1061
|
-
"[deprecated: use --json] Output JSON for scripts",
|
|
1062
|
-
false
|
|
1063
|
-
).addOption(new Option("--no-auto-update").hideHelp()).action(
|
|
1064
|
-
withErrorHandler(
|
|
1065
|
-
async (configFile, options) => {
|
|
1066
|
-
const resolvedConfigFile = configFile ?? DEFAULT_CONFIG_FILE;
|
|
1067
|
-
if (options.porcelain && !options.json) {
|
|
1068
|
-
console.error(
|
|
1069
|
-
source_default.yellow("\u26A0 --porcelain is deprecated, use --json instead")
|
|
1070
|
-
);
|
|
1071
|
-
options.json = true;
|
|
1072
|
-
}
|
|
1073
|
-
if (options.json) {
|
|
1074
|
-
options.yes = true;
|
|
1075
|
-
options.autoUpdate = false;
|
|
1076
|
-
}
|
|
1077
|
-
if (options.autoUpdate !== false) {
|
|
1078
|
-
await startSilentUpgrade("9.244.5");
|
|
1079
|
-
}
|
|
1080
|
-
try {
|
|
1081
|
-
const { config, agentName, agent, basePath } = await loadAndValidateConfig(resolvedConfigFile);
|
|
1082
|
-
await uploadInstructionsIfPresent(
|
|
1083
|
-
agentName,
|
|
1084
|
-
agent,
|
|
1085
|
-
basePath,
|
|
1086
|
-
options.json
|
|
1087
|
-
);
|
|
1088
|
-
const result = await finalizeCompose(config, agent, options);
|
|
1089
|
-
if (options.json) {
|
|
1090
|
-
console.log(JSON.stringify(result));
|
|
1091
|
-
}
|
|
1092
|
-
} catch (error) {
|
|
1093
|
-
if (options.json) {
|
|
1094
|
-
const message = error instanceof Error ? error.message : "An unexpected error occurred";
|
|
1095
|
-
console.log(JSON.stringify({ error: message }));
|
|
1096
|
-
process.exit(1);
|
|
1097
|
-
}
|
|
1098
|
-
throw error;
|
|
1099
|
-
}
|
|
1100
|
-
}
|
|
1101
|
-
)
|
|
1102
|
-
);
|
|
1103
|
-
|
|
1104
|
-
// src/commands/run/index.ts
|
|
1105
|
-
init_esm_shims();
|
|
1106
|
-
|
|
1107
369
|
// src/commands/run/run.ts
|
|
1108
|
-
init_esm_shims();
|
|
1109
370
|
function parseModelProviderType(value) {
|
|
1110
371
|
if (value === void 0) {
|
|
1111
372
|
return void 0;
|
|
@@ -1172,7 +433,7 @@ var mainRunCommand = new Command().name("run").description("Run an agent").argum
|
|
|
1172
433
|
withErrorHandler(
|
|
1173
434
|
async (identifier, prompt, options) => {
|
|
1174
435
|
if (options.autoUpdate !== false) {
|
|
1175
|
-
await startSilentUpgrade("9.244.
|
|
436
|
+
await startSilentUpgrade("9.244.7");
|
|
1176
437
|
}
|
|
1177
438
|
const { name, version } = parseIdentifier(identifier);
|
|
1178
439
|
let composeId;
|
|
@@ -1184,11 +445,7 @@ var mainRunCommand = new Command().name("run").description("Run an agent").argum
|
|
|
1184
445
|
} else {
|
|
1185
446
|
const compose = await getComposeByName(name);
|
|
1186
447
|
if (!compose) {
|
|
1187
|
-
throw new Error(`Agent not found: ${identifier}
|
|
1188
|
-
cause: new Error(
|
|
1189
|
-
"Make sure you've composed the agent with: vm0 compose"
|
|
1190
|
-
)
|
|
1191
|
-
});
|
|
448
|
+
throw new Error(`Agent not found: ${identifier}`);
|
|
1192
449
|
}
|
|
1193
450
|
composeId = compose.id;
|
|
1194
451
|
composeContent = compose.content;
|
|
@@ -1652,1287 +909,6 @@ mainRunCommand.addCommand(killCommand);
|
|
|
1652
909
|
mainRunCommand.addCommand(queueCommand);
|
|
1653
910
|
var runCommand = mainRunCommand;
|
|
1654
911
|
|
|
1655
|
-
// src/commands/volume/index.ts
|
|
1656
|
-
init_esm_shims();
|
|
1657
|
-
|
|
1658
|
-
// src/commands/volume/init.ts
|
|
1659
|
-
init_esm_shims();
|
|
1660
|
-
import path4 from "path";
|
|
1661
|
-
|
|
1662
|
-
// src/lib/storage/storage-utils.ts
|
|
1663
|
-
init_esm_shims();
|
|
1664
|
-
var import_yaml2 = __toESM(require_dist(), 1);
|
|
1665
|
-
import { readFile as readFile3, writeFile as writeFile2, mkdir as mkdir2 } from "fs/promises";
|
|
1666
|
-
import { existsSync as existsSync3 } from "fs";
|
|
1667
|
-
import path3 from "path";
|
|
1668
|
-
var CONFIG_DIR = ".vm0";
|
|
1669
|
-
var CONFIG_FILE = "storage.yaml";
|
|
1670
|
-
function isValidStorageName(name) {
|
|
1671
|
-
if (name.length < 3 || name.length > 64) {
|
|
1672
|
-
return false;
|
|
1673
|
-
}
|
|
1674
|
-
const pattern = /^[a-z0-9][a-z0-9-]{1,62}[a-z0-9]$/;
|
|
1675
|
-
return pattern.test(name) && !name.includes("--");
|
|
1676
|
-
}
|
|
1677
|
-
async function readStorageConfig(basePath = process.cwd()) {
|
|
1678
|
-
const configPath = path3.join(basePath, CONFIG_DIR, CONFIG_FILE);
|
|
1679
|
-
const legacyConfigPath = path3.join(basePath, CONFIG_DIR, "volume.yaml");
|
|
1680
|
-
let actualPath = null;
|
|
1681
|
-
if (existsSync3(configPath)) {
|
|
1682
|
-
actualPath = configPath;
|
|
1683
|
-
} else if (existsSync3(legacyConfigPath)) {
|
|
1684
|
-
actualPath = legacyConfigPath;
|
|
1685
|
-
}
|
|
1686
|
-
if (!actualPath) {
|
|
1687
|
-
return null;
|
|
1688
|
-
}
|
|
1689
|
-
const content = await readFile3(actualPath, "utf8");
|
|
1690
|
-
const config = (0, import_yaml2.parse)(content);
|
|
1691
|
-
if (!config.type) {
|
|
1692
|
-
config.type = "volume";
|
|
1693
|
-
}
|
|
1694
|
-
return config;
|
|
1695
|
-
}
|
|
1696
|
-
async function writeStorageConfig(storageName, basePath = process.cwd(), type = "volume") {
|
|
1697
|
-
const configDir = path3.join(basePath, CONFIG_DIR);
|
|
1698
|
-
const configPath = path3.join(configDir, CONFIG_FILE);
|
|
1699
|
-
if (!existsSync3(configDir)) {
|
|
1700
|
-
await mkdir2(configDir, { recursive: true });
|
|
1701
|
-
}
|
|
1702
|
-
const config = {
|
|
1703
|
-
name: storageName,
|
|
1704
|
-
type
|
|
1705
|
-
};
|
|
1706
|
-
const yamlContent = (0, import_yaml2.stringify)(config);
|
|
1707
|
-
await writeFile2(configPath, yamlContent, "utf8");
|
|
1708
|
-
}
|
|
1709
|
-
|
|
1710
|
-
// src/commands/volume/init.ts
|
|
1711
|
-
var initCommand = new Command().name("init").description("Initialize a volume in the current directory").option("-n, --name <name>", "Volume name (required in non-interactive mode)").action(
|
|
1712
|
-
withErrorHandler(async (options) => {
|
|
1713
|
-
const cwd = process.cwd();
|
|
1714
|
-
const dirName = path4.basename(cwd);
|
|
1715
|
-
const existingConfig = await readStorageConfig(cwd);
|
|
1716
|
-
if (existingConfig) {
|
|
1717
|
-
console.log(
|
|
1718
|
-
source_default.yellow(`Volume already initialized: ${existingConfig.name}`)
|
|
1719
|
-
);
|
|
1720
|
-
console.log(
|
|
1721
|
-
source_default.dim(`Config file: ${path4.join(cwd, ".vm0", "storage.yaml")}`)
|
|
1722
|
-
);
|
|
1723
|
-
return;
|
|
1724
|
-
}
|
|
1725
|
-
let volumeName;
|
|
1726
|
-
if (options.name) {
|
|
1727
|
-
volumeName = options.name;
|
|
1728
|
-
} else if (!isInteractive()) {
|
|
1729
|
-
throw new Error("--name flag is required in non-interactive mode", {
|
|
1730
|
-
cause: new Error("Usage: vm0 volume init --name <volume-name>")
|
|
1731
|
-
});
|
|
1732
|
-
} else {
|
|
1733
|
-
const defaultName = isValidStorageName(dirName) ? dirName : void 0;
|
|
1734
|
-
const name = await promptText(
|
|
1735
|
-
"Enter volume name",
|
|
1736
|
-
defaultName,
|
|
1737
|
-
(value) => {
|
|
1738
|
-
if (!isValidStorageName(value)) {
|
|
1739
|
-
return "Must be 3-64 characters, lowercase alphanumeric with hyphens";
|
|
1740
|
-
}
|
|
1741
|
-
return true;
|
|
1742
|
-
}
|
|
1743
|
-
);
|
|
1744
|
-
if (name === void 0) {
|
|
1745
|
-
console.log(source_default.dim("Cancelled"));
|
|
1746
|
-
return;
|
|
1747
|
-
}
|
|
1748
|
-
volumeName = name;
|
|
1749
|
-
}
|
|
1750
|
-
if (!isValidStorageName(volumeName)) {
|
|
1751
|
-
throw new Error(`Invalid volume name: "${volumeName}"`, {
|
|
1752
|
-
cause: new Error(
|
|
1753
|
-
"Volume names must be 3-64 characters, lowercase alphanumeric with hyphens"
|
|
1754
|
-
)
|
|
1755
|
-
});
|
|
1756
|
-
}
|
|
1757
|
-
await writeStorageConfig(volumeName, cwd);
|
|
1758
|
-
console.log(source_default.green(`\u2713 Initialized volume: ${volumeName}`));
|
|
1759
|
-
console.log(
|
|
1760
|
-
source_default.dim(
|
|
1761
|
-
` Config saved to ${path4.join(cwd, ".vm0", "storage.yaml")}`
|
|
1762
|
-
)
|
|
1763
|
-
);
|
|
1764
|
-
})
|
|
1765
|
-
);
|
|
1766
|
-
|
|
1767
|
-
// src/commands/volume/push.ts
|
|
1768
|
-
init_esm_shims();
|
|
1769
|
-
var pushCommand = new Command().name("push").description("Push local files to cloud volume").option(
|
|
1770
|
-
"-f, --force",
|
|
1771
|
-
"Force upload even if content unchanged (recreate archive)"
|
|
1772
|
-
).action(
|
|
1773
|
-
withErrorHandler(async (options) => {
|
|
1774
|
-
const cwd = process.cwd();
|
|
1775
|
-
const config = await readStorageConfig(cwd);
|
|
1776
|
-
if (!config) {
|
|
1777
|
-
throw new Error("No volume initialized in this directory", {
|
|
1778
|
-
cause: new Error("Run: vm0 volume init")
|
|
1779
|
-
});
|
|
1780
|
-
}
|
|
1781
|
-
console.log(`Pushing volume: ${config.name}`);
|
|
1782
|
-
const result = await directUpload(config.name, "volume", cwd, {
|
|
1783
|
-
onProgress: (message) => {
|
|
1784
|
-
console.log(source_default.dim(message));
|
|
1785
|
-
},
|
|
1786
|
-
force: options.force
|
|
1787
|
-
});
|
|
1788
|
-
const shortVersion = result.versionId.slice(0, 8);
|
|
1789
|
-
if (result.empty) {
|
|
1790
|
-
console.log(source_default.dim("No files found (empty volume)"));
|
|
1791
|
-
} else if (result.deduplicated) {
|
|
1792
|
-
console.log(source_default.green("\u2713 Content unchanged (deduplicated)"));
|
|
1793
|
-
} else {
|
|
1794
|
-
console.log(source_default.green("\u2713 Upload complete"));
|
|
1795
|
-
}
|
|
1796
|
-
console.log(source_default.dim(` Version: ${shortVersion}`));
|
|
1797
|
-
console.log(source_default.dim(` Files: ${result.fileCount.toLocaleString()}`));
|
|
1798
|
-
console.log(source_default.dim(` Size: ${formatBytes(result.size)}`));
|
|
1799
|
-
})
|
|
1800
|
-
);
|
|
1801
|
-
|
|
1802
|
-
// src/commands/volume/pull.ts
|
|
1803
|
-
init_esm_shims();
|
|
1804
|
-
import path5 from "path";
|
|
1805
|
-
import * as fs3 from "fs";
|
|
1806
|
-
import * as os3 from "os";
|
|
1807
|
-
|
|
1808
|
-
// src/lib/storage/pull-utils.ts
|
|
1809
|
-
init_esm_shims();
|
|
1810
|
-
async function handleEmptyStorageResponse(cwd) {
|
|
1811
|
-
console.log(source_default.dim("Syncing local files..."));
|
|
1812
|
-
const removedCount = await removeExtraFiles(cwd, /* @__PURE__ */ new Set());
|
|
1813
|
-
if (removedCount > 0) {
|
|
1814
|
-
console.log(source_default.green(`\u2713 Removed ${removedCount} files not in remote`));
|
|
1815
|
-
}
|
|
1816
|
-
console.log(source_default.green("\u2713 Synced (0 files)"));
|
|
1817
|
-
return { removedCount };
|
|
1818
|
-
}
|
|
1819
|
-
|
|
1820
|
-
// src/commands/volume/pull.ts
|
|
1821
|
-
var pullCommand = new Command().name("pull").description("Pull cloud files to local directory").argument("[versionId]", "Version ID to pull (default: latest)").action(
|
|
1822
|
-
withErrorHandler(async (versionId) => {
|
|
1823
|
-
const cwd = process.cwd();
|
|
1824
|
-
const config = await readStorageConfig(cwd);
|
|
1825
|
-
if (!config) {
|
|
1826
|
-
throw new Error("No volume initialized in this directory", {
|
|
1827
|
-
cause: new Error("Run: vm0 volume init")
|
|
1828
|
-
});
|
|
1829
|
-
}
|
|
1830
|
-
if (versionId) {
|
|
1831
|
-
console.log(`Pulling volume: ${config.name} (version: ${versionId})`);
|
|
1832
|
-
} else {
|
|
1833
|
-
console.log(`Pulling volume: ${config.name}`);
|
|
1834
|
-
}
|
|
1835
|
-
console.log(source_default.dim("Getting download URL..."));
|
|
1836
|
-
const downloadInfo = await getStorageDownload({
|
|
1837
|
-
name: config.name,
|
|
1838
|
-
type: "volume",
|
|
1839
|
-
version: versionId
|
|
1840
|
-
});
|
|
1841
|
-
if ("empty" in downloadInfo) {
|
|
1842
|
-
await handleEmptyStorageResponse(cwd);
|
|
1843
|
-
return;
|
|
1844
|
-
}
|
|
1845
|
-
const downloadUrl = downloadInfo.url;
|
|
1846
|
-
if (!downloadUrl) {
|
|
1847
|
-
throw new Error("No download URL returned");
|
|
1848
|
-
}
|
|
1849
|
-
console.log(source_default.dim("Downloading from S3..."));
|
|
1850
|
-
const s3Response = await fetch(downloadUrl);
|
|
1851
|
-
if (!s3Response.ok) {
|
|
1852
|
-
throw new Error(`S3 download failed: ${s3Response.status}`);
|
|
1853
|
-
}
|
|
1854
|
-
const arrayBuffer = await s3Response.arrayBuffer();
|
|
1855
|
-
const tarBuffer = Buffer.from(arrayBuffer);
|
|
1856
|
-
console.log(source_default.green(`\u2713 Downloaded ${formatBytes(tarBuffer.length)}`));
|
|
1857
|
-
const tmpDir = fs3.mkdtempSync(path5.join(os3.tmpdir(), "vm0-"));
|
|
1858
|
-
const tarPath = path5.join(tmpDir, "volume.tar.gz");
|
|
1859
|
-
await fs3.promises.writeFile(tarPath, tarBuffer);
|
|
1860
|
-
console.log(source_default.dim("Syncing local files..."));
|
|
1861
|
-
const remoteFiles = await listTarFiles(tarPath);
|
|
1862
|
-
const remoteFilesSet = new Set(
|
|
1863
|
-
remoteFiles.map((f) => {
|
|
1864
|
-
return f.replace(/\\/g, "/");
|
|
1865
|
-
})
|
|
1866
|
-
);
|
|
1867
|
-
const removedCount = await removeExtraFiles(cwd, remoteFilesSet);
|
|
1868
|
-
if (removedCount > 0) {
|
|
1869
|
-
console.log(
|
|
1870
|
-
source_default.green(`\u2713 Removed ${removedCount} files not in remote`)
|
|
1871
|
-
);
|
|
1872
|
-
}
|
|
1873
|
-
console.log(source_default.dim("Extracting files..."));
|
|
1874
|
-
await So({
|
|
1875
|
-
file: tarPath,
|
|
1876
|
-
cwd,
|
|
1877
|
-
gzip: true
|
|
1878
|
-
});
|
|
1879
|
-
await fs3.promises.unlink(tarPath);
|
|
1880
|
-
await fs3.promises.rmdir(tmpDir);
|
|
1881
|
-
console.log(source_default.green(`\u2713 Extracted ${remoteFiles.length} files`));
|
|
1882
|
-
})
|
|
1883
|
-
);
|
|
1884
|
-
|
|
1885
|
-
// src/commands/volume/status.ts
|
|
1886
|
-
init_esm_shims();
|
|
1887
|
-
var statusCommand2 = new Command().name("status").description("Show status of cloud volume").action(
|
|
1888
|
-
withErrorHandler(async () => {
|
|
1889
|
-
const cwd = process.cwd();
|
|
1890
|
-
const config = await readStorageConfig(cwd);
|
|
1891
|
-
if (!config) {
|
|
1892
|
-
throw new Error("No volume initialized in this directory", {
|
|
1893
|
-
cause: new Error("Run: vm0 volume init")
|
|
1894
|
-
});
|
|
1895
|
-
}
|
|
1896
|
-
if (config.type !== "volume") {
|
|
1897
|
-
throw new Error(
|
|
1898
|
-
"This directory is initialized as an artifact, not a volume",
|
|
1899
|
-
{ cause: new Error("Use: vm0 artifact status") }
|
|
1900
|
-
);
|
|
1901
|
-
}
|
|
1902
|
-
console.log(`Checking volume: ${config.name}`);
|
|
1903
|
-
try {
|
|
1904
|
-
const info = await getStorageDownload({
|
|
1905
|
-
name: config.name,
|
|
1906
|
-
type: "volume"
|
|
1907
|
-
});
|
|
1908
|
-
const shortVersion = info.versionId.slice(0, 8);
|
|
1909
|
-
if ("empty" in info) {
|
|
1910
|
-
console.log(source_default.green("\u2713 Found (empty)"));
|
|
1911
|
-
console.log(source_default.dim(` Version: ${shortVersion}`));
|
|
1912
|
-
} else {
|
|
1913
|
-
console.log(source_default.green("\u2713 Found"));
|
|
1914
|
-
console.log(source_default.dim(` Version: ${shortVersion}`));
|
|
1915
|
-
console.log(source_default.dim(` Files: ${info.fileCount.toLocaleString()}`));
|
|
1916
|
-
console.log(source_default.dim(` Size: ${formatBytes(info.size)}`));
|
|
1917
|
-
}
|
|
1918
|
-
} catch (error) {
|
|
1919
|
-
if (error instanceof ApiRequestError && error.status === 404) {
|
|
1920
|
-
throw new Error("Not found on remote", {
|
|
1921
|
-
cause: new Error("Run: vm0 volume push")
|
|
1922
|
-
});
|
|
1923
|
-
}
|
|
1924
|
-
throw error;
|
|
1925
|
-
}
|
|
1926
|
-
})
|
|
1927
|
-
);
|
|
1928
|
-
|
|
1929
|
-
// src/commands/volume/list.ts
|
|
1930
|
-
init_esm_shims();
|
|
1931
|
-
var listCommand2 = new Command().name("list").alias("ls").description("List all remote volumes").action(
|
|
1932
|
-
withErrorHandler(async () => {
|
|
1933
|
-
const items = await listStorages({ type: "volume" });
|
|
1934
|
-
if (items.length === 0) {
|
|
1935
|
-
console.log(source_default.dim("No volumes found"));
|
|
1936
|
-
console.log(
|
|
1937
|
-
source_default.dim(" Create one with: vm0 volume init && vm0 volume push")
|
|
1938
|
-
);
|
|
1939
|
-
return;
|
|
1940
|
-
}
|
|
1941
|
-
const nameWidth = Math.max(
|
|
1942
|
-
4,
|
|
1943
|
-
...items.map((i) => {
|
|
1944
|
-
return i.name.length;
|
|
1945
|
-
})
|
|
1946
|
-
);
|
|
1947
|
-
const sizeWidth = Math.max(
|
|
1948
|
-
4,
|
|
1949
|
-
...items.map((i) => {
|
|
1950
|
-
return formatBytes(i.size).length;
|
|
1951
|
-
})
|
|
1952
|
-
);
|
|
1953
|
-
const filesWidth = Math.max(
|
|
1954
|
-
5,
|
|
1955
|
-
...items.map((i) => {
|
|
1956
|
-
return i.fileCount.toString().length;
|
|
1957
|
-
})
|
|
1958
|
-
);
|
|
1959
|
-
const header = [
|
|
1960
|
-
"NAME".padEnd(nameWidth),
|
|
1961
|
-
"SIZE".padStart(sizeWidth),
|
|
1962
|
-
"FILES".padStart(filesWidth),
|
|
1963
|
-
"UPDATED"
|
|
1964
|
-
].join(" ");
|
|
1965
|
-
console.log(source_default.dim(header));
|
|
1966
|
-
for (const item of items) {
|
|
1967
|
-
const row = [
|
|
1968
|
-
item.name.padEnd(nameWidth),
|
|
1969
|
-
formatBytes(item.size).padStart(sizeWidth),
|
|
1970
|
-
item.fileCount.toString().padStart(filesWidth),
|
|
1971
|
-
formatRelativeTime(item.updatedAt)
|
|
1972
|
-
].join(" ");
|
|
1973
|
-
console.log(row);
|
|
1974
|
-
}
|
|
1975
|
-
})
|
|
1976
|
-
);
|
|
1977
|
-
|
|
1978
|
-
// src/commands/volume/clone.ts
|
|
1979
|
-
init_esm_shims();
|
|
1980
|
-
|
|
1981
|
-
// src/lib/storage/clone-utils.ts
|
|
1982
|
-
init_esm_shims();
|
|
1983
|
-
import path6 from "path";
|
|
1984
|
-
import * as fs4 from "fs";
|
|
1985
|
-
import * as os4 from "os";
|
|
1986
|
-
async function cloneStorage(name, type, destination, options = {}) {
|
|
1987
|
-
const typeLabel = type;
|
|
1988
|
-
const dirStatus = checkDirectoryStatus(destination);
|
|
1989
|
-
if (dirStatus.exists && !dirStatus.empty) {
|
|
1990
|
-
throw new Error(`Directory "${destination}" is not empty`);
|
|
1991
|
-
}
|
|
1992
|
-
console.log(source_default.dim(`Checking remote ${typeLabel}...`));
|
|
1993
|
-
const downloadInfo = await getStorageDownload({
|
|
1994
|
-
name,
|
|
1995
|
-
type,
|
|
1996
|
-
version: options.version
|
|
1997
|
-
});
|
|
1998
|
-
console.log(source_default.dim(`Creating directory: ${destination}/`));
|
|
1999
|
-
await fs4.promises.mkdir(destination, { recursive: true });
|
|
2000
|
-
if ("empty" in downloadInfo) {
|
|
2001
|
-
await writeStorageConfig(name, destination, type);
|
|
2002
|
-
console.log(source_default.green(`\u2713 Cloned empty ${typeLabel}: ${name}`));
|
|
2003
|
-
console.log(source_default.dim(`\u2713 Initialized .vm0/storage.yaml`));
|
|
2004
|
-
return {
|
|
2005
|
-
success: true,
|
|
2006
|
-
fileCount: 0,
|
|
2007
|
-
size: 0,
|
|
2008
|
-
versionId: downloadInfo.versionId
|
|
2009
|
-
};
|
|
2010
|
-
}
|
|
2011
|
-
const downloadUrl = downloadInfo.url;
|
|
2012
|
-
if (!downloadUrl) {
|
|
2013
|
-
throw new Error("No download URL returned");
|
|
2014
|
-
}
|
|
2015
|
-
console.log(source_default.dim("Downloading from S3..."));
|
|
2016
|
-
const s3Response = await fetch(downloadUrl);
|
|
2017
|
-
if (!s3Response.ok) {
|
|
2018
|
-
await fs4.promises.rm(destination, { recursive: true, force: true });
|
|
2019
|
-
throw new Error(`S3 download failed: ${s3Response.status}`);
|
|
2020
|
-
}
|
|
2021
|
-
const arrayBuffer = await s3Response.arrayBuffer();
|
|
2022
|
-
const tarBuffer = Buffer.from(arrayBuffer);
|
|
2023
|
-
console.log(source_default.green(`\u2713 Downloaded ${formatBytes(tarBuffer.length)}`));
|
|
2024
|
-
const tmpDir = fs4.mkdtempSync(path6.join(os4.tmpdir(), "vm0-clone-"));
|
|
2025
|
-
const tarPath = path6.join(tmpDir, "archive.tar.gz");
|
|
2026
|
-
await fs4.promises.writeFile(tarPath, tarBuffer);
|
|
2027
|
-
const files = await listTarFiles(tarPath);
|
|
2028
|
-
console.log(source_default.dim("Extracting files..."));
|
|
2029
|
-
await So({
|
|
2030
|
-
file: tarPath,
|
|
2031
|
-
cwd: destination,
|
|
2032
|
-
gzip: true
|
|
2033
|
-
});
|
|
2034
|
-
await fs4.promises.unlink(tarPath);
|
|
2035
|
-
await fs4.promises.rmdir(tmpDir);
|
|
2036
|
-
console.log(source_default.green(`\u2713 Extracted ${files.length} files`));
|
|
2037
|
-
await writeStorageConfig(name, destination, type);
|
|
2038
|
-
console.log(source_default.green(`\u2713 Initialized .vm0/storage.yaml`));
|
|
2039
|
-
return {
|
|
2040
|
-
success: true,
|
|
2041
|
-
fileCount: downloadInfo.fileCount,
|
|
2042
|
-
size: downloadInfo.size,
|
|
2043
|
-
versionId: downloadInfo.versionId
|
|
2044
|
-
};
|
|
2045
|
-
}
|
|
2046
|
-
|
|
2047
|
-
// src/commands/volume/clone.ts
|
|
2048
|
-
var cloneCommand = new Command().name("clone").description("Clone a remote volume to local directory (latest version)").argument("<name>", "Volume name to clone").argument("[destination]", "Destination directory (default: volume name)").action(
|
|
2049
|
-
withErrorHandler(async (name, destination) => {
|
|
2050
|
-
const targetDir = destination || name;
|
|
2051
|
-
console.log(`Cloning volume: ${name}`);
|
|
2052
|
-
const result = await cloneStorage(name, "volume", targetDir);
|
|
2053
|
-
console.log(source_default.green(`
|
|
2054
|
-
\u2713 Successfully cloned volume: ${name}`));
|
|
2055
|
-
console.log(source_default.dim(` Location: ${targetDir}/`));
|
|
2056
|
-
console.log(source_default.dim(` Version: ${result.versionId.slice(0, 8)}`));
|
|
2057
|
-
})
|
|
2058
|
-
);
|
|
2059
|
-
|
|
2060
|
-
// src/commands/volume/index.ts
|
|
2061
|
-
var volumeCommand = new Command().name("volume").description("Manage volumes (defined in compose, not versioned after run)").addCommand(initCommand).addCommand(pushCommand).addCommand(pullCommand).addCommand(statusCommand2).addCommand(listCommand2).addCommand(cloneCommand);
|
|
2062
|
-
|
|
2063
|
-
// src/commands/artifact/index.ts
|
|
2064
|
-
init_esm_shims();
|
|
2065
|
-
|
|
2066
|
-
// src/commands/artifact/init.ts
|
|
2067
|
-
init_esm_shims();
|
|
2068
|
-
import path7 from "path";
|
|
2069
|
-
var initCommand2 = new Command().name("init").description("Initialize an artifact in the current directory").option(
|
|
2070
|
-
"-n, --name <name>",
|
|
2071
|
-
"Artifact name (required in non-interactive mode)"
|
|
2072
|
-
).action(
|
|
2073
|
-
withErrorHandler(async (options) => {
|
|
2074
|
-
const cwd = process.cwd();
|
|
2075
|
-
const dirName = path7.basename(cwd);
|
|
2076
|
-
const existingConfig = await readStorageConfig(cwd);
|
|
2077
|
-
if (existingConfig) {
|
|
2078
|
-
if (existingConfig.type === "artifact") {
|
|
2079
|
-
console.log(
|
|
2080
|
-
source_default.yellow(
|
|
2081
|
-
`Artifact already initialized: ${existingConfig.name}`
|
|
2082
|
-
)
|
|
2083
|
-
);
|
|
2084
|
-
} else {
|
|
2085
|
-
console.log(
|
|
2086
|
-
source_default.yellow(
|
|
2087
|
-
`Directory already initialized as volume: ${existingConfig.name}`
|
|
2088
|
-
)
|
|
2089
|
-
);
|
|
2090
|
-
console.log(
|
|
2091
|
-
source_default.dim(
|
|
2092
|
-
" To change type, delete .vm0/storage.yaml and reinitialize"
|
|
2093
|
-
)
|
|
2094
|
-
);
|
|
2095
|
-
}
|
|
2096
|
-
console.log(
|
|
2097
|
-
source_default.dim(`Config file: ${path7.join(cwd, ".vm0", "storage.yaml")}`)
|
|
2098
|
-
);
|
|
2099
|
-
return;
|
|
2100
|
-
}
|
|
2101
|
-
let artifactName;
|
|
2102
|
-
if (options.name) {
|
|
2103
|
-
artifactName = options.name;
|
|
2104
|
-
} else if (!isInteractive()) {
|
|
2105
|
-
throw new Error("--name flag is required in non-interactive mode", {
|
|
2106
|
-
cause: new Error("Usage: vm0 artifact init --name <artifact-name>")
|
|
2107
|
-
});
|
|
2108
|
-
} else {
|
|
2109
|
-
const defaultName = isValidStorageName(dirName) ? dirName : void 0;
|
|
2110
|
-
const name = await promptText(
|
|
2111
|
-
"Enter artifact name",
|
|
2112
|
-
defaultName,
|
|
2113
|
-
(value) => {
|
|
2114
|
-
if (!isValidStorageName(value)) {
|
|
2115
|
-
return "Must be 3-64 characters, lowercase alphanumeric with hyphens";
|
|
2116
|
-
}
|
|
2117
|
-
return true;
|
|
2118
|
-
}
|
|
2119
|
-
);
|
|
2120
|
-
if (name === void 0) {
|
|
2121
|
-
console.log(source_default.dim("Cancelled"));
|
|
2122
|
-
return;
|
|
2123
|
-
}
|
|
2124
|
-
artifactName = name;
|
|
2125
|
-
}
|
|
2126
|
-
if (!isValidStorageName(artifactName)) {
|
|
2127
|
-
throw new Error(`Invalid artifact name: "${artifactName}"`, {
|
|
2128
|
-
cause: new Error(
|
|
2129
|
-
"Artifact names must be 3-64 characters, lowercase alphanumeric with hyphens"
|
|
2130
|
-
)
|
|
2131
|
-
});
|
|
2132
|
-
}
|
|
2133
|
-
await writeStorageConfig(artifactName, cwd, "artifact");
|
|
2134
|
-
console.log(source_default.green(`\u2713 Initialized artifact: ${artifactName}`));
|
|
2135
|
-
console.log(
|
|
2136
|
-
source_default.dim(
|
|
2137
|
-
` Config saved to ${path7.join(cwd, ".vm0", "storage.yaml")}`
|
|
2138
|
-
)
|
|
2139
|
-
);
|
|
2140
|
-
})
|
|
2141
|
-
);
|
|
2142
|
-
|
|
2143
|
-
// src/commands/artifact/push.ts
|
|
2144
|
-
init_esm_shims();
|
|
2145
|
-
var pushCommand2 = new Command().name("push").description("Push local files to cloud artifact").option(
|
|
2146
|
-
"-f, --force",
|
|
2147
|
-
"Force upload even if content unchanged (recreate archive)"
|
|
2148
|
-
).action(
|
|
2149
|
-
withErrorHandler(async (options) => {
|
|
2150
|
-
const cwd = process.cwd();
|
|
2151
|
-
const config = await readStorageConfig(cwd);
|
|
2152
|
-
if (!config) {
|
|
2153
|
-
throw new Error("No artifact initialized in this directory", {
|
|
2154
|
-
cause: new Error("Run: vm0 artifact init")
|
|
2155
|
-
});
|
|
2156
|
-
}
|
|
2157
|
-
if (config.type !== "artifact") {
|
|
2158
|
-
throw new Error(
|
|
2159
|
-
"This directory is initialized as a volume, not an artifact",
|
|
2160
|
-
{ cause: new Error("Use: vm0 volume push") }
|
|
2161
|
-
);
|
|
2162
|
-
}
|
|
2163
|
-
console.log(`Pushing artifact: ${config.name}`);
|
|
2164
|
-
const result = await directUpload(config.name, "artifact", cwd, {
|
|
2165
|
-
onProgress: (message) => {
|
|
2166
|
-
console.log(source_default.dim(message));
|
|
2167
|
-
},
|
|
2168
|
-
force: options.force
|
|
2169
|
-
});
|
|
2170
|
-
const shortVersion = result.versionId.slice(0, 8);
|
|
2171
|
-
if (result.empty) {
|
|
2172
|
-
console.log(source_default.dim("No files found (empty artifact)"));
|
|
2173
|
-
} else if (result.deduplicated) {
|
|
2174
|
-
console.log(source_default.green("\u2713 Content unchanged (deduplicated)"));
|
|
2175
|
-
} else {
|
|
2176
|
-
console.log(source_default.green("\u2713 Upload complete"));
|
|
2177
|
-
}
|
|
2178
|
-
console.log(source_default.dim(` Version: ${shortVersion}`));
|
|
2179
|
-
console.log(source_default.dim(` Files: ${result.fileCount.toLocaleString()}`));
|
|
2180
|
-
console.log(source_default.dim(` Size: ${formatBytes(result.size)}`));
|
|
2181
|
-
})
|
|
2182
|
-
);
|
|
2183
|
-
|
|
2184
|
-
// src/commands/artifact/pull.ts
|
|
2185
|
-
init_esm_shims();
|
|
2186
|
-
import path8 from "path";
|
|
2187
|
-
import * as fs5 from "fs";
|
|
2188
|
-
import * as os5 from "os";
|
|
2189
|
-
var pullCommand2 = new Command().name("pull").description("Pull cloud artifact to local directory").argument("[versionId]", "Version ID to pull (default: latest)").action(
|
|
2190
|
-
withErrorHandler(async (versionId) => {
|
|
2191
|
-
const cwd = process.cwd();
|
|
2192
|
-
const config = await readStorageConfig(cwd);
|
|
2193
|
-
if (!config) {
|
|
2194
|
-
throw new Error("No artifact initialized in this directory", {
|
|
2195
|
-
cause: new Error("Run: vm0 artifact init")
|
|
2196
|
-
});
|
|
2197
|
-
}
|
|
2198
|
-
if (config.type !== "artifact") {
|
|
2199
|
-
throw new Error(
|
|
2200
|
-
"This directory is initialized as a volume, not an artifact",
|
|
2201
|
-
{ cause: new Error("Use: vm0 volume pull") }
|
|
2202
|
-
);
|
|
2203
|
-
}
|
|
2204
|
-
if (versionId) {
|
|
2205
|
-
console.log(`Pulling artifact: ${config.name} (version: ${versionId})`);
|
|
2206
|
-
} else {
|
|
2207
|
-
console.log(`Pulling artifact: ${config.name}`);
|
|
2208
|
-
}
|
|
2209
|
-
console.log(source_default.dim("Getting download URL..."));
|
|
2210
|
-
const downloadInfo = await getStorageDownload({
|
|
2211
|
-
name: config.name,
|
|
2212
|
-
type: "artifact",
|
|
2213
|
-
version: versionId
|
|
2214
|
-
});
|
|
2215
|
-
if ("empty" in downloadInfo) {
|
|
2216
|
-
await handleEmptyStorageResponse(cwd);
|
|
2217
|
-
return;
|
|
2218
|
-
}
|
|
2219
|
-
const downloadUrl = downloadInfo.url;
|
|
2220
|
-
if (!downloadUrl) {
|
|
2221
|
-
throw new Error("No download URL returned");
|
|
2222
|
-
}
|
|
2223
|
-
console.log(source_default.dim("Downloading from S3..."));
|
|
2224
|
-
const s3Response = await fetch(downloadUrl);
|
|
2225
|
-
if (!s3Response.ok) {
|
|
2226
|
-
throw new Error(`S3 download failed: ${s3Response.status}`);
|
|
2227
|
-
}
|
|
2228
|
-
const arrayBuffer = await s3Response.arrayBuffer();
|
|
2229
|
-
const tarBuffer = Buffer.from(arrayBuffer);
|
|
2230
|
-
console.log(source_default.green(`\u2713 Downloaded ${formatBytes(tarBuffer.length)}`));
|
|
2231
|
-
const tmpDir = fs5.mkdtempSync(path8.join(os5.tmpdir(), "vm0-"));
|
|
2232
|
-
const tarPath = path8.join(tmpDir, "artifact.tar.gz");
|
|
2233
|
-
await fs5.promises.writeFile(tarPath, tarBuffer);
|
|
2234
|
-
console.log(source_default.dim("Syncing local files..."));
|
|
2235
|
-
const remoteFiles = await listTarFiles(tarPath);
|
|
2236
|
-
const remoteFilesSet = new Set(
|
|
2237
|
-
remoteFiles.map((f) => {
|
|
2238
|
-
return f.replace(/\\/g, "/");
|
|
2239
|
-
})
|
|
2240
|
-
);
|
|
2241
|
-
const removedCount = await removeExtraFiles(cwd, remoteFilesSet);
|
|
2242
|
-
if (removedCount > 0) {
|
|
2243
|
-
console.log(
|
|
2244
|
-
source_default.green(`\u2713 Removed ${removedCount} files not in remote`)
|
|
2245
|
-
);
|
|
2246
|
-
}
|
|
2247
|
-
console.log(source_default.dim("Extracting files..."));
|
|
2248
|
-
await So({
|
|
2249
|
-
file: tarPath,
|
|
2250
|
-
cwd,
|
|
2251
|
-
gzip: true
|
|
2252
|
-
});
|
|
2253
|
-
await fs5.promises.unlink(tarPath);
|
|
2254
|
-
await fs5.promises.rmdir(tmpDir);
|
|
2255
|
-
console.log(source_default.green(`\u2713 Extracted ${remoteFiles.length} files`));
|
|
2256
|
-
})
|
|
2257
|
-
);
|
|
2258
|
-
|
|
2259
|
-
// src/commands/artifact/status.ts
|
|
2260
|
-
init_esm_shims();
|
|
2261
|
-
var statusCommand3 = new Command().name("status").description("Show status of cloud artifact").action(
|
|
2262
|
-
withErrorHandler(async () => {
|
|
2263
|
-
const cwd = process.cwd();
|
|
2264
|
-
const config = await readStorageConfig(cwd);
|
|
2265
|
-
if (!config) {
|
|
2266
|
-
throw new Error("No artifact initialized in this directory", {
|
|
2267
|
-
cause: new Error("Run: vm0 artifact init")
|
|
2268
|
-
});
|
|
2269
|
-
}
|
|
2270
|
-
if (config.type !== "artifact") {
|
|
2271
|
-
throw new Error(
|
|
2272
|
-
"This directory is initialized as a volume, not an artifact",
|
|
2273
|
-
{ cause: new Error("Use: vm0 volume status") }
|
|
2274
|
-
);
|
|
2275
|
-
}
|
|
2276
|
-
console.log(`Checking artifact: ${config.name}`);
|
|
2277
|
-
try {
|
|
2278
|
-
const info = await getStorageDownload({
|
|
2279
|
-
name: config.name,
|
|
2280
|
-
type: "artifact"
|
|
2281
|
-
});
|
|
2282
|
-
const shortVersion = info.versionId.slice(0, 8);
|
|
2283
|
-
if ("empty" in info) {
|
|
2284
|
-
console.log(source_default.green("\u2713 Found (empty)"));
|
|
2285
|
-
console.log(source_default.dim(` Version: ${shortVersion}`));
|
|
2286
|
-
} else {
|
|
2287
|
-
console.log(source_default.green("\u2713 Found"));
|
|
2288
|
-
console.log(source_default.dim(` Version: ${shortVersion}`));
|
|
2289
|
-
console.log(source_default.dim(` Files: ${info.fileCount.toLocaleString()}`));
|
|
2290
|
-
console.log(source_default.dim(` Size: ${formatBytes(info.size)}`));
|
|
2291
|
-
}
|
|
2292
|
-
} catch (error) {
|
|
2293
|
-
if (error instanceof ApiRequestError && error.status === 404) {
|
|
2294
|
-
throw new Error("Not found on remote", {
|
|
2295
|
-
cause: new Error("Run: vm0 artifact push")
|
|
2296
|
-
});
|
|
2297
|
-
}
|
|
2298
|
-
throw error;
|
|
2299
|
-
}
|
|
2300
|
-
})
|
|
2301
|
-
);
|
|
2302
|
-
|
|
2303
|
-
// src/commands/artifact/list.ts
|
|
2304
|
-
init_esm_shims();
|
|
2305
|
-
var listCommand3 = new Command().name("list").alias("ls").description("List all remote artifacts").action(
|
|
2306
|
-
withErrorHandler(async () => {
|
|
2307
|
-
const items = await listStorages({ type: "artifact" });
|
|
2308
|
-
if (items.length === 0) {
|
|
2309
|
-
console.log(source_default.dim("No artifacts found"));
|
|
2310
|
-
console.log(
|
|
2311
|
-
source_default.dim(
|
|
2312
|
-
" Create one with: vm0 artifact init && vm0 artifact push"
|
|
2313
|
-
)
|
|
2314
|
-
);
|
|
2315
|
-
return;
|
|
2316
|
-
}
|
|
2317
|
-
const nameWidth = Math.max(
|
|
2318
|
-
4,
|
|
2319
|
-
...items.map((i) => {
|
|
2320
|
-
return i.name.length;
|
|
2321
|
-
})
|
|
2322
|
-
);
|
|
2323
|
-
const sizeWidth = Math.max(
|
|
2324
|
-
4,
|
|
2325
|
-
...items.map((i) => {
|
|
2326
|
-
return formatBytes(i.size).length;
|
|
2327
|
-
})
|
|
2328
|
-
);
|
|
2329
|
-
const filesWidth = Math.max(
|
|
2330
|
-
5,
|
|
2331
|
-
...items.map((i) => {
|
|
2332
|
-
return i.fileCount.toString().length;
|
|
2333
|
-
})
|
|
2334
|
-
);
|
|
2335
|
-
const header = [
|
|
2336
|
-
"NAME".padEnd(nameWidth),
|
|
2337
|
-
"SIZE".padStart(sizeWidth),
|
|
2338
|
-
"FILES".padStart(filesWidth),
|
|
2339
|
-
"UPDATED"
|
|
2340
|
-
].join(" ");
|
|
2341
|
-
console.log(source_default.dim(header));
|
|
2342
|
-
for (const item of items) {
|
|
2343
|
-
const row = [
|
|
2344
|
-
item.name.padEnd(nameWidth),
|
|
2345
|
-
formatBytes(item.size).padStart(sizeWidth),
|
|
2346
|
-
item.fileCount.toString().padStart(filesWidth),
|
|
2347
|
-
formatRelativeTime(item.updatedAt)
|
|
2348
|
-
].join(" ");
|
|
2349
|
-
console.log(row);
|
|
2350
|
-
}
|
|
2351
|
-
})
|
|
2352
|
-
);
|
|
2353
|
-
|
|
2354
|
-
// src/commands/artifact/clone.ts
|
|
2355
|
-
init_esm_shims();
|
|
2356
|
-
var cloneCommand2 = new Command().name("clone").description("Clone a remote artifact to local directory (latest version)").argument("<name>", "Artifact name to clone").argument("[destination]", "Destination directory (default: artifact name)").action(
|
|
2357
|
-
withErrorHandler(async (name, destination) => {
|
|
2358
|
-
const targetDir = destination || name;
|
|
2359
|
-
console.log(`Cloning artifact: ${name}`);
|
|
2360
|
-
const result = await cloneStorage(name, "artifact", targetDir);
|
|
2361
|
-
console.log(source_default.green(`
|
|
2362
|
-
\u2713 Successfully cloned artifact: ${name}`));
|
|
2363
|
-
console.log(source_default.dim(` Location: ${targetDir}/`));
|
|
2364
|
-
console.log(source_default.dim(` Version: ${result.versionId.slice(0, 8)}`));
|
|
2365
|
-
})
|
|
2366
|
-
);
|
|
2367
|
-
|
|
2368
|
-
// src/commands/artifact/index.ts
|
|
2369
|
-
var artifactCommand = new Command().name("artifact").description("Manage artifacts (specified at run, versioned after run)").addCommand(initCommand2).addCommand(pushCommand2).addCommand(pullCommand2).addCommand(statusCommand3).addCommand(listCommand3).addCommand(cloneCommand2);
|
|
2370
|
-
|
|
2371
|
-
// src/commands/logs/index.ts
|
|
2372
|
-
init_esm_shims();
|
|
2373
|
-
|
|
2374
|
-
// src/commands/logs/search.ts
|
|
2375
|
-
init_esm_shims();
|
|
2376
|
-
var SEVEN_DAYS_MS = 7 * 24 * 60 * 60 * 1e3;
|
|
2377
|
-
function supportedSearchFramework(framework) {
|
|
2378
|
-
const normalized = framework ?? void 0;
|
|
2379
|
-
return isSupportedFramework(normalized) ? normalized : void 0;
|
|
2380
|
-
}
|
|
2381
|
-
function renderSearchEvent(event, framework, renderer, normalizer) {
|
|
2382
|
-
const parsedEvents = normalizer.process(
|
|
2383
|
-
event.eventData,
|
|
2384
|
-
supportedSearchFramework(framework),
|
|
2385
|
-
new Date(event.createdAt)
|
|
2386
|
-
);
|
|
2387
|
-
for (const parsed of parsedEvents) {
|
|
2388
|
-
renderer.render(parsed);
|
|
2389
|
-
}
|
|
2390
|
-
}
|
|
2391
|
-
function flushSearchRenderer(renderer, normalizer) {
|
|
2392
|
-
for (const parsed of normalizer.flush()) {
|
|
2393
|
-
renderer.render(parsed);
|
|
2394
|
-
}
|
|
2395
|
-
renderer.flush();
|
|
2396
|
-
}
|
|
2397
|
-
function formatRunHeader(runId, agentName, timestamp) {
|
|
2398
|
-
const shortId = runId.slice(0, 8);
|
|
2399
|
-
const time = formatIsoTimestamp(timestamp);
|
|
2400
|
-
return `\u2500\u2500 Run ${shortId} (${agentName}, ${time}) \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500`;
|
|
2401
|
-
}
|
|
2402
|
-
function parseContextOptions(options) {
|
|
2403
|
-
const contextN = options.context !== void 0 ? parseBoundedLogCount(options.context, "--context", 0, 10) : 0;
|
|
2404
|
-
const before = options.beforeContext !== void 0 ? parseBoundedLogCount(options.beforeContext, "--before-context", 0, 10) : contextN;
|
|
2405
|
-
const after = options.afterContext !== void 0 ? parseBoundedLogCount(options.afterContext, "--after-context", 0, 10) : contextN;
|
|
2406
|
-
return { before, after };
|
|
2407
|
-
}
|
|
2408
|
-
function parseLimit2(value) {
|
|
2409
|
-
if (value === void 0) return void 0;
|
|
2410
|
-
return parseBoundedLogCount(value, "--limit", 1, 50);
|
|
2411
|
-
}
|
|
2412
|
-
function renderResults(response) {
|
|
2413
|
-
const grouped = /* @__PURE__ */ new Map();
|
|
2414
|
-
for (const result of response.results) {
|
|
2415
|
-
const existing = grouped.get(result.runId);
|
|
2416
|
-
if (existing) {
|
|
2417
|
-
existing.results.push(result);
|
|
2418
|
-
} else {
|
|
2419
|
-
grouped.set(result.runId, {
|
|
2420
|
-
agentName: result.agentName,
|
|
2421
|
-
results: [result]
|
|
2422
|
-
});
|
|
2423
|
-
}
|
|
2424
|
-
}
|
|
2425
|
-
let isFirstGroup = true;
|
|
2426
|
-
for (const [runId, group] of grouped) {
|
|
2427
|
-
if (!isFirstGroup) {
|
|
2428
|
-
console.log();
|
|
2429
|
-
}
|
|
2430
|
-
isFirstGroup = false;
|
|
2431
|
-
const firstTimestamp = group.results[0].matchedEvent.createdAt;
|
|
2432
|
-
console.log(
|
|
2433
|
-
source_default.bold(formatRunHeader(runId, group.agentName, firstTimestamp))
|
|
2434
|
-
);
|
|
2435
|
-
for (const result of group.results) {
|
|
2436
|
-
const renderer = new EventRenderer({
|
|
2437
|
-
showTimestamp: true,
|
|
2438
|
-
verbose: false
|
|
2439
|
-
});
|
|
2440
|
-
const normalizer = new EventStreamNormalizer();
|
|
2441
|
-
for (const event of result.contextBefore) {
|
|
2442
|
-
renderSearchEvent(event, result.framework, renderer, normalizer);
|
|
2443
|
-
}
|
|
2444
|
-
renderSearchEvent(
|
|
2445
|
-
result.matchedEvent,
|
|
2446
|
-
result.framework,
|
|
2447
|
-
renderer,
|
|
2448
|
-
normalizer
|
|
2449
|
-
);
|
|
2450
|
-
for (const event of result.contextAfter) {
|
|
2451
|
-
renderSearchEvent(event, result.framework, renderer, normalizer);
|
|
2452
|
-
}
|
|
2453
|
-
flushSearchRenderer(renderer, normalizer);
|
|
2454
|
-
}
|
|
2455
|
-
}
|
|
2456
|
-
if (response.hasMore) {
|
|
2457
|
-
console.log();
|
|
2458
|
-
console.log(
|
|
2459
|
-
source_default.dim(
|
|
2460
|
-
` Showing first ${response.results.length} matches. Use --limit to see more.`
|
|
2461
|
-
)
|
|
2462
|
-
);
|
|
2463
|
-
}
|
|
2464
|
-
}
|
|
2465
|
-
var searchCommand = new Command().name("search").description("Search agent events across runs").argument("<keyword>", "Search keyword").option("-A, --after-context <n>", "Show n events after each match").option("-B, --before-context <n>", "Show n events before each match").option("-C, --context <n>", "Show n events before and after each match").option("--agent <id>", "Filter by agent ID").option("--run <id>", "Filter by specific run ID").option("--since <time>", "Search logs since (default: 7d)").option("--limit <n>", "Maximum number of matches (default: 20)").action(
|
|
2466
|
-
withErrorHandler(async (keyword, options) => {
|
|
2467
|
-
const searchKeyword = parseSearchQuery(keyword, "Keyword");
|
|
2468
|
-
if (options.agent !== void 0 && !isUUID(options.agent)) {
|
|
2469
|
-
console.error(
|
|
2470
|
-
source_default.red(`\u2717 Invalid agent ID "${options.agent}" \u2014 expected a UUID`)
|
|
2471
|
-
);
|
|
2472
|
-
console.error(source_default.dim(" Run: vm0 run list to find agent IDs"));
|
|
2473
|
-
process.exit(1);
|
|
2474
|
-
}
|
|
2475
|
-
if (options.run !== void 0 && !isUUID(options.run)) {
|
|
2476
|
-
console.error(
|
|
2477
|
-
source_default.red(`\u2717 Invalid run ID "${options.run}" \u2014 expected a UUID`)
|
|
2478
|
-
);
|
|
2479
|
-
console.error(source_default.dim(" Run: vm0 run list to find run IDs"));
|
|
2480
|
-
process.exit(1);
|
|
2481
|
-
}
|
|
2482
|
-
const { before, after } = parseContextOptions(options);
|
|
2483
|
-
const since = options.since !== void 0 ? parseTime(options.since) : Date.now() - SEVEN_DAYS_MS;
|
|
2484
|
-
const limit = parseLimit2(options.limit);
|
|
2485
|
-
const response = await searchLogs({
|
|
2486
|
-
keyword: searchKeyword,
|
|
2487
|
-
agentId: options.agent,
|
|
2488
|
-
runId: options.run,
|
|
2489
|
-
since,
|
|
2490
|
-
limit,
|
|
2491
|
-
before,
|
|
2492
|
-
after
|
|
2493
|
-
});
|
|
2494
|
-
if (response.results.length === 0) {
|
|
2495
|
-
console.log(source_default.dim("No matches found"));
|
|
2496
|
-
console.log(
|
|
2497
|
-
source_default.dim(
|
|
2498
|
-
" Try a broader search with --since 30d or a different keyword"
|
|
2499
|
-
)
|
|
2500
|
-
);
|
|
2501
|
-
return;
|
|
2502
|
-
}
|
|
2503
|
-
renderResults(response);
|
|
2504
|
-
})
|
|
2505
|
-
);
|
|
2506
|
-
|
|
2507
|
-
// src/commands/logs/index.ts
|
|
2508
|
-
var PAGE_LIMIT = 100;
|
|
2509
|
-
function buildPlatformLogsUrl(apiUrl, runId) {
|
|
2510
|
-
const url = new URL(apiUrl);
|
|
2511
|
-
const hostname = url.hostname;
|
|
2512
|
-
if (hostname === "localhost" || hostname === "127.0.0.1") {
|
|
2513
|
-
return `http://${hostname}:3001/logs/${runId}`;
|
|
2514
|
-
}
|
|
2515
|
-
const parts = hostname.split(".");
|
|
2516
|
-
if (parts[0] === "www" || parts[0] === "app" || parts[0] === "platform" || parts[0] === "api") {
|
|
2517
|
-
parts[0] = "app";
|
|
2518
|
-
} else {
|
|
2519
|
-
parts.unshift("app");
|
|
2520
|
-
}
|
|
2521
|
-
const platformHost = parts.join(".");
|
|
2522
|
-
const port = url.port ? `:${url.port}` : "";
|
|
2523
|
-
return `https://${platformHost}${port}/logs/${runId}`;
|
|
2524
|
-
}
|
|
2525
|
-
function supportedLogFramework(framework) {
|
|
2526
|
-
return isSupportedFramework(framework) ? framework : void 0;
|
|
2527
|
-
}
|
|
2528
|
-
function hasLogFramework(framework) {
|
|
2529
|
-
return framework !== void 0 && framework !== null;
|
|
2530
|
-
}
|
|
2531
|
-
function formatMetric(metric) {
|
|
2532
|
-
const memPercent = (metric.mem_used / metric.mem_total * 100).toFixed(1);
|
|
2533
|
-
const diskPercent = (metric.disk_used / metric.disk_total * 100).toFixed(1);
|
|
2534
|
-
return `[${metric.ts}] CPU: ${metric.cpu.toFixed(1)}% | Mem: ${formatBytes(metric.mem_used)}/${formatBytes(metric.mem_total)} (${memPercent}%) | Disk: ${formatBytes(metric.disk_used)}/${formatBytes(metric.disk_total)} (${diskPercent}%)`;
|
|
2535
|
-
}
|
|
2536
|
-
function formatFirewallTag(entry) {
|
|
2537
|
-
if (!entry.firewall_name) return "";
|
|
2538
|
-
const billable = entry.firewall_billable ? ` ${source_default.yellow("$")}` : "";
|
|
2539
|
-
return ` ${source_default.cyan(`[${entry.firewall_name}${billable}]`)}`;
|
|
2540
|
-
}
|
|
2541
|
-
function formatBrowserUserAgentTag(entry) {
|
|
2542
|
-
return entry.browser_user_agent ? ` ${source_default.magenta("[browser]")}` : "";
|
|
2543
|
-
}
|
|
2544
|
-
function formatConnectorDiagnosticInfo(entry) {
|
|
2545
|
-
const tags = [];
|
|
2546
|
-
if (entry.connector_diagnostic_type) {
|
|
2547
|
-
tags.push(entry.connector_diagnostic_type);
|
|
2548
|
-
}
|
|
2549
|
-
if (entry.connector_diagnostic_reason) {
|
|
2550
|
-
tags.push(entry.connector_diagnostic_reason);
|
|
2551
|
-
}
|
|
2552
|
-
if (entry.connector_diagnostic_env_names && entry.connector_diagnostic_env_names.length > 0) {
|
|
2553
|
-
tags.push(`env: ${entry.connector_diagnostic_env_names.join(", ")}`);
|
|
2554
|
-
}
|
|
2555
|
-
if (entry.connector_diagnostic_base) {
|
|
2556
|
-
tags.push(`base: ${entry.connector_diagnostic_base}`);
|
|
2557
|
-
}
|
|
2558
|
-
if (tags.length === 0) return "";
|
|
2559
|
-
return ` ${source_default.red(`[connector diagnostic: ${tags.join("; ")}]`)}`;
|
|
2560
|
-
}
|
|
2561
|
-
function formatConnectorRouteInfo(entry) {
|
|
2562
|
-
const tags = [];
|
|
2563
|
-
if (entry.connector_route_reason) {
|
|
2564
|
-
tags.push(entry.connector_route_reason);
|
|
2565
|
-
}
|
|
2566
|
-
if (entry.connector_route_candidates && entry.connector_route_candidates.length > 0) {
|
|
2567
|
-
tags.push(`candidates: ${entry.connector_route_candidates.join(", ")}`);
|
|
2568
|
-
}
|
|
2569
|
-
if (tags.length === 0) return "";
|
|
2570
|
-
return ` ${source_default.red(`[connector route: ${tags.join("; ")}]`)}`;
|
|
2571
|
-
}
|
|
2572
|
-
function nonEmptyLogField(value) {
|
|
2573
|
-
const trimmed = value?.trim();
|
|
2574
|
-
return trimmed && trimmed.length > 0 ? trimmed : void 0;
|
|
2575
|
-
}
|
|
2576
|
-
function formatNetworkTarget(entry) {
|
|
2577
|
-
const url = nonEmptyLogField(entry.url);
|
|
2578
|
-
if (url) {
|
|
2579
|
-
return url;
|
|
2580
|
-
}
|
|
2581
|
-
const host = nonEmptyLogField(entry.host);
|
|
2582
|
-
if (!host) {
|
|
2583
|
-
return "unknown";
|
|
2584
|
-
}
|
|
2585
|
-
return entry.port !== void 0 ? `${host}:${entry.port}` : host;
|
|
2586
|
-
}
|
|
2587
|
-
function formatNetworkDeny(entry) {
|
|
2588
|
-
const method = entry.method || "???";
|
|
2589
|
-
const url = formatNetworkTarget(entry);
|
|
2590
|
-
const error = entry.firewall_error ? ` ${source_default.red(entry.firewall_error)}` : "";
|
|
2591
|
-
return `[${entry.timestamp}] ${method.padEnd(6)} ${source_default.red.bold("DENY")} ${source_default.dim(url)}${formatFirewallTag(entry)}${formatBrowserUserAgentTag(entry)}${error}${formatConnectorDiagnosticInfo(entry)}${formatConnectorRouteInfo(entry)}`;
|
|
2592
|
-
}
|
|
2593
|
-
function formatNetworkBlock(entry) {
|
|
2594
|
-
const method = entry.method || "???";
|
|
2595
|
-
const target = formatNetworkTarget(entry);
|
|
2596
|
-
const error = entry.firewall_error ? ` ${source_default.red(entry.firewall_error)}` : "";
|
|
2597
|
-
return `[${entry.timestamp}] ${method.padEnd(6)} ${source_default.yellow.bold("BLOCK")} ${source_default.dim(target)}${formatFirewallTag(entry)}${formatBrowserUserAgentTag(entry)}${error}${formatConnectorDiagnosticInfo(entry)}${formatConnectorRouteInfo(entry)}${formatAuthInfo(entry)}`;
|
|
2598
|
-
}
|
|
2599
|
-
function formatAuthInfo(entry) {
|
|
2600
|
-
const tags = [];
|
|
2601
|
-
if (entry.auth_url_rewrite) {
|
|
2602
|
-
tags.push("url-rewrite");
|
|
2603
|
-
}
|
|
2604
|
-
if (entry.auth_resolved_secrets && entry.auth_resolved_secrets.length > 0) {
|
|
2605
|
-
const refreshedSet = new Set(entry.auth_refreshed_secrets ?? []);
|
|
2606
|
-
for (const name of entry.auth_resolved_secrets) {
|
|
2607
|
-
if (refreshedSet.has(name)) {
|
|
2608
|
-
tags.push(`${name} (refreshed)`);
|
|
2609
|
-
} else if (entry.auth_cache_hit) {
|
|
2610
|
-
tags.push(`${name} (cached)`);
|
|
2611
|
-
} else {
|
|
2612
|
-
tags.push(name);
|
|
2613
|
-
}
|
|
2614
|
-
}
|
|
2615
|
-
}
|
|
2616
|
-
if (tags.length === 0) return "";
|
|
2617
|
-
return ` ${source_default.yellow(`\u2194 ${tags.join(", ")}`)}`;
|
|
2618
|
-
}
|
|
2619
|
-
function formatNetworkRequest(entry) {
|
|
2620
|
-
let statusColor;
|
|
2621
|
-
const status = entry.status || 0;
|
|
2622
|
-
if (status >= 200 && status < 300) {
|
|
2623
|
-
statusColor = source_default.green;
|
|
2624
|
-
} else if (status >= 300 && status < 400) {
|
|
2625
|
-
statusColor = source_default.yellow;
|
|
2626
|
-
} else if (status >= 400) {
|
|
2627
|
-
statusColor = source_default.red;
|
|
2628
|
-
} else {
|
|
2629
|
-
statusColor = source_default.gray;
|
|
2630
|
-
}
|
|
2631
|
-
let latencyColor;
|
|
2632
|
-
const latencyMs = entry.latency_ms || 0;
|
|
2633
|
-
if (latencyMs < 500) {
|
|
2634
|
-
latencyColor = source_default.green;
|
|
2635
|
-
} else if (latencyMs < 2e3) {
|
|
2636
|
-
latencyColor = source_default.yellow;
|
|
2637
|
-
} else {
|
|
2638
|
-
latencyColor = source_default.red;
|
|
2639
|
-
}
|
|
2640
|
-
const method = entry.method || "???";
|
|
2641
|
-
const requestSize = entry.request_size || 0;
|
|
2642
|
-
const responseSize = entry.response_size || 0;
|
|
2643
|
-
const url = formatNetworkTarget(entry);
|
|
2644
|
-
const error = entry.firewall_error ? ` ${source_default.red(entry.firewall_error)}` : "";
|
|
2645
|
-
let line = `[${entry.timestamp}] ${method.padEnd(6)} ${statusColor(status)} ${latencyColor(latencyMs + "ms")} ${formatBytes(requestSize)}/${formatBytes(responseSize)} ${source_default.dim(url)}${formatFirewallTag(entry)}${formatBrowserUserAgentTag(entry)}${error}${formatConnectorDiagnosticInfo(entry)}${formatConnectorRouteInfo(entry)}${formatAuthInfo(entry)}`;
|
|
2646
|
-
line += formatCaptureFields(entry);
|
|
2647
|
-
return line;
|
|
2648
|
-
}
|
|
2649
|
-
var BODY_PREVIEW_LENGTH = 200;
|
|
2650
|
-
function formatCaptureFields(entry) {
|
|
2651
|
-
let result = "";
|
|
2652
|
-
if (entry.request_headers) {
|
|
2653
|
-
const hdrs = Object.entries(entry.request_headers).map(([k, v]) => {
|
|
2654
|
-
return `${k}: ${v}`;
|
|
2655
|
-
}).join(", ");
|
|
2656
|
-
result += `
|
|
2657
|
-
${source_default.gray("request_headers:")} ${hdrs}`;
|
|
2658
|
-
}
|
|
2659
|
-
if (entry.request_body) {
|
|
2660
|
-
const truncated = entry.request_body_truncated ? " (truncated)" : "";
|
|
2661
|
-
const preview = entry.request_body.slice(0, BODY_PREVIEW_LENGTH);
|
|
2662
|
-
const ellipsis = entry.request_body.length > BODY_PREVIEW_LENGTH ? "..." : "";
|
|
2663
|
-
result += `
|
|
2664
|
-
${source_default.gray("request_body:")} ${preview}${ellipsis}${truncated}`;
|
|
2665
|
-
}
|
|
2666
|
-
if (entry.response_body) {
|
|
2667
|
-
const truncated = entry.response_body_truncated ? " (truncated)" : "";
|
|
2668
|
-
const preview = entry.response_body.slice(0, BODY_PREVIEW_LENGTH);
|
|
2669
|
-
const ellipsis = entry.response_body.length > BODY_PREVIEW_LENGTH ? "..." : "";
|
|
2670
|
-
result += `
|
|
2671
|
-
${source_default.gray("response_body:")} ${preview}${ellipsis}${truncated}`;
|
|
2672
|
-
}
|
|
2673
|
-
return result;
|
|
2674
|
-
}
|
|
2675
|
-
function formatNetworkTcp(entry) {
|
|
2676
|
-
const host = entry.host || "unknown";
|
|
2677
|
-
const port = entry.port || 0;
|
|
2678
|
-
const requestSize = entry.request_size || 0;
|
|
2679
|
-
const responseSize = entry.response_size || 0;
|
|
2680
|
-
const latencyMs = entry.latency_ms || 0;
|
|
2681
|
-
const error = entry.error ? ` ${source_default.red(entry.error)}` : "";
|
|
2682
|
-
return `[${entry.timestamp}] ${source_default.blue("TCP")} ${latencyMs}ms ${formatBytes(requestSize)}/${formatBytes(responseSize)} ${source_default.dim(`${host}:${port}`)}${error}`;
|
|
2683
|
-
}
|
|
2684
|
-
function formatNetworkOther(entry) {
|
|
2685
|
-
const proto = (entry.type || "???").toUpperCase();
|
|
2686
|
-
const host = entry.host || "unknown";
|
|
2687
|
-
const port = entry.port || 0;
|
|
2688
|
-
const size = entry.request_size ? ` ${formatBytes(entry.request_size)}` : "";
|
|
2689
|
-
const dnsResult = entry.type === "dns" && entry.dns_result ? ` ${source_default.gray("->")} ${source_default.dim(entry.dns_result)}` : "";
|
|
2690
|
-
return `[${entry.timestamp}] ${source_default.magenta(proto.padEnd(5))}${size} ${source_default.dim(`${host}:${port}`)}${dnsResult}`;
|
|
2691
|
-
}
|
|
2692
|
-
function formatNetworkLog(entry) {
|
|
2693
|
-
if (entry.action === "BLOCK") return formatNetworkBlock(entry);
|
|
2694
|
-
if (entry.type === "tcp") return formatNetworkTcp(entry);
|
|
2695
|
-
if (entry.type && entry.type !== "http") return formatNetworkOther(entry);
|
|
2696
|
-
if (entry.action === "DENY") return formatNetworkDeny(entry);
|
|
2697
|
-
return formatNetworkRequest(entry);
|
|
2698
|
-
}
|
|
2699
|
-
function createLogRenderer(verbose) {
|
|
2700
|
-
return new EventRenderer({
|
|
2701
|
-
showTimestamp: true,
|
|
2702
|
-
verbose
|
|
2703
|
-
});
|
|
2704
|
-
}
|
|
2705
|
-
function renderAgentEvent(event, renderer, normalizer, framework) {
|
|
2706
|
-
const parsedEvents = normalizer.process(
|
|
2707
|
-
event.eventData,
|
|
2708
|
-
framework,
|
|
2709
|
-
new Date(event.createdAt)
|
|
2710
|
-
);
|
|
2711
|
-
for (const parsed of parsedEvents) {
|
|
2712
|
-
renderer.render(parsed);
|
|
2713
|
-
}
|
|
2714
|
-
}
|
|
2715
|
-
function getLogType(options) {
|
|
2716
|
-
const selected = [
|
|
2717
|
-
options.agent,
|
|
2718
|
-
options.system,
|
|
2719
|
-
options.metrics,
|
|
2720
|
-
options.network
|
|
2721
|
-
].filter(Boolean).length;
|
|
2722
|
-
if (selected > 1) {
|
|
2723
|
-
throw new Error(
|
|
2724
|
-
"Options --agent, --system, --metrics, and --network are mutually exclusive"
|
|
2725
|
-
);
|
|
2726
|
-
}
|
|
2727
|
-
if (options.system) return "system";
|
|
2728
|
-
if (options.metrics) return "metrics";
|
|
2729
|
-
if (options.network) return "network";
|
|
2730
|
-
return "agent";
|
|
2731
|
-
}
|
|
2732
|
-
var logsCommand = new Command().name("logs").description("View and search agent run logs").argument("[runId]", "Run ID to fetch logs for").addCommand(searchCommand).option("-a, --agent", "Show agent events (default)").option("-s, --system", "Show system log").option("-m, --metrics", "Show metrics").option("-n, --network", "Show network logs (proxy traffic)").option(
|
|
2733
|
-
"--since <time>",
|
|
2734
|
-
"Show logs since timestamp (e.g., 5m, 2h, 1d, 2024-01-15T10:30:00Z, 1705312200)"
|
|
2735
|
-
).option("--tail <n>", "Show last N entries (default: 5)").option("--head <n>", "Show first N entries").option("--all", "Fetch all log entries").action(
|
|
2736
|
-
withErrorHandler(
|
|
2737
|
-
async (runId, options) => {
|
|
2738
|
-
if (!runId) {
|
|
2739
|
-
logsCommand.help();
|
|
2740
|
-
return;
|
|
2741
|
-
}
|
|
2742
|
-
if (!isUUID(runId)) {
|
|
2743
|
-
console.error(
|
|
2744
|
-
source_default.red(`\u2717 Invalid run ID "${runId}" \u2014 expected a UUID`)
|
|
2745
|
-
);
|
|
2746
|
-
console.error(source_default.dim(" Run: vm0 run list to find run IDs"));
|
|
2747
|
-
process.exit(1);
|
|
2748
|
-
}
|
|
2749
|
-
const logType = getLogType(options);
|
|
2750
|
-
const countModes = [
|
|
2751
|
-
options.tail !== void 0,
|
|
2752
|
-
options.head !== void 0,
|
|
2753
|
-
options.all === true
|
|
2754
|
-
].filter(Boolean).length;
|
|
2755
|
-
if (countModes > 1) {
|
|
2756
|
-
throw new Error(
|
|
2757
|
-
"Options --tail, --head, and --all are mutually exclusive"
|
|
2758
|
-
);
|
|
2759
|
-
}
|
|
2760
|
-
let since;
|
|
2761
|
-
if (options.since !== void 0) {
|
|
2762
|
-
since = parseTime(options.since);
|
|
2763
|
-
}
|
|
2764
|
-
const isAll = options.all === true;
|
|
2765
|
-
const isHead = options.head !== void 0;
|
|
2766
|
-
const isTail = options.tail !== void 0;
|
|
2767
|
-
let targetCount;
|
|
2768
|
-
if (isAll) {
|
|
2769
|
-
targetCount = "all";
|
|
2770
|
-
} else if (isHead) {
|
|
2771
|
-
targetCount = parsePositiveLogCount(options.head, "--head");
|
|
2772
|
-
} else if (isTail) {
|
|
2773
|
-
targetCount = parsePositiveLogCount(options.tail, "--tail");
|
|
2774
|
-
} else {
|
|
2775
|
-
targetCount = 5;
|
|
2776
|
-
}
|
|
2777
|
-
const order = isHead ? "asc" : "desc";
|
|
2778
|
-
const apiUrl = await getApiUrl();
|
|
2779
|
-
const platformUrl = buildPlatformLogsUrl(apiUrl, runId);
|
|
2780
|
-
switch (logType) {
|
|
2781
|
-
case "agent":
|
|
2782
|
-
await showAgentEvents(
|
|
2783
|
-
runId,
|
|
2784
|
-
{ since, targetCount, order },
|
|
2785
|
-
platformUrl
|
|
2786
|
-
);
|
|
2787
|
-
break;
|
|
2788
|
-
case "system":
|
|
2789
|
-
await showSystemLog(runId, { since, targetCount, order });
|
|
2790
|
-
break;
|
|
2791
|
-
case "metrics":
|
|
2792
|
-
await showMetrics(runId, { since, targetCount, order });
|
|
2793
|
-
break;
|
|
2794
|
-
case "network":
|
|
2795
|
-
await showNetworkLogs(runId, { since, targetCount, order });
|
|
2796
|
-
break;
|
|
2797
|
-
}
|
|
2798
|
-
}
|
|
2799
|
-
)
|
|
2800
|
-
);
|
|
2801
|
-
async function showAgentEvents(runId, options, platformUrl) {
|
|
2802
|
-
const events = await collectLogItems({
|
|
2803
|
-
fetchPage: async (request) => {
|
|
2804
|
-
const response = await getAgentEvents(runId, request);
|
|
2805
|
-
const responseFramework = response.framework;
|
|
2806
|
-
return {
|
|
2807
|
-
items: response.events.map((event) => {
|
|
2808
|
-
const framework2 = supportedLogFramework(
|
|
2809
|
-
responseFramework ?? void 0
|
|
2810
|
-
);
|
|
2811
|
-
return {
|
|
2812
|
-
event,
|
|
2813
|
-
...framework2 ? { framework: framework2 } : {},
|
|
2814
|
-
useDefaultFramework: !hasLogFramework(responseFramework)
|
|
2815
|
-
};
|
|
2816
|
-
}),
|
|
2817
|
-
hasMore: response.hasMore,
|
|
2818
|
-
nextCursor: response.nextCursor
|
|
2819
|
-
};
|
|
2820
|
-
},
|
|
2821
|
-
sinceTime: options.since,
|
|
2822
|
-
targetCount: options.targetCount,
|
|
2823
|
-
order: options.order,
|
|
2824
|
-
pageLimit: PAGE_LIMIT
|
|
2825
|
-
});
|
|
2826
|
-
if (events.length === 0) {
|
|
2827
|
-
console.log(source_default.yellow("No agent events found for this run"));
|
|
2828
|
-
return;
|
|
2829
|
-
}
|
|
2830
|
-
const framework = events.find((item) => {
|
|
2831
|
-
return item.framework !== void 0;
|
|
2832
|
-
})?.framework;
|
|
2833
|
-
const renderer = createLogRenderer(true);
|
|
2834
|
-
const normalizer = new EventStreamNormalizer();
|
|
2835
|
-
for (const item of events) {
|
|
2836
|
-
renderAgentEvent(
|
|
2837
|
-
item.event,
|
|
2838
|
-
renderer,
|
|
2839
|
-
normalizer,
|
|
2840
|
-
item.framework ?? (item.useDefaultFramework ? framework : void 0)
|
|
2841
|
-
);
|
|
2842
|
-
}
|
|
2843
|
-
for (const parsed of normalizer.flush()) {
|
|
2844
|
-
renderer.render(parsed);
|
|
2845
|
-
}
|
|
2846
|
-
renderer.flush();
|
|
2847
|
-
console.log(source_default.dim(`View on platform: ${platformUrl}`));
|
|
2848
|
-
}
|
|
2849
|
-
async function showSystemLog(runId, options) {
|
|
2850
|
-
const pages = [];
|
|
2851
|
-
const seenCursors = /* @__PURE__ */ new Set();
|
|
2852
|
-
let cursor;
|
|
2853
|
-
let remainingBatches = options.targetCount === "all" ? void 0 : options.targetCount;
|
|
2854
|
-
while (true) {
|
|
2855
|
-
const limit = remainingBatches === void 0 ? PAGE_LIMIT : Math.min(remainingBatches, PAGE_LIMIT);
|
|
2856
|
-
const response = await getSystemLog(runId, {
|
|
2857
|
-
sinceTime: options.since,
|
|
2858
|
-
cursor,
|
|
2859
|
-
limit,
|
|
2860
|
-
order: options.order
|
|
2861
|
-
});
|
|
2862
|
-
const pageHasLog = response.systemLog.length > 0;
|
|
2863
|
-
if (pageHasLog) {
|
|
2864
|
-
pages.push(response.systemLog);
|
|
2865
|
-
}
|
|
2866
|
-
if (remainingBatches !== void 0 && pageHasLog) {
|
|
2867
|
-
remainingBatches -= limit;
|
|
2868
|
-
if (remainingBatches <= 0) {
|
|
2869
|
-
break;
|
|
2870
|
-
}
|
|
2871
|
-
}
|
|
2872
|
-
const nextCursor = response.nextCursor ?? null;
|
|
2873
|
-
if (!response.hasMore || !nextCursor || seenCursors.has(nextCursor)) {
|
|
2874
|
-
break;
|
|
2875
|
-
}
|
|
2876
|
-
seenCursors.add(nextCursor);
|
|
2877
|
-
cursor = nextCursor;
|
|
2878
|
-
}
|
|
2879
|
-
if (pages.length === 0) {
|
|
2880
|
-
console.log(source_default.yellow("No system log found for this run"));
|
|
2881
|
-
return;
|
|
2882
|
-
}
|
|
2883
|
-
console.log(pages.join(""));
|
|
2884
|
-
}
|
|
2885
|
-
async function showMetrics(runId, options) {
|
|
2886
|
-
const metrics = await collectLogItems({
|
|
2887
|
-
fetchPage: async (request) => {
|
|
2888
|
-
const response = await getMetrics(runId, request);
|
|
2889
|
-
return {
|
|
2890
|
-
items: response.metrics,
|
|
2891
|
-
hasMore: response.hasMore,
|
|
2892
|
-
nextCursor: response.nextCursor
|
|
2893
|
-
};
|
|
2894
|
-
},
|
|
2895
|
-
sinceTime: options.since,
|
|
2896
|
-
targetCount: options.targetCount,
|
|
2897
|
-
order: options.order,
|
|
2898
|
-
pageLimit: PAGE_LIMIT
|
|
2899
|
-
});
|
|
2900
|
-
if (metrics.length === 0) {
|
|
2901
|
-
console.log(source_default.yellow("No metrics found for this run"));
|
|
2902
|
-
return;
|
|
2903
|
-
}
|
|
2904
|
-
for (const metric of metrics) {
|
|
2905
|
-
console.log(formatMetric(metric));
|
|
2906
|
-
}
|
|
2907
|
-
}
|
|
2908
|
-
async function showNetworkLogs(runId, options) {
|
|
2909
|
-
const networkLogs = await collectLogItems({
|
|
2910
|
-
fetchPage: async (request) => {
|
|
2911
|
-
const response = await getNetworkLogs(runId, request);
|
|
2912
|
-
return {
|
|
2913
|
-
items: response.networkLogs,
|
|
2914
|
-
hasMore: response.hasMore,
|
|
2915
|
-
nextCursor: response.nextCursor
|
|
2916
|
-
};
|
|
2917
|
-
},
|
|
2918
|
-
sinceTime: options.since,
|
|
2919
|
-
targetCount: options.targetCount,
|
|
2920
|
-
order: options.order,
|
|
2921
|
-
pageLimit: PAGE_LIMIT
|
|
2922
|
-
});
|
|
2923
|
-
if (networkLogs.length === 0) {
|
|
2924
|
-
console.log(
|
|
2925
|
-
source_default.yellow(
|
|
2926
|
-
"No network logs found for this run. Network logs are only captured when using a runner with proxy enabled"
|
|
2927
|
-
)
|
|
2928
|
-
);
|
|
2929
|
-
return;
|
|
2930
|
-
}
|
|
2931
|
-
for (const entry of networkLogs) {
|
|
2932
|
-
console.log(formatNetworkLog(entry));
|
|
2933
|
-
}
|
|
2934
|
-
}
|
|
2935
|
-
|
|
2936
912
|
// src/commands/whoami.ts
|
|
2937
913
|
init_esm_shims();
|
|
2938
914
|
function isInsideSandbox() {
|
|
@@ -2988,13 +964,9 @@ var whoamiCommand = new Command().name("whoami").description("Show current ident
|
|
|
2988
964
|
|
|
2989
965
|
// src/index.ts
|
|
2990
966
|
var program = new Command();
|
|
2991
|
-
program.name("vm0").description("VM0 CLI - Build and run agents with natural language").version("9.244.
|
|
967
|
+
program.name("vm0").description("VM0 CLI - Build and run agents with natural language").version("9.244.7");
|
|
2992
968
|
program.addCommand(authCommand);
|
|
2993
|
-
program.addCommand(composeCommand);
|
|
2994
969
|
program.addCommand(runCommand);
|
|
2995
|
-
program.addCommand(volumeCommand);
|
|
2996
|
-
program.addCommand(artifactCommand);
|
|
2997
|
-
program.addCommand(logsCommand);
|
|
2998
970
|
program.addCommand(whoamiCommand);
|
|
2999
971
|
if (process.argv[1]?.endsWith("index.js") || process.argv[1]?.endsWith("index.ts") || process.argv[1]?.endsWith("vm0")) {
|
|
3000
972
|
await configureGlobalProxyFromEnv();
|