specli 0.0.4 → 0.0.7

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 (209) hide show
  1. package/cli.ts +13 -4
  2. package/dist/cli.d.ts +3 -0
  3. package/dist/cli.d.ts.map +1 -0
  4. package/dist/cli.js +2331 -0
  5. package/dist/cli.js.map +53 -0
  6. package/dist/index.d.ts +2 -0
  7. package/dist/index.d.ts.map +1 -0
  8. package/dist/index.js +2032 -0
  9. package/dist/index.js.map +48 -0
  10. package/dist/src/ai/tools.d.ts +139 -0
  11. package/dist/src/ai/tools.d.ts.map +1 -0
  12. package/dist/src/ai/tools.js +1656 -0
  13. package/dist/src/ai/tools.js.map +45 -0
  14. package/dist/src/cli/auth-requirements.d.ts +10 -0
  15. package/dist/src/cli/auth-requirements.d.ts.map +1 -0
  16. package/dist/src/cli/auth-requirements.js +66 -0
  17. package/dist/src/cli/auth-requirements.js.map +10 -0
  18. package/dist/src/cli/auth-schemes.d.ts +22 -0
  19. package/dist/src/cli/auth-schemes.d.ts.map +1 -0
  20. package/dist/src/cli/auth-schemes.js +116 -0
  21. package/dist/src/cli/auth-schemes.js.map +11 -0
  22. package/dist/src/cli/capabilities.d.ts +32 -0
  23. package/dist/src/cli/capabilities.d.ts.map +1 -0
  24. package/dist/src/cli/capabilities.js +45 -0
  25. package/dist/src/cli/capabilities.js.map +10 -0
  26. package/dist/src/cli/command-id.d.ts +8 -0
  27. package/dist/src/cli/command-id.d.ts.map +1 -0
  28. package/dist/src/cli/command-id.js +18 -0
  29. package/dist/src/cli/command-id.js.map +11 -0
  30. package/dist/src/cli/command-index.d.ts +6 -0
  31. package/dist/src/cli/command-index.d.ts.map +1 -0
  32. package/dist/src/cli/command-index.js +15 -0
  33. package/dist/src/cli/command-index.js.map +10 -0
  34. package/dist/src/cli/command-model.d.ts +40 -0
  35. package/dist/src/cli/command-model.d.ts.map +1 -0
  36. package/dist/src/cli/command-model.js +274 -0
  37. package/dist/src/cli/command-model.js.map +18 -0
  38. package/dist/src/cli/compile.d.ts +15 -0
  39. package/dist/src/cli/compile.d.ts.map +1 -0
  40. package/dist/src/cli/compile.js +146 -0
  41. package/dist/src/cli/compile.js.map +11 -0
  42. package/dist/src/cli/crypto.d.ts +2 -0
  43. package/dist/src/cli/crypto.d.ts.map +1 -0
  44. package/dist/src/cli/crypto.js +15 -0
  45. package/dist/src/cli/crypto.js.map +10 -0
  46. package/dist/src/cli/derive-name.d.ts +9 -0
  47. package/dist/src/cli/derive-name.d.ts.map +1 -0
  48. package/dist/src/cli/derive-name.js +70 -0
  49. package/dist/src/cli/derive-name.js.map +10 -0
  50. package/dist/src/cli/exec.d.ts +14 -0
  51. package/dist/src/cli/exec.d.ts.map +1 -0
  52. package/dist/src/cli/exec.js +2077 -0
  53. package/dist/src/cli/exec.js.map +49 -0
  54. package/dist/src/cli/main.d.ts +10 -0
  55. package/dist/src/cli/main.d.ts.map +1 -0
  56. package/dist/src/cli/main.js +2032 -0
  57. package/dist/src/cli/main.js.map +48 -0
  58. package/dist/src/cli/naming.d.ts +12 -0
  59. package/dist/src/cli/naming.d.ts.map +1 -0
  60. package/dist/src/cli/naming.js +216 -0
  61. package/dist/src/cli/naming.js.map +12 -0
  62. package/dist/src/cli/operations.d.ts +3 -0
  63. package/dist/src/cli/operations.d.ts.map +1 -0
  64. package/dist/src/cli/operations.js +103 -0
  65. package/dist/src/cli/operations.js.map +10 -0
  66. package/dist/src/cli/params.d.ts +19 -0
  67. package/dist/src/cli/params.d.ts.map +1 -0
  68. package/dist/src/cli/params.js +79 -0
  69. package/dist/src/cli/params.js.map +12 -0
  70. package/dist/src/cli/pluralize.d.ts +2 -0
  71. package/dist/src/cli/pluralize.d.ts.map +1 -0
  72. package/dist/src/cli/pluralize.js +43 -0
  73. package/dist/src/cli/pluralize.js.map +10 -0
  74. package/dist/src/cli/positional.d.ts +19 -0
  75. package/dist/src/cli/positional.d.ts.map +1 -0
  76. package/dist/src/cli/positional.js +39 -0
  77. package/dist/src/cli/positional.js.map +10 -0
  78. package/dist/src/cli/request-body.d.ts +20 -0
  79. package/dist/src/cli/request-body.d.ts.map +1 -0
  80. package/dist/src/cli/request-body.js +82 -0
  81. package/dist/src/cli/request-body.js.map +12 -0
  82. package/dist/src/cli/runtime/argv.d.ts +3 -0
  83. package/dist/src/cli/runtime/argv.d.ts.map +1 -0
  84. package/dist/src/cli/runtime/argv.js +22 -0
  85. package/dist/src/cli/runtime/argv.js.map +10 -0
  86. package/dist/src/cli/runtime/auth/resolve.d.ts +9 -0
  87. package/dist/src/cli/runtime/auth/resolve.d.ts.map +1 -0
  88. package/dist/src/cli/runtime/auth/resolve.js +38 -0
  89. package/dist/src/cli/runtime/auth/resolve.js.map +10 -0
  90. package/dist/src/cli/runtime/body-flags.d.ts +41 -0
  91. package/dist/src/cli/runtime/body-flags.d.ts.map +1 -0
  92. package/dist/src/cli/runtime/body-flags.js +86 -0
  93. package/dist/src/cli/runtime/body-flags.js.map +10 -0
  94. package/dist/src/cli/runtime/body.d.ts +15 -0
  95. package/dist/src/cli/runtime/body.d.ts.map +1 -0
  96. package/dist/src/cli/runtime/body.js +40 -0
  97. package/dist/src/cli/runtime/body.js.map +11 -0
  98. package/dist/src/cli/runtime/collect.d.ts +2 -0
  99. package/dist/src/cli/runtime/collect.d.ts.map +1 -0
  100. package/dist/src/cli/runtime/collect.js +9 -0
  101. package/dist/src/cli/runtime/collect.js.map +10 -0
  102. package/dist/src/cli/runtime/compat.d.ts +35 -0
  103. package/dist/src/cli/runtime/compat.d.ts.map +1 -0
  104. package/dist/src/cli/runtime/compat.js +62 -0
  105. package/dist/src/cli/runtime/compat.js.map +10 -0
  106. package/dist/src/cli/runtime/context.d.ts +16 -0
  107. package/dist/src/cli/runtime/context.d.ts.map +1 -0
  108. package/dist/src/cli/runtime/context.js +936 -0
  109. package/dist/src/cli/runtime/context.js.map +32 -0
  110. package/dist/src/cli/runtime/execute.d.ts +33 -0
  111. package/dist/src/cli/runtime/execute.d.ts.map +1 -0
  112. package/dist/src/cli/runtime/execute.js +670 -0
  113. package/dist/src/cli/runtime/execute.js.map +22 -0
  114. package/dist/src/cli/runtime/generated.d.ts +14 -0
  115. package/dist/src/cli/runtime/generated.d.ts.map +1 -0
  116. package/dist/src/cli/runtime/generated.js +869 -0
  117. package/dist/src/cli/runtime/generated.js.map +23 -0
  118. package/dist/src/cli/runtime/headers.d.ts +9 -0
  119. package/dist/src/cli/runtime/headers.d.ts.map +1 -0
  120. package/dist/src/cli/runtime/headers.js +36 -0
  121. package/dist/src/cli/runtime/headers.js.map +10 -0
  122. package/dist/src/cli/runtime/index.d.ts +4 -0
  123. package/dist/src/cli/runtime/index.d.ts.map +1 -0
  124. package/dist/src/cli/runtime/index.js +1808 -0
  125. package/dist/src/cli/runtime/index.js.map +46 -0
  126. package/dist/src/cli/runtime/profile/secrets.d.ts +25 -0
  127. package/dist/src/cli/runtime/profile/secrets.d.ts.map +1 -0
  128. package/dist/src/cli/runtime/profile/secrets.js +51 -0
  129. package/dist/src/cli/runtime/profile/secrets.js.map +11 -0
  130. package/dist/src/cli/runtime/profile/store.d.ts +15 -0
  131. package/dist/src/cli/runtime/profile/store.d.ts.map +1 -0
  132. package/dist/src/cli/runtime/profile/store.js +102 -0
  133. package/dist/src/cli/runtime/profile/store.js.map +11 -0
  134. package/dist/src/cli/runtime/request.d.ts +36 -0
  135. package/dist/src/cli/runtime/request.d.ts.map +1 -0
  136. package/dist/src/cli/runtime/request.js +571 -0
  137. package/dist/src/cli/runtime/request.js.map +21 -0
  138. package/dist/src/cli/runtime/server-url.d.ts +8 -0
  139. package/dist/src/cli/runtime/server-url.d.ts.map +1 -0
  140. package/dist/src/cli/runtime/server-url.js +55 -0
  141. package/dist/src/cli/runtime/server-url.js.map +11 -0
  142. package/dist/src/cli/runtime/template.d.ts +5 -0
  143. package/dist/src/cli/runtime/template.d.ts.map +1 -0
  144. package/dist/src/cli/runtime/template.js +29 -0
  145. package/dist/src/cli/runtime/template.js.map +10 -0
  146. package/dist/src/cli/runtime/validate/ajv.d.ts +3 -0
  147. package/dist/src/cli/runtime/validate/ajv.d.ts.map +1 -0
  148. package/dist/src/cli/runtime/validate/ajv.js +17 -0
  149. package/dist/src/cli/runtime/validate/ajv.js.map +10 -0
  150. package/dist/src/cli/runtime/validate/coerce.d.ts +4 -0
  151. package/dist/src/cli/runtime/validate/coerce.d.ts.map +1 -0
  152. package/dist/src/cli/runtime/validate/coerce.js +60 -0
  153. package/dist/src/cli/runtime/validate/coerce.js.map +10 -0
  154. package/dist/src/cli/runtime/validate/error.d.ts +3 -0
  155. package/dist/src/cli/runtime/validate/error.d.ts.map +1 -0
  156. package/dist/src/cli/runtime/validate/error.js +21 -0
  157. package/dist/src/cli/runtime/validate/error.js.map +10 -0
  158. package/dist/src/cli/runtime/validate/index.d.ts +5 -0
  159. package/dist/src/cli/runtime/validate/index.d.ts.map +1 -0
  160. package/dist/src/cli/runtime/validate/index.js +122 -0
  161. package/dist/src/cli/runtime/validate/index.js.map +13 -0
  162. package/dist/src/cli/runtime/validate/schema.d.ts +9 -0
  163. package/dist/src/cli/runtime/validate/schema.d.ts.map +1 -0
  164. package/dist/src/cli/runtime/validate/schema.js +36 -0
  165. package/dist/src/cli/runtime/validate/schema.js.map +10 -0
  166. package/dist/src/cli/schema-shape.d.ts +5 -0
  167. package/dist/src/cli/schema-shape.d.ts.map +1 -0
  168. package/dist/src/cli/schema-shape.js +41 -0
  169. package/dist/src/cli/schema-shape.js.map +10 -0
  170. package/dist/src/cli/schema.d.ts +30 -0
  171. package/dist/src/cli/schema.d.ts.map +1 -0
  172. package/dist/src/cli/schema.js +38 -0
  173. package/dist/src/cli/schema.js.map +10 -0
  174. package/dist/src/cli/server.d.ts +16 -0
  175. package/dist/src/cli/server.d.ts.map +1 -0
  176. package/dist/src/cli/server.js +64 -0
  177. package/dist/src/cli/server.js.map +11 -0
  178. package/dist/src/cli/spec-id.d.ts +3 -0
  179. package/dist/src/cli/spec-id.d.ts.map +1 -0
  180. package/dist/src/cli/spec-id.js +21 -0
  181. package/dist/src/cli/spec-id.js.map +11 -0
  182. package/dist/src/cli/spec-loader.d.ts +7 -0
  183. package/dist/src/cli/spec-loader.d.ts.map +1 -0
  184. package/dist/src/cli/spec-loader.js +110 -0
  185. package/dist/src/cli/spec-loader.js.map +15 -0
  186. package/dist/src/cli/stable-json.d.ts +4 -0
  187. package/dist/src/cli/stable-json.d.ts.map +1 -0
  188. package/dist/src/cli/stable-json.js +35 -0
  189. package/dist/src/cli/stable-json.js.map +10 -0
  190. package/dist/src/cli/strings.d.ts +3 -0
  191. package/dist/src/cli/strings.d.ts.map +1 -0
  192. package/dist/src/cli/strings.js +16 -0
  193. package/dist/src/cli/strings.js.map +10 -0
  194. package/dist/src/cli/types.d.ts +53 -0
  195. package/dist/src/cli/types.d.ts.map +1 -0
  196. package/dist/src/cli/types.js +9 -0
  197. package/dist/src/cli/types.js.map +10 -0
  198. package/package.json +31 -4
  199. package/src/ai/tools.ts +211 -0
  200. package/src/cli/main.ts +73 -163
  201. package/src/cli/runtime/auth/resolve.ts +20 -0
  202. package/src/cli/runtime/body.ts +3 -3
  203. package/src/cli/runtime/compat.ts +89 -0
  204. package/src/cli/runtime/execute.ts +98 -39
  205. package/src/cli/runtime/generated.ts +111 -4
  206. package/src/cli/runtime/profile/secrets.ts +42 -1
  207. package/src/cli/runtime/profile/store.ts +15 -13
  208. package/src/cli/runtime/request.ts +22 -11
  209. package/src/cli/spec-loader.ts +2 -2
@@ -0,0 +1,53 @@
1
+ export type SpecSource = "embedded" | "file" | "url";
2
+ export type SecurityRequirement = Record<string, string[]>;
3
+ export type OpenApiDoc = {
4
+ openapi: string;
5
+ info?: {
6
+ title?: string;
7
+ version?: string;
8
+ };
9
+ servers?: Array<{
10
+ url: string;
11
+ description?: string;
12
+ variables?: unknown;
13
+ }>;
14
+ security?: SecurityRequirement[];
15
+ components?: {
16
+ securitySchemes?: Record<string, unknown>;
17
+ };
18
+ paths?: Record<string, unknown>;
19
+ };
20
+ export type NormalizedParameter = {
21
+ in: "path" | "query" | "header" | "cookie";
22
+ name: string;
23
+ required: boolean;
24
+ description?: string;
25
+ schema?: unknown;
26
+ };
27
+ export type JsonSchema = Record<string, unknown>;
28
+ export declare function isJsonSchema(value: unknown): value is JsonSchema;
29
+ export type NormalizedRequestBody = {
30
+ required: boolean;
31
+ contentTypes: string[];
32
+ schemasByContentType: Record<string, unknown | undefined>;
33
+ };
34
+ export type NormalizedOperation = {
35
+ key: string;
36
+ method: string;
37
+ path: string;
38
+ operationId?: string;
39
+ tags: string[];
40
+ summary?: string;
41
+ description?: string;
42
+ deprecated?: boolean;
43
+ security?: SecurityRequirement[];
44
+ parameters: NormalizedParameter[];
45
+ requestBody?: NormalizedRequestBody;
46
+ };
47
+ export type LoadedSpec = {
48
+ source: SpecSource;
49
+ id: string;
50
+ fingerprint: string;
51
+ doc: OpenApiDoc;
52
+ };
53
+ //# sourceMappingURL=types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../../src/cli/types.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,UAAU,GAAG,UAAU,GAAG,MAAM,GAAG,KAAK,CAAC;AAErD,MAAM,MAAM,mBAAmB,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,EAAE,CAAC,CAAC;AAE3D,MAAM,MAAM,UAAU,GAAG;IACxB,OAAO,EAAE,MAAM,CAAC;IAChB,IAAI,CAAC,EAAE;QACN,KAAK,CAAC,EAAE,MAAM,CAAC;QACf,OAAO,CAAC,EAAE,MAAM,CAAC;KACjB,CAAC;IACF,OAAO,CAAC,EAAE,KAAK,CAAC;QAAE,GAAG,EAAE,MAAM,CAAC;QAAC,WAAW,CAAC,EAAE,MAAM,CAAC;QAAC,SAAS,CAAC,EAAE,OAAO,CAAA;KAAE,CAAC,CAAC;IAC5E,QAAQ,CAAC,EAAE,mBAAmB,EAAE,CAAC;IACjC,UAAU,CAAC,EAAE;QACZ,eAAe,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;KAC1C,CAAC;IACF,KAAK,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CAChC,CAAC;AAEF,MAAM,MAAM,mBAAmB,GAAG;IACjC,EAAE,EAAE,MAAM,GAAG,OAAO,GAAG,QAAQ,GAAG,QAAQ,CAAC;IAC3C,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE,OAAO,CAAC;IAClB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,MAAM,CAAC,EAAE,OAAO,CAAC;CACjB,CAAC;AAGF,MAAM,MAAM,UAAU,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;AAEjD,wBAAgB,YAAY,CAAC,KAAK,EAAE,OAAO,GAAG,KAAK,IAAI,UAAU,CAEhE;AAED,MAAM,MAAM,qBAAqB,GAAG;IACnC,QAAQ,EAAE,OAAO,CAAC;IAClB,YAAY,EAAE,MAAM,EAAE,CAAC;IACvB,oBAAoB,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,GAAG,SAAS,CAAC,CAAC;CAC1D,CAAC;AAEF,MAAM,MAAM,mBAAmB,GAAG;IACjC,GAAG,EAAE,MAAM,CAAC;IACZ,MAAM,EAAE,MAAM,CAAC;IACf,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,IAAI,EAAE,MAAM,EAAE,CAAC;IACf,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,QAAQ,CAAC,EAAE,mBAAmB,EAAE,CAAC;IACjC,UAAU,EAAE,mBAAmB,EAAE,CAAC;IAClC,WAAW,CAAC,EAAE,qBAAqB,CAAC;CACpC,CAAC;AAEF,MAAM,MAAM,UAAU,GAAG;IACxB,MAAM,EAAE,UAAU,CAAC;IACnB,EAAE,EAAE,MAAM,CAAC;IACX,WAAW,EAAE,MAAM,CAAC;IACpB,GAAG,EAAE,UAAU,CAAC;CAChB,CAAC"}
@@ -0,0 +1,9 @@
1
+ // src/cli/types.ts
2
+ function isJsonSchema(value) {
3
+ return Boolean(value) && typeof value === "object" && !Array.isArray(value);
4
+ }
5
+ export {
6
+ isJsonSchema
7
+ };
8
+
9
+ //# debugId=D591F75F6084972664756E2164756E21
@@ -0,0 +1,10 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../../src/cli/types.ts"],
4
+ "sourcesContent": [
5
+ "export type SpecSource = \"embedded\" | \"file\" | \"url\";\n\nexport type SecurityRequirement = Record<string, string[]>;\n\nexport type OpenApiDoc = {\n\topenapi: string;\n\tinfo?: {\n\t\ttitle?: string;\n\t\tversion?: string;\n\t};\n\tservers?: Array<{ url: string; description?: string; variables?: unknown }>;\n\tsecurity?: SecurityRequirement[];\n\tcomponents?: {\n\t\tsecuritySchemes?: Record<string, unknown>;\n\t};\n\tpaths?: Record<string, unknown>;\n};\n\nexport type NormalizedParameter = {\n\tin: \"path\" | \"query\" | \"header\" | \"cookie\";\n\tname: string;\n\trequired: boolean;\n\tdescription?: string;\n\tschema?: unknown;\n};\n\n// Minimal JSON Schema-like shape for validation and flag expansion.\nexport type JsonSchema = Record<string, unknown>;\n\nexport function isJsonSchema(value: unknown): value is JsonSchema {\n\treturn Boolean(value) && typeof value === \"object\" && !Array.isArray(value);\n}\n\nexport type NormalizedRequestBody = {\n\trequired: boolean;\n\tcontentTypes: string[];\n\tschemasByContentType: Record<string, unknown | undefined>;\n};\n\nexport type NormalizedOperation = {\n\tkey: string;\n\tmethod: string;\n\tpath: string;\n\toperationId?: string;\n\ttags: string[];\n\tsummary?: string;\n\tdescription?: string;\n\tdeprecated?: boolean;\n\tsecurity?: SecurityRequirement[];\n\tparameters: NormalizedParameter[];\n\trequestBody?: NormalizedRequestBody;\n};\n\nexport type LoadedSpec = {\n\tsource: SpecSource;\n\tid: string;\n\tfingerprint: string;\n\tdoc: OpenApiDoc;\n};\n"
6
+ ],
7
+ "mappings": ";AA6BO,SAAS,YAAY,CAAC,OAAqC;AAAA,EACjE,OAAO,QAAQ,KAAK,KAAK,OAAO,UAAU,YAAY,CAAC,MAAM,QAAQ,KAAK;AAAA;",
8
+ "debugId": "D591F75F6084972664756E2164756E21",
9
+ "names": []
10
+ }
package/package.json CHANGED
@@ -1,18 +1,39 @@
1
1
  {
2
2
  "name": "specli",
3
- "module": "index.ts",
3
+ "version": "0.0.7",
4
4
  "type": "module",
5
- "version": "0.0.4",
5
+ "main": "./dist/index.js",
6
+ "module": "./dist/index.js",
7
+ "types": "./dist/index.d.ts",
6
8
  "bin": {
7
- "specli": "./cli.ts"
9
+ "specli": "./dist/cli.js"
10
+ },
11
+ "exports": {
12
+ ".": {
13
+ "bun": "./index.ts",
14
+ "import": {
15
+ "types": "./dist/index.d.ts",
16
+ "default": "./dist/index.js"
17
+ }
18
+ },
19
+ "./ai/tools": {
20
+ "bun": "./src/ai/tools.ts",
21
+ "import": {
22
+ "types": "./dist/src/ai/tools.d.ts",
23
+ "default": "./dist/src/ai/tools.js"
24
+ }
25
+ }
8
26
  },
9
27
  "files": [
28
+ "dist",
10
29
  "cli.ts",
11
30
  "index.ts",
12
31
  "src",
13
32
  "!**/*.test.ts"
14
33
  ],
15
34
  "scripts": {
35
+ "build": "bun run scripts/build.ts",
36
+ "prepublishOnly": "bun run build",
16
37
  "lint": "biome ci",
17
38
  "lint:check": "biome check --write --unsafe",
18
39
  "lint:format": "biome format --write",
@@ -21,6 +42,7 @@
21
42
  "devDependencies": {
22
43
  "@biomejs/biome": "^2.3.11",
23
44
  "@types/bun": "^1.3.6",
45
+ "@types/node": "^22.19.7",
24
46
  "@typescript/native-preview": "^7.0.0-dev.20260120.1"
25
47
  },
26
48
  "dependencies": {
@@ -28,6 +50,11 @@
28
50
  "ajv": "^8.17.1",
29
51
  "ajv-formats": "^3.0.1",
30
52
  "commander": "^14.0.2",
31
- "openapi-types": "^12.1.3"
53
+ "openapi-types": "^12.1.3",
54
+ "yaml": "^2.8.2"
55
+ },
56
+ "peerDependencies": {
57
+ "ai": "^6.0.42",
58
+ "zod": "^4.3.5"
32
59
  }
33
60
  }
@@ -0,0 +1,211 @@
1
+ /**
2
+ * AI SDK tools for specli
3
+ *
4
+ * Provides tools for AI agents to explore and execute OpenAPI specs.
5
+ *
6
+ * @example
7
+ * ```ts
8
+ * import { specli } from "specli/ai";
9
+ * import { generateText } from "ai";
10
+ *
11
+ * const result = await generateText({
12
+ * model: yourModel,
13
+ * tools: {
14
+ * api: specli({ spec: "https://api.example.com/openapi.json" }),
15
+ * },
16
+ * prompt: "List all users",
17
+ * });
18
+ * ```
19
+ */
20
+
21
+ import { tool } from "ai";
22
+ import { z } from "zod";
23
+
24
+ import type { CommandAction } from "../cli/command-model.ts";
25
+ import { buildRuntimeContext } from "../cli/runtime/context.ts";
26
+ import { execute } from "../cli/runtime/execute.ts";
27
+ import type { RuntimeGlobals } from "../cli/runtime/request.ts";
28
+
29
+ export type SpecliToolOptions = {
30
+ /** The OpenAPI spec URL or file path */
31
+ spec: string;
32
+ /** Override the server/base URL */
33
+ server?: string;
34
+ /** Server URL template variables */
35
+ serverVars?: Record<string, string>;
36
+ /** Bearer token for authentication */
37
+ bearerToken?: string;
38
+ /** API key for authentication */
39
+ apiKey?: string;
40
+ /** Basic auth credentials */
41
+ basicAuth?: { username: string; password: string };
42
+ /** Auth scheme to use (if multiple are available) */
43
+ authScheme?: string;
44
+ };
45
+
46
+ // Cache contexts to avoid reloading spec on every call
47
+ const contextCache = new Map<
48
+ string,
49
+ Awaited<ReturnType<typeof buildRuntimeContext>>
50
+ >();
51
+
52
+ async function getContext(spec: string) {
53
+ let ctx = contextCache.get(spec);
54
+ if (!ctx) {
55
+ ctx = await buildRuntimeContext({ spec });
56
+ contextCache.set(spec, ctx);
57
+ }
58
+ return ctx;
59
+ }
60
+
61
+ function findAction(
62
+ ctx: Awaited<ReturnType<typeof buildRuntimeContext>>,
63
+ resource: string,
64
+ action: string,
65
+ ): CommandAction | undefined {
66
+ const r = ctx.commands.resources.find(
67
+ (r) => r.resource.toLowerCase() === resource.toLowerCase(),
68
+ );
69
+ return r?.actions.find(
70
+ (a) => a.action.toLowerCase() === action.toLowerCase(),
71
+ );
72
+ }
73
+
74
+ /**
75
+ * Create an AI SDK tool for interacting with an OpenAPI spec.
76
+ */
77
+ export function specli(options: SpecliToolOptions) {
78
+ const {
79
+ spec,
80
+ server,
81
+ serverVars,
82
+ bearerToken,
83
+ apiKey,
84
+ basicAuth,
85
+ authScheme,
86
+ } = options;
87
+
88
+ return tool({
89
+ description: `Execute API operations. Commands: "list" (show resources/actions), "help" (action details), "exec" (call API).`,
90
+ inputSchema: z.object({
91
+ command: z.enum(["list", "help", "exec"]).describe("Command to run"),
92
+ resource: z.string().optional().describe("Resource name (e.g. users)"),
93
+ action: z
94
+ .string()
95
+ .optional()
96
+ .describe("Action name (e.g. list, get, create)"),
97
+ args: z.array(z.string()).optional().describe("Positional arguments"),
98
+ flags: z
99
+ .record(z.string(), z.unknown())
100
+ .optional()
101
+ .describe("Named flags"),
102
+ }),
103
+ execute: async ({ command, resource, action, args, flags }) => {
104
+ const ctx = await getContext(spec);
105
+
106
+ if (command === "list") {
107
+ return {
108
+ resources: ctx.commands.resources.map((r) => ({
109
+ name: r.resource,
110
+ actions: r.actions.map((a) => ({
111
+ name: a.action,
112
+ summary: a.summary,
113
+ method: a.method,
114
+ path: a.path,
115
+ args: a.positionals.map((p) => p.name),
116
+ requiredFlags: a.flags
117
+ .filter((f) => f.required)
118
+ .map((f) => f.flag),
119
+ })),
120
+ })),
121
+ };
122
+ }
123
+
124
+ if (command === "help") {
125
+ if (!resource) return { error: "Missing resource" };
126
+ const r = ctx.commands.resources.find(
127
+ (r) => r.resource.toLowerCase() === resource.toLowerCase(),
128
+ );
129
+ if (!r) return { error: `Unknown resource: ${resource}` };
130
+ if (!action) {
131
+ return {
132
+ resource: r.resource,
133
+ actions: r.actions.map((a) => a.action),
134
+ };
135
+ }
136
+ const a = r.actions.find(
137
+ (a) => a.action.toLowerCase() === action.toLowerCase(),
138
+ );
139
+ if (!a) return { error: `Unknown action: ${action}` };
140
+ return {
141
+ action: a.action,
142
+ method: a.method,
143
+ path: a.path,
144
+ summary: a.summary,
145
+ args: a.positionals.map((p) => ({
146
+ name: p.name,
147
+ description: p.description,
148
+ })),
149
+ flags: a.flags.map((f) => ({
150
+ name: f.flag,
151
+ type: f.type,
152
+ required: f.required,
153
+ description: f.description,
154
+ })),
155
+ };
156
+ }
157
+
158
+ if (command === "exec") {
159
+ if (!resource || !action)
160
+ return { error: "Missing resource or action" };
161
+ const actionDef = findAction(ctx, resource, action);
162
+ if (!actionDef) return { error: `Unknown: ${resource} ${action}` };
163
+
164
+ const positionalValues = args ?? [];
165
+ if (positionalValues.length < actionDef.positionals.length) {
166
+ return {
167
+ error: `Missing args: ${actionDef.positionals
168
+ .slice(positionalValues.length)
169
+ .map((p) => p.name)
170
+ .join(", ")}`,
171
+ };
172
+ }
173
+
174
+ const globals: RuntimeGlobals = {
175
+ server,
176
+ serverVar: serverVars
177
+ ? Object.entries(serverVars).map(([k, v]) => `${k}=${v}`)
178
+ : undefined,
179
+ auth: authScheme,
180
+ bearerToken,
181
+ apiKey,
182
+ username: basicAuth?.username,
183
+ password: basicAuth?.password,
184
+ };
185
+
186
+ try {
187
+ const result = await execute({
188
+ specId: ctx.loaded.id,
189
+ action: actionDef,
190
+ positionalValues,
191
+ flagValues: flags ?? {},
192
+ globals,
193
+ servers: ctx.servers,
194
+ authSchemes: ctx.authSchemes,
195
+ });
196
+ return { status: result.status, ok: result.ok, body: result.body };
197
+ } catch (err) {
198
+ return { error: err instanceof Error ? err.message : String(err) };
199
+ }
200
+ }
201
+
202
+ return { error: `Unknown command: ${command}` };
203
+ },
204
+ });
205
+ }
206
+
207
+ /** Clear cached spec context */
208
+ export function clearSpecliCache(spec?: string): void {
209
+ if (spec) contextCache.delete(spec);
210
+ else contextCache.clear();
211
+ }
package/src/cli/main.ts CHANGED
@@ -2,13 +2,12 @@ import { Command } from "commander";
2
2
 
3
3
  import { getArgValue, hasAnyArg } from "./runtime/argv.ts";
4
4
  import { collectRepeatable } from "./runtime/collect.ts";
5
+ import { readStdinText } from "./runtime/compat.ts";
5
6
  import { buildRuntimeContext } from "./runtime/context.ts";
6
7
  import { addGeneratedCommands } from "./runtime/generated.ts";
7
8
  import { deleteToken, getToken, setToken } from "./runtime/profile/secrets.ts";
8
9
  import {
9
- getProfile,
10
10
  readProfiles,
11
- removeProfile,
12
11
  upsertProfile,
13
12
  writeProfiles,
14
13
  } from "./runtime/profile/store.ts";
@@ -42,7 +41,6 @@ export async function main(argv: string[], options: MainOptions = {}) {
42
41
  .option("--username <username>", "Basic auth username")
43
42
  .option("--password <password>", "Basic auth password")
44
43
  .option("--api-key <key>", "API key value")
45
- .option("--profile <name>", "Profile name (stored under ~/.config/specli)")
46
44
  .option("--json", "Machine-readable output")
47
45
  .showHelpAfterError();
48
46
 
@@ -63,197 +61,109 @@ export async function main(argv: string[], options: MainOptions = {}) {
63
61
  embeddedSpecText: options.embeddedSpecText,
64
62
  });
65
63
 
66
- const profileCmd = program
67
- .command("profile")
68
- .description("Manage specli profiles");
64
+ // Simple auth commands
65
+ const defaultProfileName = "default";
69
66
 
70
- profileCmd
71
- .command("list")
72
- .description("List profiles")
73
- .action(async (_opts, command) => {
67
+ program
68
+ .command("login [token]")
69
+ .description("Store a bearer token for authentication")
70
+ .action(async (tokenArg: string | undefined, _opts, command) => {
74
71
  const globals = command.optsWithGlobals() as { json?: boolean };
75
- const file = await readProfiles();
76
-
77
- const payload = {
78
- defaultProfile: file.defaultProfile,
79
- profiles: file.profiles.map((p) => ({
80
- name: p.name,
81
- server: p.server,
82
- authScheme: p.authScheme,
83
- })),
84
- };
85
-
86
- if (globals.json) {
87
- process.stdout.write(`${JSON.stringify(payload)}\n`);
88
- return;
89
- }
90
72
 
91
- if (payload.defaultProfile) {
92
- process.stdout.write(`default: ${payload.defaultProfile}\n`);
93
- }
94
- for (const p of payload.profiles) {
95
- process.stdout.write(`${p.name}\n`);
96
- if (p.server) process.stdout.write(` server: ${p.server}\n`);
97
- if (p.authScheme)
98
- process.stdout.write(` authScheme: ${p.authScheme}\n`);
73
+ let token = tokenArg;
74
+
75
+ // If no token argument, try to read from stdin (for piping)
76
+ if (!token) {
77
+ const isTTY = process.stdin.isTTY;
78
+ if (isTTY) {
79
+ // Interactive mode - prompt user
80
+ process.stdout.write("Enter token: ");
81
+ const reader = process.stdin;
82
+ const chunks: Buffer[] = [];
83
+ for await (const chunk of reader) {
84
+ chunks.push(chunk);
85
+ // Read one line only
86
+ if (chunk.includes(10)) break; // newline
87
+ }
88
+ token = Buffer.concat(chunks).toString().trim();
89
+ } else {
90
+ // Piped input - use cross-runtime stdin reading
91
+ const text = await readStdinText();
92
+ token = text.trim();
93
+ }
99
94
  }
100
- });
101
95
 
102
- profileCmd
103
- .command("set")
104
- .description("Create or update a profile")
105
- .requiredOption("--name <name>", "Profile name")
106
- .option("--server <url>", "Default server/base URL")
107
- .option("--auth <scheme>", "Default auth scheme key")
108
- .option("--default", "Set as default profile")
109
- .action(async (opts, command) => {
110
- const globals = command.optsWithGlobals() as {
111
- json?: boolean;
112
- server?: string;
113
- auth?: string;
114
- };
115
-
116
- const file = await readProfiles();
117
- const next = upsertProfile(file, {
118
- name: String(opts.name),
119
- server:
120
- typeof opts.server === "string"
121
- ? opts.server
122
- : typeof globals.server === "string"
123
- ? globals.server
124
- : undefined,
125
- authScheme:
126
- typeof opts.auth === "string"
127
- ? opts.auth
128
- : typeof globals.auth === "string"
129
- ? globals.auth
130
- : undefined,
131
- });
132
- const final = opts.default
133
- ? { ...next, defaultProfile: String(opts.name) }
134
- : next;
135
- await writeProfiles(final);
136
-
137
- if (globals.json) {
138
- process.stdout.write(
139
- `${JSON.stringify({ ok: true, profile: String(opts.name) })}\n`,
96
+ if (!token) {
97
+ throw new Error(
98
+ "No token provided. Usage: login <token> or echo $TOKEN | login",
140
99
  );
141
- return;
142
100
  }
143
- process.stdout.write(`ok: profile ${String(opts.name)}\n`);
144
- });
145
101
 
146
- profileCmd
147
- .command("rm")
148
- .description("Remove a profile")
149
- .requiredOption("--name <name>", "Profile name")
150
- .action(async (opts, command) => {
151
- const globals = command.optsWithGlobals() as { json?: boolean };
102
+ // Ensure default profile exists
152
103
  const file = await readProfiles();
153
- const removed = removeProfile(file, String(opts.name));
154
-
155
- const final =
156
- file.defaultProfile === opts.name
157
- ? { ...removed, defaultProfile: undefined }
158
- : removed;
104
+ if (!file.profiles.find((p) => p.name === defaultProfileName)) {
105
+ const updated = upsertProfile(file, { name: defaultProfileName });
106
+ await writeProfiles({ ...updated, defaultProfile: defaultProfileName });
107
+ } else if (!file.defaultProfile) {
108
+ await writeProfiles({ ...file, defaultProfile: defaultProfileName });
109
+ }
159
110
 
160
- await writeProfiles(final);
111
+ await setToken(ctx.loaded.id, defaultProfileName, token);
161
112
 
162
113
  if (globals.json) {
163
- process.stdout.write(
164
- `${JSON.stringify({ ok: true, profile: String(opts.name) })}\n`,
165
- );
114
+ process.stdout.write(`${JSON.stringify({ ok: true })}\n`);
166
115
  return;
167
116
  }
168
-
169
- process.stdout.write(`ok: removed ${String(opts.name)}\n`);
117
+ process.stdout.write("ok: logged in\n");
170
118
  });
171
119
 
172
- profileCmd
173
- .command("use")
174
- .description("Set the default profile")
175
- .requiredOption("--name <name>", "Profile name")
176
- .action(async (opts, command) => {
120
+ program
121
+ .command("logout")
122
+ .description("Clear stored authentication token")
123
+ .action(async (_opts, command) => {
177
124
  const globals = command.optsWithGlobals() as { json?: boolean };
178
- const file = await readProfiles();
179
- const profile = getProfile(file, String(opts.name));
180
- if (!profile) throw new Error(`Profile not found: ${String(opts.name)}`);
181
125
 
182
- await writeProfiles({ ...file, defaultProfile: String(opts.name) });
126
+ const deleted = await deleteToken(ctx.loaded.id, defaultProfileName);
183
127
 
184
128
  if (globals.json) {
185
- process.stdout.write(
186
- `${JSON.stringify({ ok: true, defaultProfile: String(opts.name) })}\n`,
187
- );
129
+ process.stdout.write(`${JSON.stringify({ ok: deleted })}\n`);
188
130
  return;
189
131
  }
190
- process.stdout.write(`ok: default ${String(opts.name)}\n`);
132
+ process.stdout.write(
133
+ deleted ? "ok: logged out\n" : "ok: not logged in\n",
134
+ );
191
135
  });
192
136
 
193
- const authCmd = program.command("auth").description("Manage auth secrets");
137
+ program
138
+ .command("whoami")
139
+ .description("Show current authentication status")
140
+ .action(async (_opts, command) => {
141
+ const globals = command.optsWithGlobals() as { json?: boolean };
194
142
 
195
- authCmd
196
- .command("token")
197
- .description("Set or get bearer token for a profile")
198
- .option("--name <name>", "Profile name (defaults to global --profile)")
199
- .option("--set <token>", "Set token")
200
- .option("--get", "Get token")
201
- .option("--delete", "Delete token")
202
- .action(async (opts, command) => {
203
- const globals = command.optsWithGlobals() as {
204
- json?: boolean;
205
- profile?: string;
206
- };
143
+ const token = await getToken(ctx.loaded.id, defaultProfileName);
144
+ const hasToken = Boolean(token);
207
145
 
208
- const profileName = String(opts.name ?? globals.profile ?? "");
209
- if (!profileName) {
210
- throw new Error(
211
- "Missing profile name. Provide --name <name> or global --profile <name>.",
212
- );
213
- }
214
- if (opts.set && (opts.get || opts.delete)) {
215
- throw new Error("Use only one of --set, --get, --delete");
216
- }
217
- if (opts.get && opts.delete) {
218
- throw new Error("Use only one of --get or --delete");
219
- }
220
- if (!opts.set && !opts.get && !opts.delete) {
221
- throw new Error("Provide one of --set, --get, --delete");
146
+ // Mask the token for display (show first 8 and last 4 chars)
147
+ let maskedToken: string | null = null;
148
+ if (token && token.length > 16) {
149
+ maskedToken = `${token.slice(0, 8)}...${token.slice(-4)}`;
150
+ } else if (token) {
151
+ maskedToken = `${token.slice(0, 4)}...`;
222
152
  }
223
153
 
224
- if (typeof opts.set === "string") {
225
- await setToken(ctx.loaded.id, profileName, opts.set);
226
- if (globals.json) {
227
- process.stdout.write(
228
- `${JSON.stringify({ ok: true, profile: profileName })}\n`,
229
- );
230
- return;
231
- }
232
- process.stdout.write(`ok: token set for ${profileName}\n`);
233
- return;
234
- }
235
-
236
- if (opts.get) {
237
- const token = await getToken(ctx.loaded.id, profileName);
238
- if (globals.json) {
239
- process.stdout.write(
240
- `${JSON.stringify({ profile: profileName, token })}\n`,
241
- );
242
- return;
243
- }
244
- process.stdout.write(`${token ?? ""}\n`);
154
+ if (globals.json) {
155
+ process.stdout.write(
156
+ `${JSON.stringify({ authenticated: hasToken, token: maskedToken })}\n`,
157
+ );
245
158
  return;
246
159
  }
247
160
 
248
- if (opts.delete) {
249
- const ok = await deleteToken(ctx.loaded.id, profileName);
250
- if (globals.json) {
251
- process.stdout.write(
252
- `${JSON.stringify({ ok, profile: profileName })}\n`,
253
- );
254
- return;
255
- }
256
- process.stdout.write(`ok: ${ok ? "deleted" : "not-found"}\n`);
161
+ if (hasToken) {
162
+ process.stdout.write(`authenticated: yes\n`);
163
+ process.stdout.write(`token: ${maskedToken}\n`);
164
+ } else {
165
+ process.stdout.write(`authenticated: no\n`);
166
+ process.stdout.write(`Run 'login <token>' to authenticate.\n`);
257
167
  }
258
168
  });
259
169
 
@@ -4,8 +4,15 @@ export type AuthInputs = {
4
4
  flagAuthScheme?: string;
5
5
  profileAuthScheme?: string;
6
6
  embeddedAuthScheme?: string;
7
+ hasStoredToken?: boolean;
7
8
  };
8
9
 
10
+ const BEARER_COMPATIBLE_KINDS = new Set([
11
+ "http-bearer",
12
+ "oauth2",
13
+ "openIdConnect",
14
+ ]);
15
+
9
16
  export function resolveAuthScheme(
10
17
  authSchemes: AuthScheme[],
11
18
  required: import("../../auth-requirements.ts").AuthSummary,
@@ -35,5 +42,18 @@ export function resolveAuthScheme(
35
42
  // Otherwise if there is only one scheme in spec, pick it.
36
43
  if (authSchemes.length === 1) return authSchemes[0]?.key;
37
44
 
45
+ // If user has a stored token and operation accepts a bearer-compatible scheme,
46
+ // automatically pick the first one that matches.
47
+ if (inputs.hasStoredToken && alts.length > 0) {
48
+ for (const alt of alts) {
49
+ if (alt.length !== 1) continue;
50
+ const key = alt[0]?.key;
51
+ const scheme = authSchemes.find((s) => s.key === key);
52
+ if (scheme && BEARER_COMPATIBLE_KINDS.has(scheme.kind)) {
53
+ return key;
54
+ }
55
+ }
56
+ }
57
+
38
58
  return undefined;
39
59
  }