specli 0.0.5 → 0.0.8
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/bin/specli.js +19 -0
- package/cli.ts +13 -4
- package/dist/cli.d.ts +3 -0
- package/dist/cli.d.ts.map +1 -0
- package/dist/cli.js +2331 -0
- package/dist/cli.js.map +53 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +2032 -0
- package/dist/index.js.map +48 -0
- package/dist/src/ai/tools.d.ts +139 -0
- package/dist/src/ai/tools.d.ts.map +1 -0
- package/dist/src/ai/tools.js +1656 -0
- package/dist/src/ai/tools.js.map +45 -0
- package/dist/src/cli/auth-requirements.d.ts +10 -0
- package/dist/src/cli/auth-requirements.d.ts.map +1 -0
- package/dist/src/cli/auth-requirements.js +66 -0
- package/dist/src/cli/auth-requirements.js.map +10 -0
- package/dist/src/cli/auth-schemes.d.ts +22 -0
- package/dist/src/cli/auth-schemes.d.ts.map +1 -0
- package/dist/src/cli/auth-schemes.js +116 -0
- package/dist/src/cli/auth-schemes.js.map +11 -0
- package/dist/src/cli/capabilities.d.ts +32 -0
- package/dist/src/cli/capabilities.d.ts.map +1 -0
- package/dist/src/cli/capabilities.js +45 -0
- package/dist/src/cli/capabilities.js.map +10 -0
- package/dist/src/cli/command-id.d.ts +8 -0
- package/dist/src/cli/command-id.d.ts.map +1 -0
- package/dist/src/cli/command-id.js +18 -0
- package/dist/src/cli/command-id.js.map +11 -0
- package/dist/src/cli/command-index.d.ts +6 -0
- package/dist/src/cli/command-index.d.ts.map +1 -0
- package/dist/src/cli/command-index.js +15 -0
- package/dist/src/cli/command-index.js.map +10 -0
- package/dist/src/cli/command-model.d.ts +40 -0
- package/dist/src/cli/command-model.d.ts.map +1 -0
- package/dist/src/cli/command-model.js +274 -0
- package/dist/src/cli/command-model.js.map +18 -0
- package/dist/src/cli/compile.d.ts +15 -0
- package/dist/src/cli/compile.d.ts.map +1 -0
- package/dist/src/cli/compile.js +146 -0
- package/dist/src/cli/compile.js.map +11 -0
- package/dist/src/cli/crypto.d.ts +2 -0
- package/dist/src/cli/crypto.d.ts.map +1 -0
- package/dist/src/cli/crypto.js +15 -0
- package/dist/src/cli/crypto.js.map +10 -0
- package/dist/src/cli/derive-name.d.ts +9 -0
- package/dist/src/cli/derive-name.d.ts.map +1 -0
- package/dist/src/cli/derive-name.js +70 -0
- package/dist/src/cli/derive-name.js.map +10 -0
- package/dist/src/cli/exec.d.ts +14 -0
- package/dist/src/cli/exec.d.ts.map +1 -0
- package/dist/src/cli/exec.js +2077 -0
- package/dist/src/cli/exec.js.map +49 -0
- package/dist/src/cli/main.d.ts +10 -0
- package/dist/src/cli/main.d.ts.map +1 -0
- package/dist/src/cli/main.js +2032 -0
- package/dist/src/cli/main.js.map +48 -0
- package/dist/src/cli/naming.d.ts +12 -0
- package/dist/src/cli/naming.d.ts.map +1 -0
- package/dist/src/cli/naming.js +216 -0
- package/dist/src/cli/naming.js.map +12 -0
- package/dist/src/cli/operations.d.ts +3 -0
- package/dist/src/cli/operations.d.ts.map +1 -0
- package/dist/src/cli/operations.js +103 -0
- package/dist/src/cli/operations.js.map +10 -0
- package/dist/src/cli/params.d.ts +19 -0
- package/dist/src/cli/params.d.ts.map +1 -0
- package/dist/src/cli/params.js +79 -0
- package/dist/src/cli/params.js.map +12 -0
- package/dist/src/cli/pluralize.d.ts +2 -0
- package/dist/src/cli/pluralize.d.ts.map +1 -0
- package/dist/src/cli/pluralize.js +43 -0
- package/dist/src/cli/pluralize.js.map +10 -0
- package/dist/src/cli/positional.d.ts +19 -0
- package/dist/src/cli/positional.d.ts.map +1 -0
- package/dist/src/cli/positional.js +39 -0
- package/dist/src/cli/positional.js.map +10 -0
- package/dist/src/cli/request-body.d.ts +20 -0
- package/dist/src/cli/request-body.d.ts.map +1 -0
- package/dist/src/cli/request-body.js +82 -0
- package/dist/src/cli/request-body.js.map +12 -0
- package/dist/src/cli/runtime/argv.d.ts +3 -0
- package/dist/src/cli/runtime/argv.d.ts.map +1 -0
- package/dist/src/cli/runtime/argv.js +22 -0
- package/dist/src/cli/runtime/argv.js.map +10 -0
- package/dist/src/cli/runtime/auth/resolve.d.ts +9 -0
- package/dist/src/cli/runtime/auth/resolve.d.ts.map +1 -0
- package/dist/src/cli/runtime/auth/resolve.js +38 -0
- package/dist/src/cli/runtime/auth/resolve.js.map +10 -0
- package/dist/src/cli/runtime/body-flags.d.ts +41 -0
- package/dist/src/cli/runtime/body-flags.d.ts.map +1 -0
- package/dist/src/cli/runtime/body-flags.js +86 -0
- package/dist/src/cli/runtime/body-flags.js.map +10 -0
- package/dist/src/cli/runtime/body.d.ts +15 -0
- package/dist/src/cli/runtime/body.d.ts.map +1 -0
- package/dist/src/cli/runtime/body.js +40 -0
- package/dist/src/cli/runtime/body.js.map +11 -0
- package/dist/src/cli/runtime/collect.d.ts +2 -0
- package/dist/src/cli/runtime/collect.d.ts.map +1 -0
- package/dist/src/cli/runtime/collect.js +9 -0
- package/dist/src/cli/runtime/collect.js.map +10 -0
- package/dist/src/cli/runtime/compat.d.ts +35 -0
- package/dist/src/cli/runtime/compat.d.ts.map +1 -0
- package/dist/src/cli/runtime/compat.js +62 -0
- package/dist/src/cli/runtime/compat.js.map +10 -0
- package/dist/src/cli/runtime/context.d.ts +16 -0
- package/dist/src/cli/runtime/context.d.ts.map +1 -0
- package/dist/src/cli/runtime/context.js +936 -0
- package/dist/src/cli/runtime/context.js.map +32 -0
- package/dist/src/cli/runtime/execute.d.ts +33 -0
- package/dist/src/cli/runtime/execute.d.ts.map +1 -0
- package/dist/src/cli/runtime/execute.js +670 -0
- package/dist/src/cli/runtime/execute.js.map +22 -0
- package/dist/src/cli/runtime/generated.d.ts +14 -0
- package/dist/src/cli/runtime/generated.d.ts.map +1 -0
- package/dist/src/cli/runtime/generated.js +869 -0
- package/dist/src/cli/runtime/generated.js.map +23 -0
- package/dist/src/cli/runtime/headers.d.ts +9 -0
- package/dist/src/cli/runtime/headers.d.ts.map +1 -0
- package/dist/src/cli/runtime/headers.js +36 -0
- package/dist/src/cli/runtime/headers.js.map +10 -0
- package/dist/src/cli/runtime/index.d.ts +4 -0
- package/dist/src/cli/runtime/index.d.ts.map +1 -0
- package/dist/src/cli/runtime/index.js +1808 -0
- package/dist/src/cli/runtime/index.js.map +46 -0
- package/dist/src/cli/runtime/profile/secrets.d.ts +25 -0
- package/dist/src/cli/runtime/profile/secrets.d.ts.map +1 -0
- package/dist/src/cli/runtime/profile/secrets.js +51 -0
- package/dist/src/cli/runtime/profile/secrets.js.map +11 -0
- package/dist/src/cli/runtime/profile/store.d.ts +15 -0
- package/dist/src/cli/runtime/profile/store.d.ts.map +1 -0
- package/dist/src/cli/runtime/profile/store.js +102 -0
- package/dist/src/cli/runtime/profile/store.js.map +11 -0
- package/dist/src/cli/runtime/request.d.ts +36 -0
- package/dist/src/cli/runtime/request.d.ts.map +1 -0
- package/dist/src/cli/runtime/request.js +571 -0
- package/dist/src/cli/runtime/request.js.map +21 -0
- package/dist/src/cli/runtime/server-url.d.ts +8 -0
- package/dist/src/cli/runtime/server-url.d.ts.map +1 -0
- package/dist/src/cli/runtime/server-url.js +55 -0
- package/dist/src/cli/runtime/server-url.js.map +11 -0
- package/dist/src/cli/runtime/template.d.ts +5 -0
- package/dist/src/cli/runtime/template.d.ts.map +1 -0
- package/dist/src/cli/runtime/template.js +29 -0
- package/dist/src/cli/runtime/template.js.map +10 -0
- package/dist/src/cli/runtime/validate/ajv.d.ts +3 -0
- package/dist/src/cli/runtime/validate/ajv.d.ts.map +1 -0
- package/dist/src/cli/runtime/validate/ajv.js +17 -0
- package/dist/src/cli/runtime/validate/ajv.js.map +10 -0
- package/dist/src/cli/runtime/validate/coerce.d.ts +4 -0
- package/dist/src/cli/runtime/validate/coerce.d.ts.map +1 -0
- package/dist/src/cli/runtime/validate/coerce.js +60 -0
- package/dist/src/cli/runtime/validate/coerce.js.map +10 -0
- package/dist/src/cli/runtime/validate/error.d.ts +3 -0
- package/dist/src/cli/runtime/validate/error.d.ts.map +1 -0
- package/dist/src/cli/runtime/validate/error.js +21 -0
- package/dist/src/cli/runtime/validate/error.js.map +10 -0
- package/dist/src/cli/runtime/validate/index.d.ts +5 -0
- package/dist/src/cli/runtime/validate/index.d.ts.map +1 -0
- package/dist/src/cli/runtime/validate/index.js +122 -0
- package/dist/src/cli/runtime/validate/index.js.map +13 -0
- package/dist/src/cli/runtime/validate/schema.d.ts +9 -0
- package/dist/src/cli/runtime/validate/schema.d.ts.map +1 -0
- package/dist/src/cli/runtime/validate/schema.js +36 -0
- package/dist/src/cli/runtime/validate/schema.js.map +10 -0
- package/dist/src/cli/schema-shape.d.ts +5 -0
- package/dist/src/cli/schema-shape.d.ts.map +1 -0
- package/dist/src/cli/schema-shape.js +41 -0
- package/dist/src/cli/schema-shape.js.map +10 -0
- package/dist/src/cli/schema.d.ts +30 -0
- package/dist/src/cli/schema.d.ts.map +1 -0
- package/dist/src/cli/schema.js +38 -0
- package/dist/src/cli/schema.js.map +10 -0
- package/dist/src/cli/server.d.ts +16 -0
- package/dist/src/cli/server.d.ts.map +1 -0
- package/dist/src/cli/server.js +64 -0
- package/dist/src/cli/server.js.map +11 -0
- package/dist/src/cli/spec-id.d.ts +3 -0
- package/dist/src/cli/spec-id.d.ts.map +1 -0
- package/dist/src/cli/spec-id.js +21 -0
- package/dist/src/cli/spec-id.js.map +11 -0
- package/dist/src/cli/spec-loader.d.ts +7 -0
- package/dist/src/cli/spec-loader.d.ts.map +1 -0
- package/dist/src/cli/spec-loader.js +110 -0
- package/dist/src/cli/spec-loader.js.map +15 -0
- package/dist/src/cli/stable-json.d.ts +4 -0
- package/dist/src/cli/stable-json.d.ts.map +1 -0
- package/dist/src/cli/stable-json.js +35 -0
- package/dist/src/cli/stable-json.js.map +10 -0
- package/dist/src/cli/strings.d.ts +3 -0
- package/dist/src/cli/strings.d.ts.map +1 -0
- package/dist/src/cli/strings.js +16 -0
- package/dist/src/cli/strings.js.map +10 -0
- package/dist/src/cli/types.d.ts +53 -0
- package/dist/src/cli/types.d.ts.map +1 -0
- package/dist/src/cli/types.js +9 -0
- package/dist/src/cli/types.js.map +10 -0
- package/package.json +32 -4
- package/src/ai/tools.ts +211 -0
- package/src/cli/main.ts +3 -2
- package/src/cli/runtime/body.ts +3 -3
- package/src/cli/runtime/compat.ts +89 -0
- package/src/cli/runtime/execute.ts +98 -39
- package/src/cli/runtime/generated.ts +111 -4
- package/src/cli/runtime/profile/secrets.ts +42 -1
- package/src/cli/runtime/profile/store.ts +15 -13
- package/src/cli/runtime/request.ts +12 -4
- package/src/cli/spec-loader.ts +2 -2
|
@@ -0,0 +1,869 @@
|
|
|
1
|
+
var __defProp = Object.defineProperty;
|
|
2
|
+
var __export = (target, all) => {
|
|
3
|
+
for (var name in all)
|
|
4
|
+
__defProp(target, name, {
|
|
5
|
+
get: all[name],
|
|
6
|
+
enumerable: true,
|
|
7
|
+
configurable: true,
|
|
8
|
+
set: (newValue) => all[name] = () => newValue
|
|
9
|
+
});
|
|
10
|
+
};
|
|
11
|
+
|
|
12
|
+
// src/cli/runtime/body-flags.ts
|
|
13
|
+
var exports_body_flags = {};
|
|
14
|
+
__export(exports_body_flags, {
|
|
15
|
+
parseDotNotationFlags: () => parseDotNotationFlags,
|
|
16
|
+
generateBodyFlags: () => generateBodyFlags,
|
|
17
|
+
findMissingRequired: () => findMissingRequired
|
|
18
|
+
});
|
|
19
|
+
function generateBodyFlags(schema, reservedFlags) {
|
|
20
|
+
if (!schema || schema.type !== "object" || !schema.properties) {
|
|
21
|
+
return [];
|
|
22
|
+
}
|
|
23
|
+
const flags = [];
|
|
24
|
+
const requiredSet = new Set(schema.required ?? []);
|
|
25
|
+
collectFlags(schema.properties, [], requiredSet, flags, reservedFlags);
|
|
26
|
+
return flags;
|
|
27
|
+
}
|
|
28
|
+
function collectFlags(properties, pathPrefix, requiredAtRoot, out, reservedFlags) {
|
|
29
|
+
for (const [name, propSchema] of Object.entries(properties)) {
|
|
30
|
+
if (!name || typeof name !== "string")
|
|
31
|
+
continue;
|
|
32
|
+
if (!propSchema || typeof propSchema !== "object")
|
|
33
|
+
continue;
|
|
34
|
+
const path = [...pathPrefix, name];
|
|
35
|
+
const flagName = `--${path.join(".")}`;
|
|
36
|
+
if (reservedFlags.has(flagName))
|
|
37
|
+
continue;
|
|
38
|
+
const t = propSchema.type;
|
|
39
|
+
if (t === "object" && propSchema.properties) {
|
|
40
|
+
const nestedRequired = new Set(propSchema.required ?? []);
|
|
41
|
+
collectFlags(propSchema.properties, path, nestedRequired, out, reservedFlags);
|
|
42
|
+
} else if (t === "string" || t === "number" || t === "integer" || t === "boolean") {
|
|
43
|
+
const isRequired = pathPrefix.length === 0 ? requiredAtRoot.has(name) : false;
|
|
44
|
+
out.push({
|
|
45
|
+
flag: flagName,
|
|
46
|
+
path,
|
|
47
|
+
type: t,
|
|
48
|
+
description: propSchema.description ?? `Body field '${path.join(".")}'`,
|
|
49
|
+
required: isRequired
|
|
50
|
+
});
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
function parseDotNotationFlags(flagValues, flagDefs) {
|
|
55
|
+
const result = {};
|
|
56
|
+
for (const def of flagDefs) {
|
|
57
|
+
const dotKey = def.path.join(".");
|
|
58
|
+
const value = flagValues[dotKey];
|
|
59
|
+
if (value === undefined)
|
|
60
|
+
continue;
|
|
61
|
+
setNestedValue(result, def.path, value, def.type);
|
|
62
|
+
}
|
|
63
|
+
return result;
|
|
64
|
+
}
|
|
65
|
+
function setNestedValue(obj, path, value, type) {
|
|
66
|
+
let current = obj;
|
|
67
|
+
for (let i = 0;i < path.length - 1; i++) {
|
|
68
|
+
const key = path[i];
|
|
69
|
+
if (!(key in current) || typeof current[key] !== "object") {
|
|
70
|
+
current[key] = {};
|
|
71
|
+
}
|
|
72
|
+
current = current[key];
|
|
73
|
+
}
|
|
74
|
+
const finalKey = path[path.length - 1];
|
|
75
|
+
if (type === "boolean") {
|
|
76
|
+
current[finalKey] = true;
|
|
77
|
+
} else if (type === "integer") {
|
|
78
|
+
current[finalKey] = Number.parseInt(String(value), 10);
|
|
79
|
+
} else if (type === "number") {
|
|
80
|
+
current[finalKey] = Number(String(value));
|
|
81
|
+
} else {
|
|
82
|
+
current[finalKey] = String(value);
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
function findMissingRequired(flagValues, flagDefs) {
|
|
86
|
+
const missing = [];
|
|
87
|
+
for (const def of flagDefs) {
|
|
88
|
+
if (!def.required)
|
|
89
|
+
continue;
|
|
90
|
+
const dotKey = def.path.join(".");
|
|
91
|
+
if (flagValues[dotKey] === undefined) {
|
|
92
|
+
missing.push(dotKey);
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
return missing;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
// src/cli/runtime/generated.ts
|
|
99
|
+
import { Command } from "commander";
|
|
100
|
+
|
|
101
|
+
// src/cli/runtime/auth/resolve.ts
|
|
102
|
+
var BEARER_COMPATIBLE_KINDS = new Set([
|
|
103
|
+
"http-bearer",
|
|
104
|
+
"oauth2",
|
|
105
|
+
"openIdConnect"
|
|
106
|
+
]);
|
|
107
|
+
function resolveAuthScheme(authSchemes, required, inputs) {
|
|
108
|
+
if (inputs.flagAuthScheme)
|
|
109
|
+
return inputs.flagAuthScheme;
|
|
110
|
+
if (inputs.profileAuthScheme && authSchemes.some((s) => s.key === inputs.profileAuthScheme)) {
|
|
111
|
+
return inputs.profileAuthScheme;
|
|
112
|
+
}
|
|
113
|
+
if (inputs.embeddedAuthScheme && authSchemes.some((s) => s.key === inputs.embeddedAuthScheme)) {
|
|
114
|
+
return inputs.embeddedAuthScheme;
|
|
115
|
+
}
|
|
116
|
+
const alts = required.alternatives;
|
|
117
|
+
if (alts.length === 1 && alts[0]?.length === 1)
|
|
118
|
+
return alts[0][0]?.key;
|
|
119
|
+
if (authSchemes.length === 1)
|
|
120
|
+
return authSchemes[0]?.key;
|
|
121
|
+
if (inputs.hasStoredToken && alts.length > 0) {
|
|
122
|
+
for (const alt of alts) {
|
|
123
|
+
if (alt.length !== 1)
|
|
124
|
+
continue;
|
|
125
|
+
const key = alt[0]?.key;
|
|
126
|
+
const scheme = authSchemes.find((s) => s.key === key);
|
|
127
|
+
if (scheme && BEARER_COMPATIBLE_KINDS.has(scheme.kind)) {
|
|
128
|
+
return key;
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
return;
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
// src/cli/runtime/compat.ts
|
|
136
|
+
import { existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
|
|
137
|
+
import { parse as parseYaml } from "yaml";
|
|
138
|
+
var isBun = typeof globalThis.Bun !== "undefined";
|
|
139
|
+
async function readFileText(path) {
|
|
140
|
+
if (isBun) {
|
|
141
|
+
return Bun.file(path).text();
|
|
142
|
+
}
|
|
143
|
+
return readFileSync(path, "utf-8");
|
|
144
|
+
}
|
|
145
|
+
async function fileExists(path) {
|
|
146
|
+
if (isBun) {
|
|
147
|
+
return Bun.file(path).exists();
|
|
148
|
+
}
|
|
149
|
+
return existsSync(path);
|
|
150
|
+
}
|
|
151
|
+
function parseYamlContent(text) {
|
|
152
|
+
if (isBun) {
|
|
153
|
+
const { YAML } = globalThis.Bun;
|
|
154
|
+
return YAML.parse(text);
|
|
155
|
+
}
|
|
156
|
+
return parseYaml(text);
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
// src/cli/runtime/profile/secrets.ts
|
|
160
|
+
var bunLiteral = "bun";
|
|
161
|
+
function secretServiceForSpec(specId) {
|
|
162
|
+
return `specli:${specId}`;
|
|
163
|
+
}
|
|
164
|
+
function tokenSecretKey(specId, profile) {
|
|
165
|
+
return {
|
|
166
|
+
service: secretServiceForSpec(specId),
|
|
167
|
+
name: `profile:${profile}:token`
|
|
168
|
+
};
|
|
169
|
+
}
|
|
170
|
+
async function getToken(specId, profile) {
|
|
171
|
+
if (!isBun) {
|
|
172
|
+
return null;
|
|
173
|
+
}
|
|
174
|
+
const { secrets } = await import(bunLiteral);
|
|
175
|
+
const key = tokenSecretKey(specId, profile);
|
|
176
|
+
return await secrets.get({ service: key.service, name: key.name });
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
// src/cli/runtime/profile/store.ts
|
|
180
|
+
function configDir() {
|
|
181
|
+
const home = process.env.HOME;
|
|
182
|
+
if (!home)
|
|
183
|
+
throw new Error("Missing HOME env var");
|
|
184
|
+
return `${home}/.config/specli`;
|
|
185
|
+
}
|
|
186
|
+
function configPathJson() {
|
|
187
|
+
return `${configDir()}/profiles.json`;
|
|
188
|
+
}
|
|
189
|
+
function configPathYaml() {
|
|
190
|
+
return `${configDir()}/profiles.yaml`;
|
|
191
|
+
}
|
|
192
|
+
async function readProfiles() {
|
|
193
|
+
const jsonPath = configPathJson();
|
|
194
|
+
const yamlPath = configPathYaml();
|
|
195
|
+
const jsonExists = await fileExists(jsonPath);
|
|
196
|
+
const yamlExists = await fileExists(yamlPath);
|
|
197
|
+
const filePath = jsonExists ? jsonPath : yamlExists ? yamlPath : null;
|
|
198
|
+
if (!filePath)
|
|
199
|
+
return { profiles: [] };
|
|
200
|
+
const text = await readFileText(filePath);
|
|
201
|
+
let parsed;
|
|
202
|
+
try {
|
|
203
|
+
parsed = parseYamlContent(text);
|
|
204
|
+
} catch {
|
|
205
|
+
parsed = JSON.parse(text);
|
|
206
|
+
}
|
|
207
|
+
const obj = parsed && typeof parsed === "object" ? parsed : {};
|
|
208
|
+
const profiles = Array.isArray(obj.profiles) ? obj.profiles : [];
|
|
209
|
+
return {
|
|
210
|
+
profiles: profiles.filter(Boolean),
|
|
211
|
+
defaultProfile: typeof obj.defaultProfile === "string" ? obj.defaultProfile : undefined
|
|
212
|
+
};
|
|
213
|
+
}
|
|
214
|
+
function getProfile(data, name) {
|
|
215
|
+
const wanted = name ?? data.defaultProfile;
|
|
216
|
+
if (!wanted)
|
|
217
|
+
return;
|
|
218
|
+
return data.profiles.find((p) => p?.name === wanted);
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
// src/cli/runtime/template.ts
|
|
222
|
+
function extractTemplateVars(template) {
|
|
223
|
+
const out = [];
|
|
224
|
+
const re = /\{([^}]+)\}/g;
|
|
225
|
+
while (true) {
|
|
226
|
+
const match = re.exec(template);
|
|
227
|
+
if (!match)
|
|
228
|
+
break;
|
|
229
|
+
out.push((match[1] ?? "").trim());
|
|
230
|
+
}
|
|
231
|
+
return out.filter(Boolean);
|
|
232
|
+
}
|
|
233
|
+
function applyTemplate(template, vars, options) {
|
|
234
|
+
const encode = options?.encode ?? false;
|
|
235
|
+
return template.replace(/\{([^}]+)\}/g, (_, rawName) => {
|
|
236
|
+
const name = String(rawName).trim();
|
|
237
|
+
const value = vars[name];
|
|
238
|
+
if (typeof value !== "string") {
|
|
239
|
+
throw new Error(`Missing template variable: ${name}`);
|
|
240
|
+
}
|
|
241
|
+
return encode ? encodeURIComponent(value) : value;
|
|
242
|
+
});
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
// src/cli/runtime/server-url.ts
|
|
246
|
+
function resolveServerUrl(input) {
|
|
247
|
+
const base = input.serverOverride || input.servers[0]?.url;
|
|
248
|
+
if (!base) {
|
|
249
|
+
throw new Error("No server URL found. Provide --server <url> or define servers in the OpenAPI spec.");
|
|
250
|
+
}
|
|
251
|
+
const names = extractTemplateVars(base);
|
|
252
|
+
if (!names.length)
|
|
253
|
+
return base;
|
|
254
|
+
const vars = {};
|
|
255
|
+
for (const name of names) {
|
|
256
|
+
const provided = input.serverVars[name];
|
|
257
|
+
if (typeof provided === "string") {
|
|
258
|
+
vars[name] = provided;
|
|
259
|
+
continue;
|
|
260
|
+
}
|
|
261
|
+
const match = input.servers.find((s) => s.url === base);
|
|
262
|
+
const v = match?.variables.find((x) => x.name === name);
|
|
263
|
+
if (typeof v?.default === "string") {
|
|
264
|
+
vars[name] = v.default;
|
|
265
|
+
continue;
|
|
266
|
+
}
|
|
267
|
+
throw new Error(`Missing server variable '${name}'. Provide --server-var ${name}=...`);
|
|
268
|
+
}
|
|
269
|
+
return applyTemplate(base, vars);
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
// src/cli/runtime/validate/ajv.ts
|
|
273
|
+
import Ajv from "ajv";
|
|
274
|
+
import addFormats from "ajv-formats";
|
|
275
|
+
function createAjv() {
|
|
276
|
+
const ajv = new Ajv({
|
|
277
|
+
allErrors: true,
|
|
278
|
+
strict: false,
|
|
279
|
+
coerceTypes: false
|
|
280
|
+
});
|
|
281
|
+
addFormats(ajv);
|
|
282
|
+
return ajv;
|
|
283
|
+
}
|
|
284
|
+
// src/cli/runtime/validate/coerce.ts
|
|
285
|
+
import { InvalidArgumentError } from "commander";
|
|
286
|
+
function coerceValue(raw, type) {
|
|
287
|
+
if (type === "string" || type === "unknown")
|
|
288
|
+
return raw;
|
|
289
|
+
if (type === "boolean") {
|
|
290
|
+
if (raw === "true")
|
|
291
|
+
return true;
|
|
292
|
+
if (raw === "false")
|
|
293
|
+
return false;
|
|
294
|
+
throw new InvalidArgumentError(`Expected boolean, got '${raw}'`);
|
|
295
|
+
}
|
|
296
|
+
if (type === "integer") {
|
|
297
|
+
const n = Number.parseInt(raw, 10);
|
|
298
|
+
if (!Number.isFinite(n))
|
|
299
|
+
throw new InvalidArgumentError(`Expected integer, got '${raw}'`);
|
|
300
|
+
return n;
|
|
301
|
+
}
|
|
302
|
+
if (type === "number") {
|
|
303
|
+
const n = Number(raw);
|
|
304
|
+
if (!Number.isFinite(n))
|
|
305
|
+
throw new InvalidArgumentError(`Expected number, got '${raw}'`);
|
|
306
|
+
return n;
|
|
307
|
+
}
|
|
308
|
+
if (type === "object") {
|
|
309
|
+
try {
|
|
310
|
+
return JSON.parse(raw);
|
|
311
|
+
} catch {
|
|
312
|
+
throw new InvalidArgumentError(`Expected JSON object, got '${raw}'. Use --data/--file for complex bodies.`);
|
|
313
|
+
}
|
|
314
|
+
}
|
|
315
|
+
if (type === "array") {
|
|
316
|
+
return coerceArrayInput(raw, "string");
|
|
317
|
+
}
|
|
318
|
+
return raw;
|
|
319
|
+
}
|
|
320
|
+
function coerceArrayInput(raw, itemType) {
|
|
321
|
+
const trimmed = raw.trim();
|
|
322
|
+
if (!trimmed)
|
|
323
|
+
return [];
|
|
324
|
+
if (trimmed.startsWith("[")) {
|
|
325
|
+
let parsed;
|
|
326
|
+
try {
|
|
327
|
+
parsed = JSON.parse(trimmed);
|
|
328
|
+
} catch {
|
|
329
|
+
throw new InvalidArgumentError(`Expected JSON array, got '${raw}'`);
|
|
330
|
+
}
|
|
331
|
+
if (!Array.isArray(parsed)) {
|
|
332
|
+
throw new InvalidArgumentError(`Expected JSON array, got '${raw}'`);
|
|
333
|
+
}
|
|
334
|
+
return parsed.map((v) => coerceValue(String(v), itemType));
|
|
335
|
+
}
|
|
336
|
+
return trimmed.split(",").map((s) => s.trim()).filter(Boolean).map((s) => coerceValue(s, itemType));
|
|
337
|
+
}
|
|
338
|
+
// src/cli/runtime/validate/error.ts
|
|
339
|
+
function formatAjvErrors(errors) {
|
|
340
|
+
if (!errors?.length)
|
|
341
|
+
return "Invalid input";
|
|
342
|
+
return errors.map((e) => {
|
|
343
|
+
const path = e.instancePath || e.schemaPath || "";
|
|
344
|
+
if (e.keyword === "required" && e.params && typeof e.params === "object" && "missingProperty" in e.params) {
|
|
345
|
+
const missing = String(e.params.missingProperty);
|
|
346
|
+
const where = e.instancePath || "/";
|
|
347
|
+
return `${where} missing required property '${missing}'`.trim();
|
|
348
|
+
}
|
|
349
|
+
const msg = e.message || "invalid";
|
|
350
|
+
return `${path} ${msg}`.trim();
|
|
351
|
+
}).join(`
|
|
352
|
+
`);
|
|
353
|
+
}
|
|
354
|
+
// src/cli/runtime/validate/schema.ts
|
|
355
|
+
function deriveValidationSchemas(action) {
|
|
356
|
+
const query = { type: "object", properties: {}, required: [] };
|
|
357
|
+
const header = { type: "object", properties: {}, required: [] };
|
|
358
|
+
const cookie = { type: "object", properties: {}, required: [] };
|
|
359
|
+
for (const p of action.params) {
|
|
360
|
+
if (p.kind !== "flag")
|
|
361
|
+
continue;
|
|
362
|
+
const target = p.in === "query" ? query : p.in === "header" ? header : p.in === "cookie" ? cookie : undefined;
|
|
363
|
+
if (!target)
|
|
364
|
+
continue;
|
|
365
|
+
const schema = p.schema ?? (p.type === "unknown" ? {} : { type: p.type });
|
|
366
|
+
target.properties[p.name] = schema;
|
|
367
|
+
if (p.required) {
|
|
368
|
+
if (!target.required)
|
|
369
|
+
target.required = [];
|
|
370
|
+
target.required.push(p.name);
|
|
371
|
+
}
|
|
372
|
+
}
|
|
373
|
+
if (!query.required?.length)
|
|
374
|
+
delete query.required;
|
|
375
|
+
if (!header.required?.length)
|
|
376
|
+
delete header.required;
|
|
377
|
+
if (!cookie.required?.length)
|
|
378
|
+
delete cookie.required;
|
|
379
|
+
return {
|
|
380
|
+
querySchema: Object.keys(query.properties).length ? query : undefined,
|
|
381
|
+
headerSchema: Object.keys(header.properties).length ? header : undefined,
|
|
382
|
+
cookieSchema: Object.keys(cookie.properties).length ? cookie : undefined
|
|
383
|
+
};
|
|
384
|
+
}
|
|
385
|
+
// src/cli/runtime/request.ts
|
|
386
|
+
function parseKeyValuePairs(pairs) {
|
|
387
|
+
const out = {};
|
|
388
|
+
for (const pair of pairs ?? []) {
|
|
389
|
+
const idx = pair.indexOf("=");
|
|
390
|
+
if (idx === -1)
|
|
391
|
+
throw new Error(`Invalid pair '${pair}', expected name=value`);
|
|
392
|
+
const name = pair.slice(0, idx).trim();
|
|
393
|
+
const value = pair.slice(idx + 1).trim();
|
|
394
|
+
if (!name)
|
|
395
|
+
throw new Error(`Invalid pair '${pair}', missing name`);
|
|
396
|
+
out[name] = value;
|
|
397
|
+
}
|
|
398
|
+
return out;
|
|
399
|
+
}
|
|
400
|
+
function pickAuthSchemeKey(action, globals) {
|
|
401
|
+
if (globals.auth)
|
|
402
|
+
return globals.auth;
|
|
403
|
+
const req = action.auth.alternatives;
|
|
404
|
+
if (req.length === 1 && req[0]?.length === 1) {
|
|
405
|
+
return req[0][0]?.key;
|
|
406
|
+
}
|
|
407
|
+
return;
|
|
408
|
+
}
|
|
409
|
+
function applyAuth(headers, url, action, globals, authSchemes) {
|
|
410
|
+
const schemeKey = pickAuthSchemeKey(action, globals);
|
|
411
|
+
if (!schemeKey)
|
|
412
|
+
return { headers, url };
|
|
413
|
+
const scheme = authSchemes.find((s) => s.key === schemeKey);
|
|
414
|
+
if (!scheme) {
|
|
415
|
+
throw new Error(`Unknown auth scheme '${schemeKey}'. Available: ${authSchemes.map((s) => s.key).join(", ")}`);
|
|
416
|
+
}
|
|
417
|
+
if (scheme.kind === "http-bearer" || scheme.kind === "oauth2" || scheme.kind === "openIdConnect") {
|
|
418
|
+
const token = globals.bearerToken ?? globals.oauthToken;
|
|
419
|
+
if (!token)
|
|
420
|
+
throw new Error("Missing token. Provide --bearer-token <token>.");
|
|
421
|
+
headers.set("Authorization", `Bearer ${token}`);
|
|
422
|
+
return { headers, url };
|
|
423
|
+
}
|
|
424
|
+
if (scheme.kind === "http-basic") {
|
|
425
|
+
if (!globals.username)
|
|
426
|
+
throw new Error("Missing --username for basic auth");
|
|
427
|
+
if (!globals.password)
|
|
428
|
+
throw new Error("Missing --password for basic auth");
|
|
429
|
+
const raw = `${globals.username}:${globals.password}`;
|
|
430
|
+
const encoded = Buffer.from(raw, "utf8").toString("base64");
|
|
431
|
+
headers.set("Authorization", `Basic ${encoded}`);
|
|
432
|
+
return { headers, url };
|
|
433
|
+
}
|
|
434
|
+
if (scheme.kind === "api-key") {
|
|
435
|
+
if (!scheme.name)
|
|
436
|
+
throw new Error(`apiKey scheme '${scheme.key}' missing name`);
|
|
437
|
+
if (!scheme.in)
|
|
438
|
+
throw new Error(`apiKey scheme '${scheme.key}' missing location`);
|
|
439
|
+
if (!globals.apiKey)
|
|
440
|
+
throw new Error("Missing --api-key for apiKey auth");
|
|
441
|
+
if (scheme.in === "header") {
|
|
442
|
+
headers.set(scheme.name, globals.apiKey);
|
|
443
|
+
}
|
|
444
|
+
if (scheme.in === "query") {
|
|
445
|
+
url.searchParams.set(scheme.name, globals.apiKey);
|
|
446
|
+
}
|
|
447
|
+
if (scheme.in === "cookie") {
|
|
448
|
+
const existing = headers.get("Cookie");
|
|
449
|
+
const part = `${scheme.name}=${globals.apiKey}`;
|
|
450
|
+
headers.set("Cookie", existing ? `${existing}; ${part}` : part);
|
|
451
|
+
}
|
|
452
|
+
return { headers, url };
|
|
453
|
+
}
|
|
454
|
+
return { headers, url };
|
|
455
|
+
}
|
|
456
|
+
async function buildRequest(input) {
|
|
457
|
+
const defaultProfileName = "default";
|
|
458
|
+
const profilesFile = await readProfiles();
|
|
459
|
+
const profile = getProfile(profilesFile, defaultProfileName);
|
|
460
|
+
const embedded = input.embeddedDefaults;
|
|
461
|
+
const embeddedServerVars = parseKeyValuePairs(embedded?.serverVars);
|
|
462
|
+
const cliServerVars = parseKeyValuePairs(input.globals.serverVar);
|
|
463
|
+
const serverVars = { ...embeddedServerVars, ...cliServerVars };
|
|
464
|
+
const serverUrl = resolveServerUrl({
|
|
465
|
+
serverOverride: input.globals.server ?? profile?.server ?? embedded?.server,
|
|
466
|
+
servers: input.servers,
|
|
467
|
+
serverVars
|
|
468
|
+
});
|
|
469
|
+
const pathVars = {};
|
|
470
|
+
for (let i = 0;i < input.action.positionals.length; i++) {
|
|
471
|
+
const pos = input.action.positionals[i];
|
|
472
|
+
const raw = input.action.pathArgs[i];
|
|
473
|
+
const value = input.positionalValues[i];
|
|
474
|
+
if (typeof raw === "string" && typeof value === "string") {
|
|
475
|
+
pathVars[raw] = value;
|
|
476
|
+
}
|
|
477
|
+
if (pos?.name && typeof value === "string") {
|
|
478
|
+
pathVars[pos.name] = value;
|
|
479
|
+
}
|
|
480
|
+
}
|
|
481
|
+
const path = applyTemplate(input.action.path, pathVars, { encode: true });
|
|
482
|
+
const baseUrl = serverUrl.endsWith("/") ? serverUrl : `${serverUrl}/`;
|
|
483
|
+
const relativePath = path.startsWith("/") ? path.slice(1) : path;
|
|
484
|
+
const url = new URL(relativePath, baseUrl);
|
|
485
|
+
const headers = new Headers;
|
|
486
|
+
const queryValues = {};
|
|
487
|
+
const headerValues = {};
|
|
488
|
+
const cookieValues = {};
|
|
489
|
+
for (const p of input.action.params) {
|
|
490
|
+
if (p.kind !== "flag")
|
|
491
|
+
continue;
|
|
492
|
+
const optValue = input.flagValues[optionKeyFromFlag(p.flag)];
|
|
493
|
+
if (typeof optValue === "undefined")
|
|
494
|
+
continue;
|
|
495
|
+
if (p.in === "query") {
|
|
496
|
+
queryValues[p.name] = optValue;
|
|
497
|
+
}
|
|
498
|
+
if (p.in === "header") {
|
|
499
|
+
headerValues[p.name] = optValue;
|
|
500
|
+
}
|
|
501
|
+
if (p.in === "cookie") {
|
|
502
|
+
cookieValues[p.name] = optValue;
|
|
503
|
+
}
|
|
504
|
+
}
|
|
505
|
+
const schemas = deriveValidationSchemas(input.action);
|
|
506
|
+
const ajv2 = createAjv();
|
|
507
|
+
if (schemas.querySchema) {
|
|
508
|
+
const validate = ajv2.compile(schemas.querySchema);
|
|
509
|
+
if (!validate(queryValues)) {
|
|
510
|
+
throw new Error(formatAjvErrors(validate.errors));
|
|
511
|
+
}
|
|
512
|
+
}
|
|
513
|
+
if (schemas.headerSchema) {
|
|
514
|
+
const validate = ajv2.compile(schemas.headerSchema);
|
|
515
|
+
if (!validate(headerValues)) {
|
|
516
|
+
throw new Error(formatAjvErrors(validate.errors));
|
|
517
|
+
}
|
|
518
|
+
}
|
|
519
|
+
if (schemas.cookieSchema) {
|
|
520
|
+
const validate = ajv2.compile(schemas.cookieSchema);
|
|
521
|
+
if (!validate(cookieValues)) {
|
|
522
|
+
throw new Error(formatAjvErrors(validate.errors));
|
|
523
|
+
}
|
|
524
|
+
}
|
|
525
|
+
for (const [name, value] of Object.entries(queryValues)) {
|
|
526
|
+
if (Array.isArray(value)) {
|
|
527
|
+
for (const item of value) {
|
|
528
|
+
url.searchParams.append(name, String(item));
|
|
529
|
+
}
|
|
530
|
+
continue;
|
|
531
|
+
}
|
|
532
|
+
url.searchParams.set(name, String(value));
|
|
533
|
+
}
|
|
534
|
+
for (const [name, value] of Object.entries(headerValues)) {
|
|
535
|
+
headers.set(name, String(value));
|
|
536
|
+
}
|
|
537
|
+
for (const [name, value] of Object.entries(cookieValues)) {
|
|
538
|
+
const existing = headers.get("Cookie");
|
|
539
|
+
const part = `${name}=${String(value)}`;
|
|
540
|
+
headers.set("Cookie", existing ? `${existing}; ${part}` : part);
|
|
541
|
+
}
|
|
542
|
+
let body;
|
|
543
|
+
if (input.action.requestBody) {
|
|
544
|
+
const bodyFlagDefs = input.bodyFlagDefs ?? [];
|
|
545
|
+
const hasBodyFlags = bodyFlagDefs.some((def) => {
|
|
546
|
+
const dotKey = def.path.join(".");
|
|
547
|
+
return input.flagValues[dotKey] !== undefined;
|
|
548
|
+
});
|
|
549
|
+
const contentType = input.action.requestBody.preferredContentType;
|
|
550
|
+
if (contentType)
|
|
551
|
+
headers.set("Content-Type", contentType);
|
|
552
|
+
const schema2 = input.action.requestBodySchema;
|
|
553
|
+
const requiredFields = bodyFlagDefs.filter((d) => d.required);
|
|
554
|
+
if (!hasBodyFlags) {
|
|
555
|
+
if (requiredFields.length > 0) {
|
|
556
|
+
const flagList = requiredFields.map((d) => `--${d.path.join(".")}`);
|
|
557
|
+
throw new Error(`Required: ${flagList.join(", ")}`);
|
|
558
|
+
}
|
|
559
|
+
if (input.action.requestBody.required) {
|
|
560
|
+
body = "{}";
|
|
561
|
+
}
|
|
562
|
+
} else {
|
|
563
|
+
if (!contentType?.includes("json")) {
|
|
564
|
+
throw new Error("Body field flags are only supported for JSON request bodies.");
|
|
565
|
+
}
|
|
566
|
+
const { findMissingRequired: findMissingRequired2, parseDotNotationFlags: parseDotNotationFlags2 } = await Promise.resolve().then(() => exports_body_flags);
|
|
567
|
+
const missing = findMissingRequired2(input.flagValues, bodyFlagDefs);
|
|
568
|
+
if (missing.length > 0) {
|
|
569
|
+
const missingFlags = missing.map((m) => `--${m}`).join(", ");
|
|
570
|
+
throw new Error(`Missing required fields: ${missingFlags}`);
|
|
571
|
+
}
|
|
572
|
+
const built = parseDotNotationFlags2(input.flagValues, bodyFlagDefs);
|
|
573
|
+
if (schema2) {
|
|
574
|
+
const validate = ajv2.compile(schema2);
|
|
575
|
+
if (!validate(built)) {
|
|
576
|
+
throw new Error(formatAjvErrors(validate.errors));
|
|
577
|
+
}
|
|
578
|
+
}
|
|
579
|
+
body = JSON.stringify(built);
|
|
580
|
+
}
|
|
581
|
+
}
|
|
582
|
+
const storedToken = profile?.name ? await getToken(input.specId, profile.name) : null;
|
|
583
|
+
const resolvedAuthScheme = resolveAuthScheme(input.authSchemes, input.action.auth, {
|
|
584
|
+
flagAuthScheme: input.globals.auth,
|
|
585
|
+
profileAuthScheme: profile?.authScheme,
|
|
586
|
+
embeddedAuthScheme: embedded?.auth,
|
|
587
|
+
hasStoredToken: Boolean(storedToken)
|
|
588
|
+
});
|
|
589
|
+
const tokenFromProfile = resolvedAuthScheme ? storedToken : null;
|
|
590
|
+
const globalsWithProfileAuth = {
|
|
591
|
+
...input.globals,
|
|
592
|
+
auth: resolvedAuthScheme,
|
|
593
|
+
bearerToken: input.globals.bearerToken ?? input.globals.oauthToken ?? tokenFromProfile ?? undefined
|
|
594
|
+
};
|
|
595
|
+
const final = applyAuth(headers, url, input.action, globalsWithProfileAuth, input.authSchemes);
|
|
596
|
+
const req = new Request(final.url.toString(), {
|
|
597
|
+
method: input.action.method,
|
|
598
|
+
headers: final.headers,
|
|
599
|
+
body
|
|
600
|
+
});
|
|
601
|
+
const curl = buildCurl(req, body);
|
|
602
|
+
return { request: req, curl };
|
|
603
|
+
}
|
|
604
|
+
function buildCurl(req, body) {
|
|
605
|
+
const parts = ["curl", "-sS", "-X", req.method];
|
|
606
|
+
for (const [k, v] of req.headers.entries()) {
|
|
607
|
+
parts.push("-H", shellQuote(`${k}: ${v}`));
|
|
608
|
+
}
|
|
609
|
+
if (typeof body === "string") {
|
|
610
|
+
parts.push("--data", shellQuote(body));
|
|
611
|
+
}
|
|
612
|
+
parts.push(shellQuote(req.url));
|
|
613
|
+
return parts.join(" ");
|
|
614
|
+
}
|
|
615
|
+
function shellQuote(value) {
|
|
616
|
+
return `'${value.replace(/'/g, `'\\''`)}'`;
|
|
617
|
+
}
|
|
618
|
+
function optionKeyFromFlag(flag) {
|
|
619
|
+
const name = flag.replace(/^--/, "");
|
|
620
|
+
return name.replace(/-([a-z])/g, (_, c) => String(c).toUpperCase());
|
|
621
|
+
}
|
|
622
|
+
|
|
623
|
+
// src/cli/runtime/execute.ts
|
|
624
|
+
function formatError(message, resourceName, actionName) {
|
|
625
|
+
const helpCmd = resourceName ? `${resourceName} ${actionName} --help` : `${actionName} --help`;
|
|
626
|
+
return `${message}
|
|
627
|
+
|
|
628
|
+
Run '${helpCmd}' to see available options.`;
|
|
629
|
+
}
|
|
630
|
+
async function execute(input) {
|
|
631
|
+
const { request, curl } = await buildRequest({
|
|
632
|
+
specId: input.specId,
|
|
633
|
+
action: input.action,
|
|
634
|
+
positionalValues: input.positionalValues,
|
|
635
|
+
flagValues: input.flagValues,
|
|
636
|
+
globals: input.globals,
|
|
637
|
+
servers: input.servers,
|
|
638
|
+
authSchemes: input.authSchemes,
|
|
639
|
+
embeddedDefaults: input.embeddedDefaults,
|
|
640
|
+
bodyFlagDefs: input.bodyFlagDefs
|
|
641
|
+
});
|
|
642
|
+
const res = await fetch(request);
|
|
643
|
+
const contentType = res.headers.get("content-type") ?? "";
|
|
644
|
+
const text = await res.text();
|
|
645
|
+
let body = text;
|
|
646
|
+
if (contentType.includes("json") && text) {
|
|
647
|
+
try {
|
|
648
|
+
body = JSON.parse(text);
|
|
649
|
+
} catch {}
|
|
650
|
+
}
|
|
651
|
+
return {
|
|
652
|
+
ok: res.ok,
|
|
653
|
+
status: res.status,
|
|
654
|
+
body,
|
|
655
|
+
curl
|
|
656
|
+
};
|
|
657
|
+
}
|
|
658
|
+
async function executeAction(input) {
|
|
659
|
+
const actionName = input.action.action;
|
|
660
|
+
const resourceName = input.resourceName;
|
|
661
|
+
try {
|
|
662
|
+
if (input.globals.curl) {
|
|
663
|
+
const { curl } = await buildRequest({
|
|
664
|
+
specId: input.specId,
|
|
665
|
+
action: input.action,
|
|
666
|
+
positionalValues: input.positionalValues,
|
|
667
|
+
flagValues: input.flagValues,
|
|
668
|
+
globals: input.globals,
|
|
669
|
+
servers: input.servers,
|
|
670
|
+
authSchemes: input.authSchemes,
|
|
671
|
+
embeddedDefaults: input.embeddedDefaults,
|
|
672
|
+
bodyFlagDefs: input.bodyFlagDefs
|
|
673
|
+
});
|
|
674
|
+
process.stdout.write(`${curl}
|
|
675
|
+
`);
|
|
676
|
+
return;
|
|
677
|
+
}
|
|
678
|
+
const result = await execute(input);
|
|
679
|
+
if (!result.ok) {
|
|
680
|
+
if (input.globals.json) {
|
|
681
|
+
process.stdout.write(`${JSON.stringify({ status: result.status, body: result.body })}
|
|
682
|
+
`);
|
|
683
|
+
} else {
|
|
684
|
+
process.stderr.write(`HTTP ${result.status}
|
|
685
|
+
`);
|
|
686
|
+
process.stderr.write(`${typeof result.body === "string" ? result.body : JSON.stringify(result.body, null, 2)}
|
|
687
|
+
`);
|
|
688
|
+
}
|
|
689
|
+
process.exitCode = 1;
|
|
690
|
+
return;
|
|
691
|
+
}
|
|
692
|
+
if (input.globals.json) {
|
|
693
|
+
process.stdout.write(`${JSON.stringify(result.body)}
|
|
694
|
+
`);
|
|
695
|
+
return;
|
|
696
|
+
}
|
|
697
|
+
if (typeof result.body === "string") {
|
|
698
|
+
process.stdout.write(result.body);
|
|
699
|
+
if (!result.body.endsWith(`
|
|
700
|
+
`))
|
|
701
|
+
process.stdout.write(`
|
|
702
|
+
`);
|
|
703
|
+
} else {
|
|
704
|
+
process.stdout.write(`${JSON.stringify(result.body, null, 2)}
|
|
705
|
+
`);
|
|
706
|
+
}
|
|
707
|
+
} catch (err) {
|
|
708
|
+
const rawMessage = err instanceof Error ? err.message : String(err);
|
|
709
|
+
const message = formatError(rawMessage, resourceName, actionName);
|
|
710
|
+
if (input.globals.json) {
|
|
711
|
+
process.stdout.write(`${JSON.stringify({ error: rawMessage })}
|
|
712
|
+
`);
|
|
713
|
+
} else {
|
|
714
|
+
process.stderr.write(`error: ${message}
|
|
715
|
+
`);
|
|
716
|
+
}
|
|
717
|
+
process.exitCode = 1;
|
|
718
|
+
}
|
|
719
|
+
}
|
|
720
|
+
|
|
721
|
+
// src/cli/runtime/generated.ts
|
|
722
|
+
function formatCustomHelp(cmd, action, operationFlags, bodyFlagDefs) {
|
|
723
|
+
const lines = [];
|
|
724
|
+
const cmdName = cmd.name();
|
|
725
|
+
const parentName = cmd.parent?.name() ?? "";
|
|
726
|
+
const fullCmd = parentName ? `${parentName} ${cmdName}` : cmdName;
|
|
727
|
+
const positionals = action.positionals.map((p) => `<${p.name}>`).join(" ");
|
|
728
|
+
const usageSuffix = positionals ? ` ${positionals}` : "";
|
|
729
|
+
lines.push(`Usage: ${fullCmd}${usageSuffix} [options]`);
|
|
730
|
+
lines.push("");
|
|
731
|
+
const desc = action.summary ?? action.description ?? `${action.method} ${action.path}`;
|
|
732
|
+
lines.push(desc);
|
|
733
|
+
lines.push("");
|
|
734
|
+
const requiredOpts = [];
|
|
735
|
+
const optionalOpts = [];
|
|
736
|
+
const formatOpt = (flag, type, desc2, required) => {
|
|
737
|
+
const typeStr = type === "boolean" ? "" : ` <${type}>`;
|
|
738
|
+
const reqMarker = required ? " (required)" : "";
|
|
739
|
+
return ` ${flag}${typeStr}${reqMarker}
|
|
740
|
+
${desc2}`;
|
|
741
|
+
};
|
|
742
|
+
for (const f of operationFlags) {
|
|
743
|
+
const type = f.type === "array" ? `${f.itemType ?? "string"}[]` : f.type;
|
|
744
|
+
const line = formatOpt(f.flag, type, f.description ?? `${f.in} parameter`, f.required);
|
|
745
|
+
if (f.required) {
|
|
746
|
+
requiredOpts.push(line);
|
|
747
|
+
} else {
|
|
748
|
+
optionalOpts.push(line);
|
|
749
|
+
}
|
|
750
|
+
}
|
|
751
|
+
for (const def of bodyFlagDefs) {
|
|
752
|
+
const line = formatOpt(def.flag, def.type, def.description, def.required);
|
|
753
|
+
if (def.required) {
|
|
754
|
+
requiredOpts.push(line);
|
|
755
|
+
} else {
|
|
756
|
+
optionalOpts.push(line);
|
|
757
|
+
}
|
|
758
|
+
}
|
|
759
|
+
if (requiredOpts.length > 0) {
|
|
760
|
+
lines.push("Required:");
|
|
761
|
+
lines.push(...requiredOpts);
|
|
762
|
+
lines.push("");
|
|
763
|
+
}
|
|
764
|
+
if (optionalOpts.length > 0) {
|
|
765
|
+
lines.push("Options:");
|
|
766
|
+
lines.push(...optionalOpts);
|
|
767
|
+
lines.push("");
|
|
768
|
+
}
|
|
769
|
+
lines.push("Global:");
|
|
770
|
+
lines.push(` --curl
|
|
771
|
+
Print curl command instead of executing`);
|
|
772
|
+
lines.push(` --json
|
|
773
|
+
Output response as JSON`);
|
|
774
|
+
lines.push(` --server <url>
|
|
775
|
+
Override the API server URL`);
|
|
776
|
+
lines.push(` --bearer-token <token>
|
|
777
|
+
Provide auth token (or use 'login' command)`);
|
|
778
|
+
lines.push(` -h, --help
|
|
779
|
+
Show this help message`);
|
|
780
|
+
lines.push("");
|
|
781
|
+
return lines.join(`
|
|
782
|
+
`);
|
|
783
|
+
}
|
|
784
|
+
function addGeneratedCommands(program, context) {
|
|
785
|
+
for (const resource of context.commands.resources) {
|
|
786
|
+
const resourceCmd = program.command(resource.resource).description(`Operations for ${resource.resource}`);
|
|
787
|
+
for (const action of resource.actions) {
|
|
788
|
+
const cmd = resourceCmd.command(action.action);
|
|
789
|
+
cmd.description(action.summary ?? action.description ?? `${action.method} ${action.path}`);
|
|
790
|
+
for (const pos of action.positionals) {
|
|
791
|
+
cmd.argument(`<${pos.name}>`, pos.description);
|
|
792
|
+
}
|
|
793
|
+
for (const flag of action.flags) {
|
|
794
|
+
const opt = flag.flag;
|
|
795
|
+
const desc = flag.description ?? `${flag.in} parameter`;
|
|
796
|
+
if (flag.type === "boolean") {
|
|
797
|
+
cmd.option(opt, desc);
|
|
798
|
+
continue;
|
|
799
|
+
}
|
|
800
|
+
const isArray = flag.type === "array";
|
|
801
|
+
const itemType = flag.itemType ?? "string";
|
|
802
|
+
const flagType = isArray ? itemType : flag.type;
|
|
803
|
+
const parser = (raw) => coerceValue(raw, flagType);
|
|
804
|
+
if (isArray) {
|
|
805
|
+
const key2 = `${opt} <value>`;
|
|
806
|
+
cmd.option(key2, desc, (value, prev) => {
|
|
807
|
+
const next = [...prev ?? []];
|
|
808
|
+
const items = coerceArrayInput(value, itemType);
|
|
809
|
+
for (const item of items) {
|
|
810
|
+
next.push(item);
|
|
811
|
+
}
|
|
812
|
+
return next;
|
|
813
|
+
});
|
|
814
|
+
continue;
|
|
815
|
+
}
|
|
816
|
+
const key = `${opt} <value>`;
|
|
817
|
+
if (flag.required)
|
|
818
|
+
cmd.requiredOption(key, desc, parser);
|
|
819
|
+
else
|
|
820
|
+
cmd.option(key, desc, parser);
|
|
821
|
+
}
|
|
822
|
+
const operationFlagSet = new Set(action.flags.map((f) => f.flag));
|
|
823
|
+
const reservedFlags = new Set([...operationFlagSet, "--curl"]);
|
|
824
|
+
if (!operationFlagSet.has("--curl")) {
|
|
825
|
+
cmd.option("--curl", "Print curl command without sending");
|
|
826
|
+
}
|
|
827
|
+
let bodyFlagDefs = [];
|
|
828
|
+
if (action.requestBody) {
|
|
829
|
+
bodyFlagDefs = generateBodyFlags(action.requestBodySchema, reservedFlags);
|
|
830
|
+
for (const def of bodyFlagDefs) {
|
|
831
|
+
if (def.type === "boolean") {
|
|
832
|
+
cmd.option(def.flag, def.description);
|
|
833
|
+
} else {
|
|
834
|
+
cmd.option(`${def.flag} <value>`, def.description);
|
|
835
|
+
}
|
|
836
|
+
}
|
|
837
|
+
}
|
|
838
|
+
cmd.configureHelp({
|
|
839
|
+
formatHelp: () => formatCustomHelp(cmd, action, action.flags, bodyFlagDefs)
|
|
840
|
+
});
|
|
841
|
+
cmd.action(async (...args) => {
|
|
842
|
+
const command = args[args.length - 1];
|
|
843
|
+
const positionalValues = args.slice(0, -1).map((v) => String(v));
|
|
844
|
+
if (!(command instanceof Command)) {
|
|
845
|
+
throw new Error("Unexpected commander action signature");
|
|
846
|
+
}
|
|
847
|
+
const globals = command.optsWithGlobals();
|
|
848
|
+
const local = command.opts();
|
|
849
|
+
await executeAction({
|
|
850
|
+
action,
|
|
851
|
+
positionalValues,
|
|
852
|
+
flagValues: local,
|
|
853
|
+
globals,
|
|
854
|
+
servers: context.servers,
|
|
855
|
+
authSchemes: context.authSchemes,
|
|
856
|
+
specId: context.specId,
|
|
857
|
+
embeddedDefaults: context.embeddedDefaults,
|
|
858
|
+
bodyFlagDefs,
|
|
859
|
+
resourceName: resource.resource
|
|
860
|
+
});
|
|
861
|
+
});
|
|
862
|
+
}
|
|
863
|
+
}
|
|
864
|
+
}
|
|
865
|
+
export {
|
|
866
|
+
addGeneratedCommands
|
|
867
|
+
};
|
|
868
|
+
|
|
869
|
+
//# debugId=79BE30BC262E0F9D64756E2164756E21
|