@vm0/cli 9.244.4 → 9.244.6
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-JSLUNSTT.js → agent-AOWGKKI7.js} +7 -7
- package/{banking-6GCF5OYC.js → banking-2ZRGXQJB.js} +3 -3
- package/{chat-MLA4L7NU.js → chat-XT3ZFO2U.js} +4 -4
- package/{chunk-Q4DTF2TA.js → chunk-2GMLVBYY.js} +4 -4
- package/{chunk-QKZ37ZQL.js → chunk-GGJ3V5JF.js} +3 -3
- package/{chunk-CK6ATTWW.js → chunk-HEAUPGDO.js} +1 -2
- package/{chunk-G7TORBE5.js → chunk-J7NZ4R4R.js} +2 -2
- package/{chunk-PTF3UL74.js → chunk-LX7FTB2W.js} +2 -2
- package/{chunk-4QZ4HY4E.js → chunk-MBYTQ3JJ.js} +31 -161
- package/chunk-MBYTQ3JJ.js.map +1 -0
- package/{chunk-JFWUWF3A.js → chunk-MWXV2NJK.js} +2 -2
- package/{chunk-JFWUWF3A.js.map → chunk-MWXV2NJK.js.map} +1 -1
- package/{chunk-QWRJLFXR.js → chunk-MZUWWKCF.js} +69 -6
- package/chunk-MZUWWKCF.js.map +1 -0
- package/{chunk-PV4JMZMJ.js → chunk-OPYEYUSY.js} +28 -192
- package/chunk-OPYEYUSY.js.map +1 -0
- package/chunk-PB66MQ5U.js +55 -0
- package/chunk-PB66MQ5U.js.map +1 -0
- package/{chunk-KI4MDAT4.js → chunk-RPQMDXP2.js} +2 -2
- package/{chunk-PZFMDYSY.js → chunk-V33YE2AE.js} +2 -2
- package/{computer-use-N4WNS5N4.js → computer-use-IP4SF35H.js} +3 -3
- package/{connector-ZSIAJ6YH.js → connector-XK7EPZWH.js} +9 -21
- package/connector-XK7EPZWH.js.map +1 -0
- package/{credit-4YFX65IX.js → credit-VPTX7ZTC.js} +4 -4
- package/{developer-support-EDEFB7K5.js → developer-support-3ED5W5G3.js} +3 -3
- package/{doctor-Q54PUMYI.js → doctor-JZG5ZNW2.js} +3 -3
- package/{generate-BHCOSW7B.js → generate-PAMRL3B5.js} +4 -4
- package/{github-VEZGHR67.js → github-OUIYACCV.js} +3 -3
- package/{goal-WG5KBTXD.js → goal-PXTE4BCJ.js} +3 -3
- package/{host-FCYDK4BK.js → host-3KVI5HIB.js} +4 -5
- package/{host-FCYDK4BK.js.map → host-3KVI5HIB.js.map} +1 -1
- package/index.js +28 -1337
- package/index.js.map +1 -1
- package/{logs-ALIH26LX.js → logs-LEPXLZOA.js} +8 -8
- package/{mail-4GBO6NVZ.js → mail-22YJ3HFW.js} +5 -5
- package/{maps-W6U3SU6K.js → maps-GTBQWQOV.js} +3 -3
- package/{model-M75D7CBV.js → model-RWQNBS3N.js} +4 -4
- package/{model-provider-ZG6A3OMZ.js → model-provider-6B54DC7X.js} +4 -4
- package/{org-IWZB3I2J.js → org-JEJIR4XQ.js} +3 -3
- package/package.json +1 -1
- package/{phone-DGVBYVIJ.js → phone-CI77EG37.js} +3 -3
- package/{preference-D2DZJ6MV.js → preference-546TA23L.js} +3 -3
- package/{resource-VDFCXFF7.js → resource-HRVLBG7N.js} +4 -4
- package/{scrape-BGAOXMIW.js → scrape-VZJ67GNU.js} +3 -3
- package/{search-EZJ3WHLL.js → search-L5ICIORF.js} +7 -7
- package/{secret-G33GAOLW.js → secret-FKMH53Y4.js} +3 -3
- package/{slack-AAWBJANC.js → slack-BQ4KIZPV.js} +3 -3
- package/{teams-QR5UMEYH.js → teams-YZSKKRY4.js} +3 -3
- package/{telegram-WZWDX6D4.js → telegram-HHGB5PE3.js} +3 -3
- package/{variable-WVIKMT67.js → variable-4CIAHKLM.js} +3 -3
- package/{video-RNQNZEFT.js → video-54PFQIQD.js} +2 -2
- package/{web-ZOX7CFHF.js → web-BOBLQ27H.js} +3 -3
- package/{web-search-QS7NNSGR.js → web-search-27452WDH.js} +3 -3
- package/{whoami-OT4I5425.js → whoami-5SIOEBZY.js} +5 -5
- package/{workflow-K2KOXD3F.js → workflow-62JWPXNK.js} +3 -3
- package/zero.js +35 -35
- package/chunk-4QZ4HY4E.js.map +0 -1
- package/chunk-PV4JMZMJ.js.map +0 -1
- package/chunk-QWRJLFXR.js.map +0 -1
- package/chunk-T6TZMDU6.js +0 -132
- package/chunk-T6TZMDU6.js.map +0 -1
- package/connector-ZSIAJ6YH.js.map +0 -1
- /package/{agent-JSLUNSTT.js.map → agent-AOWGKKI7.js.map} +0 -0
- /package/{banking-6GCF5OYC.js.map → banking-2ZRGXQJB.js.map} +0 -0
- /package/{chat-MLA4L7NU.js.map → chat-XT3ZFO2U.js.map} +0 -0
- /package/{chunk-Q4DTF2TA.js.map → chunk-2GMLVBYY.js.map} +0 -0
- /package/{chunk-QKZ37ZQL.js.map → chunk-GGJ3V5JF.js.map} +0 -0
- /package/{chunk-CK6ATTWW.js.map → chunk-HEAUPGDO.js.map} +0 -0
- /package/{chunk-G7TORBE5.js.map → chunk-J7NZ4R4R.js.map} +0 -0
- /package/{chunk-PTF3UL74.js.map → chunk-LX7FTB2W.js.map} +0 -0
- /package/{chunk-KI4MDAT4.js.map → chunk-RPQMDXP2.js.map} +0 -0
- /package/{chunk-PZFMDYSY.js.map → chunk-V33YE2AE.js.map} +0 -0
- /package/{computer-use-N4WNS5N4.js.map → computer-use-IP4SF35H.js.map} +0 -0
- /package/{credit-4YFX65IX.js.map → credit-VPTX7ZTC.js.map} +0 -0
- /package/{developer-support-EDEFB7K5.js.map → developer-support-3ED5W5G3.js.map} +0 -0
- /package/{doctor-Q54PUMYI.js.map → doctor-JZG5ZNW2.js.map} +0 -0
- /package/{generate-BHCOSW7B.js.map → generate-PAMRL3B5.js.map} +0 -0
- /package/{github-VEZGHR67.js.map → github-OUIYACCV.js.map} +0 -0
- /package/{goal-WG5KBTXD.js.map → goal-PXTE4BCJ.js.map} +0 -0
- /package/{logs-ALIH26LX.js.map → logs-LEPXLZOA.js.map} +0 -0
- /package/{mail-4GBO6NVZ.js.map → mail-22YJ3HFW.js.map} +0 -0
- /package/{maps-W6U3SU6K.js.map → maps-GTBQWQOV.js.map} +0 -0
- /package/{model-M75D7CBV.js.map → model-RWQNBS3N.js.map} +0 -0
- /package/{model-provider-ZG6A3OMZ.js.map → model-provider-6B54DC7X.js.map} +0 -0
- /package/{org-IWZB3I2J.js.map → org-JEJIR4XQ.js.map} +0 -0
- /package/{phone-DGVBYVIJ.js.map → phone-CI77EG37.js.map} +0 -0
- /package/{preference-D2DZJ6MV.js.map → preference-546TA23L.js.map} +0 -0
- /package/{resource-VDFCXFF7.js.map → resource-HRVLBG7N.js.map} +0 -0
- /package/{scrape-BGAOXMIW.js.map → scrape-VZJ67GNU.js.map} +0 -0
- /package/{search-EZJ3WHLL.js.map → search-L5ICIORF.js.map} +0 -0
- /package/{secret-G33GAOLW.js.map → secret-FKMH53Y4.js.map} +0 -0
- /package/{slack-AAWBJANC.js.map → slack-BQ4KIZPV.js.map} +0 -0
- /package/{teams-QR5UMEYH.js.map → teams-YZSKKRY4.js.map} +0 -0
- /package/{telegram-WZWDX6D4.js.map → telegram-HHGB5PE3.js.map} +0 -0
- /package/{variable-WVIKMT67.js.map → variable-4CIAHKLM.js.map} +0 -0
- /package/{video-RNQNZEFT.js.map → video-54PFQIQD.js.map} +0 -0
- /package/{web-ZOX7CFHF.js.map → web-BOBLQ27H.js.map} +0 -0
- /package/{web-search-QS7NNSGR.js.map → web-search-27452WDH.js.map} +0 -0
- /package/{whoami-OT4I5425.js.map → whoami-5SIOEBZY.js.map} +0 -0
- /package/{workflow-K2KOXD3F.js.map → workflow-62JWPXNK.js.map} +0 -0
package/index.js
CHANGED
|
@@ -2,50 +2,33 @@
|
|
|
2
2
|
import { createRequire as __createRequire } from "node:module";
|
|
3
3
|
const require = __createRequire(import.meta.url);
|
|
4
4
|
import {
|
|
5
|
-
checkDirectoryStatus,
|
|
6
5
|
excludeVm0Filter,
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
listTarFiles,
|
|
10
|
-
removeExtraFiles
|
|
11
|
-
} from "./chunk-T6TZMDU6.js";
|
|
6
|
+
formatRelativeTime
|
|
7
|
+
} from "./chunk-PB66MQ5U.js";
|
|
12
8
|
import {
|
|
13
|
-
EventRenderer,
|
|
14
|
-
EventStreamNormalizer,
|
|
15
9
|
collectArtifacts,
|
|
16
10
|
collectKeyValue,
|
|
17
|
-
collectLogItems,
|
|
18
11
|
collectMounts,
|
|
19
12
|
collectVolumeVersions,
|
|
20
13
|
extractAndGroupVariables,
|
|
21
14
|
extractSecretNames,
|
|
22
15
|
extractVarNames,
|
|
23
|
-
formatIsoTimestamp,
|
|
24
16
|
getInstructionsFilename,
|
|
25
17
|
getInstructionsStorageName,
|
|
26
|
-
isSupportedFramework,
|
|
27
18
|
isUUID,
|
|
28
19
|
loadValues,
|
|
29
|
-
parseBoundedLogCount,
|
|
30
20
|
parseIdentifier,
|
|
31
21
|
parsePermissionPolicies,
|
|
32
|
-
parsePositiveLogCount,
|
|
33
|
-
parseSearchQuery,
|
|
34
22
|
parseTime,
|
|
35
23
|
pollEvents,
|
|
36
24
|
renderRunCreated,
|
|
37
25
|
require_dist,
|
|
38
26
|
showNextSteps
|
|
39
|
-
} from "./chunk-
|
|
27
|
+
} from "./chunk-MBYTQ3JJ.js";
|
|
40
28
|
import {
|
|
41
|
-
Qn
|
|
42
|
-
|
|
43
|
-
} from "./chunk-CK6ATTWW.js";
|
|
29
|
+
Qn
|
|
30
|
+
} from "./chunk-HEAUPGDO.js";
|
|
44
31
|
import "./chunk-DWWE7STW.js";
|
|
45
|
-
import {
|
|
46
|
-
isInteractive,
|
|
47
|
-
promptText
|
|
48
|
-
} from "./chunk-JFY6GK7Y.js";
|
|
49
32
|
import {
|
|
50
33
|
ALL_RUN_STATUSES,
|
|
51
34
|
agentDefinitionSchema,
|
|
@@ -54,30 +37,22 @@ import {
|
|
|
54
37
|
createOrUpdateCompose,
|
|
55
38
|
createRun,
|
|
56
39
|
directRunModelProviderTypeSchema,
|
|
57
|
-
getAgentEvents,
|
|
58
40
|
getCheckpoint,
|
|
59
41
|
getComposeById,
|
|
60
42
|
getComposeByName,
|
|
61
43
|
getComposeVersion,
|
|
62
|
-
getMetrics,
|
|
63
|
-
getNetworkLogs,
|
|
64
44
|
getRunQueue,
|
|
65
45
|
getSession,
|
|
66
|
-
getStorageDownload,
|
|
67
|
-
getSystemLog,
|
|
68
46
|
guaranteedConnectorProvidedBindingNames,
|
|
69
47
|
listRuns,
|
|
70
|
-
listStorages,
|
|
71
48
|
listZeroConnectors,
|
|
72
49
|
listZeroSecrets,
|
|
73
50
|
listZeroVariables,
|
|
74
51
|
prepareStorage,
|
|
75
|
-
searchLogs,
|
|
76
52
|
volumeConfigSchema
|
|
77
|
-
} from "./chunk-
|
|
53
|
+
} from "./chunk-OPYEYUSY.js";
|
|
78
54
|
import "./chunk-LTDQISD7.js";
|
|
79
55
|
import {
|
|
80
|
-
ApiRequestError,
|
|
81
56
|
clearConfig,
|
|
82
57
|
external_exports,
|
|
83
58
|
getActiveOrg,
|
|
@@ -88,10 +63,10 @@ import {
|
|
|
88
63
|
saveConfig,
|
|
89
64
|
source_default,
|
|
90
65
|
withErrorHandler
|
|
91
|
-
} from "./chunk-
|
|
66
|
+
} from "./chunk-MWXV2NJK.js";
|
|
92
67
|
import {
|
|
93
68
|
configureGlobalProxyFromEnv
|
|
94
|
-
} from "./chunk-
|
|
69
|
+
} from "./chunk-GGJ3V5JF.js";
|
|
95
70
|
import "./chunk-LRHXR7JT.js";
|
|
96
71
|
import {
|
|
97
72
|
Command,
|
|
@@ -345,30 +320,30 @@ var cliComposeSchema = external_exports.object({
|
|
|
345
320
|
}
|
|
346
321
|
}
|
|
347
322
|
});
|
|
348
|
-
function formatInvalidTypeIssue(
|
|
323
|
+
function formatInvalidTypeIssue(path3, issue) {
|
|
349
324
|
const runtimeReceived = Reflect.get(issue, "received");
|
|
350
325
|
const received = typeof runtimeReceived === "string" ? runtimeReceived : void 0;
|
|
351
326
|
const isMissing = received === "undefined" || issue.message.includes("received undefined") || issue.message === "Required";
|
|
352
|
-
if (
|
|
327
|
+
if (path3 === "version" && isMissing) {
|
|
353
328
|
return "Missing config.version";
|
|
354
329
|
}
|
|
355
|
-
if (
|
|
330
|
+
if (path3 === "agents" && isMissing) {
|
|
356
331
|
return "Missing agents object in config";
|
|
357
332
|
}
|
|
358
|
-
if (
|
|
359
|
-
const volumeKey =
|
|
333
|
+
if (path3.startsWith("volumes.") && path3.endsWith(".name")) {
|
|
334
|
+
const volumeKey = path3.split(".")[1];
|
|
360
335
|
return `Volume "${volumeKey}" must have a 'name' field (string)`;
|
|
361
336
|
}
|
|
362
|
-
if (
|
|
363
|
-
const volumeKey =
|
|
337
|
+
if (path3.startsWith("volumes.") && path3.endsWith(".version")) {
|
|
338
|
+
const volumeKey = path3.split(".")[1];
|
|
364
339
|
return `Volume "${volumeKey}" must have a 'version' field (string)`;
|
|
365
340
|
}
|
|
366
341
|
if (issue.expected === "array") {
|
|
367
|
-
const fieldName =
|
|
342
|
+
const fieldName = path3.replace(/^agents\.[^.]+\./, "agent.");
|
|
368
343
|
return `${fieldName} must be an array`;
|
|
369
344
|
}
|
|
370
345
|
if (issue.expected === "string" && received === "number") {
|
|
371
|
-
const fieldName =
|
|
346
|
+
const fieldName = path3.replace(/^agents\.[^.]+\./, "agent.");
|
|
372
347
|
const match = fieldName.match(/^(agent\.[^.]+)\.\d+$/);
|
|
373
348
|
if (match) {
|
|
374
349
|
return `Each entry in ${match[1]?.replace("agent.", "")} must be a string`;
|
|
@@ -379,24 +354,24 @@ function formatInvalidTypeIssue(path9, issue) {
|
|
|
379
354
|
function formatZodError(error) {
|
|
380
355
|
const issue = error.issues[0];
|
|
381
356
|
if (!issue) return "Validation failed";
|
|
382
|
-
const
|
|
357
|
+
const path3 = issue.path.join(".");
|
|
383
358
|
const message = issue.message;
|
|
384
|
-
if (!
|
|
359
|
+
if (!path3) return message;
|
|
385
360
|
if (issue.code === "invalid_type") {
|
|
386
|
-
const formatted = formatInvalidTypeIssue(
|
|
361
|
+
const formatted = formatInvalidTypeIssue(path3, issue);
|
|
387
362
|
if (formatted) return formatted;
|
|
388
363
|
}
|
|
389
|
-
if (issue.code === "invalid_key" &&
|
|
364
|
+
if (issue.code === "invalid_key" && path3.startsWith("agents.")) {
|
|
390
365
|
return "Invalid agent name format. Must be 3-64 characters, letters, numbers, and hyphens only. Must start and end with letter or number.";
|
|
391
366
|
}
|
|
392
|
-
if (message === "Invalid key in record" &&
|
|
367
|
+
if (message === "Invalid key in record" && path3.startsWith("agents.")) {
|
|
393
368
|
return "Invalid agent name format. Must be 3-64 characters, letters, numbers, and hyphens only. Must start and end with letter or number.";
|
|
394
369
|
}
|
|
395
370
|
if (issue.code === "custom") {
|
|
396
371
|
return message;
|
|
397
372
|
}
|
|
398
|
-
if (
|
|
399
|
-
const cleanPath =
|
|
373
|
+
if (path3.startsWith("agents.")) {
|
|
374
|
+
const cleanPath = path3.replace(/^agents\.[^.]+\./, "agent.");
|
|
400
375
|
if (message.startsWith("Invalid input:")) {
|
|
401
376
|
const match = message.match(/expected (\w+), received (\w+)/);
|
|
402
377
|
if (match && match[1] === "string" && match[2] === "number") {
|
|
@@ -408,7 +383,7 @@ function formatZodError(error) {
|
|
|
408
383
|
}
|
|
409
384
|
return `${cleanPath}: ${message}`;
|
|
410
385
|
}
|
|
411
|
-
return `${
|
|
386
|
+
return `${path3}: ${message}`;
|
|
412
387
|
}
|
|
413
388
|
var KNOWN_AGENT_FIELDS = Object.keys(agentDefinitionSchema.shape);
|
|
414
389
|
function levenshteinDistance(a, b) {
|
|
@@ -1075,7 +1050,7 @@ var composeCommand = new Command().name("compose").description("Create or update
|
|
|
1075
1050
|
options.autoUpdate = false;
|
|
1076
1051
|
}
|
|
1077
1052
|
if (options.autoUpdate !== false) {
|
|
1078
|
-
await startSilentUpgrade("9.244.
|
|
1053
|
+
await startSilentUpgrade("9.244.6");
|
|
1079
1054
|
}
|
|
1080
1055
|
try {
|
|
1081
1056
|
const { config, agentName, agent, basePath } = await loadAndValidateConfig(resolvedConfigFile);
|
|
@@ -1172,7 +1147,7 @@ var mainRunCommand = new Command().name("run").description("Run an agent").argum
|
|
|
1172
1147
|
withErrorHandler(
|
|
1173
1148
|
async (identifier, prompt, options) => {
|
|
1174
1149
|
if (options.autoUpdate !== false) {
|
|
1175
|
-
await startSilentUpgrade("9.244.
|
|
1150
|
+
await startSilentUpgrade("9.244.6");
|
|
1176
1151
|
}
|
|
1177
1152
|
const { name, version } = parseIdentifier(identifier);
|
|
1178
1153
|
let composeId;
|
|
@@ -1652,1287 +1627,6 @@ mainRunCommand.addCommand(killCommand);
|
|
|
1652
1627
|
mainRunCommand.addCommand(queueCommand);
|
|
1653
1628
|
var runCommand = mainRunCommand;
|
|
1654
1629
|
|
|
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
1630
|
// src/commands/whoami.ts
|
|
2937
1631
|
init_esm_shims();
|
|
2938
1632
|
function isInsideSandbox() {
|
|
@@ -2988,13 +1682,10 @@ var whoamiCommand = new Command().name("whoami").description("Show current ident
|
|
|
2988
1682
|
|
|
2989
1683
|
// src/index.ts
|
|
2990
1684
|
var program = new Command();
|
|
2991
|
-
program.name("vm0").description("VM0 CLI - Build and run agents with natural language").version("9.244.
|
|
1685
|
+
program.name("vm0").description("VM0 CLI - Build and run agents with natural language").version("9.244.6");
|
|
2992
1686
|
program.addCommand(authCommand);
|
|
2993
1687
|
program.addCommand(composeCommand);
|
|
2994
1688
|
program.addCommand(runCommand);
|
|
2995
|
-
program.addCommand(volumeCommand);
|
|
2996
|
-
program.addCommand(artifactCommand);
|
|
2997
|
-
program.addCommand(logsCommand);
|
|
2998
1689
|
program.addCommand(whoamiCommand);
|
|
2999
1690
|
if (process.argv[1]?.endsWith("index.js") || process.argv[1]?.endsWith("index.ts") || process.argv[1]?.endsWith("vm0")) {
|
|
3000
1691
|
await configureGlobalProxyFromEnv();
|