@visualautomate/cli 0.4.1 → 0.5.1

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.
Files changed (3) hide show
  1. package/README.md +30 -20
  2. package/dist/index.js +95 -34
  3. package/package.json +4 -3
package/README.md CHANGED
@@ -4,14 +4,14 @@ Build and publish VisualAutomate plugins.
4
4
 
5
5
  ```bash
6
6
  npm install -g @visualautomate/cli
7
- visualautomate login
8
- visualautomate init my-step
7
+ vsa login
8
+ vsa init my-step
9
9
  cd my-step
10
- visualautomate dev # run it on every save
11
- visualautomate push
10
+ vsa dev # the sandbox, and a run again on every save
11
+ vsa push
12
12
  ```
13
13
 
14
- `va` is the same command, shorter.
14
+ `va` and `visualautomate` are the same command.
15
15
 
16
16
  That is the whole toolchain. There is nothing else to install: the CLI carries
17
17
  the SDK inside it, compiles your TypeScript itself, and writes the type
@@ -79,20 +79,25 @@ my-app/ an app's repository
79
79
  …
80
80
  ```
81
81
 
82
- At the root of an app's repository every module runs, one after another;
83
- `--module send-message` runs one. `index.js` in a repository is run as written,
82
+ At the root of an app's repository every module runs, one after another. Name one
83
+ to run only that: `vsa test send-message`, or any short name that picks it out —
84
+ `vsa test send`. `--module <name>` is the same thing. `index.js` in a repository is run as written,
84
85
  unbundled, because that is the file the sandbox loads.
85
86
 
86
87
  ## `dev`
87
88
 
88
- `test`, and again on every save. Only the module whose folder changed runs
89
- again; a change anywhere else runs them all. Ctrl+C stops it.
90
-
91
89
  ```bash
92
- va dev
93
- va dev --module send-message
90
+ vsa dev # every module in modules/
91
+ vsa dev send-message # one of them; the shortest name that picks it will do
92
+ vsa dev --tier boosted # a bigger machine
93
+ vsa dev --local # on this machine's Node, without the sandbox
94
94
  ```
95
95
 
96
+ Everything in one command: it starts Docker if it is not running, fetches the
97
+ sandbox image the first time, runs every module, and runs them again on every
98
+ save. Only the module whose folder changed runs again; a change anywhere else
99
+ runs them all. Ctrl+C stops it.
100
+
96
101
  `context.connections` and `context.media` are **not** simulated. One is
97
102
  somebody's OAuth account and the other is an ffmpeg container; pretending to be
98
103
  either would produce a plugin that passes here and fails on a canvas. Asking for
@@ -140,14 +145,17 @@ Keeps storage and state between runs, in `.visualautomate/` next to the plugin:
140
145
  Once that folder exists it is used without the flag. Delete it to start over,
141
146
  and add it to `.gitignore`.
142
147
 
143
- ## `--docker`: the sandbox on your machine
148
+ ## The sandbox on your machine
149
+
150
+ `dev` uses it by default; `test` does with `--docker`:
144
151
 
145
152
  ```bash
146
- va test --docker
147
- va dev --docker --tier boosted
153
+ vsa dev
154
+ vsa test --docker
155
+ vsa dev --tier boosted
148
156
  ```
149
157
 
150
- Runs the test inside the plugin sandbox image,
158
+ Your plugin runs inside the plugin sandbox image,
151
159
  `ghcr.io/visualautomate/plugin-sandbox`, at the version of this CLI:
152
160
 
153
161
  - the Node version the platform runs plugins on, with every allowed npm
@@ -157,10 +165,12 @@ Runs the test inside the plugin sandbox image,
157
165
  - no Linux capabilities, a read-only filesystem apart from your project and a
158
166
  small `/tmp`, and nothing else from your machine.
159
167
 
160
- A step that runs out of memory or time here does the same on the platform. Docker
161
- must be running. `--image` uses a different image, for example one you built
162
- yourself. With `dev`, the watcher stays on your machine and each run starts a
163
- fresh container.
168
+ A step that runs out of memory or time here does the same on the platform.
169
+
170
+ Docker does not have to be running: `dev` starts Docker Desktop and waits for it,
171
+ then fetches the image if this machine does not have it. `--image` uses a
172
+ different image, for example one you built yourself. The watcher stays on your
173
+ machine, and each run starts a fresh container.
164
174
 
165
175
  `context.media` is not available locally, in or out of Docker.
166
176
 
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 wanted = name.trim().toLowerCase();
1827
+ const exact = folders.filter((folder) => folder.toLowerCase() === wanted);
1828
+ if (exact.length > 0) return exact;
1829
+ const prefix = folders.filter((folder) => folder.toLowerCase().startsWith(wanted));
1830
+ if (prefix.length > 0) return prefix;
1831
+ return folders.filter((folder) => folder.toLowerCase().includes(wanted));
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.filter((f) => f === only) : folders;
1841
+ const chosen = only ? matchFolders(folders, only) : folders;
1834
1842
  if (only && chosen.length === 0) {
1835
- throw new Error(`No module "${only}" in modules/. There is: ${folders.join(", ")}.`);
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 plugin's folder, at the root of its repository, or run `visualautomate init <name>`."
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];
@@ -2139,6 +2147,35 @@ function explainExit(code, tier) {
2139
2147
  if (code === 125) return "\u2717 Docker could not start the container. Is the image available? Try: docker pull " + IMAGE;
2140
2148
  return null;
2141
2149
  }
2150
+ function ensureDocker(say, waitMs = 12e4) {
2151
+ const problem = dockerProblem();
2152
+ if (!problem) return null;
2153
+ if (problem.includes("not installed")) return problem;
2154
+ say("Docker is not running. Starting Docker Desktop\u2026");
2155
+ const started = spawnSync("docker", ["desktop", "start"], { encoding: "utf8" });
2156
+ if (started.status !== 0) {
2157
+ say("Could not start it from here \u2014 start Docker Desktop yourself; this will carry on when it is up.");
2158
+ }
2159
+ const until = Date.now() + waitMs;
2160
+ while (Date.now() < until) {
2161
+ spawnSync(process.execPath, ["-e", "setTimeout(() => {}, 1000)"]);
2162
+ if (!dockerProblem()) {
2163
+ say("Docker is ready.");
2164
+ return null;
2165
+ }
2166
+ }
2167
+ return "Docker did not come up in time. Start Docker Desktop and try again.";
2168
+ }
2169
+ function hasImage(image) {
2170
+ return spawnSync("docker", ["image", "inspect", image], { stdio: "ignore" }).status === 0;
2171
+ }
2172
+ function pullImage(image, say) {
2173
+ if (hasImage(image)) return null;
2174
+ say(`Fetching ${image} \u2014 once, about 700 MB.`);
2175
+ const pulled = spawnSync("docker", ["pull", image], { stdio: "inherit" });
2176
+ if (pulled.status === 0) return null;
2177
+ return `Could not fetch ${image}. Check your connection, or pass --image with one you have.`;
2178
+ }
2142
2179
  function runInDocker(run) {
2143
2180
  const result = spawnSync("docker", dockerArgs(run), { stdio: "inherit" });
2144
2181
  const code = result.status ?? 1;
@@ -2148,7 +2185,7 @@ function runInDocker(run) {
2148
2185
  }
2149
2186
 
2150
2187
  // src/index.ts
2151
- var VERSION = true ? "0.4.1" : "0.0.0-dev";
2188
+ var VERSION = true ? "0.5.1" : "0.0.0-dev";
2152
2189
  var DEFAULT_API_URL = "https://visualautomate.com";
2153
2190
  function configPath() {
2154
2191
  return join3(homedir(), ".visualautomate", "config.json");
@@ -2472,9 +2509,16 @@ function packageLoader(dir) {
2472
2509
  }
2473
2510
  };
2474
2511
  }
2512
+ function imageFor(flags) {
2513
+ return flags.image || `${IMAGE}:${VERSION}`;
2514
+ }
2515
+ function prepareSandbox(flags) {
2516
+ const docker = ensureDocker(ok);
2517
+ if (docker) fail(docker);
2518
+ const image = pullImage(imageFor(flags), ok);
2519
+ if (image) fail(image);
2520
+ }
2475
2521
  function dockerRunFor(flags, args) {
2476
- const problem = dockerProblem();
2477
- if (problem) fail(problem);
2478
2522
  let tier;
2479
2523
  try {
2480
2524
  tier = tierOf(flags.tier);
@@ -2483,7 +2527,7 @@ function dockerRunFor(flags, args) {
2483
2527
  }
2484
2528
  return {
2485
2529
  cwd: process.cwd(),
2486
- image: flags.image || `${IMAGE}:${VERSION}`,
2530
+ image: imageFor(flags),
2487
2531
  tier,
2488
2532
  args,
2489
2533
  user: typeof process.getuid === "function" && typeof process.getgid === "function" ? { uid: process.getuid(), gid: process.getgid() } : void 0
@@ -2502,28 +2546,41 @@ function runTargetsInDocker(targets, all, flags) {
2502
2546
  }
2503
2547
  return passed;
2504
2548
  }
2505
- function targetsOrFail(flags) {
2549
+ function targetsOrFail(args, flags) {
2506
2550
  try {
2507
- return findTargets(process.cwd(), flags.module);
2551
+ return findTargets(process.cwd(), args[0] ?? flags.module);
2508
2552
  } catch (error) {
2509
2553
  fail(error instanceof Error ? error.message : String(error));
2510
2554
  }
2511
2555
  }
2512
- async function cmdTest(flags) {
2513
- const targets = targetsOrFail(flags);
2556
+ function sayWhatRuns(targets, picked) {
2557
+ if (targets.length === 1) {
2558
+ ok(`Running ${targets[0].name}`);
2559
+ return;
2560
+ }
2561
+ ok(`Running ${targets.length} modules: ${targets.map((t) => t.name.replace(/^modules\//, "")).join(", ")}`);
2562
+ if (!picked) ok(`One of them: vsa dev ${targets[0].name.replace(/^modules\//, "")}`);
2563
+ }
2564
+ async function cmdTest(args, flags) {
2565
+ const targets = targetsOrFail(args, flags);
2566
+ if (flags.docker) prepareSandbox(flags);
2514
2567
  const passed = flags.docker ? runTargetsInDocker(targets, targets, flags) : await runTargets(targets, flags);
2515
2568
  if (!passed) process.exitCode = 1;
2516
2569
  }
2517
- async function cmdDev(flags) {
2570
+ async function cmdDev(args, flags) {
2518
2571
  const root = process.cwd();
2519
- let targets = targetsOrFail(flags);
2572
+ const picked = args[0] ?? flags.module;
2573
+ let targets = targetsOrFail(args, flags);
2574
+ const inSandbox = flags.local !== "true";
2575
+ if (inSandbox) prepareSandbox(flags);
2520
2576
  const header = () => {
2521
2577
  process.stdout.write("\x1Bc");
2522
- ok(`visualautomate dev \u2014 ${(/* @__PURE__ */ new Date()).toLocaleTimeString()} \u2014 Ctrl+C to stop`);
2578
+ ok(`vsa dev \u2014 ${(/* @__PURE__ */ new Date()).toLocaleTimeString()} \u2014 Ctrl+C to stop`);
2523
2579
  };
2524
- const run = async (chosen) => flags.docker ? runTargetsInDocker(chosen, targets, flags) : runTargets(chosen, flags);
2580
+ const run = async (chosen) => inSandbox ? runTargetsInDocker(chosen, targets, flags) : runTargets(chosen, flags);
2525
2581
  header();
2526
- if (flags.docker) ok(`in ${flags.image || `${IMAGE}:${VERSION}`}, tier ${flags.tier || "standard"}`);
2582
+ ok(inSandbox ? `in ${imageFor(flags)}, tier ${flags.tier || "standard"}` : "on this machine, not in the sandbox");
2583
+ sayWhatRuns(targets, Boolean(picked));
2527
2584
  await run(targets);
2528
2585
  const ignored = [`${sep2}node_modules${sep2}`, `${sep2}.git${sep2}`, `${sep2}dist${sep2}`, `${sep2}.visualautomate${sep2}`];
2529
2586
  const pending = /* @__PURE__ */ new Set();
@@ -2538,7 +2595,7 @@ async function cmdDev(flags) {
2538
2595
  const changed = [...pending];
2539
2596
  pending.clear();
2540
2597
  try {
2541
- targets = findTargets(root, flags.module);
2598
+ targets = findTargets(root, picked);
2542
2599
  } catch (error) {
2543
2600
  header();
2544
2601
  ok(`\u2717 ${error instanceof Error ? error.message : String(error)}`);
@@ -2656,29 +2713,33 @@ ${text.slice(0, 500)}`);
2656
2713
  }
2657
2714
  function printHelp() {
2658
2715
  console.log(`
2659
- visualautomate \u2014 build and publish plugins
2716
+ vsa \u2014 build and publish VisualAutomate plugins
2717
+
2718
+ vsa login sign this machine in
2719
+ vsa init <name> [--template] start a plugin
2720
+ vsa dev [<module>] the sandbox, and a run again on every save
2721
+ vsa test [<module>] run it once, as the platform's check does
2722
+ vsa push publish it
2723
+ vsa whoami which account this is
2724
+ vsa logout forget the token
2725
+ vsa --version this CLI's version
2660
2726
 
2661
- visualautomate login sign this machine in
2662
- visualautomate init <name> [--template] start a plugin
2663
- visualautomate test [--module <name>] run it here, as the platform's check does
2664
- visualautomate dev [--module <name>] run it again on every save
2665
- visualautomate --version this CLI's version
2666
- visualautomate push publish it
2667
- visualautomate whoami which account this is
2668
- visualautomate logout forget the token
2727
+ \`va\` and \`visualautomate\` are the same command.
2669
2728
 
2670
2729
  Templates: ${Object.keys(TEMPLATES).join(", ")}
2671
2730
 
2672
2731
  \`test\` and \`dev\` work in a plugin made with \`init\`, at the root of a plugin's
2673
2732
  GitHub repository, and at the root of an app's repository, where every folder
2674
- in modules/ is run (or the one named with --module). The run's settings come
2733
+ in modules/ is run (or the one you name: vsa dev send-message). The run's settings come
2675
2734
  from visualautomate.test.json, test.json, or --input <file>.
2676
2735
 
2677
- Options for \`test\` and \`dev\`:
2678
- --docker run inside the plugin sandbox image (Docker must be running)
2679
- --tier <name> with --docker: standard, boosted, high or max
2680
- --image <ref> with --docker: a different image
2736
+ Options for \`dev\` and \`test\`:
2737
+ --module <name> the same as naming it: vsa dev send-message
2738
+ --tier <name> the sandbox's size: standard, boosted, high or max
2739
+ --image <ref> a different sandbox image
2681
2740
  --persist keep storage and state in .visualautomate/ between runs
2741
+ --local \`dev\` without the sandbox, on this machine's Node
2742
+ --docker \`test\` inside the sandbox, as \`dev\` does
2682
2743
 
2683
2744
  Environment (for CI, where there is no browser):
2684
2745
  VISUALAUTOMATE_API_TOKEN use instead of \`login\`
@@ -2718,17 +2779,17 @@ async function main() {
2718
2779
  case "new":
2719
2780
  return cmdInit(args, flags);
2720
2781
  case "test":
2721
- return cmdTest(flags);
2782
+ return cmdTest(args, flags);
2722
2783
  case "dev":
2723
2784
  case "watch":
2724
- return cmdDev(flags);
2785
+ return cmdDev(args, flags);
2725
2786
  case "push":
2726
2787
  case "publish":
2727
2788
  return cmdPush(flags);
2728
2789
  // `plugin test` and `plugin push` are accepted as aliases.
2729
2790
  case "plugin": {
2730
2791
  const sub = args.shift();
2731
- if (sub === "test") return cmdTest(flags);
2792
+ if (sub === "test") return cmdTest(args, flags);
2732
2793
  if (sub === "push") return cmdPush(flags);
2733
2794
  printHelp();
2734
2795
  return;
package/package.json CHANGED
@@ -1,12 +1,13 @@
1
1
  {
2
2
  "name": "@visualautomate/cli",
3
- "version": "0.4.1",
3
+ "version": "0.5.1",
4
4
  "description": "Build and publish VisualAutomate plugins.",
5
5
  "license": "MIT",
6
6
  "type": "module",
7
7
  "bin": {
8
- "visualautomate": "./dist/index.js",
9
- "va": "./dist/index.js"
8
+ "visualautomate": "dist/index.js",
9
+ "va": "dist/index.js",
10
+ "vsa": "dist/index.js"
10
11
  },
11
12
  "files": [
12
13
  "dist",