specli 0.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CLAUDE.md +111 -0
- package/PLAN.md +274 -0
- package/README.md +474 -0
- package/biome.jsonc +1 -0
- package/bun.lock +98 -0
- package/cli.ts +74 -0
- package/fixtures/openapi-array-items.json +22 -0
- package/fixtures/openapi-auth.json +34 -0
- package/fixtures/openapi-body.json +41 -0
- package/fixtures/openapi-collision.json +21 -0
- package/fixtures/openapi-oauth.json +54 -0
- package/fixtures/openapi-servers.json +35 -0
- package/fixtures/openapi.json +87 -0
- package/index.ts +1 -0
- package/package.json +27 -0
- package/scripts/smoke-specs.ts +64 -0
- package/src/cli/auth-requirements.test.ts +27 -0
- package/src/cli/auth-requirements.ts +91 -0
- package/src/cli/auth-schemes.test.ts +66 -0
- package/src/cli/auth-schemes.ts +187 -0
- package/src/cli/capabilities.test.ts +94 -0
- package/src/cli/capabilities.ts +88 -0
- package/src/cli/command-id.test.ts +32 -0
- package/src/cli/command-id.ts +16 -0
- package/src/cli/command-index.ts +19 -0
- package/src/cli/command-model.test.ts +44 -0
- package/src/cli/command-model.ts +128 -0
- package/src/cli/compile.ts +119 -0
- package/src/cli/crypto.ts +9 -0
- package/src/cli/derive-name.ts +101 -0
- package/src/cli/exec.ts +72 -0
- package/src/cli/main.ts +336 -0
- package/src/cli/naming.test.ts +86 -0
- package/src/cli/naming.ts +224 -0
- package/src/cli/operations.test.ts +57 -0
- package/src/cli/operations.ts +152 -0
- package/src/cli/params.test.ts +70 -0
- package/src/cli/params.ts +71 -0
- package/src/cli/pluralize.ts +41 -0
- package/src/cli/positional.test.ts +65 -0
- package/src/cli/positional.ts +75 -0
- package/src/cli/request-body.test.ts +35 -0
- package/src/cli/request-body.ts +94 -0
- package/src/cli/runtime/argv.ts +14 -0
- package/src/cli/runtime/auth/resolve.ts +31 -0
- package/src/cli/runtime/body.ts +24 -0
- package/src/cli/runtime/collect.ts +6 -0
- package/src/cli/runtime/context.ts +62 -0
- package/src/cli/runtime/execute.ts +138 -0
- package/src/cli/runtime/generated.ts +200 -0
- package/src/cli/runtime/headers.ts +37 -0
- package/src/cli/runtime/index.ts +3 -0
- package/src/cli/runtime/profile/secrets.ts +42 -0
- package/src/cli/runtime/profile/store.ts +98 -0
- package/src/cli/runtime/request.test.ts +153 -0
- package/src/cli/runtime/request.ts +487 -0
- package/src/cli/runtime/server-url.ts +44 -0
- package/src/cli/runtime/template.ts +26 -0
- package/src/cli/runtime/validate/ajv.ts +13 -0
- package/src/cli/runtime/validate/coerce.ts +71 -0
- package/src/cli/runtime/validate/error.ts +29 -0
- package/src/cli/runtime/validate/index.ts +4 -0
- package/src/cli/runtime/validate/schema.ts +54 -0
- package/src/cli/schema-shape.ts +36 -0
- package/src/cli/schema.ts +76 -0
- package/src/cli/server.test.ts +35 -0
- package/src/cli/server.ts +88 -0
- package/src/cli/spec-id.ts +12 -0
- package/src/cli/spec-loader.ts +58 -0
- package/src/cli/stable-json.ts +35 -0
- package/src/cli/strings.ts +21 -0
- package/src/cli/types.ts +59 -0
- package/src/compiled.ts +23 -0
- package/src/macros/env.ts +25 -0
- package/src/macros/spec.ts +17 -0
- package/tsconfig.json +29 -0
package/README.md
ADDED
|
@@ -0,0 +1,474 @@
|
|
|
1
|
+
# opencli
|
|
2
|
+
|
|
3
|
+
OpenCLI turns an OpenAPI spec into a non-interactive, “curl replacement” CLI.
|
|
4
|
+
|
|
5
|
+
It has two modes:
|
|
6
|
+
|
|
7
|
+
- **exec**: run commands dynamically from a spec URL/path.
|
|
8
|
+
- **compile**: bundle the spec into a standalone executable.
|
|
9
|
+
|
|
10
|
+
The guiding constraints:
|
|
11
|
+
|
|
12
|
+
- Bun-first (no Node runtime needed).
|
|
13
|
+
- Suitable for automation/agents (stable commands, machine output via `--json`).
|
|
14
|
+
- Best-effort validation of inputs against the OpenAPI schema.
|
|
15
|
+
|
|
16
|
+
## Status / What Works Today
|
|
17
|
+
|
|
18
|
+
It works well for a large chunk of “typical” OpenAPI 3.x REST specs:
|
|
19
|
+
|
|
20
|
+
- Multiple servers + server variables.
|
|
21
|
+
- Path/query/header/cookie parameters.
|
|
22
|
+
- Request bodies via `--data` / `--file`.
|
|
23
|
+
- JSON request body parsing + schema validation.
|
|
24
|
+
- Expanded JSON body flags for simple object bodies (`--body-*`).
|
|
25
|
+
- Auth injection for common schemes (bearer/basic/apiKey).
|
|
26
|
+
- A deterministic `__schema` output for introspection.
|
|
27
|
+
|
|
28
|
+
It is not “universal OpenAPI support” yet. See “Limitations” for important gaps.
|
|
29
|
+
|
|
30
|
+
## Install
|
|
31
|
+
|
|
32
|
+
```bash
|
|
33
|
+
bun install
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
## Quickstart
|
|
37
|
+
|
|
38
|
+
Inspect what commands will be generated:
|
|
39
|
+
|
|
40
|
+
```bash
|
|
41
|
+
bunx opencli exec ./fixtures/openapi.json __schema
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
Machine-readable schema output:
|
|
45
|
+
|
|
46
|
+
```bash
|
|
47
|
+
bunx opencli exec ./fixtures/openapi.json __schema --json
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
Minimal schema output (best for large specs):
|
|
51
|
+
|
|
52
|
+
```bash
|
|
53
|
+
bunx opencli exec ./fixtures/openapi.json __schema --json --min
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
Run a generated operation:
|
|
57
|
+
|
|
58
|
+
```bash
|
|
59
|
+
bunx opencli exec ./fixtures/openapi.json contacts list --oc-curl
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
## Build a Standalone Executable
|
|
63
|
+
|
|
64
|
+
Use the `compile` command to create a standalone binary with the spec embedded:
|
|
65
|
+
|
|
66
|
+
```bash
|
|
67
|
+
# compile with auto-derived name (from spec title)
|
|
68
|
+
bunx opencli compile ./path/to/openapi.yaml
|
|
69
|
+
# → ./dist/my-api (derived from info.title)
|
|
70
|
+
|
|
71
|
+
# compile with explicit name
|
|
72
|
+
bunx opencli compile ./path/to/openapi.yaml --name myapi
|
|
73
|
+
# → ./dist/myapi
|
|
74
|
+
|
|
75
|
+
# cross-compile (example: linux x64)
|
|
76
|
+
bunx opencli compile https://api.vercel.com/copper/_openapi.json --target bun-linux-x64 --outfile ./dist/copper-linux
|
|
77
|
+
|
|
78
|
+
# disable runtime config loading for deterministic behavior
|
|
79
|
+
bunx opencli compile ./path/to/openapi.yaml --no-dotenv --no-bunfig
|
|
80
|
+
|
|
81
|
+
# bake in defaults (these become default flags; runtime flags override)
|
|
82
|
+
bunx opencli compile https://api.vercel.com/copper/_openapi.json \
|
|
83
|
+
--name copper \
|
|
84
|
+
--server https://api.vercel.com \
|
|
85
|
+
--auth VercelOidc
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
The compiled binary is a root CLI - no `opencli` prefix needed:
|
|
89
|
+
|
|
90
|
+
```bash
|
|
91
|
+
./dist/copper contacts list
|
|
92
|
+
./dist/copper users get abc123 --json
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
Notes:
|
|
96
|
+
|
|
97
|
+
- The spec is embedded at compile-time using a Bun macro.
|
|
98
|
+
- If `--name` is not provided, it is derived from the OpenAPI `info.title` or URL hostname.
|
|
99
|
+
- Runtime flags (e.g., `--server`) override baked-in defaults.
|
|
100
|
+
|
|
101
|
+
## CLI Shape
|
|
102
|
+
|
|
103
|
+
OpenCLI generates commands of the form:
|
|
104
|
+
|
|
105
|
+
```
|
|
106
|
+
opencli <resource> <action> [...positionals] [options]
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
- `resource` comes from `tags[0]`, `operationId` prefix, or the first path segment (heuristics).
|
|
110
|
+
- `action` is inferred from HTTP method + “has id in path”, or from `operationId` suffix.
|
|
111
|
+
- Name collisions are disambiguated deterministically by suffixing the action name.
|
|
112
|
+
|
|
113
|
+
Use `__schema` to see the planned mapping for your spec.
|
|
114
|
+
|
|
115
|
+
## Global Options
|
|
116
|
+
|
|
117
|
+
Available on the root command:
|
|
118
|
+
|
|
119
|
+
- `--spec <urlOrPath>`: OpenAPI URL or file path (only needed for compiled binaries to override embedded spec)
|
|
120
|
+
- `--server <url>`: override server/base URL
|
|
121
|
+
- `--server-var <name=value>`: server URL template variable (repeatable)
|
|
122
|
+
- `--profile <name>`: profile name (config under `~/.config/opencli`)
|
|
123
|
+
|
|
124
|
+
Auth selection + credentials:
|
|
125
|
+
|
|
126
|
+
- `--auth <scheme>`: pick an auth scheme by key
|
|
127
|
+
- `--bearer-token <token>`: set `Authorization: Bearer <token>`
|
|
128
|
+
- `--oauth-token <token>`: alias of `--bearer-token`
|
|
129
|
+
- `--username <username>` / `--password <password>`: basic auth
|
|
130
|
+
- `--api-key <key>`: value for apiKey auth
|
|
131
|
+
|
|
132
|
+
Output mode:
|
|
133
|
+
|
|
134
|
+
- `--json`: machine-readable output (never prints stack traces)
|
|
135
|
+
|
|
136
|
+
## Per-Operation Common Options
|
|
137
|
+
|
|
138
|
+
Every generated operation command includes:
|
|
139
|
+
|
|
140
|
+
- `--header <header>` (repeatable): extra headers; accepts `Name: Value` or `Name=Value`
|
|
141
|
+
- `--accept <type>`: override `Accept` header
|
|
142
|
+
- `--timeout <ms>`: request timeout in milliseconds
|
|
143
|
+
- `--dry-run`: print the request that would be sent (no network call)
|
|
144
|
+
- `--curl`: print an equivalent `curl` command (no network call)
|
|
145
|
+
|
|
146
|
+
For operations with `requestBody`, it also includes:
|
|
147
|
+
|
|
148
|
+
- `--data <data>`: inline request body
|
|
149
|
+
- `--file <path>`: read request body from a file
|
|
150
|
+
- `--content-type <type>`: override `Content-Type` (defaults from OpenAPI)
|
|
151
|
+
|
|
152
|
+
## Parameter Mapping
|
|
153
|
+
|
|
154
|
+
### Path Parameters
|
|
155
|
+
|
|
156
|
+
OpenAPI parameters where `in: path` become positional arguments.
|
|
157
|
+
|
|
158
|
+
- Order is derived from the path template: `/users/{id}/keys/{key_id}` becomes `<id> <key-id>`.
|
|
159
|
+
- Values are URL-encoded when applied to the path.
|
|
160
|
+
|
|
161
|
+
### Query / Header / Cookie Parameters
|
|
162
|
+
|
|
163
|
+
OpenAPI parameters where `in: query|header|cookie` become flags.
|
|
164
|
+
|
|
165
|
+
Flag name rules:
|
|
166
|
+
|
|
167
|
+
- `--${kebabCase(parameter.name)}`
|
|
168
|
+
- Examples:
|
|
169
|
+
- `limit` -> `--limit`
|
|
170
|
+
- `X-Request-Id` -> `--x-request-id`
|
|
171
|
+
|
|
172
|
+
Required flags:
|
|
173
|
+
|
|
174
|
+
- If the spec says `required: true`, the CLI flag is marked required (Commander enforces this).
|
|
175
|
+
|
|
176
|
+
Type coercion:
|
|
177
|
+
|
|
178
|
+
- `string` -> string
|
|
179
|
+
- `integer` -> `parseInt`
|
|
180
|
+
- `number` -> `Number(...)`
|
|
181
|
+
- `boolean` -> flag presence (no value)
|
|
182
|
+
- `object` -> JSON object literal string, parsed via `JSON.parse`
|
|
183
|
+
- `array` -> see below
|
|
184
|
+
|
|
185
|
+
### Arrays (Improved UX)
|
|
186
|
+
|
|
187
|
+
Array parameters are treated as repeatable flags and appended to the query string.
|
|
188
|
+
|
|
189
|
+
All of these become `?tag=a&tag=b`:
|
|
190
|
+
|
|
191
|
+
```bash
|
|
192
|
+
opencli ... --tag a --tag b
|
|
193
|
+
opencli ... --tag a,b
|
|
194
|
+
opencli ... --tag '["a","b"]'
|
|
195
|
+
```
|
|
196
|
+
|
|
197
|
+
Implementation notes:
|
|
198
|
+
|
|
199
|
+
- The query string is built with repeated keys (`URLSearchParams.append`).
|
|
200
|
+
- Array item types are derived from `schema.items.type` when present (e.g. integer arrays validate and coerce correctly).
|
|
201
|
+
|
|
202
|
+
## Request Bodies
|
|
203
|
+
|
|
204
|
+
### Selecting the Body Input
|
|
205
|
+
|
|
206
|
+
If an operation has a `requestBody`, you may provide a body via:
|
|
207
|
+
|
|
208
|
+
- `--data <string>`
|
|
209
|
+
- `--file <path>`
|
|
210
|
+
- Expanded `--body-*` flags (JSON-only; see below)
|
|
211
|
+
|
|
212
|
+
Rules:
|
|
213
|
+
|
|
214
|
+
- `--data` and `--file` are mutually exclusive.
|
|
215
|
+
- Expanded `--body-*` flags cannot be used with `--data` or `--file`.
|
|
216
|
+
- If `requestBody.required` is true and you provide none of the above, the command fails with:
|
|
217
|
+
- `Missing request body. Provide --data, --file, or --body-* flags.`
|
|
218
|
+
|
|
219
|
+
### Content-Type
|
|
220
|
+
|
|
221
|
+
`Content-Type` is chosen as:
|
|
222
|
+
|
|
223
|
+
1. `--content-type` (explicit override)
|
|
224
|
+
2. The preferred content type derived from the OpenAPI requestBody (prefers `application/json` when present)
|
|
225
|
+
|
|
226
|
+
### JSON Parsing + Normalization
|
|
227
|
+
|
|
228
|
+
If the selected `Content-Type` includes `json`:
|
|
229
|
+
|
|
230
|
+
- `--data`/`--file` content is parsed as either JSON or YAML
|
|
231
|
+
- the request is sent as normalized JSON (`JSON.stringify(parsed)`)
|
|
232
|
+
|
|
233
|
+
If `Content-Type` does not include `json`:
|
|
234
|
+
|
|
235
|
+
- the body is treated as a raw string
|
|
236
|
+
|
|
237
|
+
### Schema Validation (Ajv)
|
|
238
|
+
|
|
239
|
+
OpenCLI uses Ajv (best-effort, `strict: false`) to validate:
|
|
240
|
+
|
|
241
|
+
- query/header/cookie params
|
|
242
|
+
- JSON request bodies when a requestBody schema is available
|
|
243
|
+
|
|
244
|
+
Validation errors are formatted into a readable multiline message. For `required` errors, the message is normalized to:
|
|
245
|
+
|
|
246
|
+
- `/<path> missing required property '<name>'`
|
|
247
|
+
|
|
248
|
+
### Expanded JSON Body Flags (`--body-*`)
|
|
249
|
+
|
|
250
|
+
When an operation has a `requestBody` and the preferred schema is a JSON object with scalar properties, OpenCLI generates convenience flags:
|
|
251
|
+
|
|
252
|
+
- For `string|number|integer`: `--body-<prop> <value>`
|
|
253
|
+
- For `boolean`: `--body-<prop>` (presence sets it to `true`)
|
|
254
|
+
|
|
255
|
+
Example (from `fixtures/openapi-body.json`):
|
|
256
|
+
|
|
257
|
+
```bash
|
|
258
|
+
bunx opencli exec ./fixtures/openapi-body.json contacts create --body-name "Ada" --oc-curl
|
|
259
|
+
```
|
|
260
|
+
|
|
261
|
+
Produces a JSON body:
|
|
262
|
+
|
|
263
|
+
```json
|
|
264
|
+
{"name":"Ada"}
|
|
265
|
+
```
|
|
266
|
+
|
|
267
|
+
Notes / edge cases:
|
|
268
|
+
|
|
269
|
+
- Expanded flags are only supported for JSON bodies. If you try to use them without a JSON content type, OpenCLI errors.
|
|
270
|
+
- Required fields in the schema are checked in a “friendly” way for expanded flags:
|
|
271
|
+
- `Missing required body field 'name'. Provide --body-name or use --data/--file.`
|
|
272
|
+
- Numeric coercion uses `Number(...)` / `parseInt(...)`. Today it does not explicitly reject `NaN` (this is an area to harden).
|
|
273
|
+
|
|
274
|
+
## Servers
|
|
275
|
+
|
|
276
|
+
OpenCLI resolves the request base URL in this order:
|
|
277
|
+
|
|
278
|
+
1. `--server <url>`
|
|
279
|
+
2. profile `server` (if `--profile` is set and the profile has a server)
|
|
280
|
+
3. the first `servers[0].url` in the OpenAPI spec
|
|
281
|
+
|
|
282
|
+
If the chosen server URL has template variables (e.g. `https://{region}.api.example.com`):
|
|
283
|
+
|
|
284
|
+
- Provide `--server-var region=us-east-1` (repeatable)
|
|
285
|
+
- If the spec defines a default for that variable, it is used automatically
|
|
286
|
+
|
|
287
|
+
## Authentication
|
|
288
|
+
|
|
289
|
+
### Supported Scheme Kinds
|
|
290
|
+
|
|
291
|
+
From `components.securitySchemes`, OpenCLI recognizes:
|
|
292
|
+
|
|
293
|
+
- HTTP bearer (`type: http`, `scheme: bearer`)
|
|
294
|
+
- HTTP basic (`type: http`, `scheme: basic`)
|
|
295
|
+
- API key (`type: apiKey`, `in: header|query|cookie`)
|
|
296
|
+
- OAuth2 (`type: oauth2`) (treated as bearer token injection)
|
|
297
|
+
- OpenID Connect (`type: openIdConnect`) (treated as bearer token injection)
|
|
298
|
+
|
|
299
|
+
### Selecting an Auth Scheme
|
|
300
|
+
|
|
301
|
+
Scheme selection happens in this order:
|
|
302
|
+
|
|
303
|
+
1. `--auth <scheme>` (explicit)
|
|
304
|
+
2. profile `authScheme` (only if that key exists in the current spec)
|
|
305
|
+
3. if the operation requires exactly one scheme, it is chosen
|
|
306
|
+
4. if the spec defines exactly one scheme total, it is chosen
|
|
307
|
+
|
|
308
|
+
This “only if present in current spec” behavior prevents accidental auth leakage between different specs.
|
|
309
|
+
|
|
310
|
+
### Providing Credentials
|
|
311
|
+
|
|
312
|
+
Bearer-like schemes (`http-bearer`, `oauth2`, `openIdConnect`):
|
|
313
|
+
|
|
314
|
+
- `--bearer-token <token>` or `--oauth-token <token>`
|
|
315
|
+
- or a profile token stored via `opencli auth token ...`
|
|
316
|
+
|
|
317
|
+
Basic auth:
|
|
318
|
+
|
|
319
|
+
- `--username <username>` and `--password <password>`
|
|
320
|
+
|
|
321
|
+
API key:
|
|
322
|
+
|
|
323
|
+
- `--api-key <key>` (injected into the header/query/cookie location declared by the scheme)
|
|
324
|
+
|
|
325
|
+
## Profiles (Non-Interactive)
|
|
326
|
+
|
|
327
|
+
Profiles are for automation.
|
|
328
|
+
|
|
329
|
+
Config file:
|
|
330
|
+
|
|
331
|
+
- Read preference: `~/.config/opencli/profiles.json`, else `~/.config/opencli/profiles.yaml` if present
|
|
332
|
+
- Writes always go to: `~/.config/opencli/profiles.json`
|
|
333
|
+
|
|
334
|
+
Secrets:
|
|
335
|
+
|
|
336
|
+
- Tokens are stored in Bun’s secrets store (`bun.secrets`) under a spec-scoped service name.
|
|
337
|
+
|
|
338
|
+
Commands:
|
|
339
|
+
|
|
340
|
+
```bash
|
|
341
|
+
opencli profile list
|
|
342
|
+
opencli profile set --name dev --server https://api.example.com --auth bearerAuth --default
|
|
343
|
+
opencli profile use --name dev
|
|
344
|
+
opencli profile rm --name dev
|
|
345
|
+
|
|
346
|
+
opencli auth token --name dev --set "..."
|
|
347
|
+
opencli auth token --name dev --get
|
|
348
|
+
opencli auth token --name dev --delete
|
|
349
|
+
```
|
|
350
|
+
|
|
351
|
+
## Output Behavior
|
|
352
|
+
|
|
353
|
+
### Default (Human + Agent Readable)
|
|
354
|
+
|
|
355
|
+
- On success:
|
|
356
|
+
- if response `content-type` includes `json`, prints pretty JSON
|
|
357
|
+
- otherwise prints raw text
|
|
358
|
+
- On non-2xx HTTP:
|
|
359
|
+
- prints `HTTP <status>` and response body
|
|
360
|
+
- exits with code 1
|
|
361
|
+
- On CLI/validation errors:
|
|
362
|
+
- prints `error: <message>`
|
|
363
|
+
- exits with code 1
|
|
364
|
+
|
|
365
|
+
### `--json` (Machine Readable)
|
|
366
|
+
|
|
367
|
+
- On success:
|
|
368
|
+
- if response is JSON, prints the parsed JSON
|
|
369
|
+
- otherwise prints the raw string
|
|
370
|
+
- With `--status` and/or `--headers`, wraps output:
|
|
371
|
+
|
|
372
|
+
```json
|
|
373
|
+
{ "status": 200, "headers": { "content-type": "..." }, "body": ... }
|
|
374
|
+
```
|
|
375
|
+
|
|
376
|
+
- On non-2xx HTTP:
|
|
377
|
+
|
|
378
|
+
```json
|
|
379
|
+
{ "status": 404, "body": ..., "headers": { ... } }
|
|
380
|
+
```
|
|
381
|
+
|
|
382
|
+
- On CLI/validation errors:
|
|
383
|
+
|
|
384
|
+
```json
|
|
385
|
+
{ "error": "..." }
|
|
386
|
+
```
|
|
387
|
+
|
|
388
|
+
### `--curl`
|
|
389
|
+
|
|
390
|
+
Prints an equivalent curl invocation without sending the request.
|
|
391
|
+
|
|
392
|
+
### `--dry-run`
|
|
393
|
+
|
|
394
|
+
Prints the method, URL, headers, and body that would be sent without sending the request.
|
|
395
|
+
|
|
396
|
+
## `__schema`
|
|
397
|
+
|
|
398
|
+
`opencli __schema` reports:
|
|
399
|
+
|
|
400
|
+
- OpenAPI title/version
|
|
401
|
+
- spec source + computed spec id + fingerprint
|
|
402
|
+
- servers/auth schemes counts
|
|
403
|
+
- list of normalized operations
|
|
404
|
+
- planned command mapping
|
|
405
|
+
|
|
406
|
+
Flags:
|
|
407
|
+
|
|
408
|
+
- `--json`: JSON output
|
|
409
|
+
- `--pretty`: pretty JSON
|
|
410
|
+
- `--min`: minimal schema payload (commands + metadata only)
|
|
411
|
+
|
|
412
|
+
## Recommended Public Specs to Test
|
|
413
|
+
|
|
414
|
+
A good “real world” smoke test matrix includes:
|
|
415
|
+
|
|
416
|
+
1. Vercel API (OpenAPI 3.x)
|
|
417
|
+
- URL: `https://api.vercel.com/copper/_openapi.json`
|
|
418
|
+
- Focus: real-world parameter naming collisions (e.g. `accept`), large-ish spec
|
|
419
|
+
|
|
420
|
+
2. GitHub REST API (OpenAPI 3.1, very large)
|
|
421
|
+
- URL (huge): `https://raw.githubusercontent.com/github/rest-api-description/main/descriptions-next/api.github.com/api.github.com.json`
|
|
422
|
+
- Focus: size, OAS 3.1, many endpoints, varied parameter shapes
|
|
423
|
+
|
|
424
|
+
3. DigitalOcean API (OpenAPI 3.0)
|
|
425
|
+
- URL: `https://raw.githubusercontent.com/digitalocean/openapi/main/specification/DigitalOcean-public.v2.yaml`
|
|
426
|
+
- Focus: big spec + deref cycles, request bodies, auth schemes
|
|
427
|
+
|
|
428
|
+
4. Stripe API (OpenAPI 3.x, very complex schemas)
|
|
429
|
+
- URL: `https://raw.githubusercontent.com/stripe/openapi/master/openapi/spec3.yaml`
|
|
430
|
+
- Focus: very heavy schema usage (`anyOf`, large components)
|
|
431
|
+
|
|
432
|
+
Smoke harness:
|
|
433
|
+
|
|
434
|
+
- Run the built-in smoke script:
|
|
435
|
+
|
|
436
|
+
```bash
|
|
437
|
+
bun run smoke:specs
|
|
438
|
+
```
|
|
439
|
+
|
|
440
|
+
Or run ad-hoc smoke tests:
|
|
441
|
+
|
|
442
|
+
```bash
|
|
443
|
+
bunx opencli exec <URL> __schema --json --min > /dev/null
|
|
444
|
+
bunx opencli exec <URL> <some-resource> <some-action> --oc-curl
|
|
445
|
+
```
|
|
446
|
+
|
|
447
|
+
Note: Kubernetes publishes a Swagger 2.0 document (`swagger.json`) which is not OpenAPI 3.x. OpenCLI currently expects `openapi: "3.x"` and will reject Swagger 2.0 specs.
|
|
448
|
+
|
|
449
|
+
## Limitations (Important)
|
|
450
|
+
|
|
451
|
+
OpenCLI is intentionally v1-simple; common gaps for real-world specs:
|
|
452
|
+
|
|
453
|
+
- OpenAPI 3.x only (Swagger 2.0 not supported).
|
|
454
|
+
- Parameter serialization is simplified:
|
|
455
|
+
- arrays are always encoded as repeated keys (`?tag=a&tag=b`)
|
|
456
|
+
- does not implement OpenAPI `style` / `explode` / deepObject / etc.
|
|
457
|
+
- Array item types are not tracked yet (arrays treated as string arrays for coercion).
|
|
458
|
+
- Request body convenience flags only support “simple object with scalar properties”.
|
|
459
|
+
- Multipart, binary uploads, and file/form modeling are not implemented.
|
|
460
|
+
- `Content-Type`/`Accept` negotiation is basic (string includes checks for `json`).
|
|
461
|
+
- OAuth2 flows are not implemented (token acquisition is out of scope); oauth2 is treated as bearer token injection.
|
|
462
|
+
|
|
463
|
+
## Development
|
|
464
|
+
|
|
465
|
+
Scripts:
|
|
466
|
+
|
|
467
|
+
- `bun run lint` (Biome CI)
|
|
468
|
+
- `bun run typecheck` (tsgo)
|
|
469
|
+
- `bun test`
|
|
470
|
+
|
|
471
|
+
Repo entry points:
|
|
472
|
+
|
|
473
|
+
- `cli.ts`: main CLI entry with `exec` and `compile` subcommands
|
|
474
|
+
- `src/compiled.ts`: entry point for compiled binaries (embedded spec via Bun macro)
|
package/biome.jsonc
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{}
|
package/bun.lock
ADDED
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
{
|
|
2
|
+
"lockfileVersion": 1,
|
|
3
|
+
"configVersion": 1,
|
|
4
|
+
"workspaces": {
|
|
5
|
+
"": {
|
|
6
|
+
"name": "opencli",
|
|
7
|
+
"dependencies": {
|
|
8
|
+
"@apidevtools/swagger-parser": "^12.1.0",
|
|
9
|
+
"ajv": "^8.17.1",
|
|
10
|
+
"ajv-formats": "^3.0.1",
|
|
11
|
+
"commander": "^14.0.2",
|
|
12
|
+
"openapi-types": "^12.1.3",
|
|
13
|
+
},
|
|
14
|
+
"devDependencies": {
|
|
15
|
+
"@biomejs/biome": "^2.3.11",
|
|
16
|
+
"@types/bun": "^1.3.6",
|
|
17
|
+
"@typescript/native-preview": "^7.0.0-dev.20260120.1",
|
|
18
|
+
},
|
|
19
|
+
},
|
|
20
|
+
},
|
|
21
|
+
"packages": {
|
|
22
|
+
"@apidevtools/json-schema-ref-parser": ["@apidevtools/json-schema-ref-parser@14.0.1", "", { "dependencies": { "@types/json-schema": "^7.0.15", "js-yaml": "^4.1.0" } }, "sha512-Oc96zvmxx1fqoSEdUmfmvvb59/KDOnUoJ7s2t7bISyAn0XEz57LCCw8k2Y4Pf3mwKaZLMciESALORLgfe2frCw=="],
|
|
23
|
+
|
|
24
|
+
"@apidevtools/openapi-schemas": ["@apidevtools/openapi-schemas@2.1.0", "", {}, "sha512-Zc1AlqrJlX3SlpupFGpiLi2EbteyP7fXmUOGup6/DnkRgjP9bgMM/ag+n91rsv0U1Gpz0H3VILA/o3bW7Ua6BQ=="],
|
|
25
|
+
|
|
26
|
+
"@apidevtools/swagger-methods": ["@apidevtools/swagger-methods@3.0.2", "", {}, "sha512-QAkD5kK2b1WfjDS/UQn/qQkbwF31uqRjPTrsCs5ZG9BQGAkjwvqGFjjPqAuzac/IYzpPtRzjCP1WrTuAIjMrXg=="],
|
|
27
|
+
|
|
28
|
+
"@apidevtools/swagger-parser": ["@apidevtools/swagger-parser@12.1.0", "", { "dependencies": { "@apidevtools/json-schema-ref-parser": "14.0.1", "@apidevtools/openapi-schemas": "^2.1.0", "@apidevtools/swagger-methods": "^3.0.2", "ajv": "^8.17.1", "ajv-draft-04": "^1.0.0", "call-me-maybe": "^1.0.2" }, "peerDependencies": { "openapi-types": ">=7" } }, "sha512-e5mJoswsnAX0jG+J09xHFYQXb/bUc5S3pLpMxUuRUA2H8T2kni3yEoyz2R3Dltw5f4A6j6rPNMpWTK+iVDFlng=="],
|
|
29
|
+
|
|
30
|
+
"@biomejs/biome": ["@biomejs/biome@2.3.11", "", { "optionalDependencies": { "@biomejs/cli-darwin-arm64": "2.3.11", "@biomejs/cli-darwin-x64": "2.3.11", "@biomejs/cli-linux-arm64": "2.3.11", "@biomejs/cli-linux-arm64-musl": "2.3.11", "@biomejs/cli-linux-x64": "2.3.11", "@biomejs/cli-linux-x64-musl": "2.3.11", "@biomejs/cli-win32-arm64": "2.3.11", "@biomejs/cli-win32-x64": "2.3.11" }, "bin": { "biome": "bin/biome" } }, "sha512-/zt+6qazBWguPG6+eWmiELqO+9jRsMZ/DBU3lfuU2ngtIQYzymocHhKiZRyrbra4aCOoyTg/BmY+6WH5mv9xmQ=="],
|
|
31
|
+
|
|
32
|
+
"@biomejs/cli-darwin-arm64": ["@biomejs/cli-darwin-arm64@2.3.11", "", { "os": "darwin", "cpu": "arm64" }, "sha512-/uXXkBcPKVQY7rc9Ys2CrlirBJYbpESEDme7RKiBD6MmqR2w3j0+ZZXRIL2xiaNPsIMMNhP1YnA+jRRxoOAFrA=="],
|
|
33
|
+
|
|
34
|
+
"@biomejs/cli-darwin-x64": ["@biomejs/cli-darwin-x64@2.3.11", "", { "os": "darwin", "cpu": "x64" }, "sha512-fh7nnvbweDPm2xEmFjfmq7zSUiox88plgdHF9OIW4i99WnXrAC3o2P3ag9judoUMv8FCSUnlwJCM1B64nO5Fbg=="],
|
|
35
|
+
|
|
36
|
+
"@biomejs/cli-linux-arm64": ["@biomejs/cli-linux-arm64@2.3.11", "", { "os": "linux", "cpu": "arm64" }, "sha512-l4xkGa9E7Uc0/05qU2lMYfN1H+fzzkHgaJoy98wO+b/7Gl78srbCRRgwYSW+BTLixTBrM6Ede5NSBwt7rd/i6g=="],
|
|
37
|
+
|
|
38
|
+
"@biomejs/cli-linux-arm64-musl": ["@biomejs/cli-linux-arm64-musl@2.3.11", "", { "os": "linux", "cpu": "arm64" }, "sha512-XPSQ+XIPZMLaZ6zveQdwNjbX+QdROEd1zPgMwD47zvHV+tCGB88VH+aynyGxAHdzL+Tm/+DtKST5SECs4iwCLg=="],
|
|
39
|
+
|
|
40
|
+
"@biomejs/cli-linux-x64": ["@biomejs/cli-linux-x64@2.3.11", "", { "os": "linux", "cpu": "x64" }, "sha512-/1s9V/H3cSe0r0Mv/Z8JryF5x9ywRxywomqZVLHAoa/uN0eY7F8gEngWKNS5vbbN/BsfpCG5yeBT5ENh50Frxg=="],
|
|
41
|
+
|
|
42
|
+
"@biomejs/cli-linux-x64-musl": ["@biomejs/cli-linux-x64-musl@2.3.11", "", { "os": "linux", "cpu": "x64" }, "sha512-vU7a8wLs5C9yJ4CB8a44r12aXYb8yYgBn+WeyzbMjaCMklzCv1oXr8x+VEyWodgJt9bDmhiaW/I0RHbn7rsNmw=="],
|
|
43
|
+
|
|
44
|
+
"@biomejs/cli-win32-arm64": ["@biomejs/cli-win32-arm64@2.3.11", "", { "os": "win32", "cpu": "arm64" }, "sha512-PZQ6ElCOnkYapSsysiTy0+fYX+agXPlWugh6+eQ6uPKI3vKAqNp6TnMhoM3oY2NltSB89hz59o8xIfOdyhi9Iw=="],
|
|
45
|
+
|
|
46
|
+
"@biomejs/cli-win32-x64": ["@biomejs/cli-win32-x64@2.3.11", "", { "os": "win32", "cpu": "x64" }, "sha512-43VrG813EW+b5+YbDbz31uUsheX+qFKCpXeY9kfdAx+ww3naKxeVkTD9zLIWxUPfJquANMHrmW3wbe/037G0Qg=="],
|
|
47
|
+
|
|
48
|
+
"@types/bun": ["@types/bun@1.3.6", "", { "dependencies": { "bun-types": "1.3.6" } }, "sha512-uWCv6FO/8LcpREhenN1d1b6fcspAB+cefwD7uti8C8VffIv0Um08TKMn98FynpTiU38+y2dUO55T11NgDt8VAA=="],
|
|
49
|
+
|
|
50
|
+
"@types/json-schema": ["@types/json-schema@7.0.15", "", {}, "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA=="],
|
|
51
|
+
|
|
52
|
+
"@types/node": ["@types/node@25.0.9", "", { "dependencies": { "undici-types": "~7.16.0" } }, "sha512-/rpCXHlCWeqClNBwUhDcusJxXYDjZTyE8v5oTO7WbL8eij2nKhUeU89/6xgjU7N4/Vh3He0BtyhJdQbDyhiXAw=="],
|
|
53
|
+
|
|
54
|
+
"@typescript/native-preview": ["@typescript/native-preview@7.0.0-dev.20260120.1", "", { "optionalDependencies": { "@typescript/native-preview-darwin-arm64": "7.0.0-dev.20260120.1", "@typescript/native-preview-darwin-x64": "7.0.0-dev.20260120.1", "@typescript/native-preview-linux-arm": "7.0.0-dev.20260120.1", "@typescript/native-preview-linux-arm64": "7.0.0-dev.20260120.1", "@typescript/native-preview-linux-x64": "7.0.0-dev.20260120.1", "@typescript/native-preview-win32-arm64": "7.0.0-dev.20260120.1", "@typescript/native-preview-win32-x64": "7.0.0-dev.20260120.1" }, "bin": { "tsgo": "bin/tsgo.js" } }, "sha512-nnEf37C9ue7OBRnF2zmV/OCBmV5Y7T/K4mCHa+nxgiXcF/1w8sA0cgdFl+gHQ0mysqUJ+Bu5btAMeWgpLyjrgg=="],
|
|
55
|
+
|
|
56
|
+
"@typescript/native-preview-darwin-arm64": ["@typescript/native-preview-darwin-arm64@7.0.0-dev.20260120.1", "", { "os": "darwin", "cpu": "arm64" }, "sha512-r3pWFuR2H7mn6ScwpH5jJljKQqKto0npVuJSk6pRwFwexpTyxOGmJTZJ1V0AWiisaNxU2+CNAqWFJSJYIE/QTg=="],
|
|
57
|
+
|
|
58
|
+
"@typescript/native-preview-darwin-x64": ["@typescript/native-preview-darwin-x64@7.0.0-dev.20260120.1", "", { "os": "darwin", "cpu": "x64" }, "sha512-cuC1+wLbUP+Ip2UT94G134fqRdp5w3b3dhcCO6/FQ4yXxvRNyv/WK+upHBUFDaeSOeHgDTyO9/QFYUWwC4If1A=="],
|
|
59
|
+
|
|
60
|
+
"@typescript/native-preview-linux-arm": ["@typescript/native-preview-linux-arm@7.0.0-dev.20260120.1", "", { "os": "linux", "cpu": "arm" }, "sha512-vN6OYVySol/kQZjJGmAzd6L30SyVlCgmCXS8WjUYtE5clN0YrzQHop16RK29fYZHMxpkOniVBtRPxUYQANZBlQ=="],
|
|
61
|
+
|
|
62
|
+
"@typescript/native-preview-linux-arm64": ["@typescript/native-preview-linux-arm64@7.0.0-dev.20260120.1", "", { "os": "linux", "cpu": "arm64" }, "sha512-zZGvEGY7wcHYefMZ87KNmvjN3NLIhsCMHEpHZiGCS3khKf+8z6ZsanrzCjOTodvL01VPyBzHxV1EtkSxAcLiQg=="],
|
|
63
|
+
|
|
64
|
+
"@typescript/native-preview-linux-x64": ["@typescript/native-preview-linux-x64@7.0.0-dev.20260120.1", "", { "os": "linux", "cpu": "x64" }, "sha512-JBfNhWd/asd5MDeS3VgRvE24pGKBkmvLub6tsux6ypr+Yhy+o0WaAEzVpmlRYZUqss2ai5tvOu4dzPBXzZAtFw=="],
|
|
65
|
+
|
|
66
|
+
"@typescript/native-preview-win32-arm64": ["@typescript/native-preview-win32-arm64@7.0.0-dev.20260120.1", "", { "os": "win32", "cpu": "arm64" }, "sha512-tTndRtYCq2xwgE0VkTi9ACNiJaV43+PqvBqCxk8ceYi3X36Ve+CCnwlZfZJ4k9NxZthtrAwF/kUmpC9iIYbq1w=="],
|
|
67
|
+
|
|
68
|
+
"@typescript/native-preview-win32-x64": ["@typescript/native-preview-win32-x64@7.0.0-dev.20260120.1", "", { "os": "win32", "cpu": "x64" }, "sha512-oZia7hFL6k9pVepfonuPI86Jmyz6WlJKR57tWCDwRNmpA7odxuTq1PbvcYgy1z4+wHF1nnKKJY0PMAiq6ac18w=="],
|
|
69
|
+
|
|
70
|
+
"ajv": ["ajv@8.17.1", "", { "dependencies": { "fast-deep-equal": "^3.1.3", "fast-uri": "^3.0.1", "json-schema-traverse": "^1.0.0", "require-from-string": "^2.0.2" } }, "sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g=="],
|
|
71
|
+
|
|
72
|
+
"ajv-draft-04": ["ajv-draft-04@1.0.0", "", { "peerDependencies": { "ajv": "^8.5.0" }, "optionalPeers": ["ajv"] }, "sha512-mv00Te6nmYbRp5DCwclxtt7yV/joXJPGS7nM+97GdxvuttCOfgI3K4U25zboyeX0O+myI8ERluxQe5wljMmVIw=="],
|
|
73
|
+
|
|
74
|
+
"ajv-formats": ["ajv-formats@3.0.1", "", { "dependencies": { "ajv": "^8.0.0" } }, "sha512-8iUql50EUR+uUcdRQ3HDqa6EVyo3docL8g5WJ3FNcWmu62IbkGUue/pEyLBW8VGKKucTPgqeks4fIU1DA4yowQ=="],
|
|
75
|
+
|
|
76
|
+
"argparse": ["argparse@2.0.1", "", {}, "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q=="],
|
|
77
|
+
|
|
78
|
+
"bun-types": ["bun-types@1.3.6", "", { "dependencies": { "@types/node": "*" } }, "sha512-OlFwHcnNV99r//9v5IIOgQ9Uk37gZqrNMCcqEaExdkVq3Avwqok1bJFmvGMCkCE0FqzdY8VMOZpfpR3lwI+CsQ=="],
|
|
79
|
+
|
|
80
|
+
"call-me-maybe": ["call-me-maybe@1.0.2", "", {}, "sha512-HpX65o1Hnr9HH25ojC1YGs7HCQLq0GCOibSaWER0eNpgJ/Z1MZv2mTc7+xh6WOPxbRVcmgbv4hGU+uSQ/2xFZQ=="],
|
|
81
|
+
|
|
82
|
+
"commander": ["commander@14.0.2", "", {}, "sha512-TywoWNNRbhoD0BXs1P3ZEScW8W5iKrnbithIl0YH+uCmBd0QpPOA8yc82DS3BIE5Ma6FnBVUsJ7wVUDz4dvOWQ=="],
|
|
83
|
+
|
|
84
|
+
"fast-deep-equal": ["fast-deep-equal@3.1.3", "", {}, "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q=="],
|
|
85
|
+
|
|
86
|
+
"fast-uri": ["fast-uri@3.1.0", "", {}, "sha512-iPeeDKJSWf4IEOasVVrknXpaBV0IApz/gp7S2bb7Z4Lljbl2MGJRqInZiUrQwV16cpzw/D3S5j5Julj/gT52AA=="],
|
|
87
|
+
|
|
88
|
+
"js-yaml": ["js-yaml@4.1.1", "", { "dependencies": { "argparse": "^2.0.1" }, "bin": { "js-yaml": "bin/js-yaml.js" } }, "sha512-qQKT4zQxXl8lLwBtHMWwaTcGfFOZviOJet3Oy/xmGk2gZH677CJM9EvtfdSkgWcATZhj/55JZ0rmy3myCT5lsA=="],
|
|
89
|
+
|
|
90
|
+
"json-schema-traverse": ["json-schema-traverse@1.0.0", "", {}, "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug=="],
|
|
91
|
+
|
|
92
|
+
"openapi-types": ["openapi-types@12.1.3", "", {}, "sha512-N4YtSYJqghVu4iek2ZUvcN/0aqH1kRDuNqzcycDxhOUpg7GdvLa2F3DgS6yBNhInhv2r/6I0Flkn7CqL8+nIcw=="],
|
|
93
|
+
|
|
94
|
+
"require-from-string": ["require-from-string@2.0.2", "", {}, "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw=="],
|
|
95
|
+
|
|
96
|
+
"undici-types": ["undici-types@7.16.0", "", {}, "sha512-Zz+aZWSj8LE6zoxD+xrjh4VfkIG8Ya6LvYkZqtUQGJPZjYl53ypCaUwWqo7eI0x66KBGeRo+mlBEkMSeSZ38Nw=="],
|
|
97
|
+
}
|
|
98
|
+
}
|
package/cli.ts
ADDED
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
#!/usr/bin/env bun
|
|
2
|
+
|
|
3
|
+
import { Command } from "commander";
|
|
4
|
+
|
|
5
|
+
function collect(value: string, previous: string[] = []): string[] {
|
|
6
|
+
return previous.concat([value]);
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
const program = new Command();
|
|
10
|
+
|
|
11
|
+
program.name("opencli").description("Generate CLIs from OpenAPI specs");
|
|
12
|
+
|
|
13
|
+
// ─────────────────────────────────────────────────────────────
|
|
14
|
+
// exec command - runs spec dynamically
|
|
15
|
+
// ─────────────────────────────────────────────────────────────
|
|
16
|
+
program
|
|
17
|
+
.command("exec <spec>")
|
|
18
|
+
.description("Execute commands from an OpenAPI spec")
|
|
19
|
+
.option("--server <url>", "Override server/base URL")
|
|
20
|
+
.option(
|
|
21
|
+
"--server-var <name=value>",
|
|
22
|
+
"Server variable (repeatable)",
|
|
23
|
+
collect,
|
|
24
|
+
[],
|
|
25
|
+
)
|
|
26
|
+
.option("--auth <scheme>", "Select auth scheme")
|
|
27
|
+
.option("--bearer-token <token>", "Bearer token")
|
|
28
|
+
.option("--oauth-token <token>", "OAuth token")
|
|
29
|
+
.option("--username <username>", "Basic auth username")
|
|
30
|
+
.option("--password <password>", "Basic auth password")
|
|
31
|
+
.option("--api-key <key>", "API key value")
|
|
32
|
+
.option("--profile <name>", "Profile name")
|
|
33
|
+
.option("--json", "Machine-readable output")
|
|
34
|
+
.allowUnknownOption()
|
|
35
|
+
.allowExcessArguments()
|
|
36
|
+
.action(async (spec, options, command) => {
|
|
37
|
+
const { execCommand } = await import("./src/cli/exec.ts");
|
|
38
|
+
await execCommand(spec, options, command.args);
|
|
39
|
+
});
|
|
40
|
+
|
|
41
|
+
// ─────────────────────────────────────────────────────────────
|
|
42
|
+
// compile command - creates standalone binary
|
|
43
|
+
// ─────────────────────────────────────────────────────────────
|
|
44
|
+
program
|
|
45
|
+
.command("compile <spec>")
|
|
46
|
+
.description("Compile an OpenAPI spec into a standalone CLI binary")
|
|
47
|
+
.option("--name <name>", "Binary name (default: derived from spec)")
|
|
48
|
+
.option("--outfile <path>", "Output path (default: ./dist/<name>)")
|
|
49
|
+
.option("--target <target>", "Bun compile target (e.g. bun-linux-x64)")
|
|
50
|
+
.option("--minify", "Enable minification")
|
|
51
|
+
.option("--bytecode", "Enable bytecode compilation")
|
|
52
|
+
.option("--no-dotenv", "Disable .env autoload")
|
|
53
|
+
.option("--no-bunfig", "Disable bunfig.toml autoload")
|
|
54
|
+
.option(
|
|
55
|
+
"--exec-argv <arg>",
|
|
56
|
+
"Embedded process.execArgv (repeatable)",
|
|
57
|
+
collect,
|
|
58
|
+
[],
|
|
59
|
+
)
|
|
60
|
+
.option("--define <k=v>", "Build-time constant (repeatable)", collect, [])
|
|
61
|
+
.option("--server <url>", "Default server URL (baked in)")
|
|
62
|
+
.option(
|
|
63
|
+
"--server-var <k=v>",
|
|
64
|
+
"Default server variable (repeatable)",
|
|
65
|
+
collect,
|
|
66
|
+
[],
|
|
67
|
+
)
|
|
68
|
+
.option("--auth <scheme>", "Default auth scheme")
|
|
69
|
+
.action(async (spec, options) => {
|
|
70
|
+
const { compileCommand } = await import("./src/cli/compile.ts");
|
|
71
|
+
await compileCommand(spec, options);
|
|
72
|
+
});
|
|
73
|
+
|
|
74
|
+
await program.parseAsync(process.argv);
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
{
|
|
2
|
+
"openapi": "3.0.3",
|
|
3
|
+
"info": { "title": "Array Items API", "version": "1.0.0" },
|
|
4
|
+
"servers": [{ "url": "https://api.example.com" }],
|
|
5
|
+
"paths": {
|
|
6
|
+
"/things": {
|
|
7
|
+
"get": {
|
|
8
|
+
"operationId": "Things.List",
|
|
9
|
+
"tags": ["Things"],
|
|
10
|
+
"parameters": [
|
|
11
|
+
{
|
|
12
|
+
"in": "query",
|
|
13
|
+
"name": "ids",
|
|
14
|
+
"required": false,
|
|
15
|
+
"schema": { "type": "array", "items": { "type": "integer" } }
|
|
16
|
+
}
|
|
17
|
+
],
|
|
18
|
+
"responses": { "200": { "description": "OK" } }
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
}
|