specli 0.0.21 → 0.0.23

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 (34) hide show
  1. package/README.md +58 -0
  2. package/dist/index.d.ts +106 -0
  3. package/dist/index.js +114 -0
  4. package/package.json +22 -16
  5. package/dist/ai/tools.test.d.ts +0 -1
  6. package/dist/ai/tools.test.js +0 -49
  7. package/dist/cli/model/capabilities.test.d.ts +0 -1
  8. package/dist/cli/model/capabilities.test.js +0 -84
  9. package/dist/cli/model/command-id.test.d.ts +0 -1
  10. package/dist/cli/model/command-id.test.js +0 -27
  11. package/dist/cli/model/command-model.test.d.ts +0 -1
  12. package/dist/cli/model/command-model.test.js +0 -40
  13. package/dist/cli/model/naming.test.d.ts +0 -1
  14. package/dist/cli/model/naming.test.js +0 -75
  15. package/dist/cli/parse/auth-requirements.test.d.ts +0 -1
  16. package/dist/cli/parse/auth-requirements.test.js +0 -16
  17. package/dist/cli/parse/auth-schemes.test.d.ts +0 -1
  18. package/dist/cli/parse/auth-schemes.test.js +0 -56
  19. package/dist/cli/parse/operations.test.d.ts +0 -1
  20. package/dist/cli/parse/operations.test.js +0 -51
  21. package/dist/cli/parse/params.test.d.ts +0 -1
  22. package/dist/cli/parse/params.test.js +0 -62
  23. package/dist/cli/parse/positional.test.d.ts +0 -1
  24. package/dist/cli/parse/positional.test.js +0 -60
  25. package/dist/cli/parse/request-body.test.d.ts +0 -1
  26. package/dist/cli/parse/request-body.test.js +0 -31
  27. package/dist/cli/parse/servers.test.d.ts +0 -1
  28. package/dist/cli/parse/servers.test.js +0 -49
  29. package/dist/cli/runtime/body-flags.test.d.ts +0 -1
  30. package/dist/cli/runtime/body-flags.test.js +0 -192
  31. package/dist/cli/runtime/request.test.d.ts +0 -1
  32. package/dist/cli/runtime/request.test.js +0 -332
  33. package/dist/cli/runtime/validate/coerce.test.d.ts +0 -1
  34. package/dist/cli/runtime/validate/coerce.test.js +0 -75
@@ -1,56 +0,0 @@
1
- import { describe, expect, test } from "bun:test";
2
- import { listAuthSchemes } from "./auth-schemes.js";
3
- describe("listAuthSchemes", () => {
4
- test("parses bearer + apiKey", () => {
5
- const doc = {
6
- openapi: "3.0.3",
7
- components: {
8
- securitySchemes: {
9
- bearerAuth: {
10
- type: "http",
11
- scheme: "bearer",
12
- bearerFormat: "JWT",
13
- },
14
- apiKeyAuth: {
15
- type: "apiKey",
16
- in: "header",
17
- name: "X-API-Key",
18
- },
19
- },
20
- },
21
- };
22
- const schemes = listAuthSchemes(doc);
23
- expect(schemes).toHaveLength(2);
24
- const bearer = schemes.find((s) => s.key === "bearerAuth");
25
- expect(bearer?.kind).toBe("http-bearer");
26
- const apiKey = schemes.find((s) => s.key === "apiKeyAuth");
27
- expect(apiKey?.kind).toBe("api-key");
28
- expect(apiKey?.in).toBe("header");
29
- expect(apiKey?.name).toBe("X-API-Key");
30
- });
31
- test("parses oauth2 flows", () => {
32
- const doc = {
33
- openapi: "3.0.3",
34
- components: {
35
- securitySchemes: {
36
- oauth: {
37
- type: "oauth2",
38
- flows: {
39
- clientCredentials: {
40
- tokenUrl: "https://example.com/oauth/token",
41
- scopes: {
42
- "read:ping": "read ping",
43
- },
44
- },
45
- },
46
- },
47
- },
48
- },
49
- };
50
- const schemes = listAuthSchemes(doc);
51
- const oauth = schemes.find((s) => s.key === "oauth");
52
- expect(oauth?.kind).toBe("oauth2");
53
- expect(oauth?.oauthFlows?.clientCredentials?.tokenUrl).toBe("https://example.com/oauth/token");
54
- expect(oauth?.oauthFlows?.clientCredentials?.scopes).toEqual(["read:ping"]);
55
- });
56
- });
@@ -1 +0,0 @@
1
- export {};
@@ -1,51 +0,0 @@
1
- import { describe, expect, test } from "bun:test";
2
- import { indexOperations } from "./operations.js";
3
- describe("indexOperations", () => {
4
- test("indexes basic operations", () => {
5
- const doc = {
6
- openapi: "3.0.3",
7
- paths: {
8
- "/contacts": {
9
- get: {
10
- operationId: "Contacts.List",
11
- tags: ["Contacts"],
12
- parameters: [
13
- {
14
- in: "query",
15
- name: "limit",
16
- schema: { type: "integer" },
17
- },
18
- ],
19
- },
20
- },
21
- "/contacts/{id}": {
22
- get: {
23
- operationId: "Contacts.Get",
24
- tags: ["Contacts"],
25
- parameters: [
26
- {
27
- in: "path",
28
- name: "id",
29
- required: true,
30
- schema: { type: "string" },
31
- },
32
- ],
33
- },
34
- },
35
- },
36
- };
37
- const ops = indexOperations(doc);
38
- expect(ops).toHaveLength(2);
39
- expect(ops[0]?.key).toBe("GET /contacts");
40
- expect(ops[0]?.path).toBe("/contacts");
41
- expect(ops[0]?.method).toBe("GET");
42
- expect(ops[0]?.parameters).toHaveLength(1);
43
- expect(ops[0]?.parameters[0]?.in).toBe("query");
44
- expect(ops[1]?.key).toBe("GET /contacts/{id}");
45
- expect(ops[1]?.path).toBe("/contacts/{id}");
46
- expect(ops[1]?.method).toBe("GET");
47
- expect(ops[1]?.parameters).toHaveLength(1);
48
- expect(ops[1]?.parameters[0]?.in).toBe("path");
49
- expect(ops[1]?.parameters[0]?.required).toBe(true);
50
- });
51
- });
@@ -1 +0,0 @@
1
- export {};
@@ -1,62 +0,0 @@
1
- import { describe, expect, test } from "bun:test";
2
- import { deriveParamSpecs } from "./params.js";
3
- describe("deriveParamSpecs", () => {
4
- test("derives basic types + flags", () => {
5
- const op = {
6
- key: "GET /contacts",
7
- method: "GET",
8
- path: "/contacts",
9
- tags: [],
10
- parameters: [
11
- {
12
- in: "query",
13
- name: "limit",
14
- required: false,
15
- schema: {
16
- type: "integer",
17
- format: "int32",
18
- enum: ["1", "2"],
19
- },
20
- },
21
- {
22
- in: "header",
23
- name: "X-Request-Id",
24
- required: false,
25
- schema: { type: "string" },
26
- },
27
- ],
28
- };
29
- const specs = deriveParamSpecs(op);
30
- expect(specs).toHaveLength(2);
31
- const limit = specs.find((p) => p.name === "limit");
32
- expect(limit?.kind).toBe("flag");
33
- expect(limit?.flag).toBe("--limit");
34
- expect(limit?.type).toBe("integer");
35
- expect(limit?.format).toBe("int32");
36
- expect(limit?.enum).toEqual(["1", "2"]);
37
- const reqId = specs.find((p) => p.name === "X-Request-Id");
38
- expect(reqId?.kind).toBe("flag");
39
- expect(reqId?.flag).toBe("--x-request-id");
40
- expect(reqId?.type).toBe("string");
41
- });
42
- test("derives array item types", () => {
43
- const op = {
44
- key: "GET /things",
45
- method: "GET",
46
- path: "/things",
47
- tags: [],
48
- parameters: [
49
- {
50
- in: "query",
51
- name: "ids",
52
- required: false,
53
- schema: { type: "array", items: { type: "integer" } },
54
- },
55
- ],
56
- };
57
- const specs = deriveParamSpecs(op);
58
- expect(specs).toHaveLength(1);
59
- expect(specs[0]?.type).toBe("array");
60
- expect(specs[0]?.itemType).toBe("integer");
61
- });
62
- });
@@ -1 +0,0 @@
1
- export {};
@@ -1,60 +0,0 @@
1
- import { describe, expect, test } from "bun:test";
2
- import { deriveFlags, derivePositionals } from "./positional.js";
3
- describe("derivePositionals", () => {
4
- test("returns ordered positionals from pathArgs", () => {
5
- const action = {
6
- pathArgs: ["id"],
7
- params: [
8
- {
9
- kind: "positional",
10
- in: "path",
11
- name: "id",
12
- flag: "--id",
13
- required: true,
14
- type: "string",
15
- },
16
- ],
17
- };
18
- const pos = derivePositionals(action);
19
- expect(pos).toEqual([
20
- {
21
- name: "id",
22
- required: true,
23
- type: "string",
24
- format: undefined,
25
- enum: undefined,
26
- description: undefined,
27
- },
28
- ]);
29
- });
30
- });
31
- describe("deriveFlags", () => {
32
- test("returns only flag params", () => {
33
- const action = {
34
- pathArgs: [],
35
- params: [
36
- {
37
- kind: "flag",
38
- in: "query",
39
- name: "limit",
40
- flag: "--limit",
41
- required: false,
42
- type: "integer",
43
- },
44
- ],
45
- };
46
- const flags = deriveFlags(action);
47
- expect(flags.flags).toEqual([
48
- {
49
- in: "query",
50
- name: "limit",
51
- flag: "--limit",
52
- required: false,
53
- description: undefined,
54
- type: "integer",
55
- format: undefined,
56
- enum: undefined,
57
- },
58
- ]);
59
- });
60
- });
@@ -1 +0,0 @@
1
- export {};
@@ -1,31 +0,0 @@
1
- import { describe, expect, test } from "bun:test";
2
- import { deriveRequestBodyInfo } from "./request-body.js";
3
- describe("deriveRequestBodyInfo", () => {
4
- test("summarizes content types and convenience flags", () => {
5
- const op = {
6
- key: "POST /contacts",
7
- method: "POST",
8
- path: "/contacts",
9
- tags: [],
10
- parameters: [],
11
- requestBody: {
12
- required: true,
13
- contentTypes: ["application/x-www-form-urlencoded", "application/json"],
14
- schemasByContentType: {
15
- "application/json": { type: "object" },
16
- "application/x-www-form-urlencoded": { type: "object" },
17
- },
18
- },
19
- };
20
- const info = deriveRequestBodyInfo(op);
21
- expect(info?.required).toBe(true);
22
- expect(info?.hasJson).toBe(true);
23
- expect(info?.hasFormUrlEncoded).toBe(true);
24
- expect(info?.hasMultipart).toBe(false);
25
- expect(info?.content.map((c) => c.contentType)).toEqual([
26
- "application/json",
27
- "application/x-www-form-urlencoded",
28
- ]);
29
- expect(info?.preferredSchema).toEqual({ type: "object" });
30
- });
31
- });
@@ -1 +0,0 @@
1
- export {};
@@ -1,49 +0,0 @@
1
- import { describe, expect, test } from "bun:test";
2
- import { listServers } from "./servers.js";
3
- describe("listServers", () => {
4
- test("extracts server variables from template", () => {
5
- const doc = {
6
- openapi: "3.0.3",
7
- servers: [
8
- {
9
- url: "https://{region}.api.example.com/{basePath}",
10
- variables: {
11
- region: {
12
- default: "us",
13
- enum: ["us", "eu"],
14
- },
15
- basePath: {
16
- default: "v1",
17
- },
18
- },
19
- },
20
- ],
21
- };
22
- const servers = listServers(doc);
23
- expect(servers).toHaveLength(1);
24
- expect(servers[0]?.variableNames).toEqual(["region", "basePath"]);
25
- expect(servers[0]?.variables.map((v) => v.name)).toEqual([
26
- "region",
27
- "basePath",
28
- ]);
29
- expect(servers[0]?.variables[0]?.enum).toEqual(["us", "eu"]);
30
- });
31
- test("includes servers defined on paths and operations", () => {
32
- const doc = {
33
- openapi: "3.0.3",
34
- paths: {
35
- "/v1/forecast": {
36
- servers: [{ url: "https://api.a.example.com" }],
37
- get: {
38
- servers: [{ url: "https://api.b.example.com" }],
39
- },
40
- },
41
- },
42
- };
43
- const servers = listServers(doc);
44
- expect(servers.map((s) => s.url)).toEqual([
45
- "https://api.a.example.com",
46
- "https://api.b.example.com",
47
- ]);
48
- });
49
- });
@@ -1 +0,0 @@
1
- export {};
@@ -1,192 +0,0 @@
1
- import { describe, expect, test } from "bun:test";
2
- import { findMissingRequired, generateBodyFlags, parseDotNotationFlags, } from "./body-flags.js";
3
- describe("generateBodyFlags", () => {
4
- test("generates flags for simple properties", () => {
5
- const flags = generateBodyFlags({
6
- type: "object",
7
- properties: {
8
- name: { type: "string" },
9
- age: { type: "integer" },
10
- active: { type: "boolean" },
11
- },
12
- required: ["name"],
13
- }, new Set());
14
- expect(flags).toHaveLength(3);
15
- expect(flags.find((f) => f.flag === "--name")).toEqual({
16
- flag: "--name",
17
- path: ["name"],
18
- type: "string",
19
- description: "Body field 'name'",
20
- required: true,
21
- });
22
- expect(flags.find((f) => f.flag === "--age")).toEqual({
23
- flag: "--age",
24
- path: ["age"],
25
- type: "integer",
26
- description: "Body field 'age'",
27
- required: false,
28
- });
29
- });
30
- test("generates dot-notation flags for nested objects", () => {
31
- const flags = generateBodyFlags({
32
- type: "object",
33
- properties: {
34
- name: { type: "string" },
35
- address: {
36
- type: "object",
37
- properties: {
38
- street: { type: "string" },
39
- city: { type: "string" },
40
- zip: { type: "string" },
41
- },
42
- },
43
- },
44
- }, new Set());
45
- expect(flags).toHaveLength(4);
46
- expect(flags.find((f) => f.flag === "--name")).toBeDefined();
47
- expect(flags.find((f) => f.flag === "--address.street")).toEqual({
48
- flag: "--address.street",
49
- path: ["address", "street"],
50
- type: "string",
51
- description: "Body field 'address.street'",
52
- required: false,
53
- });
54
- });
55
- test("handles deeply nested objects", () => {
56
- const flags = generateBodyFlags({
57
- type: "object",
58
- properties: {
59
- user: {
60
- type: "object",
61
- properties: {
62
- profile: {
63
- type: "object",
64
- properties: {
65
- bio: { type: "string" },
66
- },
67
- },
68
- },
69
- },
70
- },
71
- }, new Set());
72
- expect(flags.find((f) => f.flag === "--user.profile.bio")).toEqual({
73
- flag: "--user.profile.bio",
74
- path: ["user", "profile", "bio"],
75
- type: "string",
76
- description: "Body field 'user.profile.bio'",
77
- required: false,
78
- });
79
- });
80
- test("skips reserved flags", () => {
81
- const flags = generateBodyFlags({
82
- type: "object",
83
- properties: {
84
- name: { type: "string" },
85
- data: { type: "string" }, // --data is reserved
86
- },
87
- }, new Set(["--data"]));
88
- expect(flags).toHaveLength(1);
89
- expect(flags[0]?.flag).toBe("--name");
90
- });
91
- test("skips --curl builtin flag", () => {
92
- const reservedFlags = new Set(["--curl"]);
93
- const flags = generateBodyFlags({
94
- type: "object",
95
- properties: {
96
- name: { type: "string" },
97
- curl: { type: "boolean" }, // conflicts with --curl builtin
98
- email: { type: "string" }, // no conflict
99
- },
100
- }, reservedFlags);
101
- expect(flags).toHaveLength(2);
102
- expect(flags.map((f) => f.flag).sort()).toEqual(["--email", "--name"]);
103
- });
104
- test("uses description from schema", () => {
105
- const flags = generateBodyFlags({
106
- type: "object",
107
- properties: {
108
- email: { type: "string", description: "User email address" },
109
- },
110
- }, new Set());
111
- expect(flags[0]?.description).toBe("User email address");
112
- });
113
- });
114
- describe("parseDotNotationFlags", () => {
115
- test("parses flat flags", () => {
116
- const flagDefs = generateBodyFlags({
117
- type: "object",
118
- properties: {
119
- name: { type: "string" },
120
- age: { type: "integer" },
121
- },
122
- }, new Set());
123
- const result = parseDotNotationFlags({ name: "Ada", age: "30" }, flagDefs);
124
- expect(result).toEqual({
125
- name: "Ada",
126
- age: 30,
127
- });
128
- });
129
- test("parses nested flags into objects", () => {
130
- const flagDefs = generateBodyFlags({
131
- type: "object",
132
- properties: {
133
- name: { type: "string" },
134
- address: {
135
- type: "object",
136
- properties: {
137
- street: { type: "string" },
138
- city: { type: "string" },
139
- },
140
- },
141
- },
142
- }, new Set());
143
- // Commander keeps dots: --address.street -> "address.street"
144
- const result = parseDotNotationFlags({
145
- name: "Ada",
146
- "address.street": "123 Main",
147
- "address.city": "NYC",
148
- }, flagDefs);
149
- expect(result).toEqual({
150
- name: "Ada",
151
- address: {
152
- street: "123 Main",
153
- city: "NYC",
154
- },
155
- });
156
- });
157
- test("handles boolean flags", () => {
158
- const flagDefs = generateBodyFlags({
159
- type: "object",
160
- properties: {
161
- active: { type: "boolean" },
162
- },
163
- }, new Set());
164
- const result = parseDotNotationFlags({ active: true }, flagDefs);
165
- expect(result).toEqual({ active: true });
166
- });
167
- });
168
- describe("findMissingRequired", () => {
169
- test("finds missing required fields", () => {
170
- const flagDefs = generateBodyFlags({
171
- type: "object",
172
- properties: {
173
- name: { type: "string" },
174
- email: { type: "string" },
175
- },
176
- required: ["name", "email"],
177
- }, new Set());
178
- const missing = findMissingRequired({ name: "Ada" }, flagDefs);
179
- expect(missing).toEqual(["email"]);
180
- });
181
- test("returns empty when all required fields present", () => {
182
- const flagDefs = generateBodyFlags({
183
- type: "object",
184
- properties: {
185
- name: { type: "string" },
186
- },
187
- required: ["name"],
188
- }, new Set());
189
- const missing = findMissingRequired({ name: "Ada" }, flagDefs);
190
- expect(missing).toEqual([]);
191
- });
192
- });
@@ -1 +0,0 @@
1
- export {};