specli 0.0.1 → 0.0.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (42) hide show
  1. package/README.md +83 -49
  2. package/cli.ts +4 -10
  3. package/package.json +8 -2
  4. package/src/cli/compile.ts +5 -28
  5. package/src/cli/derive-name.ts +2 -2
  6. package/src/cli/exec.ts +1 -1
  7. package/src/cli/main.ts +12 -27
  8. package/src/cli/runtime/auth/resolve.ts +10 -2
  9. package/src/cli/runtime/body-flags.ts +176 -0
  10. package/src/cli/runtime/execute.ts +17 -22
  11. package/src/cli/runtime/generated.ts +23 -54
  12. package/src/cli/runtime/profile/secrets.ts +1 -1
  13. package/src/cli/runtime/profile/store.ts +1 -1
  14. package/src/cli/runtime/request.ts +48 -80
  15. package/src/cli/stable-json.ts +2 -2
  16. package/src/compiled.ts +13 -15
  17. package/src/macros/env.ts +0 -4
  18. package/CLAUDE.md +0 -111
  19. package/PLAN.md +0 -274
  20. package/biome.jsonc +0 -1
  21. package/bun.lock +0 -98
  22. package/fixtures/openapi-array-items.json +0 -22
  23. package/fixtures/openapi-auth.json +0 -34
  24. package/fixtures/openapi-body.json +0 -41
  25. package/fixtures/openapi-collision.json +0 -21
  26. package/fixtures/openapi-oauth.json +0 -54
  27. package/fixtures/openapi-servers.json +0 -35
  28. package/fixtures/openapi.json +0 -87
  29. package/scripts/smoke-specs.ts +0 -64
  30. package/src/cli/auth-requirements.test.ts +0 -27
  31. package/src/cli/auth-schemes.test.ts +0 -66
  32. package/src/cli/capabilities.test.ts +0 -94
  33. package/src/cli/command-id.test.ts +0 -32
  34. package/src/cli/command-model.test.ts +0 -44
  35. package/src/cli/naming.test.ts +0 -86
  36. package/src/cli/operations.test.ts +0 -57
  37. package/src/cli/params.test.ts +0 -70
  38. package/src/cli/positional.test.ts +0 -65
  39. package/src/cli/request-body.test.ts +0 -35
  40. package/src/cli/runtime/request.test.ts +0 -153
  41. package/src/cli/server.test.ts +0 -35
  42. package/tsconfig.json +0 -29
package/README.md CHANGED
@@ -1,6 +1,6 @@
1
- # opencli
1
+ # specli
2
2
 
3
- OpenCLI turns an OpenAPI spec into a non-interactive, “curl replacement” CLI.
3
+ specli turns an OpenAPI spec into a non-interactive, “curl replacement” CLI.
4
4
 
5
5
  It has two modes:
6
6
 
@@ -21,7 +21,7 @@ It works well for a large chunk of “typical” OpenAPI 3.x REST specs:
21
21
  - Path/query/header/cookie parameters.
22
22
  - Request bodies via `--data` / `--file`.
23
23
  - JSON request body parsing + schema validation.
24
- - Expanded JSON body flags for simple object bodies (`--body-*`).
24
+ - Body field flags matching OpenAPI schema properties (including nested objects with dot notation).
25
25
  - Auth injection for common schemes (bearer/basic/apiKey).
26
26
  - A deterministic `__schema` output for introspection.
27
27
 
@@ -38,25 +38,25 @@ bun install
38
38
  Inspect what commands will be generated:
39
39
 
40
40
  ```bash
41
- bunx opencli exec ./fixtures/openapi.json __schema
41
+ bunx specli exec ./fixtures/openapi.json __schema
42
42
  ```
43
43
 
44
44
  Machine-readable schema output:
45
45
 
46
46
  ```bash
47
- bunx opencli exec ./fixtures/openapi.json __schema --json
47
+ bunx specli exec ./fixtures/openapi.json __schema --json
48
48
  ```
49
49
 
50
50
  Minimal schema output (best for large specs):
51
51
 
52
52
  ```bash
53
- bunx opencli exec ./fixtures/openapi.json __schema --json --min
53
+ bunx specli exec ./fixtures/openapi.json __schema --json --min
54
54
  ```
55
55
 
56
56
  Run a generated operation:
57
57
 
58
58
  ```bash
59
- bunx opencli exec ./fixtures/openapi.json contacts list --oc-curl
59
+ bunx specli exec ./fixtures/openapi.json contacts list --curl
60
60
  ```
61
61
 
62
62
  ## Build a Standalone Executable
@@ -65,27 +65,27 @@ Use the `compile` command to create a standalone binary with the spec embedded:
65
65
 
66
66
  ```bash
67
67
  # compile with auto-derived name (from spec title)
68
- bunx opencli compile ./path/to/openapi.yaml
68
+ bunx specli compile ./path/to/openapi.yaml
69
69
  # → ./dist/my-api (derived from info.title)
70
70
 
71
71
  # compile with explicit name
72
- bunx opencli compile ./path/to/openapi.yaml --name myapi
72
+ bunx specli compile ./path/to/openapi.yaml --name myapi
73
73
  # → ./dist/myapi
74
74
 
75
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
76
+ bunx specli compile https://api.vercel.com/copper/_openapi.json --target bun-linux-x64 --outfile ./dist/copper-linux
77
77
 
78
78
  # disable runtime config loading for deterministic behavior
79
- bunx opencli compile ./path/to/openapi.yaml --no-dotenv --no-bunfig
79
+ bunx specli compile ./path/to/openapi.yaml --no-dotenv --no-bunfig
80
80
 
81
81
  # bake in defaults (these become default flags; runtime flags override)
82
- bunx opencli compile https://api.vercel.com/copper/_openapi.json \
82
+ bunx specli compile https://api.vercel.com/copper/_openapi.json \
83
83
  --name copper \
84
84
  --server https://api.vercel.com \
85
85
  --auth VercelOidc
86
86
  ```
87
87
 
88
- The compiled binary is a root CLI - no `opencli` prefix needed:
88
+ The compiled binary is a root CLI - no `specli` prefix needed:
89
89
 
90
90
  ```bash
91
91
  ./dist/copper contacts list
@@ -100,10 +100,10 @@ Notes:
100
100
 
101
101
  ## CLI Shape
102
102
 
103
- OpenCLI generates commands of the form:
103
+ specli generates commands of the form:
104
104
 
105
105
  ```
106
- opencli <resource> <action> [...positionals] [options]
106
+ specli <resource> <action> [...positionals] [options]
107
107
  ```
108
108
 
109
109
  - `resource` comes from `tags[0]`, `operationId` prefix, or the first path segment (heuristics).
@@ -119,7 +119,7 @@ Available on the root command:
119
119
  - `--spec <urlOrPath>`: OpenAPI URL or file path (only needed for compiled binaries to override embedded spec)
120
120
  - `--server <url>`: override server/base URL
121
121
  - `--server-var <name=value>`: server URL template variable (repeatable)
122
- - `--profile <name>`: profile name (config under `~/.config/opencli`)
122
+ - `--profile <name>`: profile name (config under `~/.config/specli`)
123
123
 
124
124
  Auth selection + credentials:
125
125
 
@@ -189,9 +189,9 @@ Array parameters are treated as repeatable flags and appended to the query strin
189
189
  All of these become `?tag=a&tag=b`:
190
190
 
191
191
  ```bash
192
- opencli ... --tag a --tag b
193
- opencli ... --tag a,b
194
- opencli ... --tag '["a","b"]'
192
+ specli ... --tag a --tag b
193
+ specli ... --tag a,b
194
+ specli ... --tag '["a","b"]'
195
195
  ```
196
196
 
197
197
  Implementation notes:
@@ -207,14 +207,14 @@ If an operation has a `requestBody`, you may provide a body via:
207
207
 
208
208
  - `--data <string>`
209
209
  - `--file <path>`
210
- - Expanded `--body-*` flags (JSON-only; see below)
210
+ - Body field flags (JSON-only; see below)
211
211
 
212
212
  Rules:
213
213
 
214
214
  - `--data` and `--file` are mutually exclusive.
215
- - Expanded `--body-*` flags cannot be used with `--data` or `--file`.
215
+ - Body field flags cannot be used with `--data` or `--file`.
216
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.`
217
+ - `Missing request body. Provide --data, --file, or body field flags.`
218
218
 
219
219
  ### Content-Type
220
220
 
@@ -236,7 +236,7 @@ If `Content-Type` does not include `json`:
236
236
 
237
237
  ### Schema Validation (Ajv)
238
238
 
239
- OpenCLI uses Ajv (best-effort, `strict: false`) to validate:
239
+ specli uses Ajv (best-effort, `strict: false`) to validate:
240
240
 
241
241
  - query/header/cookie params
242
242
  - JSON request bodies when a requestBody schema is available
@@ -245,17 +245,18 @@ Validation errors are formatted into a readable multiline message. For `required
245
245
 
246
246
  - `/<path> missing required property '<name>'`
247
247
 
248
- ### Expanded JSON Body Flags (`--body-*`)
248
+ ### Body Field Flags
249
249
 
250
- When an operation has a `requestBody` and the preferred schema is a JSON object with scalar properties, OpenCLI generates convenience flags:
250
+ When an operation has a `requestBody` and the preferred schema is a JSON object, specli generates convenience flags that match the property names:
251
251
 
252
- - For `string|number|integer`: `--body-<prop> <value>`
253
- - For `boolean`: `--body-<prop>` (presence sets it to `true`)
252
+ - For `string|number|integer`: `--<prop> <value>`
253
+ - For `boolean`: `--<prop>` (presence sets it to `true`)
254
+ - For nested objects: `--<parent>.<child> <value>` (dot notation)
254
255
 
255
256
  Example (from `fixtures/openapi-body.json`):
256
257
 
257
258
  ```bash
258
- bunx opencli exec ./fixtures/openapi-body.json contacts create --body-name "Ada" --oc-curl
259
+ bunx specli exec ./fixtures/openapi-body.json contacts create --name "Ada" --curl
259
260
  ```
260
261
 
261
262
  Produces a JSON body:
@@ -264,16 +265,49 @@ Produces a JSON body:
264
265
  {"name":"Ada"}
265
266
  ```
266
267
 
268
+ #### Nested Objects
269
+
270
+ Nested object properties use dot notation. For a schema like:
271
+
272
+ ```json
273
+ {
274
+ "type": "object",
275
+ "properties": {
276
+ "name": { "type": "string" },
277
+ "address": {
278
+ "type": "object",
279
+ "properties": {
280
+ "street": { "type": "string" },
281
+ "city": { "type": "string" }
282
+ }
283
+ }
284
+ }
285
+ }
286
+ ```
287
+
288
+ You can use:
289
+
290
+ ```bash
291
+ mycli contacts create --name "Ada" --address.street "123 Main St" --address.city "NYC"
292
+ ```
293
+
294
+ Which produces:
295
+
296
+ ```json
297
+ {"name":"Ada","address":{"street":"123 Main St","city":"NYC"}}
298
+ ```
299
+
267
300
  Notes / edge cases:
268
301
 
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.`
302
+ - Body field flags are only supported for JSON bodies. If you try to use them without a JSON content type, specli errors.
303
+ - Required fields in the schema are checked in a "friendly" way:
304
+ - `Missing required body field 'name'. Provide --name or use --data/--file.`
305
+ - If a body field flag conflicts with an operation parameter flag, the operation parameter takes precedence.
272
306
  - Numeric coercion uses `Number(...)` / `parseInt(...)`. Today it does not explicitly reject `NaN` (this is an area to harden).
273
307
 
274
308
  ## Servers
275
309
 
276
- OpenCLI resolves the request base URL in this order:
310
+ specli resolves the request base URL in this order:
277
311
 
278
312
  1. `--server <url>`
279
313
  2. profile `server` (if `--profile` is set and the profile has a server)
@@ -288,7 +322,7 @@ If the chosen server URL has template variables (e.g. `https://{region}.api.exam
288
322
 
289
323
  ### Supported Scheme Kinds
290
324
 
291
- From `components.securitySchemes`, OpenCLI recognizes:
325
+ From `components.securitySchemes`, specli recognizes:
292
326
 
293
327
  - HTTP bearer (`type: http`, `scheme: bearer`)
294
328
  - HTTP basic (`type: http`, `scheme: basic`)
@@ -312,7 +346,7 @@ This “only if present in current spec” behavior prevents accidental auth lea
312
346
  Bearer-like schemes (`http-bearer`, `oauth2`, `openIdConnect`):
313
347
 
314
348
  - `--bearer-token <token>` or `--oauth-token <token>`
315
- - or a profile token stored via `opencli auth token ...`
349
+ - or a profile token stored via `specli auth token ...`
316
350
 
317
351
  Basic auth:
318
352
 
@@ -328,8 +362,8 @@ Profiles are for automation.
328
362
 
329
363
  Config file:
330
364
 
331
- - Read preference: `~/.config/opencli/profiles.json`, else `~/.config/opencli/profiles.yaml` if present
332
- - Writes always go to: `~/.config/opencli/profiles.json`
365
+ - Read preference: `~/.config/specli/profiles.json`, else `~/.config/specli/profiles.yaml` if present
366
+ - Writes always go to: `~/.config/specli/profiles.json`
333
367
 
334
368
  Secrets:
335
369
 
@@ -338,14 +372,14 @@ Secrets:
338
372
  Commands:
339
373
 
340
374
  ```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
375
+ specli profile list
376
+ specli profile set --name dev --server https://api.example.com --auth bearerAuth --default
377
+ specli profile use --name dev
378
+ specli profile rm --name dev
379
+
380
+ specli auth token --name dev --set "..."
381
+ specli auth token --name dev --get
382
+ specli auth token --name dev --delete
349
383
  ```
350
384
 
351
385
  ## Output Behavior
@@ -395,7 +429,7 @@ Prints the method, URL, headers, and body that would be sent without sending the
395
429
 
396
430
  ## `__schema`
397
431
 
398
- `opencli __schema` reports:
432
+ `specli __schema` reports:
399
433
 
400
434
  - OpenAPI title/version
401
435
  - spec source + computed spec id + fingerprint
@@ -440,15 +474,15 @@ bun run smoke:specs
440
474
  Or run ad-hoc smoke tests:
441
475
 
442
476
  ```bash
443
- bunx opencli exec <URL> __schema --json --min > /dev/null
444
- bunx opencli exec <URL> <some-resource> <some-action> --oc-curl
477
+ bunx specli exec <URL> __schema --json --min > /dev/null
478
+ bunx specli exec <URL> <some-resource> <some-action> --curl
445
479
  ```
446
480
 
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.
481
+ Note: Kubernetes publishes a Swagger 2.0 document (`swagger.json`) which is not OpenAPI 3.x. specli currently expects `openapi: "3.x"` and will reject Swagger 2.0 specs.
448
482
 
449
483
  ## Limitations (Important)
450
484
 
451
- OpenCLI is intentionally v1-simple; common gaps for real-world specs:
485
+ specli is intentionally v1-simple; common gaps for real-world specs:
452
486
 
453
487
  - OpenAPI 3.x only (Swagger 2.0 not supported).
454
488
  - Parameter serialization is simplified:
package/cli.ts CHANGED
@@ -8,7 +8,7 @@ function collect(value: string, previous: string[] = []): string[] {
8
8
 
9
9
  const program = new Command();
10
10
 
11
- program.name("opencli").description("Generate CLIs from OpenAPI specs");
11
+ program.name("specli").description("Generate CLIs from OpenAPI specs");
12
12
 
13
13
  // ─────────────────────────────────────────────────────────────
14
14
  // exec command - runs spec dynamically
@@ -51,21 +51,15 @@ program
51
51
  .option("--bytecode", "Enable bytecode compilation")
52
52
  .option("--no-dotenv", "Disable .env autoload")
53
53
  .option("--no-bunfig", "Disable bunfig.toml autoload")
54
- .option(
55
- "--exec-argv <arg>",
56
- "Embedded process.execArgv (repeatable)",
57
- collect,
58
- [],
59
- )
60
54
  .option("--define <k=v>", "Build-time constant (repeatable)", collect, [])
61
- .option("--server <url>", "Default server URL (baked in)")
55
+ .option("--server <url>", "Default server URL (embedded)")
62
56
  .option(
63
57
  "--server-var <k=v>",
64
- "Default server variable (repeatable)",
58
+ "Default server variable (repeatable, embedded)",
65
59
  collect,
66
60
  [],
67
61
  )
68
- .option("--auth <scheme>", "Default auth scheme")
62
+ .option("--auth <scheme>", "Default auth scheme (embedded)")
69
63
  .action(async (spec, options) => {
70
64
  const { compileCommand } = await import("./src/cli/compile.ts");
71
65
  await compileCommand(spec, options);
package/package.json CHANGED
@@ -2,10 +2,16 @@
2
2
  "name": "specli",
3
3
  "module": "index.ts",
4
4
  "type": "module",
5
- "version": "0.0.1",
5
+ "version": "0.0.2",
6
6
  "bin": {
7
- "opencli": "./cli.ts"
7
+ "specli": "./cli.ts"
8
8
  },
9
+ "files": [
10
+ "cli.ts",
11
+ "index.ts",
12
+ "src",
13
+ "!**/*.test.ts"
14
+ ],
9
15
  "scripts": {
10
16
  "lint": "biome ci",
11
17
  "lint:check": "biome check --write --unsafe",
@@ -8,7 +8,6 @@ export type CompileOptions = {
8
8
  bytecode?: boolean;
9
9
  dotenv?: boolean; // --no-dotenv sets this to false
10
10
  bunfig?: boolean; // --no-bunfig sets this to false
11
- execArgv?: string[];
12
11
  define?: string[];
13
12
  server?: string;
14
13
  serverVar?: string[];
@@ -37,23 +36,6 @@ export async function compileCommand(
37
36
  ? (options.target as Bun.Build.Target)
38
37
  : (`bun-${process.platform}-${process.arch}` as Bun.Build.Target);
39
38
 
40
- // Build embedded execArgv for runtime defaults
41
- const embeddedExecArgv: string[] = [];
42
- if (options.server) {
43
- embeddedExecArgv.push(`--server=${options.server}`);
44
- }
45
- if (options.serverVar) {
46
- for (const pair of options.serverVar) {
47
- embeddedExecArgv.push(`--server-var=${pair}`);
48
- }
49
- }
50
- if (options.auth) {
51
- embeddedExecArgv.push(`--auth=${options.auth}`);
52
- }
53
-
54
- // User-provided exec-argv
55
- const compileExecArgv = options.execArgv ?? [];
56
-
57
39
  // Parse --define pairs
58
40
  const define: Record<string, string> = {};
59
41
  if (options.define) {
@@ -78,14 +60,6 @@ export async function compileCommand(
78
60
  buildArgs.push("--define", `${k}=${v}`);
79
61
  }
80
62
 
81
- const execArgv =
82
- embeddedExecArgv.length || compileExecArgv.length
83
- ? [...embeddedExecArgv, ...compileExecArgv]
84
- : [];
85
- for (const arg of execArgv) {
86
- buildArgs.push("--compile-exec-argv", arg);
87
- }
88
-
89
63
  if (options.dotenv === false) buildArgs.push("--no-compile-autoload-dotenv");
90
64
  if (options.bunfig === false) buildArgs.push("--no-compile-autoload-bunfig");
91
65
 
@@ -97,8 +71,11 @@ export async function compileCommand(
97
71
  stderr: "pipe",
98
72
  env: {
99
73
  ...process.env,
100
- OPENCLI_EMBED_SPEC: spec,
101
- OPENCLI_CLI_NAME: name,
74
+ SPECLI_SPEC: spec,
75
+ SPECLI_NAME: name,
76
+ SPECLI_SERVER: options.server ?? "",
77
+ SPECLI_SERVER_VARS: options.serverVar?.join(",") ?? "",
78
+ SPECLI_AUTH: options.auth ?? "",
102
79
  },
103
80
  });
104
81
 
@@ -12,7 +12,7 @@ const RESERVED_NAMES = [
12
12
  * Priority:
13
13
  * 1. info.title (kebab-cased, sanitized)
14
14
  * 2. Host from spec URL (if URL provided)
15
- * 3. Fallback to "opencli"
15
+ * 3. Fallback to "specli"
16
16
  */
17
17
  export async function deriveBinaryName(spec: string): Promise<string> {
18
18
  try {
@@ -48,7 +48,7 @@ export async function deriveBinaryName(spec: string): Promise<string> {
48
48
  }
49
49
 
50
50
  // Fallback
51
- return "opencli";
51
+ return "specli";
52
52
  }
53
53
 
54
54
  async function loadSpecText(spec: string): Promise<string> {
package/src/cli/exec.ts CHANGED
@@ -26,7 +26,7 @@ export async function execCommand(
26
26
  // [node, script, --spec, <spec>, ...options, ...remainingArgs]
27
27
  const argv = [
28
28
  process.argv[0] ?? "bun",
29
- process.argv[1] ?? "opencli",
29
+ process.argv[1] ?? "specli",
30
30
  "--spec",
31
31
  spec,
32
32
  ];
package/src/cli/main.ts CHANGED
@@ -18,13 +18,16 @@ import { stableStringify } from "./stable-json.ts";
18
18
  type MainOptions = {
19
19
  embeddedSpecText?: string;
20
20
  cliName?: string;
21
+ server?: string;
22
+ serverVars?: string[];
23
+ auth?: string;
21
24
  };
22
25
 
23
26
  export async function main(argv: string[], options: MainOptions = {}) {
24
27
  const program = new Command();
25
28
 
26
29
  program
27
- .name(options.cliName ?? "opencli")
30
+ .name(options.cliName ?? "specli")
28
31
  .description("Generate a CLI from an OpenAPI spec")
29
32
  .option("--spec <urlOrPath>", "OpenAPI URL or file path")
30
33
  .option("--server <url>", "Override server/base URL")
@@ -39,31 +42,10 @@ export async function main(argv: string[], options: MainOptions = {}) {
39
42
  .option("--username <username>", "Basic auth username")
40
43
  .option("--password <password>", "Basic auth password")
41
44
  .option("--api-key <key>", "API key value")
42
- .option("--profile <name>", "Profile name (stored under ~/.config/opencli)")
45
+ .option("--profile <name>", "Profile name (stored under ~/.config/specli)")
43
46
  .option("--json", "Machine-readable output")
44
47
  .showHelpAfterError();
45
48
 
46
- // Provide namespaced variants for flags which may collide with operation flags.
47
- // (Some real-world APIs define parameters named "accept", etc.)
48
- program
49
- .option(
50
- "--oc-header <header>",
51
- "Extra header (repeatable, namespaced)",
52
- collectRepeatable,
53
- )
54
- .option("--oc-accept <type>", "Override Accept header (namespaced)")
55
- .option("--oc-status", "Include status in --json output (namespaced)")
56
- .option("--oc-headers", "Include headers in --json output (namespaced)")
57
- .option("--oc-dry-run", "Print request without sending (namespaced)")
58
- .option("--oc-curl", "Print curl command without sending (namespaced)")
59
- .option("--oc-timeout <ms>", "Request timeout in milliseconds (namespaced)")
60
- .option("--oc-data <data>", "Inline request body (namespaced)")
61
- .option("--oc-file <path>", "Request body from file (namespaced)")
62
- .option(
63
- "--oc-content-type <type>",
64
- "Override Content-Type (defaults from OpenAPI) (namespaced)",
65
- );
66
-
67
49
  // If user asks for help and we have no embedded spec and no --spec, show minimal help.
68
50
  const spec = getArgValue(argv, "--spec");
69
51
  const wantsHelp = hasAnyArg(argv, ["-h", "--help"]);
@@ -83,7 +65,7 @@ export async function main(argv: string[], options: MainOptions = {}) {
83
65
 
84
66
  const profileCmd = program
85
67
  .command("profile")
86
- .description("Manage OpenCLI profiles");
68
+ .description("Manage specli profiles");
87
69
 
88
70
  profileCmd
89
71
  .command("list")
@@ -318,9 +300,7 @@ export async function main(argv: string[], options: MainOptions = {}) {
318
300
  const args = op.pathArgs.length
319
301
  ? ` ${op.pathArgs.map((a) => `<${a}>`).join(" ")}`
320
302
  : "";
321
- process.stdout.write(
322
- `- opencli ${op.resource} ${op.action}${args}\n`,
323
- );
303
+ process.stdout.write(`- specli ${op.resource} ${op.action}${args}\n`);
324
304
  }
325
305
  }
326
306
  });
@@ -330,6 +310,11 @@ export async function main(argv: string[], options: MainOptions = {}) {
330
310
  authSchemes: ctx.authSchemes,
331
311
  commands: ctx.commands,
332
312
  specId: ctx.loaded.id,
313
+ embeddedDefaults: {
314
+ server: options.server,
315
+ serverVars: options.serverVars,
316
+ auth: options.auth,
317
+ },
333
318
  });
334
319
 
335
320
  await program.parseAsync(argv);
@@ -1,8 +1,9 @@
1
1
  import type { AuthScheme } from "../../auth-schemes.ts";
2
2
 
3
3
  export type AuthInputs = {
4
- profileAuthScheme?: string;
5
4
  flagAuthScheme?: string;
5
+ profileAuthScheme?: string;
6
+ embeddedAuthScheme?: string;
6
7
  };
7
8
 
8
9
  export function resolveAuthScheme(
@@ -10,7 +11,7 @@ export function resolveAuthScheme(
10
11
  required: import("../../auth-requirements.ts").AuthSummary,
11
12
  inputs: AuthInputs,
12
13
  ): string | undefined {
13
- // Explicit flag wins (but may still be validated later when applying).
14
+ // Priority: CLI flag > profile > embedded default
14
15
  if (inputs.flagAuthScheme) return inputs.flagAuthScheme;
15
16
 
16
17
  if (
@@ -20,6 +21,13 @@ export function resolveAuthScheme(
20
21
  return inputs.profileAuthScheme;
21
22
  }
22
23
 
24
+ if (
25
+ inputs.embeddedAuthScheme &&
26
+ authSchemes.some((s) => s.key === inputs.embeddedAuthScheme)
27
+ ) {
28
+ return inputs.embeddedAuthScheme;
29
+ }
30
+
23
31
  // If operation requires exactly one scheme, choose it.
24
32
  const alts = required.alternatives;
25
33
  if (alts.length === 1 && alts[0]?.length === 1) return alts[0][0]?.key;