@vm0/cli 1.1.0 → 1.3.0
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/index.js +384 -23
- package/package.json +2 -1
package/index.js
CHANGED
|
@@ -736,15 +736,15 @@ function mergeDefs(...defs) {
|
|
|
736
736
|
function cloneDef(schema) {
|
|
737
737
|
return mergeDefs(schema._zod.def);
|
|
738
738
|
}
|
|
739
|
-
function getElementAtPath(obj,
|
|
740
|
-
if (!
|
|
739
|
+
function getElementAtPath(obj, path5) {
|
|
740
|
+
if (!path5)
|
|
741
741
|
return obj;
|
|
742
|
-
return
|
|
742
|
+
return path5.reduce((acc, key) => acc?.[key], obj);
|
|
743
743
|
}
|
|
744
744
|
function promiseAllObject(promisesObj) {
|
|
745
745
|
const keys = Object.keys(promisesObj);
|
|
746
|
-
const
|
|
747
|
-
return Promise.all(
|
|
746
|
+
const promises3 = keys.map((key) => promisesObj[key]);
|
|
747
|
+
return Promise.all(promises3).then((results) => {
|
|
748
748
|
const resolvedObj = {};
|
|
749
749
|
for (let i = 0; i < keys.length; i++) {
|
|
750
750
|
resolvedObj[keys[i]] = results[i];
|
|
@@ -1098,11 +1098,11 @@ function aborted(x, startIndex = 0) {
|
|
|
1098
1098
|
}
|
|
1099
1099
|
return false;
|
|
1100
1100
|
}
|
|
1101
|
-
function prefixIssues(
|
|
1101
|
+
function prefixIssues(path5, issues) {
|
|
1102
1102
|
return issues.map((iss) => {
|
|
1103
1103
|
var _a;
|
|
1104
1104
|
(_a = iss).path ?? (_a.path = []);
|
|
1105
|
-
iss.path.unshift(
|
|
1105
|
+
iss.path.unshift(path5);
|
|
1106
1106
|
return iss;
|
|
1107
1107
|
});
|
|
1108
1108
|
}
|
|
@@ -1270,7 +1270,7 @@ function treeifyError(error43, _mapper) {
|
|
|
1270
1270
|
return issue2.message;
|
|
1271
1271
|
};
|
|
1272
1272
|
const result = { errors: [] };
|
|
1273
|
-
const processError = (error44,
|
|
1273
|
+
const processError = (error44, path5 = []) => {
|
|
1274
1274
|
var _a, _b;
|
|
1275
1275
|
for (const issue2 of error44.issues) {
|
|
1276
1276
|
if (issue2.code === "invalid_union" && issue2.errors.length) {
|
|
@@ -1280,7 +1280,7 @@ function treeifyError(error43, _mapper) {
|
|
|
1280
1280
|
} else if (issue2.code === "invalid_element") {
|
|
1281
1281
|
processError({ issues: issue2.issues }, issue2.path);
|
|
1282
1282
|
} else {
|
|
1283
|
-
const fullpath = [...
|
|
1283
|
+
const fullpath = [...path5, ...issue2.path];
|
|
1284
1284
|
if (fullpath.length === 0) {
|
|
1285
1285
|
result.errors.push(mapper(issue2));
|
|
1286
1286
|
continue;
|
|
@@ -1312,8 +1312,8 @@ function treeifyError(error43, _mapper) {
|
|
|
1312
1312
|
}
|
|
1313
1313
|
function toDotPath(_path) {
|
|
1314
1314
|
const segs = [];
|
|
1315
|
-
const
|
|
1316
|
-
for (const seg of
|
|
1315
|
+
const path5 = _path.map((seg) => typeof seg === "object" ? seg.key : seg);
|
|
1316
|
+
for (const seg of path5) {
|
|
1317
1317
|
if (typeof seg === "number")
|
|
1318
1318
|
segs.push(`[${seg}]`);
|
|
1319
1319
|
else if (typeof seg === "symbol")
|
|
@@ -12147,8 +12147,8 @@ var helloContract = c.router({
|
|
|
12147
12147
|
var FOO = "hello";
|
|
12148
12148
|
|
|
12149
12149
|
// src/index.ts
|
|
12150
|
-
import { Command as
|
|
12151
|
-
import
|
|
12150
|
+
import { Command as Command7 } from "commander";
|
|
12151
|
+
import chalk8 from "chalk";
|
|
12152
12152
|
|
|
12153
12153
|
// src/lib/auth.ts
|
|
12154
12154
|
import chalk from "chalk";
|
|
@@ -12348,7 +12348,7 @@ var ApiClient = class {
|
|
|
12348
12348
|
);
|
|
12349
12349
|
if (!response.ok) {
|
|
12350
12350
|
const error43 = await response.json();
|
|
12351
|
-
throw new Error(error43.error || `Config not found: ${name}`);
|
|
12351
|
+
throw new Error(error43.error?.message || `Config not found: ${name}`);
|
|
12352
12352
|
}
|
|
12353
12353
|
return await response.json();
|
|
12354
12354
|
}
|
|
@@ -12362,7 +12362,7 @@ var ApiClient = class {
|
|
|
12362
12362
|
});
|
|
12363
12363
|
if (!response.ok) {
|
|
12364
12364
|
const error43 = await response.json();
|
|
12365
|
-
throw new Error(error43.error || "Failed to create config");
|
|
12365
|
+
throw new Error(error43.error?.message || "Failed to create config");
|
|
12366
12366
|
}
|
|
12367
12367
|
return await response.json();
|
|
12368
12368
|
}
|
|
@@ -12376,7 +12376,8 @@ var ApiClient = class {
|
|
|
12376
12376
|
});
|
|
12377
12377
|
if (!response.ok) {
|
|
12378
12378
|
const error43 = await response.json();
|
|
12379
|
-
|
|
12379
|
+
const message = error43.error?.message || "Failed to create run";
|
|
12380
|
+
throw new Error(message);
|
|
12380
12381
|
}
|
|
12381
12382
|
return await response.json();
|
|
12382
12383
|
}
|
|
@@ -12394,7 +12395,7 @@ var ApiClient = class {
|
|
|
12394
12395
|
);
|
|
12395
12396
|
if (!response.ok) {
|
|
12396
12397
|
const error43 = await response.json();
|
|
12397
|
-
throw new Error(error43.error || "Failed to fetch events");
|
|
12398
|
+
throw new Error(error43.error?.message || "Failed to fetch events");
|
|
12398
12399
|
}
|
|
12399
12400
|
return await response.json();
|
|
12400
12401
|
}
|
|
@@ -12408,10 +12409,53 @@ var ApiClient = class {
|
|
|
12408
12409
|
});
|
|
12409
12410
|
if (!response.ok) {
|
|
12410
12411
|
const error43 = await response.json();
|
|
12411
|
-
throw new Error(error43.error || "Failed to resume run");
|
|
12412
|
+
throw new Error(error43.error?.message || "Failed to resume run");
|
|
12412
12413
|
}
|
|
12413
12414
|
return await response.json();
|
|
12414
12415
|
}
|
|
12416
|
+
/**
|
|
12417
|
+
* Generic GET request
|
|
12418
|
+
*/
|
|
12419
|
+
async get(path5) {
|
|
12420
|
+
const baseUrl = await this.getBaseUrl();
|
|
12421
|
+
const token = await getToken();
|
|
12422
|
+
if (!token) {
|
|
12423
|
+
throw new Error("Not authenticated. Run: vm0 auth login");
|
|
12424
|
+
}
|
|
12425
|
+
const headers = {
|
|
12426
|
+
Authorization: `Bearer ${token}`
|
|
12427
|
+
};
|
|
12428
|
+
const bypassSecret = process.env.VERCEL_AUTOMATION_BYPASS_SECRET;
|
|
12429
|
+
if (bypassSecret) {
|
|
12430
|
+
headers["x-vercel-protection-bypass"] = bypassSecret;
|
|
12431
|
+
}
|
|
12432
|
+
return fetch(`${baseUrl}${path5}`, {
|
|
12433
|
+
method: "GET",
|
|
12434
|
+
headers
|
|
12435
|
+
});
|
|
12436
|
+
}
|
|
12437
|
+
/**
|
|
12438
|
+
* Generic POST request
|
|
12439
|
+
*/
|
|
12440
|
+
async post(path5, options) {
|
|
12441
|
+
const baseUrl = await this.getBaseUrl();
|
|
12442
|
+
const token = await getToken();
|
|
12443
|
+
if (!token) {
|
|
12444
|
+
throw new Error("Not authenticated. Run: vm0 auth login");
|
|
12445
|
+
}
|
|
12446
|
+
const headers = {
|
|
12447
|
+
Authorization: `Bearer ${token}`
|
|
12448
|
+
};
|
|
12449
|
+
const bypassSecret = process.env.VERCEL_AUTOMATION_BYPASS_SECRET;
|
|
12450
|
+
if (bypassSecret) {
|
|
12451
|
+
headers["x-vercel-protection-bypass"] = bypassSecret;
|
|
12452
|
+
}
|
|
12453
|
+
return fetch(`${baseUrl}${path5}`, {
|
|
12454
|
+
method: "POST",
|
|
12455
|
+
headers,
|
|
12456
|
+
body: options?.body
|
|
12457
|
+
});
|
|
12458
|
+
}
|
|
12415
12459
|
};
|
|
12416
12460
|
var apiClient = new ApiClient();
|
|
12417
12461
|
|
|
@@ -12453,6 +12497,39 @@ function expandEnvVars(value) {
|
|
|
12453
12497
|
return process.env[varName] ?? "";
|
|
12454
12498
|
});
|
|
12455
12499
|
}
|
|
12500
|
+
function extractEnvVarReferences(obj) {
|
|
12501
|
+
const varNames = /* @__PURE__ */ new Set();
|
|
12502
|
+
function scan(value) {
|
|
12503
|
+
if (typeof value === "string") {
|
|
12504
|
+
const matches = value.matchAll(/\$\{([^}]+)\}/g);
|
|
12505
|
+
for (const match of matches) {
|
|
12506
|
+
const varName = match[1];
|
|
12507
|
+
if (varName) {
|
|
12508
|
+
varNames.add(varName);
|
|
12509
|
+
}
|
|
12510
|
+
}
|
|
12511
|
+
} else if (Array.isArray(value)) {
|
|
12512
|
+
for (const item of value) {
|
|
12513
|
+
scan(item);
|
|
12514
|
+
}
|
|
12515
|
+
} else if (value !== null && typeof value === "object") {
|
|
12516
|
+
for (const val of Object.values(value)) {
|
|
12517
|
+
scan(val);
|
|
12518
|
+
}
|
|
12519
|
+
}
|
|
12520
|
+
}
|
|
12521
|
+
scan(obj);
|
|
12522
|
+
return Array.from(varNames);
|
|
12523
|
+
}
|
|
12524
|
+
function validateEnvVars(varNames) {
|
|
12525
|
+
const missing = [];
|
|
12526
|
+
for (const varName of varNames) {
|
|
12527
|
+
if (process.env[varName] === void 0) {
|
|
12528
|
+
missing.push(varName);
|
|
12529
|
+
}
|
|
12530
|
+
}
|
|
12531
|
+
return missing;
|
|
12532
|
+
}
|
|
12456
12533
|
function expandEnvVarsInObject(obj) {
|
|
12457
12534
|
if (typeof obj === "string") {
|
|
12458
12535
|
return expandEnvVars(obj);
|
|
@@ -12488,6 +12565,23 @@ var buildCommand = new Command().name("build").description("Create or update age
|
|
|
12488
12565
|
}
|
|
12489
12566
|
process.exit(1);
|
|
12490
12567
|
}
|
|
12568
|
+
const referencedVars = extractEnvVarReferences(config2);
|
|
12569
|
+
const missingVars = validateEnvVars(referencedVars);
|
|
12570
|
+
if (missingVars.length > 0) {
|
|
12571
|
+
console.error(chalk2.red("\u2717 Missing required environment variables:"));
|
|
12572
|
+
for (const varName of missingVars) {
|
|
12573
|
+
console.error(chalk2.red(` - ${varName}`));
|
|
12574
|
+
}
|
|
12575
|
+
console.error();
|
|
12576
|
+
console.error(
|
|
12577
|
+
chalk2.gray("Please set these variables before running 'vm0 build'.")
|
|
12578
|
+
);
|
|
12579
|
+
console.error(chalk2.gray("Example:"));
|
|
12580
|
+
for (const varName of missingVars) {
|
|
12581
|
+
console.error(chalk2.gray(` export ${varName}=your-value`));
|
|
12582
|
+
}
|
|
12583
|
+
process.exit(1);
|
|
12584
|
+
}
|
|
12491
12585
|
config2 = expandEnvVarsInObject(config2);
|
|
12492
12586
|
const validation = validateAgentConfig(config2);
|
|
12493
12587
|
if (!validation.valid) {
|
|
@@ -12793,7 +12887,18 @@ var EventRenderer = class {
|
|
|
12793
12887
|
}
|
|
12794
12888
|
static renderVm0Error(event) {
|
|
12795
12889
|
console.log(chalk3.red("[vm0_error]") + " \u2717 Run failed");
|
|
12796
|
-
|
|
12890
|
+
let errorMessage = "";
|
|
12891
|
+
if (typeof event.data.error === "string") {
|
|
12892
|
+
errorMessage = event.data.error;
|
|
12893
|
+
} else if (event.data.error && typeof event.data.error === "object") {
|
|
12894
|
+
const errorObj = event.data.error;
|
|
12895
|
+
if ("message" in errorObj && typeof errorObj.message === "string") {
|
|
12896
|
+
errorMessage = errorObj.message;
|
|
12897
|
+
} else {
|
|
12898
|
+
errorMessage = JSON.stringify(event.data.error);
|
|
12899
|
+
}
|
|
12900
|
+
}
|
|
12901
|
+
console.log(` Error: ${chalk3.red(errorMessage || "Unknown error")}`);
|
|
12797
12902
|
if (event.data.errorType) {
|
|
12798
12903
|
console.log(` Type: ${chalk3.gray(String(event.data.errorType))}`);
|
|
12799
12904
|
}
|
|
@@ -12965,15 +13070,270 @@ runCmd.command("resume").description("Resume an agent run from a checkpoint").ar
|
|
|
12965
13070
|
});
|
|
12966
13071
|
var runCommand = runCmd;
|
|
12967
13072
|
|
|
13073
|
+
// src/commands/volume/index.ts
|
|
13074
|
+
import { Command as Command6 } from "commander";
|
|
13075
|
+
|
|
13076
|
+
// src/commands/volume/init.ts
|
|
13077
|
+
import { Command as Command3 } from "commander";
|
|
13078
|
+
import chalk5 from "chalk";
|
|
13079
|
+
import path2 from "path";
|
|
13080
|
+
|
|
13081
|
+
// src/lib/volume-utils.ts
|
|
13082
|
+
import { readFile as readFile3, writeFile as writeFile2, mkdir as mkdir2 } from "fs/promises";
|
|
13083
|
+
import { existsSync as existsSync3 } from "fs";
|
|
13084
|
+
import { parse as parseYaml2, stringify as stringifyYaml } from "yaml";
|
|
13085
|
+
import path from "path";
|
|
13086
|
+
var CONFIG_DIR2 = ".vm0";
|
|
13087
|
+
var CONFIG_FILE2 = "volume.yaml";
|
|
13088
|
+
function isValidVolumeName(name) {
|
|
13089
|
+
if (name.length < 3 || name.length > 64) {
|
|
13090
|
+
return false;
|
|
13091
|
+
}
|
|
13092
|
+
const pattern = /^[a-z0-9][a-z0-9-]{1,62}[a-z0-9]$/;
|
|
13093
|
+
return pattern.test(name) && !name.includes("--");
|
|
13094
|
+
}
|
|
13095
|
+
async function readVolumeConfig(basePath = process.cwd()) {
|
|
13096
|
+
const configPath = path.join(basePath, CONFIG_DIR2, CONFIG_FILE2);
|
|
13097
|
+
if (!existsSync3(configPath)) {
|
|
13098
|
+
return null;
|
|
13099
|
+
}
|
|
13100
|
+
const content = await readFile3(configPath, "utf8");
|
|
13101
|
+
const config2 = parseYaml2(content);
|
|
13102
|
+
return config2;
|
|
13103
|
+
}
|
|
13104
|
+
async function writeVolumeConfig(volumeName, basePath = process.cwd()) {
|
|
13105
|
+
const configDir = path.join(basePath, CONFIG_DIR2);
|
|
13106
|
+
const configPath = path.join(configDir, CONFIG_FILE2);
|
|
13107
|
+
if (!existsSync3(configDir)) {
|
|
13108
|
+
await mkdir2(configDir, { recursive: true });
|
|
13109
|
+
}
|
|
13110
|
+
const config2 = {
|
|
13111
|
+
name: volumeName
|
|
13112
|
+
};
|
|
13113
|
+
const yamlContent = stringifyYaml(config2);
|
|
13114
|
+
await writeFile2(configPath, yamlContent, "utf8");
|
|
13115
|
+
}
|
|
13116
|
+
|
|
13117
|
+
// src/commands/volume/init.ts
|
|
13118
|
+
var initCommand = new Command3().name("init").description("Initialize a volume in the current directory").action(async () => {
|
|
13119
|
+
try {
|
|
13120
|
+
const cwd = process.cwd();
|
|
13121
|
+
const dirName = path2.basename(cwd);
|
|
13122
|
+
const existingConfig = await readVolumeConfig(cwd);
|
|
13123
|
+
if (existingConfig) {
|
|
13124
|
+
console.log(
|
|
13125
|
+
chalk5.yellow(`Volume already initialized: ${existingConfig.name}`)
|
|
13126
|
+
);
|
|
13127
|
+
console.log(
|
|
13128
|
+
chalk5.gray(`Config file: ${path2.join(cwd, ".vm0", "volume.yaml")}`)
|
|
13129
|
+
);
|
|
13130
|
+
return;
|
|
13131
|
+
}
|
|
13132
|
+
const volumeName = dirName;
|
|
13133
|
+
if (!isValidVolumeName(volumeName)) {
|
|
13134
|
+
console.error(chalk5.red(`\u2717 Invalid volume name: "${dirName}"`));
|
|
13135
|
+
console.error(
|
|
13136
|
+
chalk5.gray(
|
|
13137
|
+
" Volume names must be 3-64 characters, lowercase alphanumeric with hyphens"
|
|
13138
|
+
)
|
|
13139
|
+
);
|
|
13140
|
+
console.error(
|
|
13141
|
+
chalk5.gray(" Example: my-dataset, user-data-v2, training-set-2024")
|
|
13142
|
+
);
|
|
13143
|
+
process.exit(1);
|
|
13144
|
+
}
|
|
13145
|
+
await writeVolumeConfig(volumeName, cwd);
|
|
13146
|
+
console.log(chalk5.green(`\u2713 Initialized volume: ${volumeName}`));
|
|
13147
|
+
console.log(
|
|
13148
|
+
chalk5.gray(
|
|
13149
|
+
`\u2713 Config saved to ${path2.join(cwd, ".vm0", "volume.yaml")}`
|
|
13150
|
+
)
|
|
13151
|
+
);
|
|
13152
|
+
} catch (error43) {
|
|
13153
|
+
console.error(chalk5.red("\u2717 Failed to initialize volume"));
|
|
13154
|
+
if (error43 instanceof Error) {
|
|
13155
|
+
console.error(chalk5.gray(` ${error43.message}`));
|
|
13156
|
+
}
|
|
13157
|
+
process.exit(1);
|
|
13158
|
+
}
|
|
13159
|
+
});
|
|
13160
|
+
|
|
13161
|
+
// src/commands/volume/push.ts
|
|
13162
|
+
import { Command as Command4 } from "commander";
|
|
13163
|
+
import chalk6 from "chalk";
|
|
13164
|
+
import path3 from "path";
|
|
13165
|
+
import * as fs from "fs";
|
|
13166
|
+
import AdmZip from "adm-zip";
|
|
13167
|
+
async function getAllFiles(dirPath, baseDir = dirPath) {
|
|
13168
|
+
const files = [];
|
|
13169
|
+
const entries = await fs.promises.readdir(dirPath, { withFileTypes: true });
|
|
13170
|
+
for (const entry of entries) {
|
|
13171
|
+
const fullPath = path3.join(dirPath, entry.name);
|
|
13172
|
+
const relativePath = path3.relative(baseDir, fullPath);
|
|
13173
|
+
if (relativePath.startsWith(".vm0")) {
|
|
13174
|
+
continue;
|
|
13175
|
+
}
|
|
13176
|
+
if (entry.isDirectory()) {
|
|
13177
|
+
const subFiles = await getAllFiles(fullPath, baseDir);
|
|
13178
|
+
files.push(...subFiles);
|
|
13179
|
+
} else {
|
|
13180
|
+
files.push(fullPath);
|
|
13181
|
+
}
|
|
13182
|
+
}
|
|
13183
|
+
return files;
|
|
13184
|
+
}
|
|
13185
|
+
function formatBytes(bytes) {
|
|
13186
|
+
if (bytes === 0) return "0 B";
|
|
13187
|
+
const k = 1024;
|
|
13188
|
+
const sizes = ["B", "KB", "MB", "GB"];
|
|
13189
|
+
const i = Math.floor(Math.log(bytes) / Math.log(k));
|
|
13190
|
+
return `${(bytes / Math.pow(k, i)).toFixed(2)} ${sizes[i]}`;
|
|
13191
|
+
}
|
|
13192
|
+
var pushCommand = new Command4().name("push").description("Push local files to cloud volume").action(async () => {
|
|
13193
|
+
try {
|
|
13194
|
+
const cwd = process.cwd();
|
|
13195
|
+
const config2 = await readVolumeConfig(cwd);
|
|
13196
|
+
if (!config2) {
|
|
13197
|
+
console.error(chalk6.red("\u2717 No volume initialized in this directory"));
|
|
13198
|
+
console.error(chalk6.gray(" Run: vm0 volume init"));
|
|
13199
|
+
process.exit(1);
|
|
13200
|
+
}
|
|
13201
|
+
console.log(chalk6.cyan(`Pushing volume: ${config2.name}`));
|
|
13202
|
+
console.log(chalk6.gray("Collecting files..."));
|
|
13203
|
+
const files = await getAllFiles(cwd);
|
|
13204
|
+
if (files.length === 0) {
|
|
13205
|
+
console.log(chalk6.yellow("No files to upload"));
|
|
13206
|
+
return;
|
|
13207
|
+
}
|
|
13208
|
+
let totalSize = 0;
|
|
13209
|
+
for (const file2 of files) {
|
|
13210
|
+
const stats = await fs.promises.stat(file2);
|
|
13211
|
+
totalSize += stats.size;
|
|
13212
|
+
}
|
|
13213
|
+
console.log(
|
|
13214
|
+
chalk6.gray(`Found ${files.length} files (${formatBytes(totalSize)})`)
|
|
13215
|
+
);
|
|
13216
|
+
console.log(chalk6.gray("Compressing files..."));
|
|
13217
|
+
const zip = new AdmZip();
|
|
13218
|
+
for (const file2 of files) {
|
|
13219
|
+
const relativePath = path3.relative(cwd, file2);
|
|
13220
|
+
zip.addLocalFile(file2, path3.dirname(relativePath));
|
|
13221
|
+
}
|
|
13222
|
+
const zipBuffer = zip.toBuffer();
|
|
13223
|
+
console.log(
|
|
13224
|
+
chalk6.green(`\u2713 Compressed to ${formatBytes(zipBuffer.length)}`)
|
|
13225
|
+
);
|
|
13226
|
+
console.log(chalk6.gray("Uploading..."));
|
|
13227
|
+
const formData = new FormData();
|
|
13228
|
+
formData.append("volumeName", config2.name);
|
|
13229
|
+
formData.append(
|
|
13230
|
+
"file",
|
|
13231
|
+
new Blob([zipBuffer], { type: "application/zip" }),
|
|
13232
|
+
"volume.zip"
|
|
13233
|
+
);
|
|
13234
|
+
const response = await apiClient.post("/api/volumes", {
|
|
13235
|
+
body: formData
|
|
13236
|
+
});
|
|
13237
|
+
if (!response.ok) {
|
|
13238
|
+
const error43 = await response.json();
|
|
13239
|
+
throw new Error(error43.error || "Upload failed");
|
|
13240
|
+
}
|
|
13241
|
+
const result = await response.json();
|
|
13242
|
+
console.log(chalk6.green("\u2713 Upload complete"));
|
|
13243
|
+
console.log(chalk6.gray(` Files: ${result.fileCount.toLocaleString()}`));
|
|
13244
|
+
console.log(chalk6.gray(` Size: ${formatBytes(result.size)}`));
|
|
13245
|
+
} catch (error43) {
|
|
13246
|
+
console.error(chalk6.red("\u2717 Push failed"));
|
|
13247
|
+
if (error43 instanceof Error) {
|
|
13248
|
+
console.error(chalk6.gray(` ${error43.message}`));
|
|
13249
|
+
}
|
|
13250
|
+
process.exit(1);
|
|
13251
|
+
}
|
|
13252
|
+
});
|
|
13253
|
+
|
|
13254
|
+
// src/commands/volume/pull.ts
|
|
13255
|
+
import { Command as Command5 } from "commander";
|
|
13256
|
+
import chalk7 from "chalk";
|
|
13257
|
+
import path4 from "path";
|
|
13258
|
+
import * as fs2 from "fs";
|
|
13259
|
+
import AdmZip2 from "adm-zip";
|
|
13260
|
+
function formatBytes2(bytes) {
|
|
13261
|
+
if (bytes === 0) return "0 B";
|
|
13262
|
+
const k = 1024;
|
|
13263
|
+
const sizes = ["B", "KB", "MB", "GB"];
|
|
13264
|
+
const i = Math.floor(Math.log(bytes) / Math.log(k));
|
|
13265
|
+
return `${(bytes / Math.pow(k, i)).toFixed(2)} ${sizes[i]}`;
|
|
13266
|
+
}
|
|
13267
|
+
var pullCommand = new Command5().name("pull").description("Pull cloud files to local directory").action(async () => {
|
|
13268
|
+
try {
|
|
13269
|
+
const cwd = process.cwd();
|
|
13270
|
+
const config2 = await readVolumeConfig(cwd);
|
|
13271
|
+
if (!config2) {
|
|
13272
|
+
console.error(chalk7.red("\u2717 No volume initialized in this directory"));
|
|
13273
|
+
console.error(chalk7.gray(" Run: vm0 volume init"));
|
|
13274
|
+
process.exit(1);
|
|
13275
|
+
}
|
|
13276
|
+
console.log(chalk7.cyan(`Pulling volume: ${config2.name}`));
|
|
13277
|
+
console.log(chalk7.gray("Downloading..."));
|
|
13278
|
+
const response = await apiClient.get(
|
|
13279
|
+
`/api/volumes?name=${encodeURIComponent(config2.name)}`
|
|
13280
|
+
);
|
|
13281
|
+
if (!response.ok) {
|
|
13282
|
+
if (response.status === 404) {
|
|
13283
|
+
console.error(chalk7.red(`\u2717 Volume "${config2.name}" not found`));
|
|
13284
|
+
console.error(
|
|
13285
|
+
chalk7.gray(
|
|
13286
|
+
" Make sure the volume name is correct in .vm0/volume.yaml"
|
|
13287
|
+
)
|
|
13288
|
+
);
|
|
13289
|
+
console.error(
|
|
13290
|
+
chalk7.gray(" Or push the volume first with: vm0 volume push")
|
|
13291
|
+
);
|
|
13292
|
+
} else {
|
|
13293
|
+
const error43 = await response.json();
|
|
13294
|
+
throw new Error(error43.error || "Download failed");
|
|
13295
|
+
}
|
|
13296
|
+
process.exit(1);
|
|
13297
|
+
}
|
|
13298
|
+
const arrayBuffer = await response.arrayBuffer();
|
|
13299
|
+
const zipBuffer = Buffer.from(arrayBuffer);
|
|
13300
|
+
console.log(chalk7.green(`\u2713 Downloaded ${formatBytes2(zipBuffer.length)}`));
|
|
13301
|
+
console.log(chalk7.gray("Extracting files..."));
|
|
13302
|
+
const zip = new AdmZip2(zipBuffer);
|
|
13303
|
+
const zipEntries = zip.getEntries();
|
|
13304
|
+
let extractedCount = 0;
|
|
13305
|
+
for (const entry of zipEntries) {
|
|
13306
|
+
if (!entry.isDirectory) {
|
|
13307
|
+
const targetPath = path4.join(cwd, entry.entryName);
|
|
13308
|
+
const dir = path4.dirname(targetPath);
|
|
13309
|
+
await fs2.promises.mkdir(dir, { recursive: true });
|
|
13310
|
+
const data = entry.getData();
|
|
13311
|
+
await fs2.promises.writeFile(targetPath, data);
|
|
13312
|
+
extractedCount++;
|
|
13313
|
+
}
|
|
13314
|
+
}
|
|
13315
|
+
console.log(chalk7.green(`\u2713 Extracted ${extractedCount} files`));
|
|
13316
|
+
} catch (error43) {
|
|
13317
|
+
console.error(chalk7.red("\u2717 Pull failed"));
|
|
13318
|
+
if (error43 instanceof Error) {
|
|
13319
|
+
console.error(chalk7.gray(` ${error43.message}`));
|
|
13320
|
+
}
|
|
13321
|
+
process.exit(1);
|
|
13322
|
+
}
|
|
13323
|
+
});
|
|
13324
|
+
|
|
13325
|
+
// src/commands/volume/index.ts
|
|
13326
|
+
var volumeCommand = new Command6().name("volume").description("Manage cloud volumes").addCommand(initCommand).addCommand(pushCommand).addCommand(pullCommand);
|
|
13327
|
+
|
|
12968
13328
|
// src/index.ts
|
|
12969
|
-
var program = new
|
|
13329
|
+
var program = new Command7();
|
|
12970
13330
|
program.name("vm0").description("VM0 CLI - A modern build tool").version("0.1.0");
|
|
12971
13331
|
program.command("hello").description("Say hello from the App").action(() => {
|
|
12972
|
-
console.log(
|
|
12973
|
-
console.log(
|
|
13332
|
+
console.log(chalk8.blue("Welcome to the VM0 CLI!"));
|
|
13333
|
+
console.log(chalk8.green(`Core says: ${FOO}`));
|
|
12974
13334
|
});
|
|
12975
13335
|
program.command("info").description("Display environment information").action(async () => {
|
|
12976
|
-
console.log(
|
|
13336
|
+
console.log(chalk8.cyan("System Information:"));
|
|
12977
13337
|
console.log(`Node Version: ${process.version}`);
|
|
12978
13338
|
console.log(`Platform: ${process.platform}`);
|
|
12979
13339
|
console.log(`Architecture: ${process.arch}`);
|
|
@@ -12992,6 +13352,7 @@ authCommand.command("status").description("Show current authentication status").
|
|
|
12992
13352
|
});
|
|
12993
13353
|
program.addCommand(buildCommand);
|
|
12994
13354
|
program.addCommand(runCommand);
|
|
13355
|
+
program.addCommand(volumeCommand);
|
|
12995
13356
|
if (process.argv[1]?.endsWith("index.js") || process.argv[1]?.endsWith("index.ts") || process.argv[1]?.endsWith("vm0")) {
|
|
12996
13357
|
program.parse();
|
|
12997
13358
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@vm0/cli",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.3.0",
|
|
4
4
|
"description": "CLI application",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
@@ -10,6 +10,7 @@
|
|
|
10
10
|
"."
|
|
11
11
|
],
|
|
12
12
|
"dependencies": {
|
|
13
|
+
"adm-zip": "^0.5.16",
|
|
13
14
|
"chalk": "^5.6.0",
|
|
14
15
|
"commander": "^14.0.0",
|
|
15
16
|
"yaml": "^2.3.4"
|