@visualautomate/cli 0.6.1 → 0.8.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/README.md +292 -243
- package/dist/index.js +1716 -516
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -1,247 +1,296 @@
|
|
|
1
|
-
# @visualautomate/cli
|
|
2
|
-
|
|
3
|
-
Build and test VisualAutomate plugins.
|
|
4
|
-
|
|
5
|
-
```bash
|
|
6
|
-
npm install -g @visualautomate/cli
|
|
7
|
-
vsa init my-step
|
|
8
|
-
cd my-step
|
|
9
|
-
vsa dev # the sandbox, and a run again on every save
|
|
10
|
-
```
|
|
11
|
-
|
|
12
|
-
Nothing here signs in. Your plugin reaches the platform through its GitHub
|
|
13
|
-
repository: push a commit, the tests run there, and Publish on the portal takes
|
|
14
|
-
it from the commit that passed.
|
|
15
|
-
|
|
16
|
-
`va` and `visualautomate` are the same command.
|
|
17
|
-
|
|
18
|
-
That is the whole toolchain. There is nothing else to install: the CLI carries
|
|
19
|
-
the SDK inside it, compiles your TypeScript itself, and writes the type
|
|
20
|
-
declarations into the project. A plugin directory has no `node_modules`, no
|
|
21
|
-
build step and no dependencies.
|
|
22
|
-
|
|
23
|
-
## What `init` gives you
|
|
24
|
-
|
|
25
|
-
```
|
|
26
|
-
plugin.ts your code
|
|
27
|
-
manifest.json what the canvas shows, with the rest of the options
|
|
28
|
-
written out as commented examples
|
|
29
|
-
plugin.json name, version, description, category
|
|
30
|
-
test.json the input, config, storage and state `test` runs with
|
|
31
|
-
visualautomate.d.ts the types, written in rather than installed
|
|
32
|
-
tsconfig.json so your editor type-checks it
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
vsa
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
1
|
+
# @visualautomate/cli
|
|
2
|
+
|
|
3
|
+
Build and test VisualAutomate plugins.
|
|
4
|
+
|
|
5
|
+
```bash
|
|
6
|
+
npm install -g @visualautomate/cli
|
|
7
|
+
vsa init my-step
|
|
8
|
+
cd my-step
|
|
9
|
+
vsa dev # the sandbox, and a run again on every save
|
|
10
|
+
```
|
|
11
|
+
|
|
12
|
+
Nothing here signs in. Your plugin reaches the platform through its GitHub
|
|
13
|
+
repository: push a commit, the tests run there, and Publish on the portal takes
|
|
14
|
+
it from the commit that passed.
|
|
15
|
+
|
|
16
|
+
`va` and `visualautomate` are the same command.
|
|
17
|
+
|
|
18
|
+
That is the whole toolchain. There is nothing else to install: the CLI carries
|
|
19
|
+
the SDK inside it, compiles your TypeScript itself, and writes the type
|
|
20
|
+
declarations into the project. A plugin directory has no `node_modules`, no
|
|
21
|
+
build step and no dependencies.
|
|
22
|
+
|
|
23
|
+
## What `init` gives you
|
|
24
|
+
|
|
25
|
+
```
|
|
26
|
+
plugin.ts your code
|
|
27
|
+
manifest.json what the canvas shows, with the rest of the options
|
|
28
|
+
written out as commented examples
|
|
29
|
+
plugin.json name, version, description, category
|
|
30
|
+
test.json the input, config, storage and state `test` runs with
|
|
31
|
+
visualautomate.d.ts the types, written in rather than installed
|
|
32
|
+
tsconfig.json so your editor type-checks it
|
|
33
|
+
AGENTS.md, CLAUDE.md the platform's rules, for an AI assistant working here
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
Templates: `blank`, `http-request`, `file`. Pass one with
|
|
37
|
+
`--template`. Each runs unmodified.
|
|
38
|
+
|
|
39
|
+
## Types in your editor
|
|
40
|
+
|
|
41
|
+
Your plugin's code imports nothing — the platform refuses the word `import`
|
|
42
|
+
anywhere in the file, comments included, and the SDK is not a package a step may
|
|
43
|
+
require. So the types are written into your project instead, and one line names
|
|
44
|
+
them:
|
|
45
|
+
|
|
46
|
+
```js
|
|
47
|
+
/** @type {PluginModule} */
|
|
48
|
+
module.exports = {
|
|
49
|
+
execute: async (input, config, context) => {
|
|
50
|
+
// ^ completes, with the documentation
|
|
51
|
+
},
|
|
52
|
+
}
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
`dev` and `test` write `visualautomate.d.ts` and a `jsconfig.json` beside your
|
|
56
|
+
code and keep them current; `vsa types` does it on its own. In an app's
|
|
57
|
+
repository they go at the root, where they cover every module. Nothing is
|
|
58
|
+
installed, nothing of it exists while your plugin runs, and a file you wrote
|
|
59
|
+
yourself is never written over — delete ours and it comes back.
|
|
60
|
+
|
|
61
|
+
## `skill`
|
|
62
|
+
|
|
63
|
+
The guide an AI assistant reads to build steps for you: describe a step in a sentence ("a step that
|
|
64
|
+
creates an invoice in Moneybird") and the assistant writes the manifest, the code and the test file,
|
|
65
|
+
runs `vsa test`, and tells you how to publish it.
|
|
66
|
+
|
|
67
|
+
```bash
|
|
68
|
+
vsa skill # .claude/skills/visualautomate-steps in this project, plus AGENTS.md and CLAUDE.md
|
|
69
|
+
vsa skill --global # ~/.claude/skills/visualautomate-steps, for every project
|
|
70
|
+
vsa skill --dir out # the skill's folder anywhere, e.g. to zip and upload to Claude on the web
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
Claude Code finds the skill on its own. `AGENTS.md` is read by Cursor, Copilot, Codex and others;
|
|
74
|
+
`CLAUDE.md` is one line that pulls it in for Claude Code. Both carry the platform's rules: no `import`,
|
|
75
|
+
only allowed packages, never a credential, `{ output, data }`. `vsa init` writes them into every new
|
|
76
|
+
project as well.
|
|
77
|
+
|
|
78
|
+
Running it again brings the files up to date with this CLI. An `AGENTS.md` or `CLAUDE.md` you wrote
|
|
79
|
+
yourself is never written over: ours starts with a marker line, and a file without it is left alone.
|
|
80
|
+
|
|
81
|
+
## `add:property`
|
|
82
|
+
|
|
83
|
+
A field on the node — what somebody fills in when they drop your module into a
|
|
84
|
+
workflow — written into `manifest.json`:
|
|
85
|
+
|
|
86
|
+
```bash
|
|
87
|
+
vsa add:property string apiKey "The key to call with"
|
|
88
|
+
vsa add:property number retries "How many times to try again" 2 --required
|
|
89
|
+
vsa add:property select mode "Which way round" fast --options "Fast:fast,Thorough:thorough"
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
`<type> <name> [description] [default]`, where the name is what your code reads:
|
|
93
|
+
`config.apiKey`. The label is made from the name unless `--label` says otherwise,
|
|
94
|
+
and the default is stored as its type has it, so `2` is a number and `false` is a
|
|
95
|
+
boolean.
|
|
96
|
+
|
|
97
|
+
Types: `string`, `number`, `boolean`, `select`, `multiselect`, `json`, `code`,
|
|
98
|
+
`secret`, `color`, `date`, `file`, `path`, `paths`. A `select` or `multiselect` needs
|
|
99
|
+
`--options`. There is no `connection`: only VisualAutomate's own apps act as a connected account.
|
|
100
|
+
|
|
80
101
|
The same field is added to `visualautomate.test.json` under `config`, with its
|
|
81
102
|
default or a value of the right shape, so the next `vsa test` passes something
|
|
82
103
|
for it instead of nothing. A value you have already put there is kept.
|
|
83
104
|
|
|
84
|
-
At the root of an app's repository, `--module <name>` says which one — the same
|
|
85
|
-
short names `test` and `dev` take. The manifest is edited where it stands: your
|
|
86
|
-
comments, the examples underneath and your own formatting stay as they are.
|
|
87
|
-
|
|
88
|
-
## `test`
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
```
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
`
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
105
|
+
At the root of an app's repository, `--module <name>` says which one — the same
|
|
106
|
+
short names `test` and `dev` take. The manifest is edited where it stands: your
|
|
107
|
+
comments, the examples underneath and your own formatting stay as they are.
|
|
108
|
+
|
|
109
|
+
## `test`
|
|
110
|
+
|
|
111
|
+
The developer portal's check on a push, run here. In order:
|
|
112
|
+
|
|
113
|
+
0. **The manifest.** The rules the portal is known to refuse, and warnings for
|
|
114
|
+
what it would drop: an `auth` that names a host, a `baseUrl` that is not
|
|
115
|
+
`https://`, a request path that is a full URL. And anything that would act as
|
|
116
|
+
a connected account -- a `connection` property, `optionsFrom`,
|
|
117
|
+
`action.connection`, a `trigger`, `auth.provider` -- which only VisualAutomate's
|
|
118
|
+
own apps may do. A declared step (an `action` block) stops here, checked: the
|
|
119
|
+
platform does not run it on a push either. A declared step is published from the portal, not GitHub: New
|
|
120
|
+
module → Call an API, paste its manifest.json, Test the call, publish.
|
|
121
|
+
1. **The platform's code checks.** The same rules the upload applies: only
|
|
122
|
+
allowed packages in `require`, no dynamic `require`, none of the refused
|
|
123
|
+
patterns (`eval`, `process.env`, `import`, …). Code that fails here would be
|
|
124
|
+
refused on push, and says why.
|
|
125
|
+
2. **The sandbox's `require`.** A step gets the allowed npm packages and nothing
|
|
126
|
+
else — no `fs`, no `http`. Those packages are installed in the sandbox; here
|
|
127
|
+
they come from your project, and a missing one prints the `npm install`
|
|
128
|
+
command for the version the sandbox has.
|
|
129
|
+
3. **The runner's way of loading code.** In a `vm` context of its own, with the
|
|
130
|
+
platform's globals and no others: `fetch`, `btoa`, `TextEncoder`, `Blob`,
|
|
131
|
+
`FormData` and the rest, but no `Buffer`, `process`, `globalThis` or
|
|
132
|
+
`structuredClone`, and a `console` with `log`, `error`, `warn` and `info`. A
|
|
133
|
+
step that leans on something Node has and the platform does not fails here.
|
|
134
|
+
4. **A context that behaves.** `storage` and `state` are real, in memory, with
|
|
135
|
+
the store's limits (a value at most 128 KB, `ttlSeconds` at least 60), and
|
|
136
|
+
the result shows what the step left behind:
|
|
137
|
+
|
|
138
|
+
```
|
|
139
|
+
✓ passed success, 3ms
|
|
140
|
+
output success (expected success, the default)
|
|
141
|
+
data { "path": "notes/hello-shouted.txt", "size": 17 }
|
|
142
|
+
files notes/hello.txt, notes/hello-shouted.txt
|
|
143
|
+
state {"runs":1}
|
|
144
|
+
```
|
|
145
|
+
|
|
146
|
+
A run passes when it succeeds and leaves by the test file's `expectOutput`, or by
|
|
147
|
+
`success` when it names none, within manifest.json's `timeoutMs` (30 seconds when
|
|
148
|
+
it names none): the same verdict as the GitHub check.
|
|
149
|
+
|
|
150
|
+
**Publishing needs a run on success.** The portal publishes only code whose push
|
|
151
|
+
test came back on `success`. A test that expects `error` passes, and `vsa test`
|
|
152
|
+
says it does not count. For a step that acts as an account, name one of your
|
|
153
|
+
connected accounts in the test file's `config`, under the property's name: the
|
|
154
|
+
push check then acts as it. The module's page on the portal lists the ids.
|
|
155
|
+
|
|
156
|
+
`vsa test` runs on this machine's Node and says so when that is not the
|
|
157
|
+
platform's (Node 24); `vsa test --docker` and `vsa dev` run on the platform's.
|
|
158
|
+
|
|
159
|
+
The verdict is green when it passed and red when it did not, in the sandbox as
|
|
160
|
+
well as out here. `NO_COLOR` turns that off, and output that is not a terminal —
|
|
161
|
+
a pipe, a file, a CI log — is plain anyway; `FORCE_COLOR` turns it back on.
|
|
162
|
+
|
|
163
|
+
### In a GitHub repository
|
|
164
|
+
|
|
165
|
+
`test` works wherever the platform tests your code on a push:
|
|
166
|
+
|
|
167
|
+
```
|
|
168
|
+
my-plugin/ a plugin's repository
|
|
169
|
+
manifest.json
|
|
170
|
+
index.js
|
|
171
|
+
visualautomate.test.json
|
|
172
|
+
|
|
173
|
+
my-app/ an app's repository
|
|
174
|
+
modules/
|
|
175
|
+
send-message/
|
|
176
|
+
manifest.json
|
|
177
|
+
index.js
|
|
178
|
+
visualautomate.test.json
|
|
179
|
+
list-channels/
|
|
180
|
+
…
|
|
181
|
+
```
|
|
182
|
+
|
|
183
|
+
At the root of an app's repository every module runs, one after another. Name one
|
|
184
|
+
to run only that: `vsa test send-message`, or any short name that picks it out —
|
|
185
|
+
`vsa test send`. `--module <name>` is the same thing. `index.js` in a repository is run as written,
|
|
186
|
+
unbundled, because that is the file the sandbox loads.
|
|
187
|
+
|
|
188
|
+
## `dev`
|
|
189
|
+
|
|
190
|
+
```bash
|
|
191
|
+
vsa dev # every module in modules/
|
|
192
|
+
vsa dev send-message # one of them; the shortest name that picks it will do
|
|
193
|
+
vsa dev --tier boosted # a bigger machine
|
|
194
|
+
vsa dev --local # on this machine's Node, without the sandbox
|
|
195
|
+
```
|
|
196
|
+
|
|
197
|
+
Everything in one command: it starts Docker if it is not running, fetches the
|
|
198
|
+
sandbox image the first time, runs every module, and runs them again on every
|
|
199
|
+
save. Only the module whose folder changed runs again; a change anywhere else
|
|
200
|
+
runs them all. Ctrl+C stops it.
|
|
201
|
+
|
|
202
|
+
`context.media` is **not** simulated: it is an ffmpeg container, and pretending
|
|
203
|
+
to be one would produce a plugin that passes here and fails on a canvas. It fails
|
|
204
|
+
with a sentence saying so. There is no `context.connections` for your steps: only
|
|
205
|
+
VisualAutomate's own apps act as a connected account.
|
|
206
|
+
|
|
207
|
+
`visualautomate.test.json` (or `test.json`, or `--input <file>`) seeds the run:
|
|
208
|
+
|
|
209
|
+
```json
|
|
210
|
+
{
|
|
211
|
+
"config": { "source": "notes/hello.txt" },
|
|
212
|
+
"input": {},
|
|
213
|
+
"storage": { "notes/hello.txt": "hello from a file" },
|
|
214
|
+
"state": { "runs": 0 },
|
|
215
|
+
"expectOutput": "success",
|
|
216
|
+
"allowedDomains": ["api.example.com"]
|
|
217
|
+
}
|
|
218
|
+
```
|
|
219
|
+
|
|
220
|
+
The platform's push check reads `config`, `input` and `expectOutput`; `storage`,
|
|
221
|
+
`state`, `secrets` and `allowedDomains` are for running locally. The time limit
|
|
222
|
+
is manifest.json's `timeoutMs`, not the test file's: `vsa test` warns about a
|
|
223
|
+
`timeoutMs` here, because the push check ignores it. A file that is not valid JSON stops the run rather than running with
|
|
224
|
+
nothing.
|
|
225
|
+
|
|
226
|
+
## Where `fetch` may go
|
|
227
|
+
|
|
228
|
+
On the platform a step may contact the hosts allowed for its plugin, which are
|
|
229
|
+
set on the platform rather than declared in the manifest, plus any host in the
|
|
230
|
+
configuration filled in for that step. A declared domain covers its
|
|
231
|
+
subdomains; localhost, private networks and metadata addresses are never
|
|
232
|
+
allowed.
|
|
233
|
+
|
|
234
|
+
Put the domains in `allowedDomains` and `test` applies the same rule: a request
|
|
235
|
+
anywhere else fails. Without `allowedDomains` nothing is blocked, but every host
|
|
236
|
+
the platform would refuse is listed after the run.
|
|
237
|
+
|
|
238
|
+
## `--persist`
|
|
239
|
+
|
|
240
|
+
Keeps storage and state between runs, in `.visualautomate/` next to the plugin:
|
|
241
|
+
|
|
242
|
+
```
|
|
243
|
+
.visualautomate/storage/<path> files the step wrote
|
|
244
|
+
.visualautomate/state.json the workflow state
|
|
245
|
+
```
|
|
246
|
+
|
|
247
|
+
Once that folder exists it is used without the flag. Delete it to start over,
|
|
248
|
+
and add it to `.gitignore`.
|
|
249
|
+
|
|
250
|
+
## The sandbox on your machine
|
|
251
|
+
|
|
252
|
+
`dev` uses it by default; `test` does with `--docker`:
|
|
253
|
+
|
|
254
|
+
```bash
|
|
255
|
+
vsa dev
|
|
256
|
+
vsa test --docker
|
|
257
|
+
vsa dev --tier boosted
|
|
258
|
+
```
|
|
259
|
+
|
|
260
|
+
Your plugin runs inside the plugin sandbox image,
|
|
261
|
+
`ghcr.io/visualautomate/plugin-sandbox`, at the version of this CLI:
|
|
262
|
+
|
|
263
|
+
- Node 24, the version the platform runs plugins on, with every allowed npm
|
|
264
|
+
package installed at the platform's versions — no `npm install` needed;
|
|
265
|
+
- the CPU and memory of the platform's machine: `standard` (½ vCPU, 4 GB, the
|
|
266
|
+
default), `boosted` (1 vCPU, 6 GB) or `high` (2 vCPU, 8 GB). `max` is no longer
|
|
267
|
+
offered and runs as `high`, as on the platform;
|
|
268
|
+
- no Linux capabilities, a read-only filesystem apart from your project and a
|
|
269
|
+
small `/tmp`, and nothing else from your machine.
|
|
270
|
+
|
|
271
|
+
A step that runs out of memory or time here does the same on the platform.
|
|
272
|
+
|
|
273
|
+
Docker does not have to be running: `dev` starts Docker Desktop and waits for it,
|
|
274
|
+
then fetches the image if this machine does not have it. `--image` uses a
|
|
275
|
+
different image, for example one you built yourself. The watcher stays on your
|
|
276
|
+
machine, and each run starts a fresh container.
|
|
277
|
+
|
|
278
|
+
`context.media` is not available locally, in or out of Docker.
|
|
279
|
+
|
|
280
|
+
## Publishing
|
|
281
|
+
|
|
282
|
+
Not from here. Connect the repository to your app on the portal, push your
|
|
283
|
+
commit, and the platform runs the same checks `vsa test` runs against every
|
|
284
|
+
module it touched. Publish on the portal takes the code from a commit whose test
|
|
285
|
+
came back on `success`, and a version snapshot is saved so you can roll back. A
|
|
286
|
+
declared step is published once Test the call in the portal's editor has passed
|
|
287
|
+
on its saved version.
|
|
288
|
+
|
|
289
|
+
Comments in `manifest.json` are a convenience of this tool: the file on your
|
|
290
|
+
disk may have them, and they are stripped before the manifest is read.
|
|
291
|
+
|
|
292
|
+
## Where things are
|
|
293
|
+
|
|
294
|
+
The runtime API your plugin is written against is documented in the types
|
|
295
|
+
(`visualautomate.d.ts` in your project, `@visualautomate/plugin-sdk` if you
|
|
296
|
+
prefer to install it) and on the platform's own docs page.
|