@visualautomate/cli 0.5.0 → 0.5.2
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/README.md +10 -5
- package/dist/index.js +111 -49
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -59,6 +59,10 @@ state {"runs":1}
|
|
|
59
59
|
A run passes when it succeeds and, if the test file names an `expectOutput`,
|
|
60
60
|
leaves by that port — the same verdict as the GitHub check.
|
|
61
61
|
|
|
62
|
+
The verdict is green when it passed and red when it did not, in the sandbox as
|
|
63
|
+
well as out here. `NO_COLOR` turns that off, and output that is not a terminal —
|
|
64
|
+
a pipe, a file, a CI log — is plain anyway; `FORCE_COLOR` turns it back on.
|
|
65
|
+
|
|
62
66
|
### In a GitHub repository
|
|
63
67
|
|
|
64
68
|
`test` works wherever the platform tests your code on a push:
|
|
@@ -79,16 +83,17 @@ my-app/ an app's repository
|
|
|
79
83
|
…
|
|
80
84
|
```
|
|
81
85
|
|
|
82
|
-
At the root of an app's repository every module runs, one after another
|
|
83
|
-
|
|
86
|
+
At the root of an app's repository every module runs, one after another. Name one
|
|
87
|
+
to run only that: `vsa test send-message`, or any short name that picks it out —
|
|
88
|
+
`vsa test send`. `--module <name>` is the same thing. `index.js` in a repository is run as written,
|
|
84
89
|
unbundled, because that is the file the sandbox loads.
|
|
85
90
|
|
|
86
91
|
## `dev`
|
|
87
92
|
|
|
88
93
|
```bash
|
|
89
|
-
vsa dev
|
|
90
|
-
vsa dev
|
|
91
|
-
vsa dev --tier boosted
|
|
94
|
+
vsa dev # every module in modules/
|
|
95
|
+
vsa dev send-message # one of them; the shortest name that picks it will do
|
|
96
|
+
vsa dev --tier boosted # a bigger machine
|
|
92
97
|
vsa dev --local # on this machine's Node, without the sandbox
|
|
93
98
|
```
|
|
94
99
|
|
package/dist/index.js
CHANGED
|
@@ -1822,6 +1822,14 @@ var ENTRIES = ["plugin.ts", "plugin.js", "index.ts", "index.js"];
|
|
|
1822
1822
|
function entryIn(dir) {
|
|
1823
1823
|
return ENTRIES.map((file2) => join(dir, file2)).find((file2) => existsSync(file2)) ?? null;
|
|
1824
1824
|
}
|
|
1825
|
+
function matchFolders(folders, name) {
|
|
1826
|
+
const wanted2 = name.trim().toLowerCase();
|
|
1827
|
+
const exact = folders.filter((folder) => folder.toLowerCase() === wanted2);
|
|
1828
|
+
if (exact.length > 0) return exact;
|
|
1829
|
+
const prefix = folders.filter((folder) => folder.toLowerCase().startsWith(wanted2));
|
|
1830
|
+
if (prefix.length > 0) return prefix;
|
|
1831
|
+
return folders.filter((folder) => folder.toLowerCase().includes(wanted2));
|
|
1832
|
+
}
|
|
1825
1833
|
function findTargets(cwd, only) {
|
|
1826
1834
|
const dir = resolve(cwd);
|
|
1827
1835
|
const modulesDir = join(dir, "modules");
|
|
@@ -1830,16 +1838,16 @@ function findTargets(cwd, only) {
|
|
|
1830
1838
|
if (folders.length === 0) {
|
|
1831
1839
|
throw new Error("There is a modules/ folder here, but none of its folders has a manifest.json.");
|
|
1832
1840
|
}
|
|
1833
|
-
const chosen = only ? folders
|
|
1841
|
+
const chosen = only ? matchFolders(folders, only) : folders;
|
|
1834
1842
|
if (only && chosen.length === 0) {
|
|
1835
|
-
throw new Error(`No module "${only}"
|
|
1843
|
+
throw new Error(`No module matches "${only}". This repository has: ${folders.join(", ")}.`);
|
|
1836
1844
|
}
|
|
1837
1845
|
return chosen.map((folder) => targetIn(join(modulesDir, folder), `modules/${folder}`)).filter(Boolean);
|
|
1838
1846
|
}
|
|
1839
1847
|
const target = targetIn(dir, basename(dir));
|
|
1840
1848
|
if (!target) {
|
|
1841
1849
|
throw new Error(
|
|
1842
|
-
"Nothing to test here: no plugin.ts, plugin.js or index.js, and no modules/ folder.\nRun this in a
|
|
1850
|
+
"Nothing to test here: no plugin.ts, plugin.js or index.js, and no modules/ folder.\nRun this in a module's folder, at the root of its repository, or run `vsa init <name>`."
|
|
1843
1851
|
);
|
|
1844
1852
|
}
|
|
1845
1853
|
return [target];
|
|
@@ -2079,6 +2087,38 @@ async function savePersisted(dir, files, state) {
|
|
|
2079
2087
|
|
|
2080
2088
|
// src/docker.ts
|
|
2081
2089
|
import { spawnSync } from "node:child_process";
|
|
2090
|
+
|
|
2091
|
+
// src/colour.ts
|
|
2092
|
+
var CODES = {
|
|
2093
|
+
reset: "\x1B[0m",
|
|
2094
|
+
bold: "\x1B[1m",
|
|
2095
|
+
dim: "\x1B[2m",
|
|
2096
|
+
red: "\x1B[31m",
|
|
2097
|
+
green: "\x1B[32m",
|
|
2098
|
+
yellow: "\x1B[33m",
|
|
2099
|
+
blue: "\x1B[34m"
|
|
2100
|
+
};
|
|
2101
|
+
function wanted() {
|
|
2102
|
+
const env = process.env;
|
|
2103
|
+
if (env.NO_COLOR !== void 0 && env.NO_COLOR !== "") return false;
|
|
2104
|
+
if (env.FORCE_COLOR !== void 0 && env.FORCE_COLOR !== "" && env.FORCE_COLOR !== "0") return true;
|
|
2105
|
+
return Boolean(process.stdout.isTTY);
|
|
2106
|
+
}
|
|
2107
|
+
var ON = wanted();
|
|
2108
|
+
function paint(code) {
|
|
2109
|
+
return (text) => ON ? `${code}${text}${CODES.reset}` : text;
|
|
2110
|
+
}
|
|
2111
|
+
var red = paint(CODES.red);
|
|
2112
|
+
var green = paint(CODES.green);
|
|
2113
|
+
var yellow = paint(CODES.yellow);
|
|
2114
|
+
var blue = paint(CODES.blue);
|
|
2115
|
+
var dim = paint(CODES.dim);
|
|
2116
|
+
var bold = paint(CODES.bold);
|
|
2117
|
+
function colourEnv() {
|
|
2118
|
+
return ON ? ["--env", "FORCE_COLOR=1"] : ["--env", "NO_COLOR=1"];
|
|
2119
|
+
}
|
|
2120
|
+
|
|
2121
|
+
// src/docker.ts
|
|
2082
2122
|
var IMAGE = "ghcr.io/visualautomate/plugin-sandbox";
|
|
2083
2123
|
var TIERS = {
|
|
2084
2124
|
standard: { cpus: "0.25", memory: "1g" },
|
|
@@ -2111,6 +2151,9 @@ function dockerArgs(run) {
|
|
|
2111
2151
|
"ALL",
|
|
2112
2152
|
"--security-opt",
|
|
2113
2153
|
"no-new-privileges",
|
|
2154
|
+
// So a run inside the container is as readable as one out here: its
|
|
2155
|
+
// stdout is a pipe to Docker, which would otherwise turn colour off.
|
|
2156
|
+
...colourEnv(),
|
|
2114
2157
|
...run.user ? ["--user", `${run.user.uid}:${run.user.gid}`] : [],
|
|
2115
2158
|
run.image,
|
|
2116
2159
|
...run.args
|
|
@@ -2134,16 +2177,18 @@ function dockerProblem() {
|
|
|
2134
2177
|
function explainExit(code, tier) {
|
|
2135
2178
|
if (code === 137) {
|
|
2136
2179
|
const next = Object.keys(TIERS)[Object.keys(TIERS).indexOf(tier) + 1];
|
|
2137
|
-
return
|
|
2180
|
+
return red(
|
|
2181
|
+
`\u2717 stopped the step used more than the ${TIERS[tier].memory.replace("g", " GB")} of memory the ${tier} tier has, and the container was killed. On the platform it would stop the same way.` + (next ? ` To check it fits a bigger machine: --tier ${next}` : "")
|
|
2182
|
+
);
|
|
2138
2183
|
}
|
|
2139
|
-
if (code === 125) return "\u2717 Docker could not start the container. Is the image available? Try: docker pull " + IMAGE;
|
|
2184
|
+
if (code === 125) return red("\u2717 Docker could not start the container. Is the image available? Try: docker pull " + IMAGE);
|
|
2140
2185
|
return null;
|
|
2141
2186
|
}
|
|
2142
2187
|
function ensureDocker(say, waitMs = 12e4) {
|
|
2143
2188
|
const problem = dockerProblem();
|
|
2144
2189
|
if (!problem) return null;
|
|
2145
2190
|
if (problem.includes("not installed")) return problem;
|
|
2146
|
-
say("Docker is not running. Starting Docker Desktop\u2026");
|
|
2191
|
+
say(yellow("Docker is not running. Starting Docker Desktop\u2026"));
|
|
2147
2192
|
const started = spawnSync("docker", ["desktop", "start"], { encoding: "utf8" });
|
|
2148
2193
|
if (started.status !== 0) {
|
|
2149
2194
|
say("Could not start it from here \u2014 start Docker Desktop yourself; this will carry on when it is up.");
|
|
@@ -2177,7 +2222,7 @@ function runInDocker(run) {
|
|
|
2177
2222
|
}
|
|
2178
2223
|
|
|
2179
2224
|
// src/index.ts
|
|
2180
|
-
var VERSION = true ? "0.5.
|
|
2225
|
+
var VERSION = true ? "0.5.2" : "0.0.0-dev";
|
|
2181
2226
|
var DEFAULT_API_URL = "https://visualautomate.com";
|
|
2182
2227
|
function configPath() {
|
|
2183
2228
|
return join3(homedir(), ".visualautomate", "config.json");
|
|
@@ -2204,7 +2249,7 @@ async function tokenFor(flags) {
|
|
|
2204
2249
|
return { token, apiUrl };
|
|
2205
2250
|
}
|
|
2206
2251
|
function fail(message) {
|
|
2207
|
-
console.error(message);
|
|
2252
|
+
console.error(red(message));
|
|
2208
2253
|
throw new SilentExit();
|
|
2209
2254
|
}
|
|
2210
2255
|
var SilentExit = class extends Error {
|
|
@@ -2281,8 +2326,8 @@ async function cmdLogin(flags) {
|
|
|
2281
2326
|
});
|
|
2282
2327
|
const who = await whoami(apiUrl, token);
|
|
2283
2328
|
await writeConfig({ token, apiUrl, email: who.email });
|
|
2284
|
-
ok(`
|
|
2285
|
-
\u2713 Signed in as ${who.email}`);
|
|
2329
|
+
ok(green(`
|
|
2330
|
+
\u2713 Signed in as ${who.email}`));
|
|
2286
2331
|
ok(` Token stored in ${configPath()}`);
|
|
2287
2332
|
}
|
|
2288
2333
|
async function whoami(apiUrl, token) {
|
|
@@ -2368,9 +2413,9 @@ visualautomate push # publish it to your account
|
|
|
2368
2413
|
\`test.json\` is the input, config and state \`test\` runs with.
|
|
2369
2414
|
`
|
|
2370
2415
|
);
|
|
2371
|
-
ok(`
|
|
2416
|
+
ok(green(`
|
|
2372
2417
|
\u2713 Created ${name} from the "${templateName}" template
|
|
2373
|
-
`);
|
|
2418
|
+
`));
|
|
2374
2419
|
ok(" cd " + name);
|
|
2375
2420
|
ok(" visualautomate test");
|
|
2376
2421
|
ok(" visualautomate push\n");
|
|
@@ -2404,12 +2449,12 @@ function line(indent, text) {
|
|
|
2404
2449
|
async function runTarget(target, flags, several) {
|
|
2405
2450
|
const say = (text) => line(several, text);
|
|
2406
2451
|
if (several) ok(`
|
|
2407
|
-
|
|
2452
|
+
${bold(`\u25B8 ${target.name}`)}`);
|
|
2408
2453
|
try {
|
|
2409
2454
|
const code = await codeFor(target);
|
|
2410
2455
|
const check = validatePluginCode(code);
|
|
2411
2456
|
if (!check.valid) {
|
|
2412
|
-
say(
|
|
2457
|
+
say(`${red("\u2717 refused")} ${check.error}`);
|
|
2413
2458
|
return false;
|
|
2414
2459
|
}
|
|
2415
2460
|
const settings = await readTestFile(target.dir, flags.input);
|
|
@@ -2423,7 +2468,7 @@ async function runTarget(target, flags, several) {
|
|
|
2423
2468
|
const manifest = parseJsonc(await readFile3(manifestPath, "utf8")).value;
|
|
2424
2469
|
outputs = manifest.outputs ?? [];
|
|
2425
2470
|
} catch (error) {
|
|
2426
|
-
say(`\u2717 manifest.json is not valid JSON: ${error instanceof Error ? error.message : String(error)}`);
|
|
2471
|
+
say(red(`\u2717 manifest.json is not valid JSON: ${error instanceof Error ? error.message : String(error)}`));
|
|
2427
2472
|
return false;
|
|
2428
2473
|
}
|
|
2429
2474
|
}
|
|
@@ -2449,33 +2494,37 @@ async function runTarget(target, flags, several) {
|
|
|
2449
2494
|
const port = result.output?.output ?? null;
|
|
2450
2495
|
const expected = settings.expectOutput;
|
|
2451
2496
|
const passed = result.status === "success" && (!expected || port === expected);
|
|
2452
|
-
|
|
2453
|
-
|
|
2497
|
+
const verdict = passed ? green(bold("\u2713 passed")) : red(bold("\u2717 failed"));
|
|
2498
|
+
say(`${verdict} ${dim(`${result.status}, ${result.durationMs}ms`)}`);
|
|
2499
|
+
if (result.error) say(`${dim("error")} ${red(result.error)}`);
|
|
2454
2500
|
if (result.output) {
|
|
2455
|
-
|
|
2456
|
-
say(
|
|
2501
|
+
const port_ = passed || !expected ? port : red(String(port));
|
|
2502
|
+
say(`${dim("output")} ${port_}${expected ? dim(` (expected ${expected})`) : ""}`);
|
|
2503
|
+
say(`${dim("data")} ${JSON.stringify(result.output.data, null, 2).replace(/\n/g, "\n ")}`);
|
|
2457
2504
|
}
|
|
2458
|
-
if (result.files.size > 0) say(
|
|
2459
|
-
if (result.state.size > 0) say(
|
|
2460
|
-
for (const entry of result.logs ?? []) say(
|
|
2505
|
+
if (result.files.size > 0) say(`${dim("files")} ${[...result.files.keys()].join(", ")}`);
|
|
2506
|
+
if (result.state.size > 0) say(`${dim("state")} ${JSON.stringify(Object.fromEntries(result.state))}`);
|
|
2507
|
+
for (const entry of result.logs ?? []) say(`${dim("log")} ${entry}`);
|
|
2461
2508
|
for (const [host, enforced] of denied) {
|
|
2462
2509
|
say(
|
|
2463
|
-
enforced ?
|
|
2510
|
+
enforced ? `${dim("egress")} ${red(`blocked ${host}`)} \u2014 not in allowedDomains` : yellow(`! egress ${host} is not allowed on the platform unless the plugin declares it. `) + `Add "allowedDomains": ["${host}"] to the test file to check with the same rule.`
|
|
2464
2511
|
);
|
|
2465
2512
|
}
|
|
2466
2513
|
if (persist) {
|
|
2467
2514
|
const refused = await savePersisted(target.dir, result.files, result.state);
|
|
2468
|
-
say(
|
|
2469
|
-
for (const path of refused) say(`! storage path "${path}" points outside the storage folder and was not written`);
|
|
2515
|
+
say(`${dim("kept")} ${dim(`storage and state in ${relative3(process.cwd(), join3(target.dir, ".visualautomate")) || ".visualautomate"}`)}`);
|
|
2516
|
+
for (const path of refused) say(yellow(`! storage path "${path}" points outside the storage folder and was not written`));
|
|
2470
2517
|
}
|
|
2471
2518
|
if (port && outputs.length > 0 && !outputs.includes(port)) {
|
|
2472
2519
|
say(
|
|
2473
|
-
|
|
2520
|
+
yellow(
|
|
2521
|
+
`! This returned output "${port}", which manifest.json does not list (it has ${outputs.map((o) => `"${o}"`).join(", ")}). On a canvas nothing would follow it.`
|
|
2522
|
+
)
|
|
2474
2523
|
);
|
|
2475
2524
|
}
|
|
2476
2525
|
return passed;
|
|
2477
2526
|
} catch (error) {
|
|
2478
|
-
say(`\u2717 ${error instanceof Error ? error.message : String(error)}`);
|
|
2527
|
+
say(red(`\u2717 ${error instanceof Error ? error.message : String(error)}`));
|
|
2479
2528
|
return false;
|
|
2480
2529
|
}
|
|
2481
2530
|
}
|
|
@@ -2484,8 +2533,11 @@ async function runTargets(targets, flags) {
|
|
|
2484
2533
|
for (const target of targets) {
|
|
2485
2534
|
if (await runTarget(target, flags, targets.length > 1)) passed++;
|
|
2486
2535
|
}
|
|
2487
|
-
if (targets.length > 1)
|
|
2488
|
-
|
|
2536
|
+
if (targets.length > 1) {
|
|
2537
|
+
const tally = `${passed} of ${targets.length} passed`;
|
|
2538
|
+
ok(`
|
|
2539
|
+
${passed === targets.length ? green(bold(tally)) : red(bold(tally))}`);
|
|
2540
|
+
}
|
|
2489
2541
|
return passed === targets.length;
|
|
2490
2542
|
}
|
|
2491
2543
|
function packageLoader(dir) {
|
|
@@ -2538,31 +2590,41 @@ function runTargetsInDocker(targets, all, flags) {
|
|
|
2538
2590
|
}
|
|
2539
2591
|
return passed;
|
|
2540
2592
|
}
|
|
2541
|
-
function targetsOrFail(flags) {
|
|
2593
|
+
function targetsOrFail(args, flags) {
|
|
2542
2594
|
try {
|
|
2543
|
-
return findTargets(process.cwd(), flags.module);
|
|
2595
|
+
return findTargets(process.cwd(), args[0] ?? flags.module);
|
|
2544
2596
|
} catch (error) {
|
|
2545
2597
|
fail(error instanceof Error ? error.message : String(error));
|
|
2546
2598
|
}
|
|
2547
2599
|
}
|
|
2548
|
-
|
|
2549
|
-
|
|
2600
|
+
function sayWhatRuns(targets, picked) {
|
|
2601
|
+
if (targets.length === 1) {
|
|
2602
|
+
ok(`Running ${targets[0].name}`);
|
|
2603
|
+
return;
|
|
2604
|
+
}
|
|
2605
|
+
ok(`Running ${targets.length} modules: ${targets.map((t) => t.name.replace(/^modules\//, "")).join(", ")}`);
|
|
2606
|
+
if (!picked) ok(dim(`One of them: vsa dev ${targets[0].name.replace(/^modules\//, "")}`));
|
|
2607
|
+
}
|
|
2608
|
+
async function cmdTest(args, flags) {
|
|
2609
|
+
const targets = targetsOrFail(args, flags);
|
|
2550
2610
|
if (flags.docker) prepareSandbox(flags);
|
|
2551
2611
|
const passed = flags.docker ? runTargetsInDocker(targets, targets, flags) : await runTargets(targets, flags);
|
|
2552
2612
|
if (!passed) process.exitCode = 1;
|
|
2553
2613
|
}
|
|
2554
|
-
async function cmdDev(flags) {
|
|
2614
|
+
async function cmdDev(args, flags) {
|
|
2555
2615
|
const root = process.cwd();
|
|
2556
|
-
|
|
2616
|
+
const picked = args[0] ?? flags.module;
|
|
2617
|
+
let targets = targetsOrFail(args, flags);
|
|
2557
2618
|
const inSandbox = flags.local !== "true";
|
|
2558
2619
|
if (inSandbox) prepareSandbox(flags);
|
|
2559
2620
|
const header = () => {
|
|
2560
2621
|
process.stdout.write("\x1Bc");
|
|
2561
|
-
ok(
|
|
2622
|
+
ok(`${bold("vsa dev")} ${dim(`\u2014 ${(/* @__PURE__ */ new Date()).toLocaleTimeString()} \u2014 Ctrl+C to stop`)}`);
|
|
2562
2623
|
};
|
|
2563
2624
|
const run = async (chosen) => inSandbox ? runTargetsInDocker(chosen, targets, flags) : runTargets(chosen, flags);
|
|
2564
2625
|
header();
|
|
2565
|
-
ok(inSandbox ? `in ${imageFor(flags)}, tier ${flags.tier || "standard"}` : "on this machine, not in the sandbox");
|
|
2626
|
+
ok(dim(inSandbox ? `in ${imageFor(flags)}, tier ${flags.tier || "standard"}` : "on this machine, not in the sandbox"));
|
|
2627
|
+
sayWhatRuns(targets, Boolean(picked));
|
|
2566
2628
|
await run(targets);
|
|
2567
2629
|
const ignored = [`${sep2}node_modules${sep2}`, `${sep2}.git${sep2}`, `${sep2}dist${sep2}`, `${sep2}.visualautomate${sep2}`];
|
|
2568
2630
|
const pending = /* @__PURE__ */ new Set();
|
|
@@ -2577,17 +2639,17 @@ async function cmdDev(flags) {
|
|
|
2577
2639
|
const changed = [...pending];
|
|
2578
2640
|
pending.clear();
|
|
2579
2641
|
try {
|
|
2580
|
-
targets = findTargets(root,
|
|
2642
|
+
targets = findTargets(root, picked);
|
|
2581
2643
|
} catch (error) {
|
|
2582
2644
|
header();
|
|
2583
|
-
ok(`\u2717 ${error instanceof Error ? error.message : String(error)}`);
|
|
2645
|
+
ok(red(`\u2717 ${error instanceof Error ? error.message : String(error)}`));
|
|
2584
2646
|
running = false;
|
|
2585
2647
|
return;
|
|
2586
2648
|
}
|
|
2587
2649
|
const hit = changed.map((path) => targetForPath(targets, path));
|
|
2588
2650
|
const chosen = hit.some((t) => t === null) ? targets : [...new Set(hit)];
|
|
2589
2651
|
header();
|
|
2590
|
-
ok(`changed: ${changed.map((p) => relative3(root, p)).join(", ")}`);
|
|
2652
|
+
ok(dim(`changed: ${changed.map((p) => relative3(root, p)).join(", ")}`));
|
|
2591
2653
|
await run(chosen);
|
|
2592
2654
|
running = false;
|
|
2593
2655
|
};
|
|
@@ -2689,8 +2751,8 @@ ${text.slice(0, 500)}`);
|
|
|
2689
2751
|
if (!res.ok || !data.success) {
|
|
2690
2752
|
fail(`Push refused: ${data.error ?? `status ${res.status}`}`);
|
|
2691
2753
|
}
|
|
2692
|
-
ok(`
|
|
2693
|
-
\u2713 Published (id: ${data.pluginId})`);
|
|
2754
|
+
ok(green(`
|
|
2755
|
+
\u2713 Published (id: ${data.pluginId})`));
|
|
2694
2756
|
ok(` A version snapshot was saved, so you can roll back from the dashboard.`);
|
|
2695
2757
|
}
|
|
2696
2758
|
function printHelp() {
|
|
@@ -2699,8 +2761,8 @@ vsa \u2014 build and publish VisualAutomate plugins
|
|
|
2699
2761
|
|
|
2700
2762
|
vsa login sign this machine in
|
|
2701
2763
|
vsa init <name> [--template] start a plugin
|
|
2702
|
-
vsa dev [
|
|
2703
|
-
vsa test [
|
|
2764
|
+
vsa dev [<module>] the sandbox, and a run again on every save
|
|
2765
|
+
vsa test [<module>] run it once, as the platform's check does
|
|
2704
2766
|
vsa push publish it
|
|
2705
2767
|
vsa whoami which account this is
|
|
2706
2768
|
vsa logout forget the token
|
|
@@ -2712,11 +2774,11 @@ Templates: ${Object.keys(TEMPLATES).join(", ")}
|
|
|
2712
2774
|
|
|
2713
2775
|
\`test\` and \`dev\` work in a plugin made with \`init\`, at the root of a plugin's
|
|
2714
2776
|
GitHub repository, and at the root of an app's repository, where every folder
|
|
2715
|
-
in modules/ is run (or the one
|
|
2777
|
+
in modules/ is run (or the one you name: vsa dev send-message). The run's settings come
|
|
2716
2778
|
from visualautomate.test.json, test.json, or --input <file>.
|
|
2717
2779
|
|
|
2718
2780
|
Options for \`dev\` and \`test\`:
|
|
2719
|
-
--module <name>
|
|
2781
|
+
--module <name> the same as naming it: vsa dev send-message
|
|
2720
2782
|
--tier <name> the sandbox's size: standard, boosted, high or max
|
|
2721
2783
|
--image <ref> a different sandbox image
|
|
2722
2784
|
--persist keep storage and state in .visualautomate/ between runs
|
|
@@ -2761,17 +2823,17 @@ async function main() {
|
|
|
2761
2823
|
case "new":
|
|
2762
2824
|
return cmdInit(args, flags);
|
|
2763
2825
|
case "test":
|
|
2764
|
-
return cmdTest(flags);
|
|
2826
|
+
return cmdTest(args, flags);
|
|
2765
2827
|
case "dev":
|
|
2766
2828
|
case "watch":
|
|
2767
|
-
return cmdDev(flags);
|
|
2829
|
+
return cmdDev(args, flags);
|
|
2768
2830
|
case "push":
|
|
2769
2831
|
case "publish":
|
|
2770
2832
|
return cmdPush(flags);
|
|
2771
2833
|
// `plugin test` and `plugin push` are accepted as aliases.
|
|
2772
2834
|
case "plugin": {
|
|
2773
2835
|
const sub = args.shift();
|
|
2774
|
-
if (sub === "test") return cmdTest(flags);
|
|
2836
|
+
if (sub === "test") return cmdTest(args, flags);
|
|
2775
2837
|
if (sub === "push") return cmdPush(flags);
|
|
2776
2838
|
printHelp();
|
|
2777
2839
|
return;
|