@exedev/sdk 0.0.6 → 0.1.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/LICENSE +21 -0
- package/README.md +52 -33
- package/dist/client.d.ts +25 -30
- package/dist/client.d.ts.map +1 -1
- package/dist/client.js +137 -78
- package/dist/client.js.map +1 -1
- package/dist/commands.gen.d.ts +1432 -0
- package/dist/commands.gen.d.ts.map +1 -0
- package/dist/commands.gen.js +1373 -0
- package/dist/commands.gen.js.map +1 -0
- package/dist/index.d.ts +5 -3
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -1
- package/dist/index.js.map +1 -1
- package/dist/types.d.ts +83 -7
- package/dist/types.d.ts.map +1 -1
- package/package.json +8 -5
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Bold Software, Inc.
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
CHANGED
|
@@ -15,27 +15,21 @@ import exedev from "@exedev/sdk";
|
|
|
15
15
|
|
|
16
16
|
const exe = exedev({ token: process.env.EXE_TOKEN! });
|
|
17
17
|
|
|
18
|
-
//
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
const
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
await exe.
|
|
28
|
-
|
|
29
|
-
// Get VM details
|
|
30
|
-
const info = await exe.stat("my-vm");
|
|
31
|
-
console.log(info);
|
|
18
|
+
// Every exe.dev command is a method. Positional arguments are parameters;
|
|
19
|
+
// flags go in an options object, spelled as on the command line.
|
|
20
|
+
const vm = await exe.new({ name: "my-vm", image: "ubuntu:24.04", tag: ["dev"] });
|
|
21
|
+
console.log(vm.https_url);
|
|
22
|
+
|
|
23
|
+
const { vms } = await exe.ls();
|
|
24
|
+
await exe.resize("my-vm", { memory: "8GB" });
|
|
25
|
+
await exe.pause("my-vm");
|
|
26
|
+
await exe.resume("my-vm");
|
|
27
|
+
await exe.shareAdd("my-vm", "friend@example.com", { message: "have a look" });
|
|
28
|
+
await exe.rm("my-vm");
|
|
32
29
|
|
|
33
|
-
// Run
|
|
30
|
+
// Run a raw command line.
|
|
34
31
|
const result = await exe.exec("help");
|
|
35
32
|
console.log(result.body);
|
|
36
|
-
|
|
37
|
-
// Delete a VM
|
|
38
|
-
await exe.rm("my-vm");
|
|
39
33
|
```
|
|
40
34
|
|
|
41
35
|
## Authentication
|
|
@@ -43,7 +37,7 @@ await exe.rm("my-vm");
|
|
|
43
37
|
Generate an API token via SSH:
|
|
44
38
|
|
|
45
39
|
```sh
|
|
46
|
-
ssh exe.dev
|
|
40
|
+
ssh exe.dev ssh-key generate-api-key --label=my-app --exp=30d
|
|
47
41
|
```
|
|
48
42
|
|
|
49
43
|
See the [HTTPS API docs](https://exe.dev/docs/https-api) for full details on
|
|
@@ -57,23 +51,24 @@ authentication, token permissions, and scoping.
|
|
|
57
51
|
| --------- | ------ | -------- | ------------------- |
|
|
58
52
|
| `token` | string | Yes | |
|
|
59
53
|
| `baseURL` | string | No | `https://exe.dev` |
|
|
54
|
+
| `fetch` | function | No | global `fetch` |
|
|
60
55
|
|
|
61
|
-
###
|
|
56
|
+
### Commands
|
|
62
57
|
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
| `whoami()` | Get authenticated user |
|
|
68
|
-
| `stat(vmName)` | Get VM details |
|
|
69
|
-
| `new(name, options?)` | Create a VM |
|
|
70
|
-
| `rm(vmName)` | Delete a VM |
|
|
71
|
-
| `restart(vmName)` | Restart a VM |
|
|
72
|
-
| `rename(oldName, newName)` | Rename a VM |
|
|
73
|
-
| `sshKeyList()` | List SSH keys |
|
|
74
|
-
| `shareShow(vmName)` | Show sharing settings |
|
|
58
|
+
Each public exe.dev command that runs over the HTTPS API is a method, named by
|
|
59
|
+
camel-casing its path: `ls`, `new`, `ssh-key list` is `sshKeyList`,
|
|
60
|
+
`team settings auto-join` is `teamSettingsAutoJoin`. Each method's doc comment
|
|
61
|
+
is the command's own help text, so `help <command>` and your editor agree.
|
|
75
62
|
|
|
76
|
-
|
|
63
|
+
Methods resolve to the command's JSON output, typed for the commands listed in
|
|
64
|
+
`CommandResults` (`src/types.ts`) and `unknown` otherwise. They reject with
|
|
65
|
+
`ExeAPIError` (`status`, `command`, `body`) when a command fails. Every
|
|
66
|
+
options object accepts `signal` (an `AbortSignal`) for cancellation.
|
|
67
|
+
|
|
68
|
+
The command methods live in `src/commands.gen.ts`, which is generated from the
|
|
69
|
+
exe.dev command tree; don't edit it by hand. To pick up new commands, run
|
|
70
|
+
`npm run sync-commands -- path/to/exe` (default `../exe`) with an up-to-date
|
|
71
|
+
checkout of the exe.dev repository, then `npm test`.
|
|
77
72
|
|
|
78
73
|
### Named import
|
|
79
74
|
|
|
@@ -81,6 +76,30 @@ All methods accept an optional `{ signal: AbortSignal }` as the last argument fo
|
|
|
81
76
|
import { exedev } from "@exedev/sdk";
|
|
82
77
|
```
|
|
83
78
|
|
|
79
|
+
## Development
|
|
80
|
+
|
|
81
|
+
```sh
|
|
82
|
+
npm install
|
|
83
|
+
npm test
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
`npm test` builds the package, then runs the unit tests and the integration
|
|
87
|
+
tests. The integration tests run against the real exe.dev API and create,
|
|
88
|
+
restart, rename and delete a VM. They need a tag-scoped API token, which can
|
|
89
|
+
only see and touch VMs with its tag, and they fail without one (set
|
|
90
|
+
`EXE_SKIP_INTEGRATION=1` to skip them). To create the token:
|
|
91
|
+
|
|
92
|
+
```sh
|
|
93
|
+
mkdir -p ~/.config/exedev
|
|
94
|
+
ssh exe.dev 'ssh-key generate-api-key --label=sdk-test --tag=sdk-test --exp=30d --cmds="help,ls,new,whoami,stat,rm,restart,rename,ssh-key list,share show" --json' \
|
|
95
|
+
| jq -r .token > ~/.config/exedev/sdk-test-token
|
|
96
|
+
chmod 600 ~/.config/exedev/sdk-test-token
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
The tests read the token from `$EXE_TOKEN` or `~/.config/exedev/sdk-test-token`.
|
|
100
|
+
They check that the VM they create is tagged `sdk-test` (override with
|
|
101
|
+
`$EXE_TEST_TAG`). Revoke the token with `ssh exe.dev ssh-key remove sdk-test`.
|
|
102
|
+
|
|
84
103
|
## License
|
|
85
104
|
|
|
86
105
|
MIT
|
package/dist/client.d.ts
CHANGED
|
@@ -1,14 +1,15 @@
|
|
|
1
|
-
import
|
|
1
|
+
import { Commands } from "./commands.gen.js";
|
|
2
|
+
import type { Arg, CallOptions, FlagKind } from "./commands.gen.js";
|
|
2
3
|
export interface ExedevOptions {
|
|
3
4
|
/** Bearer token (exe0.* or exe1.*). */
|
|
4
5
|
token: string;
|
|
5
6
|
/** Base URL for the API. Defaults to "https://exe.dev". */
|
|
6
7
|
baseURL?: string;
|
|
8
|
+
/** fetch implementation. Defaults to the global fetch. */
|
|
9
|
+
fetch?: typeof fetch;
|
|
7
10
|
}
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
signal?: AbortSignal;
|
|
11
|
-
}
|
|
11
|
+
/** @deprecated Use CallOptions. */
|
|
12
|
+
export type ExecOptions = CallOptions;
|
|
12
13
|
export interface ExecResult {
|
|
13
14
|
/** HTTP status code. */
|
|
14
15
|
status: number;
|
|
@@ -17,29 +18,19 @@ export interface ExecResult {
|
|
|
17
18
|
/** Whether the request succeeded (2xx). */
|
|
18
19
|
ok: boolean;
|
|
19
20
|
}
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
/** Delete a VM. */
|
|
34
|
-
rm(vmName: string, opts?: ExecOptions): Promise<void>;
|
|
35
|
-
/** Restart a VM. */
|
|
36
|
-
restart(vmName: string, opts?: ExecOptions): Promise<void>;
|
|
37
|
-
/** Rename a VM. */
|
|
38
|
-
rename(oldName: string, newName: string, opts?: ExecOptions): Promise<void>;
|
|
39
|
-
/** List SSH keys. */
|
|
40
|
-
sshKeyList(opts?: ExecOptions): Promise<string>;
|
|
41
|
-
/** Show sharing settings for a VM. */
|
|
42
|
-
shareShow(vmName: string, opts?: ExecOptions): Promise<string>;
|
|
21
|
+
/**
|
|
22
|
+
* An exe.dev API client. Each exe.dev command is a method (see Commands);
|
|
23
|
+
* exec runs a raw command line.
|
|
24
|
+
*
|
|
25
|
+
* Command methods resolve to the command's parsed JSON output and reject with
|
|
26
|
+
* ExeAPIError when the command fails.
|
|
27
|
+
*/
|
|
28
|
+
export declare class ExedevClient extends Commands {
|
|
29
|
+
#private;
|
|
30
|
+
constructor(opts: ExedevOptions);
|
|
31
|
+
/** Executes a raw command line against the exe.dev API. */
|
|
32
|
+
exec(command: string, options?: CallOptions): Promise<ExecResult>;
|
|
33
|
+
protected run(path: string, args: readonly Arg[], options: CallOptions | undefined, flags: Readonly<Record<string, FlagKind>>): Promise<any>;
|
|
43
34
|
}
|
|
44
35
|
/**
|
|
45
36
|
* Create an exe.dev API client.
|
|
@@ -49,12 +40,16 @@ export interface ExedevClient {
|
|
|
49
40
|
* import exedev from "@exedev/sdk";
|
|
50
41
|
*
|
|
51
42
|
* const exe = exedev({ token: process.env.EXE_TOKEN! });
|
|
52
|
-
* const vms = await exe.ls();
|
|
43
|
+
* const { vms } = await exe.ls();
|
|
53
44
|
* console.log(vms);
|
|
54
45
|
* ```
|
|
55
46
|
*/
|
|
56
47
|
export default function exedev(opts: ExedevOptions): ExedevClient;
|
|
57
|
-
/**
|
|
48
|
+
/** Builds the command line for a generated command method. */
|
|
49
|
+
export declare function commandLine(path: string, args: readonly Arg[], options: CallOptions | undefined, flags: Readonly<Record<string, FlagKind>>): string;
|
|
50
|
+
/** Quotes s for the exe.dev command-line parser (POSIX shell rules). */
|
|
51
|
+
export declare function quote(s: string): string;
|
|
52
|
+
/** Error thrown when the exe.dev API reports a failed command. */
|
|
58
53
|
export declare class ExeAPIError extends Error {
|
|
59
54
|
readonly command: string;
|
|
60
55
|
readonly status: number;
|
package/dist/client.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../src/client.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,EAAE,
|
|
1
|
+
{"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../src/client.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,mBAAmB,CAAC;AAC7C,OAAO,KAAK,EAAE,GAAG,EAAE,WAAW,EAAE,QAAQ,EAAE,MAAM,mBAAmB,CAAC;AAEpE,MAAM,WAAW,aAAa;IAC5B,uCAAuC;IACvC,KAAK,EAAE,MAAM,CAAC;IACd,2DAA2D;IAC3D,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,0DAA0D;IAC1D,KAAK,CAAC,EAAE,OAAO,KAAK,CAAC;CACtB;AAED,mCAAmC;AACnC,MAAM,MAAM,WAAW,GAAG,WAAW,CAAC;AAEtC,MAAM,WAAW,UAAU;IACzB,wBAAwB;IACxB,MAAM,EAAE,MAAM,CAAC;IACf,yBAAyB;IACzB,IAAI,EAAE,MAAM,CAAC;IACb,2CAA2C;IAC3C,EAAE,EAAE,OAAO,CAAC;CACb;AAED;;;;;;GAMG;AACH,qBAAa,YAAa,SAAQ,QAAQ;;gBAK5B,IAAI,EAAE,aAAa;IAU/B,2DAA2D;IACrD,IAAI,CAAC,OAAO,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,WAAW,GAAG,OAAO,CAAC,UAAU,CAAC;cAcvD,GAAG,CACjB,IAAI,EAAE,MAAM,EACZ,IAAI,EAAE,SAAS,GAAG,EAAE,EACpB,OAAO,EAAE,WAAW,GAAG,SAAS,EAChC,KAAK,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,QAAQ,CAAC,CAAC,GACxC,OAAO,CAAC,GAAG,CAAC;CAiBhB;AAED;;;;;;;;;;;GAWG;AACH,MAAM,CAAC,OAAO,UAAU,MAAM,CAAC,IAAI,EAAE,aAAa,GAAG,YAAY,CAEhE;AAED,8DAA8D;AAC9D,wBAAgB,WAAW,CACzB,IAAI,EAAE,MAAM,EACZ,IAAI,EAAE,SAAS,GAAG,EAAE,EACpB,OAAO,EAAE,WAAW,GAAG,SAAS,EAChC,KAAK,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,QAAQ,CAAC,CAAC,GACxC,MAAM,CA2CR;AAED,wEAAwE;AACxE,wBAAgB,KAAK,CAAC,CAAC,EAAE,MAAM,GAAG,MAAM,CAKvC;AA4BD,kEAAkE;AAClE,qBAAa,WAAY,SAAQ,KAAK;IACpC,SAAgB,OAAO,EAAE,MAAM,CAAC;IAChC,SAAgB,MAAM,EAAE,MAAM,CAAC;IAC/B,SAAgB,IAAI,EAAE,MAAM,CAAC;gBAEjB,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM;CAO1D"}
|
package/dist/client.js
CHANGED
|
@@ -1,109 +1,168 @@
|
|
|
1
|
+
import { Commands } from "./commands.gen.js";
|
|
1
2
|
/**
|
|
2
|
-
*
|
|
3
|
-
*
|
|
4
|
-
* @example
|
|
5
|
-
* ```ts
|
|
6
|
-
* import exedev from "@exedev/sdk";
|
|
3
|
+
* An exe.dev API client. Each exe.dev command is a method (see Commands);
|
|
4
|
+
* exec runs a raw command line.
|
|
7
5
|
*
|
|
8
|
-
*
|
|
9
|
-
*
|
|
10
|
-
* console.log(vms);
|
|
11
|
-
* ```
|
|
6
|
+
* Command methods resolve to the command's parsed JSON output and reject with
|
|
7
|
+
* ExeAPIError when the command fails.
|
|
12
8
|
*/
|
|
13
|
-
export
|
|
14
|
-
|
|
15
|
-
|
|
9
|
+
export class ExedevClient extends Commands {
|
|
10
|
+
#token;
|
|
11
|
+
#baseURL;
|
|
12
|
+
#fetch;
|
|
13
|
+
constructor(opts) {
|
|
14
|
+
super();
|
|
15
|
+
if (!opts?.token) {
|
|
16
|
+
throw new Error("exedev: token is required");
|
|
17
|
+
}
|
|
18
|
+
this.#token = opts.token;
|
|
19
|
+
this.#baseURL = (opts.baseURL ?? "https://exe.dev").replace(/\/$/, "");
|
|
20
|
+
this.#fetch = opts.fetch ?? ((input, init) => fetch(input, init));
|
|
16
21
|
}
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
const res = await fetch(`${baseURL}/exec`, {
|
|
22
|
+
/** Executes a raw command line against the exe.dev API. */
|
|
23
|
+
async exec(command, options) {
|
|
24
|
+
const res = await this.#fetch(`${this.#baseURL}/exec`, {
|
|
21
25
|
method: "POST",
|
|
22
26
|
headers: {
|
|
23
|
-
Authorization: `Bearer ${token}`,
|
|
27
|
+
Authorization: `Bearer ${this.#token}`,
|
|
24
28
|
"Content-Type": "text/plain",
|
|
25
29
|
},
|
|
26
30
|
body: command,
|
|
27
|
-
signal:
|
|
31
|
+
signal: options?.signal,
|
|
28
32
|
});
|
|
29
33
|
const body = await res.text();
|
|
30
34
|
return { status: res.status, body, ok: res.ok };
|
|
31
35
|
}
|
|
32
|
-
async
|
|
33
|
-
const
|
|
36
|
+
async run(path, args, options, flags) {
|
|
37
|
+
const command = commandLine(path, args, options, flags);
|
|
38
|
+
const result = await this.exec(command, options);
|
|
34
39
|
if (!result.ok) {
|
|
35
40
|
throw new ExeAPIError(command, result.status, result.body);
|
|
36
41
|
}
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
return {
|
|
40
|
-
exec,
|
|
41
|
-
async ls(execOpts) {
|
|
42
|
-
const res = await execJSON("ls", execOpts);
|
|
43
|
-
return res.vms;
|
|
44
|
-
},
|
|
45
|
-
async whoami(execOpts) {
|
|
46
|
-
return execJSON("whoami", execOpts);
|
|
47
|
-
},
|
|
48
|
-
async stat(vmName, execOpts) {
|
|
49
|
-
return execJSON(`stat ${vmName}`, execOpts);
|
|
50
|
-
},
|
|
51
|
-
async new(name, options, execOpts) {
|
|
52
|
-
let cmd = `new ${name}`;
|
|
53
|
-
if (options?.region) {
|
|
54
|
-
cmd += ` --region=${options.region}`;
|
|
55
|
-
}
|
|
56
|
-
const result = await exec(cmd, execOpts);
|
|
57
|
-
if (!result.ok) {
|
|
58
|
-
throw new ExeAPIError(cmd, result.status, result.body);
|
|
59
|
-
}
|
|
60
|
-
return result.body.trim();
|
|
61
|
-
},
|
|
62
|
-
async rm(vmName, execOpts) {
|
|
63
|
-
const result = await exec(`rm ${vmName}`, execOpts);
|
|
64
|
-
if (!result.ok) {
|
|
65
|
-
throw new ExeAPIError(`rm ${vmName}`, result.status, result.body);
|
|
66
|
-
}
|
|
67
|
-
},
|
|
68
|
-
async restart(vmName, execOpts) {
|
|
69
|
-
const result = await exec(`restart ${vmName}`, execOpts);
|
|
70
|
-
if (!result.ok) {
|
|
71
|
-
throw new ExeAPIError(`restart ${vmName}`, result.status, result.body);
|
|
72
|
-
}
|
|
73
|
-
},
|
|
74
|
-
async rename(oldName, newName, execOpts) {
|
|
75
|
-
const result = await exec(`rename ${oldName} ${newName}`, execOpts);
|
|
76
|
-
if (!result.ok) {
|
|
77
|
-
throw new ExeAPIError(`rename ${oldName} ${newName}`, result.status, result.body);
|
|
78
|
-
}
|
|
79
|
-
},
|
|
80
|
-
async sshKeyList(execOpts) {
|
|
81
|
-
const result = await exec("ssh-key list", execOpts);
|
|
82
|
-
if (!result.ok) {
|
|
83
|
-
throw new ExeAPIError("ssh-key list", result.status, result.body);
|
|
84
|
-
}
|
|
42
|
+
const docs = parseDocuments(result.body);
|
|
43
|
+
if (docs === undefined) {
|
|
85
44
|
return result.body;
|
|
86
|
-
}
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
45
|
+
}
|
|
46
|
+
// Some commands report a failure as an {"error": ...} document with a
|
|
47
|
+
// 200 status (e.g. rm of a missing VM).
|
|
48
|
+
if (docs.some(isErrorDocument)) {
|
|
49
|
+
throw new ExeAPIError(command, result.status, result.body);
|
|
50
|
+
}
|
|
51
|
+
return docs.length === 1 ? docs[0] : docs;
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
/**
|
|
55
|
+
* Create an exe.dev API client.
|
|
56
|
+
*
|
|
57
|
+
* @example
|
|
58
|
+
* ```ts
|
|
59
|
+
* import exedev from "@exedev/sdk";
|
|
60
|
+
*
|
|
61
|
+
* const exe = exedev({ token: process.env.EXE_TOKEN! });
|
|
62
|
+
* const { vms } = await exe.ls();
|
|
63
|
+
* console.log(vms);
|
|
64
|
+
* ```
|
|
65
|
+
*/
|
|
66
|
+
export default function exedev(opts) {
|
|
67
|
+
return new ExedevClient(opts);
|
|
68
|
+
}
|
|
69
|
+
/** Builds the command line for a generated command method. */
|
|
70
|
+
export function commandLine(path, args, options, flags) {
|
|
71
|
+
const words = [path];
|
|
72
|
+
for (const [key, value] of Object.entries(options ?? {})) {
|
|
73
|
+
if (key === "signal" || value === undefined) {
|
|
74
|
+
continue;
|
|
75
|
+
}
|
|
76
|
+
const kind = Object.hasOwn(flags, key) ? flags[key] : undefined;
|
|
77
|
+
switch (kind) {
|
|
78
|
+
case "bool":
|
|
79
|
+
words.push(value ? `--${key}` : `--${key}=false`);
|
|
80
|
+
break;
|
|
81
|
+
case "number":
|
|
82
|
+
case "string":
|
|
83
|
+
words.push(`--${key}=${quote(String(value))}`);
|
|
84
|
+
break;
|
|
85
|
+
case "repeated":
|
|
86
|
+
for (const v of [value].flat()) {
|
|
87
|
+
words.push(`--${key}=${quote(String(v))}`);
|
|
88
|
+
}
|
|
89
|
+
break;
|
|
90
|
+
default:
|
|
91
|
+
throw new TypeError(`${path}: unknown option ${JSON.stringify(key)}`);
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
const positionals = [];
|
|
95
|
+
let skipped = false;
|
|
96
|
+
for (const arg of args) {
|
|
97
|
+
if (arg === undefined) {
|
|
98
|
+
skipped = true;
|
|
99
|
+
continue;
|
|
100
|
+
}
|
|
101
|
+
for (const a of [arg].flat()) {
|
|
102
|
+
if (skipped) {
|
|
103
|
+
throw new TypeError(`${path}: argument ${JSON.stringify(a)} follows an omitted argument`);
|
|
91
104
|
}
|
|
92
|
-
|
|
93
|
-
}
|
|
94
|
-
}
|
|
105
|
+
positionals.push(a);
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
if (positionals.some((p) => p.startsWith("-"))) {
|
|
109
|
+
words.push("--");
|
|
110
|
+
}
|
|
111
|
+
words.push(...positionals.map(quote));
|
|
112
|
+
return words.join(" ");
|
|
95
113
|
}
|
|
96
|
-
/**
|
|
114
|
+
/** Quotes s for the exe.dev command-line parser (POSIX shell rules). */
|
|
115
|
+
export function quote(s) {
|
|
116
|
+
if (/^[A-Za-z0-9_@%+=:,./-]+$/.test(s)) {
|
|
117
|
+
return s;
|
|
118
|
+
}
|
|
119
|
+
return `'${s.replaceAll("'", `'"'"'`)}'`;
|
|
120
|
+
}
|
|
121
|
+
/**
|
|
122
|
+
* Parses a response body as one JSON document, or as newline-separated
|
|
123
|
+
* documents. Returns undefined for an empty or non-JSON body.
|
|
124
|
+
*/
|
|
125
|
+
function parseDocuments(body) {
|
|
126
|
+
const text = body.trim();
|
|
127
|
+
if (text === "") {
|
|
128
|
+
return undefined;
|
|
129
|
+
}
|
|
130
|
+
try {
|
|
131
|
+
return [JSON.parse(text)];
|
|
132
|
+
}
|
|
133
|
+
catch {
|
|
134
|
+
// Not a single document.
|
|
135
|
+
}
|
|
136
|
+
try {
|
|
137
|
+
return text.split("\n").filter((line) => line.trim() !== "").map((line) => JSON.parse(line));
|
|
138
|
+
}
|
|
139
|
+
catch {
|
|
140
|
+
return undefined;
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
function isErrorDocument(doc) {
|
|
144
|
+
return typeof doc === "object" && doc !== null && !Array.isArray(doc) &&
|
|
145
|
+
Object.keys(doc).length === 1 && "error" in doc;
|
|
146
|
+
}
|
|
147
|
+
/** Error thrown when the exe.dev API reports a failed command. */
|
|
97
148
|
export class ExeAPIError extends Error {
|
|
98
149
|
command;
|
|
99
150
|
status;
|
|
100
151
|
body;
|
|
101
152
|
constructor(command, status, body) {
|
|
102
|
-
super(`exe.dev API error (${status}): ${body
|
|
153
|
+
super(`exe.dev API error (${status}): ${errorMessage(body)}`);
|
|
103
154
|
this.name = "ExeAPIError";
|
|
104
155
|
this.command = command;
|
|
105
156
|
this.status = status;
|
|
106
157
|
this.body = body;
|
|
107
158
|
}
|
|
108
159
|
}
|
|
160
|
+
function errorMessage(body) {
|
|
161
|
+
for (const doc of parseDocuments(body) ?? []) {
|
|
162
|
+
if (isErrorDocument(doc)) {
|
|
163
|
+
return String(doc.error);
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
return body.trim();
|
|
167
|
+
}
|
|
109
168
|
//# sourceMappingURL=client.js.map
|
package/dist/client.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"client.js","sourceRoot":"","sources":["../src/client.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"client.js","sourceRoot":"","sources":["../src/client.ts"],"names":[],"mappings":"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"}
|