@exedev/sdk 0.1.0 → 0.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.
@@ -1,10 +1,26 @@
1
1
  // Code generated by "go run ./cmd/gensdkjs" in the exe.dev repository. DO NOT EDIT.
2
- //
3
- // Every public exe.dev command that runs over the HTTPS API (POST /exec), as a
4
- // method of Commands, documented with the command's own help text. Positional
5
- // arguments are parameters; flags are keys of the options object, spelled as
6
- // on the command line.
7
- export class Commands {
2
+ import { Transport, ExeAPIError, ExeProtocolError, SSHStreamError, abortable, delay, isErrorDocument } from "./client.js";
3
+ export class ExedevClient {
4
+ #transport;
5
+ constructor(options) { this.#transport = new Transport(options); }
6
+ /** Reference an existing VM without a request. Use refresh() to fetch its metadata. */
7
+ vm(name) {
8
+ return new VM(this.#transport, { name: vmName(name), tags: [] });
9
+ }
10
+ /**
11
+ * Delete VMs in one request; the server deletes several at a time. Unlike
12
+ * vm.rm(), VMs that were missing or could not be deleted do not throw: they
13
+ * are listed in failed, with reasons in errors. Requires rm permission.
14
+ */
15
+ async rm(vms, options) {
16
+ // A string is iterable: rm("abc") must not delete a, b, and c.
17
+ if (!Array.isArray(vms))
18
+ throw new TypeError("exedev: rm takes an array of VMs or VM names");
19
+ const names = [...new Set(vms.map(vmName))];
20
+ if (names.length === 0)
21
+ return { deleted: [], failed: [], not_found: [], errors: {} };
22
+ return rmResult(await this.#transport.documents("rm", [names], options, {}));
23
+ }
8
24
  /**
9
25
  * Browse documentation
10
26
  *
@@ -12,8 +28,9 @@ export class Commands {
12
28
  *
13
29
  * Tag-scoped API keys may run this command.
14
30
  */
15
- doc(slugOrQueries, options) {
16
- return this.run("doc", [slugOrQueries], options, {});
31
+ async doc(slugOrQueries, options) {
32
+ const result = await this.#transport.call("doc", [slugOrQueries], options, {}, false);
33
+ return result;
17
34
  }
18
35
  /**
19
36
  * List your VMs
@@ -22,11 +39,12 @@ export class Commands {
22
39
  *
23
40
  * Tag-scoped API keys may run this command.
24
41
  */
25
- ls(nameOrPattern, options) {
26
- return this.run("ls", [nameOrPattern], options, {
42
+ async ls(nameOrPattern, options) {
43
+ const result = await this.#transport.call("ls", [nameOrPattern], options, {
27
44
  group: "string",
28
45
  l: "bool",
29
- });
46
+ }, true);
47
+ return listVMs(this.#transport, result);
30
48
  }
31
49
  /**
32
50
  * Create a new VM
@@ -45,8 +63,10 @@ export class Commands {
45
63
  *
46
64
  * Tag-scoped API keys may run this command.
47
65
  */
48
- new(options) {
49
- return this.run("new", [], options, {
66
+ async new(options) {
67
+ const ready = readiness(options ?? {});
68
+ const { waitUntil, timeoutMs, ...flags } = options ?? {};
69
+ const result = await this.#transport.call("new", [], flags, {
50
70
  comment: "string",
51
71
  cpu: "number",
52
72
  disk: "string",
@@ -64,297 +84,22 @@ export class Commands {
64
84
  "setup-script": "string",
65
85
  standalone: "bool",
66
86
  tag: "repeated",
67
- });
68
- }
69
- /**
70
- * Delete a VM
71
- *
72
- * Usage: `rm <vmname>...`
73
- *
74
- * Tag-scoped API keys may run this command.
75
- */
76
- rm(vmnames, options) {
77
- return this.run("rm", [vmnames], options, {});
78
- }
79
- /**
80
- * Restart a VM
81
- *
82
- * Usage: `restart <vmname>`
83
- *
84
- * Tag-scoped API keys may run this command.
85
- */
86
- restart(vmname, options) {
87
- return this.run("restart", [vmname], options, {});
88
- }
89
- /**
90
- * Pause a VM (snapshot RAM to disk, free its CPU and memory)
91
- *
92
- * Usage: `pause <vmname>`
93
- *
94
- * CLI examples:
95
- * ```
96
- * pause my-vm # snapshot RAM to disk; resume later with 'resume my-vm'
97
- * ```
98
- *
99
- * Tag-scoped API keys may run this command.
100
- */
101
- pause(vmname, options) {
102
- return this.run("pause", [vmname], options, {});
103
- }
104
- /**
105
- * Resume a paused VM (restore RAM from disk)
106
- *
107
- * Usage: `resume <vmname>`
108
- *
109
- * Tag-scoped API keys may run this command.
110
- */
111
- resume(vmname, options) {
112
- return this.run("resume", [vmname], options, {});
113
- }
114
- /**
115
- * rename a vm
116
- *
117
- * Usage: `rename <oldname> <newname>`
118
- *
119
- * Tag-scoped API keys may run this command.
120
- */
121
- rename(oldname, newname, options) {
122
- return this.run("rename", [oldname, newname], options, {});
123
- }
124
- /**
125
- * Add or remove tags on a VM
126
- *
127
- * Usage: `tag [-d] <vm> <tag-name> [tag-name...]`
128
- *
129
- * CLI examples:
130
- * ```
131
- * tag my-vm prod web # add tags
132
- * tag -d my-vm prod web # remove tags
133
- * ```
134
- *
135
- * Tag-scoped API keys may run this command.
136
- */
137
- tag(vm, tagNames, options) {
138
- return this.run("tag", [vm, tagNames], options, {
139
- d: "bool",
140
- });
141
- }
142
- /**
143
- * Copy an existing VM
144
- *
145
- * Usage: `cp <source-vm> [new-name] [--pool=<name>] [--memory=<size>] [--cpu=<count>] [--disk=<size>]`
146
- *
147
- * CLI examples:
148
- * ```
149
- * cp my-vm # copy with auto-generated name
150
- * cp my-vm my-vm-copy # copy with specific name
151
- * cp my-vm --cpu=4 --memory=16GB # copy with different resources
152
- * cp my-vm my-pool-copy --pool=build # copy into a team pool
153
- * cp my-vm --copy-tags=false # copy without tags
154
- * ```
155
- *
156
- * Tag-scoped API keys may run this command.
157
- */
158
- cp(sourceVm, newName, options) {
159
- return this.run("cp", [sourceVm, newName], options, {
160
- "copy-tags": "bool",
161
- cpu: "number",
162
- disk: "string",
163
- memory: "string",
164
- pool: "string",
165
- sandbox: "bool",
166
- standalone: "bool",
167
- });
168
- }
169
- /**
170
- * Resize a VM's resources (memory, CPU, disk)
171
- *
172
- * Usage: `resize <vmname> [--memory=<size>] [--cpu=<count>] [--disk=<size>]`
173
- *
174
- * Tag-scoped API keys may run this command.
175
- */
176
- resize(vmname, options) {
177
- return this.run("resize", [vmname], options, {
178
- cpu: "number",
179
- disk: "string",
180
- memory: "string",
181
- });
182
- }
183
- /**
184
- * Set or clear a short comment on a VM
185
- *
186
- * Usage: `comment <hostname> <text>`
187
- *
188
- * CLI examples:
189
- * ```
190
- * comment my-vm staging copy
191
- * comment my-vm "" # clear the comment
192
- * ```
193
- *
194
- * Tag-scoped API keys may run this command.
195
- */
196
- comment(hostname, text, options) {
197
- return this.run("comment", [hostname, text], options, {});
198
- }
199
- /**
200
- * Link a custom domain to a VM
201
- *
202
- * Usage: `domain add [--wildcard] <vm> <domain>`
203
- */
204
- domainAdd(vm, domain, options) {
205
- return this.run("domain add", [vm, domain], options, {
206
- wildcard: "bool",
207
- });
208
- }
209
- /**
210
- * Remove a custom domain from a VM
211
- *
212
- * Usage: `domain rm <vm> <domain>`
213
- */
214
- domainRm(vm, domain, options) {
215
- return this.run("domain rm", [vm, domain], options, {});
87
+ }, true);
88
+ const vm = new VM(this.#transport, vmInfo(result, "new"));
89
+ if (ready)
90
+ await vm.waitUntilReady(ready);
91
+ return vm;
216
92
  }
217
93
  /**
218
94
  * List custom domains for a VM (or -a for all your VMs)
219
95
  *
220
96
  * Usage: `domain ls <vm> | domain ls -a`
221
97
  */
222
- domainLs(args, options) {
223
- return this.run("domain ls", [args], options, {
98
+ async domainLs(args, options) {
99
+ const result = await this.#transport.call("domain ls", [args], options, {
224
100
  a: "bool",
225
- });
226
- }
227
- /**
228
- * Show current shares for a VM
229
- *
230
- * Usage: `share show <vm>`
231
- *
232
- * Tag-scoped API keys may run this command.
233
- */
234
- shareShow(vm, options) {
235
- return this.run("share show", [vm], options, {
236
- qr: "bool",
237
- });
238
- }
239
- /**
240
- * Set the HTTP proxy port for a VM
241
- *
242
- * Usage: `share port <vm> [port]`
243
- *
244
- * CLI examples:
245
- * ```
246
- * share port mybox 8080
247
- * ```
248
- *
249
- * Tag-scoped API keys may run this command.
250
- */
251
- sharePort(vm, port, options) {
252
- return this.run("share port", [vm, port], options, {});
253
- }
254
- /**
255
- * Make the HTTP proxy publicly accessible
256
- *
257
- * Usage: `share set-public <vm>`
258
- *
259
- * Tag-scoped API keys may run this command.
260
- */
261
- shareSetPublic(vm, options) {
262
- return this.run("share set-public", [vm], options, {});
263
- }
264
- /**
265
- * Restrict the HTTP proxy to authenticated users
266
- *
267
- * Usage: `share set-private <vm>`
268
- *
269
- * Tag-scoped API keys may run this command.
270
- */
271
- shareSetPrivate(vm, options) {
272
- return this.run("share set-private", [vm], options, {});
273
- }
274
- /**
275
- * Share VM with a user via email, or grant shell access with --root
276
- *
277
- * Usage: `share add <vm> <email|team> [--root] [--message='...']`
278
- *
279
- * CLI examples:
280
- * ```
281
- * share add mybox user@example.com
282
- * share add mybox user@example.com --message='Check this out'
283
- * share add mybox team
284
- * share add mybox teammate@example.com --root
285
- * share add mybox team --root
286
- * ```
287
- *
288
- * Tag-scoped API keys may run this command.
289
- */
290
- shareAdd(vm, emailOrTeam, options) {
291
- return this.run("share add", [vm, emailOrTeam], options, {
292
- message: "string",
293
- qr: "bool",
294
- root: "bool",
295
- });
296
- }
297
- /**
298
- * Revoke a user's access to a VM, or downgrade shell access to web with --root
299
- *
300
- * Usage: `share remove <vm> <email|team> [--root]`
301
- *
302
- * CLI examples:
303
- * ```
304
- * share remove mybox user@example.com
305
- * share remove mybox team
306
- * share remove mybox teammate@example.com --root
307
- * share remove mybox team --root
308
- * ```
309
- *
310
- * Tag-scoped API keys may run this command.
311
- */
312
- shareRemove(vm, emailOrTeam, options) {
313
- return this.run("share remove", [vm, emailOrTeam], options, {
314
- root: "bool",
315
- });
316
- }
317
- /**
318
- * Create a shareable link for a VM
319
- *
320
- * Usage: `share add-link <vm>`
321
- *
322
- * Tag-scoped API keys may run this command.
323
- */
324
- shareAddLink(vm, options) {
325
- return this.run("share add-link", [vm], options, {
326
- qr: "bool",
327
- });
328
- }
329
- /**
330
- * Revoke a shareable link
331
- *
332
- * Usage: `share remove-link <vm> <token>`
333
- *
334
- * Tag-scoped API keys may run this command.
335
- */
336
- shareRemoveLink(vm, token, options) {
337
- return this.run("share remove-link", [vm, token], options, {});
338
- }
339
- /**
340
- * Enable or disable inbound email for a VM
341
- *
342
- * Usage: `share receive-email <vm> [on|off] [--reply-policy=all|known|owner|none]`
343
- *
344
- * CLI examples:
345
- * ```
346
- * share receive-email mybox on
347
- * share receive-email mybox off
348
- * share receive-email mybox --reply-policy=known
349
- * share receive-email mybox
350
- * ```
351
- *
352
- * Tag-scoped API keys may run this command.
353
- */
354
- shareReceiveEmail(vm, onOrOff, options) {
355
- return this.run("share receive-email", [vm, onOrOff], options, {
356
- "reply-policy": "string",
357
- });
101
+ }, false);
102
+ return result;
358
103
  }
359
104
  /**
360
105
  * Show user information (email, keys, etc)
@@ -363,8 +108,9 @@ export class Commands {
363
108
  *
364
109
  * Tag-scoped API keys may run this command.
365
110
  */
366
- whoami(options) {
367
- return this.run("whoami", [], options, {});
111
+ async whoami(options) {
112
+ const result = await this.#transport.call("whoami", [], options, {}, true);
113
+ return result;
368
114
  }
369
115
  /**
370
116
  * List all SSH keys associated with your account
@@ -373,8 +119,9 @@ export class Commands {
373
119
  *
374
120
  * Tag-scoped API keys may run this command.
375
121
  */
376
- sshKeyList(options) {
377
- return this.run("ssh-key list", [], options, {});
122
+ async sshKeyList(options) {
123
+ const result = await this.#transport.call("ssh-key list", [], options, {}, false);
124
+ return result;
378
125
  }
379
126
  /**
380
127
  * Add a new SSH key to your account
@@ -399,10 +146,11 @@ export class Commands {
399
146
  *
400
147
  * Tag-scoped API keys may run this command.
401
148
  */
402
- sshKeyAdd(publicKey, options) {
403
- return this.run("ssh-key add", [publicKey], options, {
149
+ async sshKeyAdd(publicKey, options) {
150
+ const result = await this.#transport.call("ssh-key add", [publicKey], options, {
404
151
  tag: "string",
405
- });
152
+ }, false);
153
+ return result;
406
154
  }
407
155
  /**
408
156
  * Remove an SSH key from your account
@@ -411,8 +159,9 @@ export class Commands {
411
159
  *
412
160
  * Tag-scoped API keys may run this command.
413
161
  */
414
- sshKeyRemove(nameOrFingerprintOrPublicKey, options) {
415
- return this.run("ssh-key remove", [nameOrFingerprintOrPublicKey], options, {});
162
+ async sshKeyRemove(nameOrFingerprintOrPublicKey, options) {
163
+ const result = await this.#transport.call("ssh-key remove", [nameOrFingerprintOrPublicKey], options, {}, false);
164
+ return result;
416
165
  }
417
166
  /**
418
167
  * Rename an SSH key
@@ -421,8 +170,9 @@ export class Commands {
421
170
  *
422
171
  * Tag-scoped API keys may run this command.
423
172
  */
424
- sshKeyRename(oldName, newName, options) {
425
- return this.run("ssh-key rename", [oldName, newName], options, {});
173
+ async sshKeyRename(oldName, newName, options) {
174
+ const result = await this.#transport.call("ssh-key rename", [oldName, newName], options, {}, false);
175
+ return result;
426
176
  }
427
177
  /**
428
178
  * Generate an API key for the exe.dev HTTPS API or for a specific VM
@@ -440,21 +190,23 @@ export class Commands {
440
190
  *
441
191
  * Tag-scoped API keys may run this command.
442
192
  */
443
- sshKeyGenerateApiKey(options) {
444
- return this.run("ssh-key generate-api-key", [], options, {
193
+ async sshKeyGenerateApiKey(options) {
194
+ const result = await this.#transport.call("ssh-key generate-api-key", [], options, {
445
195
  cmds: "string",
446
196
  exp: "string",
447
197
  label: "string",
448
198
  vm: "string",
449
- });
199
+ }, false);
200
+ return result;
450
201
  }
451
202
  /**
452
203
  * Set your preferred region for new VMs.
453
204
  *
454
205
  * Usage: `set-region <region-code>`
455
206
  */
456
- setRegion(regionCode, options) {
457
- return this.run("set-region", [regionCode], options, {});
207
+ async setRegion(regionCode, options) {
208
+ const result = await this.#transport.call("set-region", [regionCode], options, {}, false);
209
+ return result;
458
210
  }
459
211
  /**
460
212
  * List your integrations
@@ -463,37 +215,40 @@ export class Commands {
463
215
  *
464
216
  * Tag-scoped API keys may run this command.
465
217
  */
466
- integrationsList(options) {
467
- return this.run("integrations list", [], options, {
218
+ async integrationsList(options) {
219
+ const result = await this.#transport.call("integrations list", [], options, {
468
220
  usage: "bool",
469
- });
221
+ }, false);
222
+ return result;
470
223
  }
471
224
  /**
472
225
  * Set up GitHub integration
473
226
  *
474
227
  * Usage: `integrations setup github [--list|--verify|-d]`
475
228
  */
476
- integrationsSetupGithub(options) {
477
- return this.run("integrations setup github", [], options, {
229
+ async integrationsSetupGithub(options) {
230
+ const result = await this.#transport.call("integrations setup github", [], options, {
478
231
  d: "bool",
479
232
  delete: "bool",
480
233
  list: "bool",
481
234
  verify: "bool",
482
- });
235
+ }, false);
236
+ return result;
483
237
  }
484
238
  /**
485
239
  * Set up ChatGPT account access for LLM integrations
486
240
  *
487
241
  * Usage: `integrations setup chatgpt [--name=<account-name>|--list|--verify|-d]`
488
242
  */
489
- integrationsSetupChatgpt(options) {
490
- return this.run("integrations setup chatgpt", [], options, {
243
+ async integrationsSetupChatgpt(options) {
244
+ const result = await this.#transport.call("integrations setup chatgpt", [], options, {
491
245
  d: "bool",
492
246
  delete: "bool",
493
247
  list: "bool",
494
248
  name: "string",
495
249
  verify: "bool",
496
- });
250
+ }, false);
251
+ return result;
497
252
  }
498
253
  /**
499
254
  * Add a new integration
@@ -502,8 +257,8 @@ export class Commands {
502
257
  *
503
258
  * Usage: `integrations add <type> [--default | --name=<name> [--team] [args...]]`
504
259
  */
505
- integrationsAdd(type, options) {
506
- return this.run("integrations add", [type], options, {
260
+ async integrationsAdd(type, options) {
261
+ const result = await this.#transport.call("integrations add", [type], options, {
507
262
  "act-as-user": "bool",
508
263
  attach: "repeated",
509
264
  bearer: "string",
@@ -521,27 +276,30 @@ export class Commands {
521
276
  "strip-prefix": "string",
522
277
  target: "string",
523
278
  team: "bool",
524
- });
279
+ }, false);
280
+ return result;
525
281
  }
526
282
  /**
527
283
  * Remove an integration
528
284
  *
529
285
  * Usage: `integrations remove <name> [--team]`
530
286
  */
531
- integrationsRemove(name, options) {
532
- return this.run("integrations remove", [name], options, {
287
+ async integrationsRemove(name, options) {
288
+ const result = await this.#transport.call("integrations remove", [name], options, {
533
289
  team: "bool",
534
- });
290
+ }, false);
291
+ return result;
535
292
  }
536
293
  /**
537
294
  * Test an integration's credential (connection check)
538
295
  *
539
296
  * Usage: `integrations test <name> [--team]`
540
297
  */
541
- integrationsTest(name, options) {
542
- return this.run("integrations test", [name], options, {
298
+ async integrationsTest(name, options) {
299
+ const result = await this.#transport.call("integrations test", [name], options, {
543
300
  team: "bool",
544
- });
301
+ }, false);
302
+ return result;
545
303
  }
546
304
  /**
547
305
  * Edit an integration
@@ -550,8 +308,8 @@ export class Commands {
550
308
  *
551
309
  * Usage: `integrations edit <name> [--team] [args...]`
552
310
  */
553
- integrationsEdit(name, args, options) {
554
- return this.run("integrations edit", [name, args], options, {
311
+ async integrationsEdit(name, args, options) {
312
+ const result = await this.#transport.call("integrations edit", [name, args], options, {
555
313
  "act-as-user": "bool",
556
314
  bearer: "string",
557
315
  "clear-header": "bool",
@@ -566,7 +324,8 @@ export class Commands {
566
324
  target: "string",
567
325
  team: "bool",
568
326
  "webhook-url": "string",
569
- });
327
+ }, false);
328
+ return result;
570
329
  }
571
330
  /**
572
331
  * Attach an integration to a VM, tag, or all VMs
@@ -594,32 +353,35 @@ export class Commands {
594
353
  * int attach gmail vm:dev1 --for 2h
595
354
  * ```
596
355
  */
597
- integrationsAttach(name, spec, options) {
598
- return this.run("integrations attach", [name, spec], options, {
356
+ async integrationsAttach(name, spec, options) {
357
+ const result = await this.#transport.call("integrations attach", [name, spec], options, {
599
358
  for: "string",
600
359
  team: "bool",
601
360
  until: "string",
602
- });
361
+ }, false);
362
+ return result;
603
363
  }
604
364
  /**
605
365
  * Detach an integration from a VM, tag, or all VMs
606
366
  *
607
367
  * Usage: `integrations detach <name> <spec> [--team]`
608
368
  */
609
- integrationsDetach(name, spec, options) {
610
- return this.run("integrations detach", [name, spec], options, {
369
+ async integrationsDetach(name, spec, options) {
370
+ const result = await this.#transport.call("integrations detach", [name, spec], options, {
611
371
  team: "bool",
612
- });
372
+ }, false);
373
+ return result;
613
374
  }
614
375
  /**
615
376
  * Rename an integration
616
377
  *
617
378
  * Usage: `integrations rename <name> <new-name> [--team]`
618
379
  */
619
- integrationsRename(name, newName, options) {
620
- return this.run("integrations rename", [name, newName], options, {
380
+ async integrationsRename(name, newName, options) {
381
+ const result = await this.#transport.call("integrations rename", [name, newName], options, {
621
382
  team: "bool",
622
- });
383
+ }, false);
384
+ return result;
623
385
  }
624
386
  /**
625
387
  * Browse the catalog of ready-made service integrations
@@ -632,74 +394,83 @@ export class Commands {
632
394
  * int catalog stripe
633
395
  * ```
634
396
  */
635
- integrationsCatalog(serviceOrSearchTerm, options) {
636
- return this.run("integrations catalog", [serviceOrSearchTerm], options, {});
397
+ async integrationsCatalog(serviceOrSearchTerm, options) {
398
+ const result = await this.#transport.call("integrations catalog", [serviceOrSearchTerm], options, {}, false);
399
+ return result;
637
400
  }
638
401
  /**
639
402
  * View and manage your team
640
403
  */
641
- team(options) {
642
- return this.run("team", [], options, {});
404
+ async team(options) {
405
+ const result = await this.#transport.call("team", [], options, {}, false);
406
+ return result;
643
407
  }
644
408
  /**
645
409
  * Disband your team
646
410
  *
647
411
  * Usage: `team disable`
648
412
  */
649
- teamDisable(options) {
650
- return this.run("team disable", [], options, {
413
+ async teamDisable(options) {
414
+ const result = await this.#transport.call("team disable", [], options, {
651
415
  yes: "bool",
652
- });
416
+ }, false);
417
+ return result;
653
418
  }
654
419
  /**
655
420
  * List team members
656
421
  *
657
422
  * Usage: `team members`
658
423
  */
659
- teamMembers(options) {
660
- return this.run("team members", [], options, {});
424
+ async teamMembers(options) {
425
+ const result = await this.#transport.call("team members", [], options, {}, false);
426
+ return result;
661
427
  }
662
428
  /**
663
429
  * Show team pool, disk, and bandwidth usage for the billing period
664
430
  *
665
431
  * Usage: `team usage`
666
432
  */
667
- teamUsage(options) {
668
- return this.run("team usage", [], options, {});
433
+ async teamUsage(options) {
434
+ const result = await this.#transport.call("team usage", [], options, {}, false);
435
+ return result;
669
436
  }
670
437
  /**
671
438
  * Add a user to the team
672
439
  *
673
440
  * Usage: `team add <email> [<user|admin|billing_owner>]`
674
441
  */
675
- teamAdd(email, userOrAdminOrBillingOwner, options) {
676
- return this.run("team add", [email, userOrAdminOrBillingOwner], options, {});
442
+ async teamAdd(email, userOrAdminOrBillingOwner, options) {
443
+ const result = await this.#transport.call("team add", [email, userOrAdminOrBillingOwner], options, {}, false);
444
+ return result;
677
445
  }
678
446
  /**
679
447
  * Remove a user from the team
680
448
  *
681
449
  * Usage: `team remove <email> [--transfer-vms-to <email>]`
682
450
  */
683
- teamRemove(email, options) {
684
- return this.run("team remove", [email], options, {
451
+ async teamRemove(email, options) {
452
+ const result = await this.#transport.call("team remove", [email], options, {
685
453
  "transfer-vms-to": "string",
686
- });
454
+ }, false);
455
+ return result;
687
456
  }
688
457
  /**
689
458
  * Change a team member's role
690
459
  *
691
460
  * Usage: `team role <email> <user|admin|billing_owner>`
692
461
  */
693
- teamRole(email, userOrAdminOrBillingOwner, options) {
694
- return this.run("team role", [email, userOrAdminOrBillingOwner], options, {});
462
+ async teamRole(email, userOrAdminOrBillingOwner, options) {
463
+ const result = await this.#transport.call("team role", [email, userOrAdminOrBillingOwner], options, {}, false);
464
+ return result;
695
465
  }
696
466
  /**
697
467
  * Rename your team
698
468
  *
699
469
  * Usage: `team rename <name>`
700
470
  */
701
- teamRename(name, options) {
702
- return this.run("team rename", [name], options, {});
471
+ async teamRename(name, options) {
472
+ const result = await this.#transport.call("team rename", [name], options, {}, false);
473
+ return result;
703
474
  }
704
475
  /**
705
476
  * Show your team's plan
@@ -712,18 +483,19 @@ export class Commands {
712
483
  * team billing plan --all # plans your team can switch to
713
484
  * ```
714
485
  */
715
- teamBillingPlan(options) {
716
- return this.run("team billing plan", [], options, {
486
+ async teamBillingPlan(options) {
487
+ const result = await this.#transport.call("team billing plan", [], options, {
717
488
  all: "bool",
718
- });
489
+ }, false);
490
+ return result;
719
491
  }
720
492
  /**
721
493
  * Update team billing information
722
494
  *
723
495
  * Usage: `team billing update [--email=<email>] [--name=<name>] [--business-name=<name>] [--phone=<phone>] [--address-line1=<line>] [--address-line2=<line>] [--address-city=<city>] [--address-state=<state>] [--address-postal-code=<code>] [--address-country=<country>] [--tax-id-type=<type>] [--tax-id-value=<value>]`
724
496
  */
725
- teamBillingUpdate(options) {
726
- return this.run("team billing update", [], options, {
497
+ async teamBillingUpdate(options) {
498
+ const result = await this.#transport.call("team billing update", [], options, {
727
499
  "address-city": "string",
728
500
  "address-country": "string",
729
501
  "address-line1": "string",
@@ -736,127 +508,142 @@ export class Commands {
736
508
  phone: "string",
737
509
  "tax-id-type": "string",
738
510
  "tax-id-value": "string",
739
- });
511
+ }, false);
512
+ return result;
740
513
  }
741
514
  /**
742
515
  * Transfer a VM to another team member
743
516
  *
744
517
  * Usage: `team transfer <vm_name> <target_email>`
745
518
  */
746
- teamTransfer(vmName, targetEmail, options) {
747
- return this.run("team transfer", [vmName, targetEmail], options, {});
519
+ async teamTransfer(vmName, targetEmail, options) {
520
+ const result = await this.#transport.call("team transfer", [vmName, targetEmail], options, {}, false);
521
+ return result;
748
522
  }
749
523
  /**
750
524
  * View and manage team auth settings
751
525
  *
752
526
  * Usage: `team auth`
753
527
  */
754
- teamAuth(options) {
755
- return this.run("team auth", [], options, {});
528
+ async teamAuth(options) {
529
+ const result = await this.#transport.call("team auth", [], options, {}, false);
530
+ return result;
756
531
  }
757
532
  /**
758
533
  * Require OIDC for web login (web|off)
759
534
  *
760
535
  * Usage: `team auth require-oidc <web|off>`
761
536
  */
762
- teamAuthRequireOidc(webOrOff, options) {
763
- return this.run("team auth require-oidc", [webOrOff], options, {});
537
+ async teamAuthRequireOidc(webOrOff, options) {
538
+ const result = await this.#transport.call("team auth require-oidc", [webOrOff], options, {}, false);
539
+ return result;
764
540
  }
765
541
  /**
766
542
  * Set the team auth provider (default, google, oidc)
767
543
  *
768
544
  * Usage: `team auth set <default|google|oidc> [--issuer-url=<url> --client-id=<id> --client-secret=<secret>]`
769
545
  */
770
- teamAuthSet(defaultOrGoogleOrOidc, options) {
771
- return this.run("team auth set", [defaultOrGoogleOrOidc], options, {
546
+ async teamAuthSet(defaultOrGoogleOrOidc, options) {
547
+ const result = await this.#transport.call("team auth set", [defaultOrGoogleOrOidc], options, {
772
548
  "client-id": "string",
773
549
  "client-secret": "string",
774
550
  "display-name": "string",
775
551
  "issuer-url": "string",
776
- });
552
+ }, false);
553
+ return result;
777
554
  }
778
555
  /**
779
556
  * View and manage team settings
780
557
  *
781
558
  * Usage: `team settings`
782
559
  */
783
- teamSettings(options) {
784
- return this.run("team settings", [], options, {});
560
+ async teamSettings(options) {
561
+ const result = await this.#transport.call("team settings", [], options, {}, false);
562
+ return result;
785
563
  }
786
564
  /**
787
565
  * View and manage automatic VM placement
788
566
  *
789
567
  * Usage: `team settings vm-placement`
790
568
  */
791
- teamSettingsVmPlacement(options) {
792
- return this.run("team settings vm-placement", [], options, {});
569
+ async teamSettingsVmPlacement(options) {
570
+ const result = await this.#transport.call("team settings vm-placement", [], options, {}, false);
571
+ return result;
793
572
  }
794
573
  /**
795
574
  * Use ordinary VM placement
796
575
  *
797
576
  * Usage: `team settings vm-placement default`
798
577
  */
799
- teamSettingsVmPlacementDefault(options) {
800
- return this.run("team settings vm-placement default", [], options, {});
578
+ async teamSettingsVmPlacementDefault(options) {
579
+ const result = await this.#transport.call("team settings vm-placement default", [], options, {}, false);
580
+ return result;
801
581
  }
802
582
  /**
803
583
  * Send bare new commands to a shared team pool
804
584
  *
805
585
  * Usage: `team settings vm-placement pool <name>`
806
586
  */
807
- teamSettingsVmPlacementPool(name, options) {
808
- return this.run("team settings vm-placement pool", [name], options, {});
587
+ async teamSettingsVmPlacementPool(name, options) {
588
+ const result = await this.#transport.call("team settings vm-placement pool", [name], options, {}, false);
589
+ return result;
809
590
  }
810
591
  /**
811
592
  * Create or reuse one team pool per member for bare new commands
812
593
  *
813
594
  * Usage: `team settings vm-placement member-pool --cpus=N [--max-vms=N]`
814
595
  */
815
- teamSettingsVmPlacementMemberPool(options) {
816
- return this.run("team settings vm-placement member-pool", [], options, {
596
+ async teamSettingsVmPlacementMemberPool(options) {
597
+ const result = await this.#transport.call("team settings vm-placement member-pool", [], options, {
817
598
  cpus: "number",
818
599
  "max-vms": "number",
819
- });
600
+ }, false);
601
+ return result;
820
602
  }
821
603
  /**
822
604
  * Keep bare new commands outside pools while migrating (legacy)
823
605
  *
824
606
  * Usage: `team settings vm-placement poolless`
825
607
  */
826
- teamSettingsVmPlacementPoolless(options) {
827
- return this.run("team settings vm-placement poolless", [], options, {});
608
+ async teamSettingsVmPlacementPoolless(options) {
609
+ const result = await this.#transport.call("team settings vm-placement poolless", [], options, {}, false);
610
+ return result;
828
611
  }
829
612
  /**
830
613
  * Set who can create standalone VMs
831
614
  *
832
615
  * Usage: `team settings standalone <off|admins-only|all-users>`
833
616
  */
834
- teamSettingsStandalone(offOrAdminsOnlyOrAllUsers, options) {
835
- return this.run("team settings standalone", [offOrAdminsOnlyOrAllUsers], options, {});
617
+ async teamSettingsStandalone(offOrAdminsOnlyOrAllUsers, options) {
618
+ const result = await this.#transport.call("team settings standalone", [offOrAdminsOnlyOrAllUsers], options, {}, false);
619
+ return result;
836
620
  }
837
621
  /**
838
622
  * Allow or block the exe.dev LLM gateway (your own API keys are unaffected)
839
623
  *
840
624
  * Usage: `team settings llm-gateway <on|off>`
841
625
  */
842
- teamSettingsLlmGateway(onOrOff, options) {
843
- return this.run("team settings llm-gateway", [onOrOff], options, {});
626
+ async teamSettingsLlmGateway(onOrOff, options) {
627
+ const result = await this.#transport.call("team settings llm-gateway", [onOrOff], options, {}, false);
628
+ return result;
844
629
  }
845
630
  /**
846
631
  * Allow team VMs to share CPU cores with each other
847
632
  *
848
633
  * Usage: `team settings core-sharing <on|off>`
849
634
  */
850
- teamSettingsCoreSharing(onOrOff, options) {
851
- return this.run("team settings core-sharing", [onOrOff], options, {});
635
+ async teamSettingsCoreSharing(onOrOff, options) {
636
+ const result = await this.#transport.call("team settings core-sharing", [onOrOff], options, {}, false);
637
+ return result;
852
638
  }
853
639
  /**
854
640
  * Set who can share team VMs
855
641
  *
856
642
  * Usage: `team settings vm-sharing <admins-only|all-members>`
857
643
  */
858
- teamSettingsVmSharing(adminsOnlyOrAllMembers, options) {
859
- return this.run("team settings vm-sharing", [adminsOnlyOrAllMembers], options, {});
644
+ async teamSettingsVmSharing(adminsOnlyOrAllMembers, options) {
645
+ const result = await this.#transport.call("team settings vm-sharing", [adminsOnlyOrAllMembers], options, {}, false);
646
+ return result;
860
647
  }
861
648
  /**
862
649
  * Set how long members stay signed in on the web and mobile apps
@@ -869,16 +656,18 @@ export class Commands {
869
656
  * team settings session-length default
870
657
  * ```
871
658
  */
872
- teamSettingsSessionLength(length, options) {
873
- return this.run("team settings session-length", [length], options, {});
659
+ async teamSettingsSessionLength(length, options) {
660
+ const result = await this.#transport.call("team settings session-length", [length], options, {}, false);
661
+ return result;
874
662
  }
875
663
  /**
876
664
  * Allow users from your email domain to join this team on signup
877
665
  *
878
666
  * Usage: `team settings auto-join <on|off>`
879
667
  */
880
- teamSettingsAutoJoin(onOrOff, options) {
881
- return this.run("team settings auto-join", [onOrOff], options, {});
668
+ async teamSettingsAutoJoin(onOrOff, options) {
669
+ const result = await this.#transport.call("team settings auto-join", [onOrOff], options, {}, false);
670
+ return result;
882
671
  }
883
672
  /**
884
673
  * List all VMs across your team
@@ -887,42 +676,46 @@ export class Commands {
887
676
  *
888
677
  * Tag-scoped API keys may run this command.
889
678
  */
890
- teamVmLs(nameOrPattern, options) {
891
- return this.run("team vm ls", [nameOrPattern], options, {
679
+ async teamVmLs(nameOrPattern, options) {
680
+ const result = await this.#transport.call("team vm ls", [nameOrPattern], options, {
892
681
  group: "string",
893
682
  l: "bool",
894
- });
683
+ }, false);
684
+ return result;
895
685
  }
896
686
  /**
897
687
  * Create a pool: reserved capacity for your team's VMs
898
688
  *
899
689
  * Usage: `pool new <name> --cpus=N [--region=<region>] [--max-vms=M]`
900
690
  */
901
- poolNew(name, options) {
902
- return this.run("pool new", [name], options, {
691
+ async poolNew(name, options) {
692
+ const result = await this.#transport.call("pool new", [name], options, {
903
693
  cpus: "number",
904
694
  "max-vms": "number",
905
695
  region: "string",
906
- });
696
+ }, false);
697
+ return result;
907
698
  }
908
699
  /**
909
700
  * Show your team's dedicated hosts, where its new pools are placed
910
701
  *
911
702
  * Usage: `pool hosts`
912
703
  */
913
- poolHosts(options) {
914
- return this.run("pool hosts", [], options, {});
704
+ async poolHosts(options) {
705
+ const result = await this.#transport.call("pool hosts", [], options, {}, false);
706
+ return result;
915
707
  }
916
708
  /**
917
709
  * List your team's pools, or show live or historical usage with --usage
918
710
  *
919
711
  * Usage: `pool list [pool-name] [--usage [--range=live|24h|7d|30d]]`
920
712
  */
921
- poolList(poolName, options) {
922
- return this.run("pool list", [poolName], options, {
713
+ async poolList(poolName, options) {
714
+ const result = await this.#transport.call("pool list", [poolName], options, {
923
715
  range: "string",
924
716
  usage: "bool",
925
- });
717
+ }, false);
718
+ return result;
926
719
  }
927
720
  /**
928
721
  * Adopt a VM into a pool; this can take several minutes
@@ -934,11 +727,12 @@ export class Commands {
934
727
  * pool adopt --vm=builder --pool=compile
935
728
  * ```
936
729
  */
937
- poolAdopt(options) {
938
- return this.run("pool adopt", [], options, {
730
+ async poolAdopt(options) {
731
+ const result = await this.#transport.call("pool adopt", [], options, {
939
732
  pool: "string",
940
733
  vm: "string",
941
- });
734
+ }, false);
735
+ return result;
942
736
  }
943
737
  /**
944
738
  * Detach a VM from its pool into its own standalone capacity (--poolless leaves it outside any pool)
@@ -951,39 +745,43 @@ export class Commands {
951
745
  * pool detach --vm=builder --poolless
952
746
  * ```
953
747
  */
954
- poolDetach(options) {
955
- return this.run("pool detach", [], options, {
748
+ async poolDetach(options) {
749
+ const result = await this.#transport.call("pool detach", [], options, {
956
750
  poolless: "bool",
957
751
  vm: "string",
958
- });
752
+ }, false);
753
+ return result;
959
754
  }
960
755
  /**
961
756
  * Change a pool's CPU capacity or VM cap
962
757
  *
963
758
  * Usage: `pool resize <name> --cpus=N | pool resize <name> --max-vms=N [--force]`
964
759
  */
965
- poolResize(args, options) {
966
- return this.run("pool resize", [args], options, {
760
+ async poolResize(args, options) {
761
+ const result = await this.#transport.call("pool resize", [args], options, {
967
762
  cpus: "number",
968
763
  force: "bool",
969
764
  "max-vms": "string",
970
- });
765
+ }, false);
766
+ return result;
971
767
  }
972
768
  /**
973
769
  * Rename a pool without changing its capacity or VMs
974
770
  *
975
771
  * Usage: `pool rename <old-name> <new-name>`
976
772
  */
977
- poolRename(oldName, newName, options) {
978
- return this.run("pool rename", [oldName, newName], options, {});
773
+ async poolRename(oldName, newName, options) {
774
+ const result = await this.#transport.call("pool rename", [oldName, newName], options, {}, false);
775
+ return result;
979
776
  }
980
777
  /**
981
778
  * Delete a pool (refused while it has VMs; --force detaches them)
982
779
  *
983
780
  * Usage: `pool delete <name> [--force]`
984
781
  */
985
- poolDelete(name, options) {
986
- return this.run("pool delete", [name], options, {});
782
+ async poolDelete(name, options) {
783
+ const result = await this.#transport.call("pool delete", [name], options, {}, false);
784
+ return result;
987
785
  }
988
786
  /**
989
787
  * Show your current plan and resource limits
@@ -996,10 +794,11 @@ export class Commands {
996
794
  * billing plan --all # plans you can switch to
997
795
  * ```
998
796
  */
999
- billingPlan(options) {
1000
- return this.run("billing plan", [], options, {
797
+ async billingPlan(options) {
798
+ const result = await this.#transport.call("billing plan", [], options, {
1001
799
  all: "bool",
1002
- });
800
+ }, false);
801
+ return result;
1003
802
  }
1004
803
  /**
1005
804
  * Show VM resource usage against your plan quotas
@@ -1012,19 +811,21 @@ export class Commands {
1012
811
  * billing credits usage # Shelley/LLM credit spend instead
1013
812
  * ```
1014
813
  */
1015
- billingUsage(options) {
1016
- return this.run("billing usage", [], options, {
814
+ async billingUsage(options) {
815
+ const result = await this.#transport.call("billing usage", [], options, {
1017
816
  group: "string",
1018
817
  range: "string",
1019
- });
818
+ }, false);
819
+ return result;
1020
820
  }
1021
821
  /**
1022
822
  * Show Shelley credit balances
1023
823
  *
1024
824
  * Usage: `billing credits [usage|transactions|buy]`
1025
825
  */
1026
- billingCredits(options) {
1027
- return this.run("billing credits", [], options, {});
826
+ async billingCredits(options) {
827
+ const result = await this.#transport.call("billing credits", [], options, {}, false);
828
+ return result;
1028
829
  }
1029
830
  /**
1030
831
  * Show Shelley (LLM) credit spend by model, day, or VM
@@ -1038,22 +839,24 @@ export class Commands {
1038
839
  * billing credits usage --month=2026-05 --group=box
1039
840
  * ```
1040
841
  */
1041
- billingCreditsUsage(options) {
1042
- return this.run("billing credits usage", [], options, {
842
+ async billingCreditsUsage(options) {
843
+ const result = await this.#transport.call("billing credits usage", [], options, {
1043
844
  detail: "bool",
1044
845
  group: "string",
1045
846
  month: "string",
1046
- });
847
+ }, false);
848
+ return result;
1047
849
  }
1048
850
  /**
1049
851
  * Show your credit purchases and gifts
1050
852
  *
1051
853
  * Usage: `billing credits transactions [--limit=N]`
1052
854
  */
1053
- billingCreditsTransactions(options) {
1054
- return this.run("billing credits transactions", [], options, {
855
+ async billingCreditsTransactions(options) {
856
+ const result = await this.#transport.call("billing credits transactions", [], options, {
1055
857
  limit: "string",
1056
- });
858
+ }, false);
859
+ return result;
1057
860
  }
1058
861
  /**
1059
862
  * Buy Shelley credits with your personal payment method
@@ -1066,19 +869,21 @@ export class Commands {
1066
869
  * billing credits buy 100 --yes
1067
870
  * ```
1068
871
  */
1069
- billingCreditsBuy(dollars, options) {
1070
- return this.run("billing credits buy", [dollars], options, {
872
+ async billingCreditsBuy(dollars, options) {
873
+ const result = await this.#transport.call("billing credits buy", [dollars], options, {
1071
874
  "idempotency-key": "string",
1072
875
  yes: "bool",
1073
- });
876
+ }, false);
877
+ return result;
1074
878
  }
1075
879
  /**
1076
880
  * Show invite rewards you've earned
1077
881
  *
1078
882
  * Usage: `billing rewards`
1079
883
  */
1080
- billingRewards(options) {
1081
- return this.run("billing rewards", [], options, {});
884
+ async billingRewards(options) {
885
+ const result = await this.#transport.call("billing rewards", [], options, {}, false);
886
+ return result;
1082
887
  }
1083
888
  /**
1084
889
  * Change your subscription capacity
@@ -1091,27 +896,30 @@ export class Commands {
1091
896
  * billing capacity --cpu=4 --yes
1092
897
  * ```
1093
898
  */
1094
- billingCapacity(options) {
1095
- return this.run("billing capacity", [], options, {
899
+ async billingCapacity(options) {
900
+ const result = await this.#transport.call("billing capacity", [], options, {
1096
901
  cpu: "number",
1097
902
  yes: "bool",
1098
- });
903
+ }, false);
904
+ return result;
1099
905
  }
1100
906
  /**
1101
907
  * Show your payment methods
1102
908
  *
1103
909
  * Usage: `billing payment [list]`
1104
910
  */
1105
- billingPayment(options) {
1106
- return this.run("billing payment", [], options, {});
911
+ async billingPayment(options) {
912
+ const result = await this.#transport.call("billing payment", [], options, {}, false);
913
+ return result;
1107
914
  }
1108
915
  /**
1109
916
  * List all payment methods on file
1110
917
  *
1111
918
  * Usage: `billing payment list`
1112
919
  */
1113
- billingPaymentList(options) {
1114
- return this.run("billing payment list", [], options, {});
920
+ async billingPaymentList(options) {
921
+ const result = await this.#transport.call("billing payment list", [], options, {}, false);
922
+ return result;
1115
923
  }
1116
924
  /**
1117
925
  * Remove a saved payment method
@@ -1123,8 +931,9 @@ export class Commands {
1123
931
  * billing payment remove 4f1c2a9b8d3e
1124
932
  * ```
1125
933
  */
1126
- billingPaymentRemove(ref, options) {
1127
- return this.run("billing payment remove", [ref], options, {});
934
+ async billingPaymentRemove(ref, options) {
935
+ const result = await this.#transport.call("billing payment remove", [ref], options, {}, false);
936
+ return result;
1128
937
  }
1129
938
  /**
1130
939
  * Make a saved payment method the default
@@ -1136,32 +945,35 @@ export class Commands {
1136
945
  * billing payment default 4f1c2a9b8d3e
1137
946
  * ```
1138
947
  */
1139
- billingPaymentDefault(ref, options) {
1140
- return this.run("billing payment default", [ref], options, {});
948
+ async billingPaymentDefault(ref, options) {
949
+ const result = await this.#transport.call("billing payment default", [ref], options, {}, false);
950
+ return result;
1141
951
  }
1142
952
  /**
1143
953
  * Open the billing management page
1144
954
  *
1145
955
  * Usage: `billing manage`
1146
956
  */
1147
- billingManage(options) {
1148
- return this.run("billing manage", [], options, {});
957
+ async billingManage(options) {
958
+ const result = await this.#transport.call("billing manage", [], options, {}, false);
959
+ return result;
1149
960
  }
1150
961
  /**
1151
962
  * Open the personal plan activation page
1152
963
  *
1153
964
  * Usage: `billing activate`
1154
965
  */
1155
- billingActivate(options) {
1156
- return this.run("billing activate", [], options, {});
966
+ async billingActivate(options) {
967
+ const result = await this.#transport.call("billing activate", [], options, {}, false);
968
+ return result;
1157
969
  }
1158
970
  /**
1159
971
  * Update your billing contact information
1160
972
  *
1161
973
  * Usage: `billing update [--email=<email>] [--name=<name>] [--business-name=<name>] [--phone=<phone>] [--address-line1=<line>] [--address-line2=<line>] [--address-city=<city>] [--address-state=<state>] [--address-postal-code=<code>] [--address-country=<country>] [--tax-id-type=<type>] [--tax-id-value=<value>]`
1162
974
  */
1163
- billingUpdate(options) {
1164
- return this.run("billing update", [], options, {
975
+ async billingUpdate(options) {
976
+ const result = await this.#transport.call("billing update", [], options, {
1165
977
  "address-city": "string",
1166
978
  "address-country": "string",
1167
979
  "address-line1": "string",
@@ -1174,15 +986,17 @@ export class Commands {
1174
986
  phone: "string",
1175
987
  "tax-id-type": "string",
1176
988
  "tax-id-value": "string",
1177
- });
989
+ }, false);
990
+ return result;
1178
991
  }
1179
992
  /**
1180
993
  * Show recent invoices
1181
994
  *
1182
995
  * Usage: `billing invoices`
1183
996
  */
1184
- billingInvoices(options) {
1185
- return this.run("billing invoices", [], options, {});
997
+ async billingInvoices(options) {
998
+ const result = await this.#transport.call("billing invoices", [], options, {}, false);
999
+ return result;
1186
1000
  }
1187
1001
  /**
1188
1002
  * List invoices for your personal or team account
@@ -1194,10 +1008,11 @@ export class Commands {
1194
1008
  * billing invoices list --account=team
1195
1009
  * ```
1196
1010
  */
1197
- billingInvoicesList(options) {
1198
- return this.run("billing invoices list", [], options, {
1011
+ async billingInvoicesList(options) {
1012
+ const result = await this.#transport.call("billing invoices list", [], options, {
1199
1013
  account: "string",
1200
- });
1014
+ }, false);
1015
+ return result;
1201
1016
  }
1202
1017
  /**
1203
1018
  * Show one invoice with its usage breakdown
@@ -1209,10 +1024,11 @@ export class Commands {
1209
1024
  * billing invoices show in_1Abc --vms
1210
1025
  * ```
1211
1026
  */
1212
- billingInvoicesShow(id, options) {
1213
- return this.run("billing invoices show", [id], options, {
1027
+ async billingInvoicesShow(id, options) {
1028
+ const result = await this.#transport.call("billing invoices show", [id], options, {
1214
1029
  vms: "bool",
1215
- });
1030
+ }, false);
1031
+ return result;
1216
1032
  }
1217
1033
  /**
1218
1034
  * Show credit purchase receipts and purchases (this billing account only)
@@ -1224,11 +1040,12 @@ export class Commands {
1224
1040
  * billing receipts --from=2026-01-01 --to=2026-06-30
1225
1041
  * ```
1226
1042
  */
1227
- billingReceipts(options) {
1228
- return this.run("billing receipts", [], options, {
1043
+ async billingReceipts(options) {
1044
+ const result = await this.#transport.call("billing receipts", [], options, {
1229
1045
  from: "string",
1230
1046
  to: "string",
1231
- });
1047
+ }, false);
1048
+ return result;
1232
1049
  }
1233
1050
  /**
1234
1051
  * Open a consolidated credit purchase statement
@@ -1240,105 +1057,528 @@ export class Commands {
1240
1057
  * billing statement --from=2026-01-01 --to=2026-06-30
1241
1058
  * ```
1242
1059
  */
1243
- billingStatement(options) {
1244
- return this.run("billing statement", [], options, {
1060
+ async billingStatement(options) {
1061
+ const result = await this.#transport.call("billing statement", [], options, {
1245
1062
  from: "string",
1246
1063
  to: "string",
1247
- });
1064
+ }, false);
1065
+ return result;
1248
1066
  }
1249
1067
  /**
1250
1068
  * Connect a marketplace subscription to your team
1251
1069
  *
1252
1070
  * Usage: `billing provider link <aws|azure> --token=<link-token> [--size=<team-size>] [--team-name=<name>]`
1253
1071
  */
1254
- billingProviderLink(awsOrAzure, options) {
1255
- return this.run("billing provider link", [awsOrAzure], options, {
1072
+ async billingProviderLink(awsOrAzure, options) {
1073
+ const result = await this.#transport.call("billing provider link", [awsOrAzure], options, {
1256
1074
  size: "string",
1257
1075
  "team-name": "string",
1258
1076
  token: "string",
1259
- });
1077
+ }, false);
1078
+ return result;
1260
1079
  }
1261
1080
  /**
1262
1081
  * Manage your invite link and rewards
1263
1082
  */
1264
- invite(options) {
1265
- return this.run("invite", [], options, {});
1083
+ async invite(options) {
1084
+ const result = await this.#transport.call("invite", [], options, {}, false);
1085
+ return result;
1266
1086
  }
1267
1087
  /**
1268
1088
  * Show your active invite link and reward
1269
1089
  *
1270
1090
  * Usage: `invite show`
1271
1091
  */
1272
- inviteShow(options) {
1273
- return this.run("invite show", [], options, {});
1092
+ async inviteShow(options) {
1093
+ const result = await this.#transport.call("invite show", [], options, {}, false);
1094
+ return result;
1274
1095
  }
1275
1096
  /**
1276
1097
  * Print only your active invite link
1277
1098
  *
1278
1099
  * Usage: `invite link`
1279
1100
  */
1280
- inviteLink(options) {
1281
- return this.run("invite link", [], options, {});
1101
+ async inviteLink(options) {
1102
+ const result = await this.#transport.call("invite link", [], options, {}, false);
1103
+ return result;
1282
1104
  }
1283
1105
  /**
1284
1106
  * List invite rewards you can use
1285
1107
  *
1286
1108
  * Usage: `invite rewards`
1287
1109
  */
1288
- inviteRewards(options) {
1289
- return this.run("invite rewards", [], options, {});
1110
+ async inviteRewards(options) {
1111
+ const result = await this.#transport.call("invite rewards", [], options, {}, false);
1112
+ return result;
1290
1113
  }
1291
1114
  /**
1292
1115
  * Choose the reward for your invite link
1293
1116
  *
1294
1117
  * Usage: `invite set-reward standard|bonus-credits|extra-memory|extra-disk`
1295
1118
  */
1296
- inviteSetReward(standardOrBonusCreditsOrExtraMemoryOrExtraDisk, options) {
1297
- return this.run("invite set-reward", [standardOrBonusCreditsOrExtraMemoryOrExtraDisk], options, {});
1119
+ async inviteSetReward(standardOrBonusCreditsOrExtraMemoryOrExtraDisk, options) {
1120
+ const result = await this.#transport.call("invite set-reward", [standardOrBonusCreditsOrExtraMemoryOrExtraDisk], options, {}, false);
1121
+ return result;
1298
1122
  }
1299
1123
  /**
1300
1124
  * Show signups, upgrades, and reward status
1301
1125
  *
1302
1126
  * Usage: `invite activity`
1303
1127
  */
1304
- inviteActivity(options) {
1305
- return this.run("invite activity", [], options, {});
1128
+ async inviteActivity(options) {
1129
+ const result = await this.#transport.call("invite activity", [], options, {}, false);
1130
+ return result;
1306
1131
  }
1307
1132
  /**
1308
1133
  * Request more trial invites
1309
1134
  *
1310
1135
  * Usage: `invite request`
1311
1136
  */
1312
- inviteRequest(options) {
1313
- return this.run("invite request", [], options, {});
1137
+ async inviteRequest(options) {
1138
+ const result = await this.#transport.call("invite request", [], options, {}, false);
1139
+ return result;
1314
1140
  }
1315
1141
  /**
1316
1142
  * Open the invites page
1317
1143
  *
1318
1144
  * Usage: `invite manage`
1319
1145
  */
1320
- inviteManage(options) {
1321
- return this.run("invite manage", [], options, {});
1146
+ async inviteManage(options) {
1147
+ const result = await this.#transport.call("invite manage", [], options, {}, false);
1148
+ return result;
1322
1149
  }
1323
1150
  /**
1324
- * Install or upgrade Shelley to the current version
1151
+ * Generate a magic link to log in to the website
1325
1152
  *
1326
- * Usage: `shelley install <vm>`
1153
+ * Usage: `browser`
1154
+ */
1155
+ async browser(options) {
1156
+ const result = await this.#transport.call("browser", [], options, {
1157
+ qr: "bool",
1158
+ }, false);
1159
+ return result;
1160
+ }
1161
+ }
1162
+ /** A VM reference. info is a snapshot; refresh() fetches current metadata. */
1163
+ export class VM {
1164
+ #transport;
1165
+ #info;
1166
+ /** @internal Obtain VMs through exe.new(), exe.ls(), exe.vm(), or vm.cp(). */
1167
+ constructor(transport, info) {
1168
+ this.#transport = transport;
1169
+ this.#info = Object.freeze({ ...info, tags: Object.freeze([...info.tags]) });
1170
+ }
1171
+ get info() { return this.#info; }
1172
+ get name() { return this.#info.name; }
1173
+ get httpsUrl() { return this.#info.httpsUrl; }
1174
+ get sshDest() { return this.#info.sshDest; }
1175
+ get tags() { return this.#info.tags; }
1176
+ /** Run a shell command once. Nonzero exit codes are results, not exceptions. */
1177
+ ssh(command, options) {
1178
+ return this.#transport.ssh(this.name, command, options);
1179
+ }
1180
+ /** Refresh metadata without changing this reference's identity. Requires ls permission. */
1181
+ async refresh(options) {
1182
+ const result = await this.#transport.call("ls", [this.name], options, {}, true);
1183
+ const vm = listVMs(this.#transport, result).find((vm) => vm.name === this.name);
1184
+ if (!vm)
1185
+ throw new ExeAPIError("ls", 404, "VM not found");
1186
+ this.#info = vm.info;
1187
+ return this;
1188
+ }
1189
+ /**
1190
+ * Probe SSH with `true` until it exits successfully. Never reruns a user command.
1191
+ * With waitUntil: "setup", then poll exe-setup.service until the setup script
1192
+ * has finished, throwing VMSetupError (as VMReadyError's cause) if it failed.
1193
+ * Images without that unit (anything but exeuntu) have no setup to wait for.
1194
+ * This does not wait for application servers. Requires ssh permission.
1195
+ */
1196
+ async waitUntilReady(options = {}) {
1197
+ const { waitUntil = "ssh" } = options;
1198
+ if (waitUntil !== "ssh" && waitUntil !== "setup")
1199
+ throw new TypeError("exedev: waitUntil must be ssh or setup");
1200
+ const timeoutMs = readyTimeout(options.timeoutMs, waitUntil);
1201
+ options.signal?.throwIfAborted();
1202
+ const controller = new AbortController();
1203
+ const abort = () => controller.abort(options.signal.reason);
1204
+ options.signal?.addEventListener("abort", abort, { once: true });
1205
+ const timer = setTimeout(() => controller.abort(new DOMException(waitUntil === "setup" ? "setup did not finish in time" : "SSH readiness timed out", "TimeoutError")), timeoutMs);
1206
+ let backoff = 100;
1207
+ try {
1208
+ while (true) {
1209
+ try {
1210
+ const probe = waitUntil === "setup" ? setupProbe : "true";
1211
+ const result = await abortable(this.ssh(probe, {
1212
+ signal: controller.signal, capture: waitUntil === "setup", maxOutputBytes: 1 << 20,
1213
+ }), controller.signal);
1214
+ controller.signal.throwIfAborted();
1215
+ if (result.exitCode !== 0)
1216
+ throw new ExeProtocolError("ssh readiness probe");
1217
+ if (waitUntil === "ssh")
1218
+ return this;
1219
+ const state = setupState(result.stdout);
1220
+ if (state === "done")
1221
+ return this;
1222
+ if (state instanceof VMSetupError)
1223
+ throw state;
1224
+ backoff = Math.max(backoff, 1000);
1225
+ }
1226
+ catch (err) {
1227
+ controller.signal.throwIfAborted();
1228
+ // /exec reports these failures before a remote command starts, and
1229
+ // probes are safe to rerun after a lost connection. Other rejections
1230
+ // (not found, permissions, no SSH configured) fail fast.
1231
+ const retry = err instanceof SSHStreamError && err.code === "interrupted" ||
1232
+ err instanceof ExeAPIError && err.status === 422 &&
1233
+ /failed to (?:connect|dial|create session|run command on VM)/i.test(err.details().body);
1234
+ if (!retry)
1235
+ throw err;
1236
+ }
1237
+ await delay(backoff, controller.signal);
1238
+ backoff = Math.min(backoff * 2, 2000);
1239
+ }
1240
+ }
1241
+ catch (err) {
1242
+ options.signal?.throwIfAborted();
1243
+ throw new VMReadyError(this, err);
1244
+ }
1245
+ finally {
1246
+ clearTimeout(timer);
1247
+ options.signal?.removeEventListener("abort", abort);
1248
+ }
1249
+ }
1250
+ /**
1251
+ * Delete a VM
1252
+ *
1253
+ * Usage: `rm <vmname>...`
1327
1254
  *
1328
1255
  * Tag-scoped API keys may run this command.
1329
1256
  */
1330
- shelleyInstall(vm, options) {
1331
- return this.run("shelley install", [vm], options, {});
1257
+ async rm(options) {
1258
+ const result = await this.#transport.documents("rm", [this.name], options, {});
1259
+ const summary = rmResult(result);
1260
+ if (summary.failed.length === 0 && summary.deleted.length === 1)
1261
+ return summary;
1262
+ throw new ExeAPIError("rm", summary.not_found?.length ? 404 : 422, result.body);
1332
1263
  }
1333
1264
  /**
1334
- * Generate a magic link to log in to the website
1265
+ * Restart a VM
1335
1266
  *
1336
- * Usage: `browser`
1267
+ * Usage: `restart <vmname>`
1268
+ *
1269
+ * Tag-scoped API keys may run this command.
1270
+ */
1271
+ async restart(options) {
1272
+ const ready = readiness(options ?? {});
1273
+ const { waitUntil, timeoutMs, ...flags } = options ?? {};
1274
+ const result = await this.#transport.call("restart", [this.name], flags, {}, true);
1275
+ if (ready)
1276
+ await this.waitUntilReady(ready);
1277
+ return this;
1278
+ }
1279
+ /**
1280
+ * Pause a VM (snapshot RAM to disk, free its CPU and memory)
1281
+ *
1282
+ * Usage: `pause <vmname>`
1283
+ *
1284
+ * CLI examples:
1285
+ * ```
1286
+ * pause my-vm # snapshot RAM to disk; resume later with 'resume my-vm'
1287
+ * ```
1288
+ *
1289
+ * Tag-scoped API keys may run this command.
1290
+ */
1291
+ async pause(options) {
1292
+ const result = await this.#transport.call("pause", [this.name], options, {}, true);
1293
+ return result;
1294
+ }
1295
+ /**
1296
+ * Resume a paused VM (restore RAM from disk)
1297
+ *
1298
+ * Usage: `resume <vmname>`
1299
+ *
1300
+ * Tag-scoped API keys may run this command.
1301
+ */
1302
+ async resume(options) {
1303
+ const ready = readiness(options ?? {});
1304
+ const { waitUntil, timeoutMs, ...flags } = options ?? {};
1305
+ const result = await this.#transport.call("resume", [this.name], flags, {}, true);
1306
+ if (ready)
1307
+ await this.waitUntilReady(ready);
1308
+ return this;
1309
+ }
1310
+ /**
1311
+ * rename a vm
1312
+ *
1313
+ * Usage: `rename <oldname> <newname>`
1314
+ *
1315
+ * Tag-scoped API keys may run this command.
1337
1316
  */
1338
- browser(options) {
1339
- return this.run("browser", [], options, {
1317
+ async rename(newname, options) {
1318
+ if (newname === this.name)
1319
+ return this;
1320
+ const result = await this.#transport.call("rename", [this.name, newname], options, {}, true);
1321
+ if (result?.new_name !== newname || result?.old_name !== this.name)
1322
+ throw new ExeProtocolError("rename");
1323
+ // Destinations and other metadata may have changed. Do not invent URLs.
1324
+ this.#info = Object.freeze({ name: newname, tags: this.tags });
1325
+ return this;
1326
+ }
1327
+ /**
1328
+ * Add or remove tags on a VM
1329
+ *
1330
+ * Usage: `tag [-d] <vm> <tag-name> [tag-name...]`
1331
+ *
1332
+ * CLI examples:
1333
+ * ```
1334
+ * tag my-vm prod web # add tags
1335
+ * tag -d my-vm prod web # remove tags
1336
+ * ```
1337
+ *
1338
+ * Tag-scoped API keys may run this command.
1339
+ */
1340
+ async tag(tagNames, options) {
1341
+ const result = await this.#transport.call("tag", [this.name, tagNames], options, {
1342
+ d: "bool",
1343
+ }, true);
1344
+ return result;
1345
+ }
1346
+ /**
1347
+ * Copy an existing VM
1348
+ *
1349
+ * Usage: `cp <source-vm> [new-name] [--pool=<name>] [--memory=<size>] [--cpu=<count>] [--disk=<size>]`
1350
+ *
1351
+ * CLI examples:
1352
+ * ```
1353
+ * cp my-vm # copy with auto-generated name
1354
+ * cp my-vm my-vm-copy # copy with specific name
1355
+ * cp my-vm --cpu=4 --memory=16GB # copy with different resources
1356
+ * cp my-vm my-pool-copy --pool=build # copy into a team pool
1357
+ * cp my-vm --copy-tags=false # copy without tags
1358
+ * ```
1359
+ *
1360
+ * Tag-scoped API keys may run this command.
1361
+ */
1362
+ async cp(options) {
1363
+ const ready = readiness(options ?? {});
1364
+ const { waitUntil, timeoutMs, name, ...flags } = options ?? {};
1365
+ const result = await this.#transport.call("cp", [this.name, name], flags, {
1366
+ "copy-tags": "bool",
1367
+ cpu: "number",
1368
+ disk: "string",
1369
+ memory: "string",
1370
+ pool: "string",
1371
+ sandbox: "bool",
1372
+ standalone: "bool",
1373
+ }, true);
1374
+ const vm = new VM(this.#transport, vmInfo(result, "cp"));
1375
+ if (ready)
1376
+ await vm.waitUntilReady(ready);
1377
+ return vm;
1378
+ }
1379
+ /**
1380
+ * Resize a VM's resources (memory, CPU, disk)
1381
+ *
1382
+ * Usage: `resize <vmname> [--memory=<size>] [--cpu=<count>] [--disk=<size>]`
1383
+ *
1384
+ * Tag-scoped API keys may run this command.
1385
+ */
1386
+ async resize(options) {
1387
+ const result = await this.#transport.call("resize", [this.name], options, {
1388
+ cpu: "number",
1389
+ disk: "string",
1390
+ memory: "string",
1391
+ }, true);
1392
+ return result;
1393
+ }
1394
+ /**
1395
+ * Set or clear a short comment on a VM
1396
+ *
1397
+ * Usage: `comment <hostname> <text>`
1398
+ *
1399
+ * CLI examples:
1400
+ * ```
1401
+ * comment my-vm staging copy
1402
+ * comment my-vm "" # clear the comment
1403
+ * ```
1404
+ *
1405
+ * Tag-scoped API keys may run this command.
1406
+ */
1407
+ async comment(text, options) {
1408
+ const result = await this.#transport.call("comment", [this.name, text], options, {}, true);
1409
+ return result;
1410
+ }
1411
+ /**
1412
+ * Link a custom domain to a VM
1413
+ *
1414
+ * Usage: `domain add [--wildcard] <vm> <domain>`
1415
+ */
1416
+ async domainAdd(domain, options) {
1417
+ const result = await this.#transport.call("domain add", [this.name, domain], options, {
1418
+ wildcard: "bool",
1419
+ }, true);
1420
+ return result;
1421
+ }
1422
+ /**
1423
+ * Remove a custom domain from a VM
1424
+ *
1425
+ * Usage: `domain rm <vm> <domain>`
1426
+ */
1427
+ async domainRm(domain, options) {
1428
+ const result = await this.#transport.call("domain rm", [this.name, domain], options, {}, true);
1429
+ return result;
1430
+ }
1431
+ /**
1432
+ * Show current shares for a VM
1433
+ *
1434
+ * Usage: `share show <vm>`
1435
+ *
1436
+ * Tag-scoped API keys may run this command.
1437
+ */
1438
+ async shareShow(options) {
1439
+ const result = await this.#transport.call("share show", [this.name], options, {
1340
1440
  qr: "bool",
1341
- });
1441
+ }, true);
1442
+ return result;
1443
+ }
1444
+ /**
1445
+ * Set the HTTP proxy port for a VM
1446
+ *
1447
+ * Usage: `share port <vm> [port]`
1448
+ *
1449
+ * CLI examples:
1450
+ * ```
1451
+ * share port mybox 8080
1452
+ * ```
1453
+ *
1454
+ * Tag-scoped API keys may run this command.
1455
+ */
1456
+ async sharePort(port, options) {
1457
+ const result = await this.#transport.call("share port", [this.name, port], options, {}, true);
1458
+ return result;
1459
+ }
1460
+ /**
1461
+ * Make the HTTP proxy publicly accessible
1462
+ *
1463
+ * Usage: `share set-public <vm>`
1464
+ *
1465
+ * Tag-scoped API keys may run this command.
1466
+ */
1467
+ async shareSetPublic(options) {
1468
+ const result = await this.#transport.call("share set-public", [this.name], options, {}, true);
1469
+ return result;
1470
+ }
1471
+ /**
1472
+ * Restrict the HTTP proxy to authenticated users
1473
+ *
1474
+ * Usage: `share set-private <vm>`
1475
+ *
1476
+ * Tag-scoped API keys may run this command.
1477
+ */
1478
+ async shareSetPrivate(options) {
1479
+ const result = await this.#transport.call("share set-private", [this.name], options, {}, true);
1480
+ return result;
1481
+ }
1482
+ /**
1483
+ * Share VM with a user via email, or grant shell access with --root
1484
+ *
1485
+ * Usage: `share add <vm> <email|team> [--root] [--message='...']`
1486
+ *
1487
+ * CLI examples:
1488
+ * ```
1489
+ * share add mybox user@example.com
1490
+ * share add mybox user@example.com --message='Check this out'
1491
+ * share add mybox team
1492
+ * share add mybox teammate@example.com --root
1493
+ * share add mybox team --root
1494
+ * ```
1495
+ *
1496
+ * Tag-scoped API keys may run this command.
1497
+ */
1498
+ async shareAdd(emailOrTeam, options) {
1499
+ const result = await this.#transport.call("share add", [this.name, emailOrTeam], options, {
1500
+ message: "string",
1501
+ qr: "bool",
1502
+ root: "bool",
1503
+ }, true);
1504
+ return result;
1505
+ }
1506
+ /**
1507
+ * Revoke a user's access to a VM, or downgrade shell access to web with --root
1508
+ *
1509
+ * Usage: `share remove <vm> <email|team> [--root]`
1510
+ *
1511
+ * CLI examples:
1512
+ * ```
1513
+ * share remove mybox user@example.com
1514
+ * share remove mybox team
1515
+ * share remove mybox teammate@example.com --root
1516
+ * share remove mybox team --root
1517
+ * ```
1518
+ *
1519
+ * Tag-scoped API keys may run this command.
1520
+ */
1521
+ async shareRemove(emailOrTeam, options) {
1522
+ const result = await this.#transport.call("share remove", [this.name, emailOrTeam], options, {
1523
+ root: "bool",
1524
+ }, true);
1525
+ return result;
1526
+ }
1527
+ /**
1528
+ * Create a shareable link for a VM
1529
+ *
1530
+ * Usage: `share add-link <vm>`
1531
+ *
1532
+ * Tag-scoped API keys may run this command.
1533
+ */
1534
+ async shareAddLink(options) {
1535
+ const result = await this.#transport.call("share add-link", [this.name], options, {
1536
+ qr: "bool",
1537
+ }, true);
1538
+ return result;
1539
+ }
1540
+ /**
1541
+ * Revoke a shareable link
1542
+ *
1543
+ * Usage: `share remove-link <vm> <token>`
1544
+ *
1545
+ * Tag-scoped API keys may run this command.
1546
+ */
1547
+ async shareRemoveLink(token, options) {
1548
+ const result = await this.#transport.call("share remove-link", [this.name, token], options, {}, true);
1549
+ return result;
1550
+ }
1551
+ /**
1552
+ * Enable or disable inbound email for a VM
1553
+ *
1554
+ * Usage: `share receive-email <vm> [on|off] [--reply-policy=all|known|owner|none]`
1555
+ *
1556
+ * CLI examples:
1557
+ * ```
1558
+ * share receive-email mybox on
1559
+ * share receive-email mybox off
1560
+ * share receive-email mybox --reply-policy=known
1561
+ * share receive-email mybox
1562
+ * ```
1563
+ *
1564
+ * Tag-scoped API keys may run this command.
1565
+ */
1566
+ async shareReceiveEmail(onOrOff, options) {
1567
+ const result = await this.#transport.call("share receive-email", [this.name, onOrOff], options, {
1568
+ "reply-policy": "string",
1569
+ }, true);
1570
+ return result;
1571
+ }
1572
+ /**
1573
+ * Install or upgrade Shelley to the current version
1574
+ *
1575
+ * Usage: `shelley install <vm>`
1576
+ *
1577
+ * Tag-scoped API keys may run this command.
1578
+ */
1579
+ async shelleyInstall(options) {
1580
+ const result = await this.#transport.call("shelley install", [this.name], options, {}, true);
1581
+ return result;
1342
1582
  }
1343
1583
  /**
1344
1584
  * Allow exe.dev support to log in to a VM
@@ -1347,8 +1587,9 @@ export class Commands {
1347
1587
  *
1348
1588
  * Tag-scoped API keys may run this command.
1349
1589
  */
1350
- grantSupportRoot(vmname, onOrOff, options) {
1351
- return this.run("grant-support-root", [vmname, onOrOff], options, {});
1590
+ async grantSupportRoot(onOrOff, options) {
1591
+ const result = await this.#transport.call("grant-support-root", [this.name, onOrOff], options, {}, false);
1592
+ return result;
1352
1593
  }
1353
1594
  /**
1354
1595
  * Show vCPU, disk, IO, and network (RX/TX) metrics for a VM
@@ -1364,10 +1605,163 @@ export class Commands {
1364
1605
  *
1365
1606
  * Tag-scoped API keys may run this command.
1366
1607
  */
1367
- stat(vmName, options) {
1368
- return this.run("stat", [vmName], options, {
1608
+ async stat(options) {
1609
+ const result = await this.#transport.call("stat", [this.name], options, {
1369
1610
  range: "string",
1370
- });
1611
+ }, true);
1612
+ return result;
1613
+ }
1614
+ }
1615
+ /** Readiness failed after a VM was obtained/created. The VM is not deleted. */
1616
+ export class VMReadyError extends Error {
1617
+ vm;
1618
+ constructor(vm, cause) {
1619
+ super(`exedev: VM ${vm.name} ${readyFailure(cause)}`, { cause });
1620
+ this.vm = vm;
1621
+ this.name = "VMReadyError";
1622
+ }
1623
+ }
1624
+ function readyFailure(cause) {
1625
+ if (cause instanceof VMSetupError)
1626
+ return cause.message.replace(/^exedev: /, "");
1627
+ if (cause instanceof ExeAPIError && cause.status === 403 && cause.operation === "ssh") {
1628
+ return `is not known to be ready: this token may not run ssh, which readiness needs (use waitUntil: "created")`;
1629
+ }
1630
+ if (cause instanceof DOMException && cause.name === "TimeoutError")
1631
+ return `is not ready: ${cause.message}`;
1632
+ return "is not SSH-ready";
1633
+ }
1634
+ /** The VM's setup script failed. details() has the end of its journal. */
1635
+ export class VMSetupError extends Error {
1636
+ result;
1637
+ exitCode;
1638
+ signal;
1639
+ #output;
1640
+ constructor(
1641
+ /** systemd's Result for exe-setup.service, e.g. "exit-code", "signal", or "timeout". */
1642
+ result, exitCode, signal, output) {
1643
+ super(`exedev: setup script failed (${exitCode !== undefined ? `exit code ${exitCode}` : signal !== undefined ? `signal ${signal}` : result})`);
1644
+ this.result = result;
1645
+ this.exitCode = exitCode;
1646
+ this.signal = signal;
1647
+ this.name = "VMSetupError";
1648
+ this.#output = output;
1649
+ }
1650
+ /** The script's last journal lines; may contain secrets. */
1651
+ details() { return { output: this.#output }; }
1652
+ }
1653
+ function readyTimeout(value, waitUntil) {
1654
+ value ??= waitUntil === "setup" ? 600_000 : 60_000;
1655
+ if (!Number.isSafeInteger(value) || value <= 0 || value > 2_147_483_647) {
1656
+ throw new TypeError("exedev: timeoutMs must be a positive integer no greater than 2147483647");
1657
+ }
1658
+ return value;
1659
+ }
1660
+ function readiness(options) {
1661
+ const { waitUntil = "ssh", timeoutMs, signal } = options;
1662
+ if (waitUntil !== "ssh" && waitUntil !== "setup" && waitUntil !== "created") {
1663
+ throw new TypeError("exedev: waitUntil must be ssh, setup, or created");
1664
+ }
1665
+ readyTimeout(timeoutMs, waitUntil); // Validate before creating/copying/restarting a VM.
1666
+ return waitUntil === "created" ? undefined : { waitUntil, timeoutMs, signal };
1667
+ }
1668
+ // Report exe-setup.service's state, and on failure the end of its journal.
1669
+ // exessh can answer before exe-init hands PID 1 to systemd, and then before
1670
+ // systemd's bus is up ("Failed to connect to bus"): both are "booting".
1671
+ const setupProbe = `if [ ! -d /run/systemd/system ]; then
1672
+ if [ -e /etc/systemd/system/exe-setup.service ]; then echo State=booting; else echo State=unsupported; fi
1673
+ exit 0
1674
+ fi
1675
+ props=$(systemctl show exe-setup.service -p LoadState -p ActiveState -p Job -p Result -p ExecMainCode -p ExecMainStatus -p ExecMainExitTimestampMonotonic -p ConditionResult -p ConditionTimestampMonotonic 2>/dev/null) || { echo State=booting; exit 0; }
1676
+ echo "$props"
1677
+ echo "SystemState=$(systemctl is-system-running 2>/dev/null)"
1678
+ if [ "$(systemctl show exe-setup.service -p ActiveState --value)" = failed ]; then
1679
+ echo ---
1680
+ journalctl -b -u exe-setup.service -o cat --no-pager -n 50 2>&1
1681
+ fi`;
1682
+ /** "done" covers success and having no setup script, or no setup runner, to wait for. */
1683
+ function setupState(output) {
1684
+ const [head, ...journal] = output.split("\n---\n");
1685
+ const p = {};
1686
+ for (const line of head.split("\n")) {
1687
+ const at = line.indexOf("=");
1688
+ if (at > 0)
1689
+ p[line.slice(0, at)] = line.slice(at + 1).trim();
1690
+ }
1691
+ const number = (key) => /^\d+$/.test(p[key] ?? "") ? Number(p[key]) : undefined;
1692
+ if (p.State === "booting")
1693
+ return "pending";
1694
+ if (p.State === "unsupported" || p.LoadState === "not-found")
1695
+ return "done";
1696
+ if (!p.LoadState || !p.ActiveState)
1697
+ throw new ExeProtocolError("ssh setup probe");
1698
+ if (p.Job && p.Job !== "0" || ["activating", "deactivating", "reloading"].includes(p.ActiveState))
1699
+ return "pending";
1700
+ if (p.ActiveState === "failed" || number("ExecMainExitTimestampMonotonic") && p.Result !== "success") {
1701
+ // ExecMainCode: 1 exited, 2 killed, 3 dumped core. ExecMainStatus is the code or signal.
1702
+ const code = number("ExecMainCode"), status = number("ExecMainStatus");
1703
+ return new VMSetupError(p.Result || "failed", code === 1 ? status : undefined, code === 2 || code === 3 ? status : undefined, journal.join("\n---\n"));
1704
+ }
1705
+ if (number("ExecMainExitTimestampMonotonic"))
1706
+ return "done";
1707
+ if (number("ConditionTimestampMonotonic") && p.ConditionResult === "no")
1708
+ return "done";
1709
+ // Not reached yet while the system boots; otherwise it is not going to run.
1710
+ return ["initializing", "starting"].includes(p.SystemState) ? "pending" : "done";
1711
+ }
1712
+ function rmResult(value) {
1713
+ const summary = value.docs?.filter((doc) => !isErrorDocument(doc));
1714
+ const strings = (v) => Array.isArray(v) && v.every((s) => typeof s === "string");
1715
+ const obj = summary?.length === 1 ? summary[0] : undefined;
1716
+ if (!obj || typeof obj !== "object" || !strings(obj.deleted) || !strings(obj.failed) ||
1717
+ obj.not_found !== undefined && !strings(obj.not_found) ||
1718
+ obj.errors !== undefined && (typeof obj.errors !== "object" || obj.errors === null || Array.isArray(obj.errors) ||
1719
+ !Object.values(obj.errors).every((s) => typeof s === "string"))) {
1720
+ throw new ExeProtocolError("rm");
1721
+ }
1722
+ return obj;
1723
+ }
1724
+ function vmName(name) {
1725
+ if (name instanceof VM)
1726
+ return name.name;
1727
+ if (typeof name !== "string" || !/^[a-zA-Z0-9][a-zA-Z0-9-]*$/.test(name)) {
1728
+ throw new TypeError("exedev: expected a VM name, not a command or SSH destination");
1729
+ }
1730
+ return name;
1731
+ }
1732
+ function vmInfo(value, operation) {
1733
+ if (!value || typeof value !== "object" || Array.isArray(value))
1734
+ throw new ExeProtocolError(operation);
1735
+ const obj = value;
1736
+ const string = (key, required = false) => {
1737
+ const value = obj[key];
1738
+ if (value === undefined && !required)
1739
+ return undefined;
1740
+ if (typeof value !== "string" || required && !value)
1741
+ throw new ExeProtocolError(operation);
1742
+ return value;
1743
+ };
1744
+ const name = string(operation === "cp" ? "name" : "vm_name", true);
1745
+ if (!/^[a-zA-Z0-9][a-zA-Z0-9-]*$/.test(name))
1746
+ throw new ExeProtocolError(operation);
1747
+ const tags = obj.tags ?? [];
1748
+ if (!Array.isArray(tags) || !tags.every((tag) => typeof tag === "string"))
1749
+ throw new ExeProtocolError(operation);
1750
+ return {
1751
+ name, tags,
1752
+ sshDest: string(operation === "cp" ? "ssh" : "ssh_dest", true),
1753
+ httpsUrl: string("https_url", operation !== "cp"),
1754
+ status: string(operation === "cp" ? "state" : "status"),
1755
+ region: string("region"),
1756
+ comment: string("comment"),
1757
+ createdAt: string("created_at"),
1758
+ promptError: string("error"),
1759
+ };
1760
+ }
1761
+ function listVMs(transport, value) {
1762
+ if (!value || typeof value !== "object" || !("vms" in value) || !Array.isArray(value.vms)) {
1763
+ throw new ExeProtocolError("ls");
1371
1764
  }
1765
+ return value.vms.map((row) => new VM(transport, vmInfo(row, "ls")));
1372
1766
  }
1373
1767
  //# sourceMappingURL=commands.gen.js.map