@asnd/skill-creator 0.1.2 → 0.1.3
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/README.md +64 -125
- package/dist/cli/dynamic.d.ts +26 -0
- package/dist/cli/dynamic.js +136 -0
- package/dist/cli/dynamic.js.map +1 -0
- package/dist/cli/main.d.ts +2 -4
- package/dist/cli/main.js +376 -1530
- package/dist/cli/main.js.map +1 -1
- package/dist/cli/parse.d.ts +9 -0
- package/dist/cli/parse.js +38 -0
- package/dist/cli/parse.js.map +1 -0
- package/dist/commands/agents.d.ts +96 -0
- package/dist/commands/agents.js +116 -0
- package/dist/commands/agents.js.map +1 -0
- package/dist/commands/install.d.ts +12 -0
- package/dist/commands/install.js +229 -0
- package/dist/commands/install.js.map +1 -0
- package/dist/core/cache.d.ts +3 -0
- package/dist/core/cache.js +52 -0
- package/dist/core/cache.js.map +1 -0
- package/dist/core/coerce.d.ts +8 -0
- package/dist/core/coerce.js +128 -0
- package/dist/core/coerce.js.map +1 -0
- package/dist/core/filter.d.ts +7 -0
- package/dist/core/filter.js +16 -0
- package/dist/core/filter.js.map +1 -0
- package/dist/core/listing.d.ts +11 -0
- package/dist/core/listing.js +31 -0
- package/dist/core/listing.js.map +1 -0
- package/dist/core/names.d.ts +1 -0
- package/dist/core/names.js +7 -0
- package/dist/core/names.js.map +1 -0
- package/dist/core/output.d.ts +12 -0
- package/dist/core/output.js +34 -0
- package/dist/core/output.js.map +1 -0
- package/dist/core/secrets.d.ts +1 -0
- package/dist/core/secrets.js +21 -0
- package/dist/core/secrets.js.map +1 -0
- package/dist/core/types.d.ts +37 -0
- package/dist/core/types.js +2 -0
- package/dist/core/types.js.map +1 -0
- package/dist/core/usage.d.ts +13 -0
- package/dist/core/usage.js +33 -0
- package/dist/core/usage.js.map +1 -0
- package/dist/graphql/execute.d.ts +8 -0
- package/dist/graphql/execute.js +133 -0
- package/dist/graphql/execute.js.map +1 -0
- package/dist/graphql/extract.d.ts +6 -0
- package/dist/graphql/extract.js +109 -0
- package/dist/graphql/extract.js.map +1 -0
- package/dist/graphql/load.d.ts +11 -0
- package/dist/graphql/load.js +95 -0
- package/dist/graphql/load.js.map +1 -0
- package/dist/mcp/extract.d.ts +8 -0
- package/dist/mcp/extract.js +40 -0
- package/dist/mcp/extract.js.map +1 -0
- package/dist/mcp/http.d.ts +8 -0
- package/dist/mcp/http.js +80 -0
- package/dist/mcp/http.js.map +1 -0
- package/dist/mcp/stdio.d.ts +9 -0
- package/dist/mcp/stdio.js +67 -0
- package/dist/mcp/stdio.js.map +1 -0
- package/dist/openapi/execute.d.ts +12 -0
- package/dist/openapi/execute.js +44 -0
- package/dist/openapi/execute.js.map +1 -0
- package/dist/openapi/extract.d.ts +2 -0
- package/dist/openapi/extract.js +123 -0
- package/dist/openapi/extract.js.map +1 -0
- package/dist/openapi/load.d.ts +11 -0
- package/dist/openapi/load.js +52 -0
- package/dist/openapi/load.js.map +1 -0
- package/dist/openapi/params.d.ts +12 -0
- package/dist/openapi/params.js +47 -0
- package/dist/openapi/params.js.map +1 -0
- package/dist/openapi/refs.d.ts +1 -0
- package/dist/openapi/refs.js +8 -0
- package/dist/openapi/refs.js.map +1 -0
- package/dist/skills/agents.d.ts +77 -0
- package/dist/skills/agents.js +85 -0
- package/dist/skills/agents.js.map +1 -0
- package/dist/skills/generate.d.ts +1 -0
- package/dist/skills/generate.js +494 -0
- package/dist/skills/generate.js.map +1 -0
- package/package.json +7 -10
- package/prompts/skill-creator.md +169 -0
- package/dist/cli/package-SLCRJ4QY.js +0 -89
- package/dist/cli/package-SLCRJ4QY.js.map +0 -1
- package/prompts/generate-skill.md +0 -22
- package/skills/skill-creator/SKILL.md +0 -260
package/dist/cli/main.js
CHANGED
|
@@ -1,1583 +1,431 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
6
|
-
import {
|
|
7
|
-
import {
|
|
8
|
-
import {
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
import {
|
|
12
|
-
import
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
// src/core/coerce.ts
|
|
58
|
-
import { Ajv } from "ajv";
|
|
59
|
-
import addFormatsPlugin from "ajv-formats";
|
|
60
|
-
var ajv = new Ajv({ allErrors: true, strict: false });
|
|
61
|
-
var addFormats = addFormatsPlugin;
|
|
62
|
-
addFormats(ajv);
|
|
63
|
-
var validatorCache = /* @__PURE__ */ new WeakMap();
|
|
64
|
-
function schemaTypeToCliType(schema) {
|
|
65
|
-
switch (schema.type) {
|
|
66
|
-
case "integer":
|
|
67
|
-
return { type: "integer", suffix: "" };
|
|
68
|
-
case "number":
|
|
69
|
-
return { type: "number", suffix: "" };
|
|
70
|
-
case "boolean":
|
|
71
|
-
return { type: "boolean", suffix: "" };
|
|
72
|
-
case "array":
|
|
73
|
-
return { type: "string", suffix: " (JSON array)" };
|
|
74
|
-
case "object":
|
|
75
|
-
return { type: "string", suffix: " (JSON object)" };
|
|
76
|
-
default:
|
|
77
|
-
return { type: "string", suffix: "" };
|
|
78
|
-
}
|
|
79
|
-
}
|
|
80
|
-
function coerceAndValidateValue(value, schema = {}, label = "value") {
|
|
81
|
-
const coerced = coerceValue(value, schema);
|
|
82
|
-
validateValue(coerced, schema, label);
|
|
83
|
-
return coerced;
|
|
84
|
-
}
|
|
85
|
-
function validateValue(value, schema = {}, label = "value") {
|
|
86
|
-
if (Object.keys(schema).length === 0) return;
|
|
87
|
-
const validate = validatorFor(schema);
|
|
88
|
-
if (validate(value)) return;
|
|
89
|
-
throw new Error(`${label} failed validation: ${formatAjvErrors(validate.errors ?? [], label)}`);
|
|
90
|
-
}
|
|
91
|
-
function validatorFor(schema) {
|
|
92
|
-
const cached = validatorCache.get(schema);
|
|
93
|
-
if (cached !== void 0) return cached;
|
|
94
|
-
const validate = ajv.compile(normalizeSchema(schema));
|
|
95
|
-
validatorCache.set(schema, validate);
|
|
96
|
-
return validate;
|
|
97
|
-
}
|
|
98
|
-
function normalizeSchema(schema) {
|
|
99
|
-
if (schema.nullable === true && typeof schema.type === "string") {
|
|
100
|
-
return { ...schema, type: [schema.type, "null"] };
|
|
101
|
-
}
|
|
102
|
-
return schema;
|
|
103
|
-
}
|
|
104
|
-
function formatAjvErrors(errors, label) {
|
|
105
|
-
return errors.map((error) => {
|
|
106
|
-
const path = error.instancePath.length > 0 ? `${label}${error.instancePath}` : label;
|
|
107
|
-
return `${path} ${error.message ?? "is invalid"}`;
|
|
108
|
-
}).join("; ");
|
|
109
|
-
}
|
|
110
|
-
function coerceValue(value, schema = {}) {
|
|
111
|
-
if (value === null || value === void 0) return null;
|
|
112
|
-
switch (schema.type) {
|
|
113
|
-
case "array":
|
|
114
|
-
return coerceArray(value, schema.items);
|
|
115
|
-
case "object":
|
|
116
|
-
return coerceObject(value);
|
|
117
|
-
case "boolean":
|
|
118
|
-
return Boolean(value);
|
|
119
|
-
case "integer":
|
|
120
|
-
return typeof value === "number" ? Math.trunc(value) : Number.parseInt(String(value), 10);
|
|
121
|
-
case "number":
|
|
122
|
-
return typeof value === "number" ? value : Number.parseFloat(String(value));
|
|
123
|
-
default:
|
|
124
|
-
return coerceSchemaless(value);
|
|
125
|
-
}
|
|
126
|
-
}
|
|
127
|
-
function coerceArray(value, itemSchema) {
|
|
128
|
-
if (Array.isArray(value)) return value;
|
|
129
|
-
if (typeof value !== "string") return value;
|
|
130
|
-
try {
|
|
131
|
-
const parsed = JSON.parse(value);
|
|
132
|
-
if (Array.isArray(parsed)) return parsed;
|
|
133
|
-
} catch {
|
|
134
|
-
}
|
|
135
|
-
const values = value.includes(",") ? value.split(",").map((part) => part.trim()) : [value];
|
|
136
|
-
return values.map((item) => coerceItem(item, itemSchema?.type));
|
|
137
|
-
}
|
|
138
|
-
function coerceItem(value, type) {
|
|
139
|
-
switch (type) {
|
|
140
|
-
case "integer":
|
|
141
|
-
return Number.parseInt(value, 10);
|
|
142
|
-
case "number":
|
|
143
|
-
return Number.parseFloat(value);
|
|
144
|
-
case "boolean":
|
|
145
|
-
return ["true", "1", "yes"].includes(value.toLowerCase());
|
|
146
|
-
default:
|
|
147
|
-
return value;
|
|
148
|
-
}
|
|
149
|
-
}
|
|
150
|
-
function coerceObject(value) {
|
|
151
|
-
if (typeof value !== "string") return value;
|
|
152
|
-
try {
|
|
153
|
-
return JSON.parse(value);
|
|
154
|
-
} catch {
|
|
155
|
-
return value;
|
|
156
|
-
}
|
|
157
|
-
}
|
|
158
|
-
function coerceSchemaless(value) {
|
|
159
|
-
if (typeof value !== "string") return value;
|
|
160
|
-
const trimmed = value.trim();
|
|
161
|
-
if (!trimmed || !["{", "["].includes(trimmed[0] ?? "")) return value;
|
|
162
|
-
try {
|
|
163
|
-
const parsed = JSON.parse(trimmed);
|
|
164
|
-
return typeof parsed === "object" && parsed !== null ? parsed : value;
|
|
165
|
-
} catch {
|
|
166
|
-
return value;
|
|
167
|
-
}
|
|
168
|
-
}
|
|
169
|
-
|
|
170
|
-
// src/core/secrets.ts
|
|
171
|
-
import { readFile } from "fs/promises";
|
|
172
|
-
async function resolveSecret(value) {
|
|
173
|
-
if (value.startsWith("env:")) {
|
|
174
|
-
const name = value.slice(4);
|
|
175
|
-
const resolved = process.env[name];
|
|
176
|
-
if (resolved === void 0)
|
|
177
|
-
throw new Error(`environment variable ${JSON.stringify(name)} is not set`);
|
|
178
|
-
return resolved;
|
|
179
|
-
}
|
|
180
|
-
if (value.startsWith("file:")) {
|
|
181
|
-
const path = value.slice(5);
|
|
182
|
-
try {
|
|
183
|
-
return (await readFile(path, "utf8")).replace(/\n$/, "");
|
|
184
|
-
} catch {
|
|
185
|
-
throw new Error(`secret file not found: ${path}`);
|
|
186
|
-
}
|
|
187
|
-
}
|
|
188
|
-
return value;
|
|
189
|
-
}
|
|
190
|
-
|
|
191
|
-
// src/graphql/execute.ts
|
|
192
|
-
import {
|
|
193
|
-
Kind,
|
|
194
|
-
OperationTypeNode,
|
|
195
|
-
parse,
|
|
196
|
-
print
|
|
197
|
-
} from "graphql";
|
|
198
|
-
import { ClientError, GraphQLClient } from "graphql-request";
|
|
199
|
-
|
|
200
|
-
// src/graphql/extract.ts
|
|
201
|
-
import {
|
|
202
|
-
getNamedType,
|
|
203
|
-
isEnumType,
|
|
204
|
-
isInputObjectType,
|
|
205
|
-
isInterfaceType,
|
|
206
|
-
isListType,
|
|
207
|
-
isNonNullType,
|
|
208
|
-
isObjectType,
|
|
209
|
-
isScalarType
|
|
210
|
-
} from "graphql";
|
|
211
|
-
|
|
212
|
-
// src/core/names.ts
|
|
213
|
-
function toKebab(name) {
|
|
214
|
-
return name.replace(/([a-z0-9])([A-Z])/g, "$1-$2").replace(/_/g, "-").toLowerCase();
|
|
215
|
-
}
|
|
216
|
-
|
|
217
|
-
// src/graphql/extract.ts
|
|
218
|
-
function extractGraphqlCommands(schema) {
|
|
219
|
-
const commands = [];
|
|
220
|
-
addRootFields(commands, schema.getQueryType()?.getFields(), "query");
|
|
221
|
-
addRootFields(commands, schema.getMutationType()?.getFields(), "mutation");
|
|
222
|
-
return commands;
|
|
223
|
-
}
|
|
224
|
-
function buildGraphqlSelectionSet(type, fields, depth = 2) {
|
|
225
|
-
if (fields !== void 0 && fields.trim().length > 0) return `{ ${fields.trim()} }`;
|
|
226
|
-
return buildDefaultSelectionSet(type, depth, /* @__PURE__ */ new Set());
|
|
227
|
-
}
|
|
228
|
-
function addRootFields(commands, fields, operationType) {
|
|
229
|
-
if (fields === void 0) return;
|
|
230
|
-
for (const field of Object.values(fields)) {
|
|
231
|
-
const description = optionalString(field.description);
|
|
232
|
-
commands.push({
|
|
233
|
-
name: toKebab(field.name),
|
|
234
|
-
...description === void 0 ? {} : { description },
|
|
235
|
-
params: field.args.map(
|
|
236
|
-
(arg) => graphqlArgToParam(arg.name, arg.type, optionalString(arg.description))
|
|
237
|
-
),
|
|
238
|
-
graphqlOperationType: operationType,
|
|
239
|
-
graphqlFieldName: field.name,
|
|
240
|
-
graphqlReturnType: field.type
|
|
241
|
-
});
|
|
242
|
-
}
|
|
243
|
-
}
|
|
244
|
-
function graphqlArgToParam(name, type, description) {
|
|
245
|
-
const schema = graphqlInputTypeToJsonSchema(type);
|
|
246
|
-
const cliType = schemaTypeToCliType(schema).type;
|
|
247
|
-
return {
|
|
248
|
-
name: toKebab(name),
|
|
249
|
-
originalName: name,
|
|
250
|
-
type: cliType,
|
|
251
|
-
required: isNonNullType(type),
|
|
252
|
-
...description === void 0 ? {} : { description },
|
|
253
|
-
location: "graphql_arg",
|
|
254
|
-
schema
|
|
255
|
-
};
|
|
256
|
-
}
|
|
257
|
-
function graphqlInputTypeToJsonSchema(type) {
|
|
258
|
-
if (isNonNullType(type)) {
|
|
259
|
-
return { ...graphqlInputTypeToJsonSchema(type.ofType), graphqlType: graphqlTypeToString(type) };
|
|
260
|
-
}
|
|
261
|
-
if (isListType(type)) {
|
|
262
|
-
return {
|
|
263
|
-
type: "array",
|
|
264
|
-
items: graphqlInputTypeToJsonSchema(type.ofType),
|
|
265
|
-
graphqlType: graphqlTypeToString(type)
|
|
266
|
-
};
|
|
267
|
-
}
|
|
268
|
-
const namedType = getNamedType(type);
|
|
269
|
-
const base = { graphqlType: graphqlTypeToString(type) };
|
|
270
|
-
if (isEnumType(namedType)) {
|
|
271
|
-
return { ...base, type: "string", enum: namedType.getValues().map((value) => value.name) };
|
|
272
|
-
}
|
|
273
|
-
if (isInputObjectType(namedType)) return { ...base, type: "object" };
|
|
274
|
-
if (isScalarType(namedType)) {
|
|
275
|
-
switch (namedType.name) {
|
|
276
|
-
case "Int":
|
|
277
|
-
return { ...base, type: "integer" };
|
|
278
|
-
case "Float":
|
|
279
|
-
return { ...base, type: "number" };
|
|
280
|
-
case "Boolean":
|
|
281
|
-
return { ...base, type: "boolean" };
|
|
282
|
-
default:
|
|
283
|
-
return { ...base, type: "string" };
|
|
284
|
-
}
|
|
285
|
-
}
|
|
286
|
-
return { ...base, type: "string" };
|
|
287
|
-
}
|
|
288
|
-
function graphqlTypeToString(type) {
|
|
289
|
-
if (isNonNullType(type)) return `${graphqlTypeToString(type.ofType)}!`;
|
|
290
|
-
if (isListType(type)) return `[${graphqlTypeToString(type.ofType)}]`;
|
|
291
|
-
return getNamedType(type).name;
|
|
292
|
-
}
|
|
293
|
-
function optionalString(value) {
|
|
294
|
-
return value ?? void 0;
|
|
295
|
-
}
|
|
296
|
-
function buildDefaultSelectionSet(type, depth, seenTypes) {
|
|
297
|
-
const namedType = getNamedType(type);
|
|
298
|
-
if (isScalarType(namedType) || isEnumType(namedType)) return "";
|
|
299
|
-
if (depth <= 0) return "";
|
|
300
|
-
if (!isObjectType(namedType) && !isInterfaceType(namedType)) return "{ __typename }";
|
|
301
|
-
if (seenTypes.has(namedType.name)) return "";
|
|
302
|
-
const nextSeen = new Set(seenTypes);
|
|
303
|
-
nextSeen.add(namedType.name);
|
|
304
|
-
const selections = Object.values(namedType.getFields()).filter((field) => field.args.length === 0).map((field) => {
|
|
305
|
-
const fieldNamedType = getNamedType(field.type);
|
|
306
|
-
if (isScalarType(fieldNamedType) || isEnumType(fieldNamedType)) return field.name;
|
|
307
|
-
const nested = buildDefaultSelectionSet(field.type, depth - 1, nextSeen);
|
|
308
|
-
return nested ? `${field.name} ${nested}` : "";
|
|
309
|
-
}).filter((field) => field.length > 0);
|
|
310
|
-
return selections.length === 0 ? "" : `{ ${selections.join(" ")} }`;
|
|
311
|
-
}
|
|
312
|
-
|
|
313
|
-
// src/graphql/execute.ts
|
|
314
|
-
async function executeGraphql(command, values, options) {
|
|
315
|
-
const fieldName = command.graphqlFieldName ?? command.name;
|
|
316
|
-
const variables = collectVariables(command, values);
|
|
317
|
-
const query = buildGraphqlOperation(
|
|
318
|
-
command,
|
|
319
|
-
fieldName,
|
|
320
|
-
variables,
|
|
321
|
-
options.fields,
|
|
322
|
-
options.selectionDepth ?? 2
|
|
323
|
-
);
|
|
324
|
-
const client = new GraphQLClient(options.endpoint, {
|
|
325
|
-
headers: Object.fromEntries(options.authHeaders ?? [])
|
|
326
|
-
});
|
|
327
|
-
try {
|
|
328
|
-
const data = await client.request(query, variables);
|
|
329
|
-
if (!isRecord(data)) return data;
|
|
330
|
-
return data[fieldName];
|
|
331
|
-
} catch (error) {
|
|
332
|
-
throw normalizeGraphqlRequestError(error);
|
|
333
|
-
}
|
|
334
|
-
}
|
|
335
|
-
function collectVariables(command, values) {
|
|
336
|
-
const variables = {};
|
|
337
|
-
for (const param of command.params) {
|
|
338
|
-
const value = values[param.name] ?? values[param.originalName];
|
|
339
|
-
if (value !== void 0) {
|
|
340
|
-
variables[param.originalName] = coerceAndValidateValue(
|
|
341
|
-
value,
|
|
342
|
-
param.schema ?? {},
|
|
343
|
-
`--${param.name}`
|
|
344
|
-
);
|
|
345
|
-
}
|
|
346
|
-
}
|
|
347
|
-
return variables;
|
|
348
|
-
}
|
|
349
|
-
function buildGraphqlOperation(command, fieldName, variables, fields, selectionDepth) {
|
|
350
|
-
const activeParams = command.params.filter(
|
|
351
|
-
(param) => param.required || variables[param.originalName] !== void 0
|
|
352
|
-
);
|
|
353
|
-
const variableDefinitions = activeParams.map(
|
|
354
|
-
(param) => ({
|
|
355
|
-
kind: Kind.VARIABLE_DEFINITION,
|
|
356
|
-
variable: {
|
|
357
|
-
kind: Kind.VARIABLE,
|
|
358
|
-
name: { kind: Kind.NAME, value: param.originalName }
|
|
359
|
-
},
|
|
360
|
-
type: parseGraphqlType(graphqlParamType(param))
|
|
361
|
-
})
|
|
362
|
-
);
|
|
363
|
-
const selectionSet = buildSelectionSetNode(command, fields, selectionDepth);
|
|
364
|
-
const field = {
|
|
365
|
-
kind: Kind.FIELD,
|
|
366
|
-
name: { kind: Kind.NAME, value: fieldName },
|
|
367
|
-
arguments: activeParams.map((param) => ({
|
|
368
|
-
kind: Kind.ARGUMENT,
|
|
369
|
-
name: { kind: Kind.NAME, value: param.originalName },
|
|
370
|
-
value: {
|
|
371
|
-
kind: Kind.VARIABLE,
|
|
372
|
-
name: { kind: Kind.NAME, value: param.originalName }
|
|
373
|
-
}
|
|
374
|
-
})),
|
|
375
|
-
...selectionSet === void 0 ? {} : { selectionSet }
|
|
376
|
-
};
|
|
377
|
-
const operation = {
|
|
378
|
-
kind: Kind.OPERATION_DEFINITION,
|
|
379
|
-
operation: command.graphqlOperationType === "mutation" ? OperationTypeNode.MUTATION : OperationTypeNode.QUERY,
|
|
380
|
-
name: { kind: Kind.NAME, value: commandName(command) },
|
|
381
|
-
...variableDefinitions.length === 0 ? {} : { variableDefinitions },
|
|
382
|
-
selectionSet: { kind: Kind.SELECTION_SET, selections: [field] }
|
|
383
|
-
};
|
|
384
|
-
const document = { kind: Kind.DOCUMENT, definitions: [operation] };
|
|
385
|
-
return print(document);
|
|
386
|
-
}
|
|
387
|
-
function buildSelectionSetNode(command, fields, selectionDepth) {
|
|
388
|
-
if (command.graphqlReturnType === void 0) return void 0;
|
|
389
|
-
const selectionSet = buildGraphqlSelectionSet(
|
|
390
|
-
command.graphqlReturnType,
|
|
391
|
-
fields,
|
|
392
|
-
selectionDepth
|
|
393
|
-
);
|
|
394
|
-
if (selectionSet.length === 0) return void 0;
|
|
395
|
-
return parseSelectionSet(selectionSet);
|
|
396
|
-
}
|
|
397
|
-
function parseGraphqlType(type) {
|
|
398
|
-
const operation = parseSingleOperation(`query __Type($value: ${type}) { __typename }`);
|
|
399
|
-
const variable = operation.variableDefinitions?.[0];
|
|
400
|
-
if (variable === void 0) throw new Error(`invalid GraphQL variable type: ${type}`);
|
|
401
|
-
return variable.type;
|
|
402
|
-
}
|
|
403
|
-
function parseSelectionSet(selectionSet) {
|
|
404
|
-
const operation = parseSingleOperation(`query __Selection { _selection ${selectionSet} }`);
|
|
405
|
-
const selection = operation.selectionSet.selections[0];
|
|
406
|
-
if (selection?.kind !== Kind.FIELD || selection.selectionSet === void 0) {
|
|
407
|
-
throw new Error("invalid GraphQL selection set");
|
|
408
|
-
}
|
|
409
|
-
return selection.selectionSet;
|
|
410
|
-
}
|
|
411
|
-
function parseSingleOperation(source) {
|
|
412
|
-
const document = parse(source);
|
|
413
|
-
const definition = document.definitions[0];
|
|
414
|
-
if (definition?.kind !== Kind.OPERATION_DEFINITION) {
|
|
415
|
-
throw new Error("failed to build GraphQL operation");
|
|
416
|
-
}
|
|
417
|
-
return definition;
|
|
418
|
-
}
|
|
419
|
-
function graphqlParamType(param) {
|
|
420
|
-
const type = param.schema?.graphqlType;
|
|
421
|
-
return typeof type === "string" ? type : jsonSchemaToGraphqlType(param.schema?.type);
|
|
422
|
-
}
|
|
423
|
-
function jsonSchemaToGraphqlType(type) {
|
|
424
|
-
switch (type) {
|
|
425
|
-
case "integer":
|
|
426
|
-
return "Int";
|
|
427
|
-
case "number":
|
|
428
|
-
return "Float";
|
|
429
|
-
case "boolean":
|
|
430
|
-
return "Boolean";
|
|
431
|
-
default:
|
|
432
|
-
return "String";
|
|
433
|
-
}
|
|
434
|
-
}
|
|
435
|
-
function commandName(command) {
|
|
436
|
-
return `${command.graphqlOperationType ?? "query"}_${command.graphqlFieldName ?? command.name}`.replace(
|
|
437
|
-
/[^_0-9A-Za-z]/g,
|
|
438
|
-
"_"
|
|
439
|
-
);
|
|
440
|
-
}
|
|
441
|
-
function normalizeGraphqlRequestError(error) {
|
|
442
|
-
if (error instanceof ClientError) {
|
|
443
|
-
if (error.response.errors !== void 0 && error.response.errors.length > 0) {
|
|
444
|
-
return new Error(`GraphQL error: ${formatGraphqlErrors(error.response.errors)}`);
|
|
445
|
-
}
|
|
446
|
-
return new Error(
|
|
447
|
-
`GraphQL HTTP ${error.response.status}: ${JSON.stringify(error.response, null, 0)}`
|
|
448
|
-
);
|
|
449
|
-
}
|
|
450
|
-
return error instanceof Error ? error : new Error(String(error));
|
|
451
|
-
}
|
|
452
|
-
function formatGraphqlErrors(errors) {
|
|
453
|
-
return errors.map((error) => error.message ?? "unknown error").join("; ");
|
|
454
|
-
}
|
|
455
|
-
function isRecord(value) {
|
|
456
|
-
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
457
|
-
}
|
|
458
|
-
|
|
459
|
-
// src/graphql/load.ts
|
|
460
|
-
import { readFile as readFile2 } from "fs/promises";
|
|
461
|
-
import { GraphQLFileLoader } from "@graphql-tools/graphql-file-loader";
|
|
462
|
-
import ky from "ky";
|
|
463
|
-
import { JsonFileLoader } from "@graphql-tools/json-file-loader";
|
|
464
|
-
import { loadSchema } from "@graphql-tools/load";
|
|
465
|
-
import { UrlLoader } from "@graphql-tools/url-loader";
|
|
466
|
-
import {
|
|
467
|
-
buildClientSchema,
|
|
468
|
-
buildSchema,
|
|
469
|
-
getIntrospectionQuery,
|
|
470
|
-
introspectionFromSchema
|
|
471
|
-
} from "graphql";
|
|
472
|
-
import { GraphQLClient as GraphQLClient2 } from "graphql-request";
|
|
473
|
-
async function loadGraphqlSchema(endpoint, options = {}) {
|
|
474
|
-
const authHeaders = options.authHeaders ?? [];
|
|
475
|
-
if (options.schemaSource !== void 0) {
|
|
476
|
-
return loadProvidedGraphqlSchema(options.schemaSource, authHeaders);
|
|
477
|
-
}
|
|
478
|
-
const cacheKey = `graphql-${options.cacheKey ?? cacheKeyFor({ endpoint, authHeaders: options.authHeaders ?? [] })}`;
|
|
479
|
-
const ttlSeconds = options.ttlSeconds ?? 3600;
|
|
480
|
-
if (options.cacheDir !== void 0 && !options.refresh) {
|
|
481
|
-
const cached = await loadCached(options.cacheDir, cacheKey, ttlSeconds);
|
|
482
|
-
if (cached !== null) return buildClientSchema(cached);
|
|
483
|
-
}
|
|
484
|
-
try {
|
|
485
|
-
const schema = await loadRemoteGraphqlSchema(endpoint, authHeaders);
|
|
486
|
-
if (options.cacheDir !== void 0) {
|
|
487
|
-
await saveCache(options.cacheDir, cacheKey, introspectionFromSchema(schema));
|
|
488
|
-
}
|
|
489
|
-
return schema;
|
|
490
|
-
} catch (error) {
|
|
491
|
-
if (options.cacheDir !== void 0) {
|
|
492
|
-
const stale = await loadCached(
|
|
493
|
-
options.cacheDir,
|
|
494
|
-
cacheKey,
|
|
495
|
-
Number.POSITIVE_INFINITY
|
|
496
|
-
);
|
|
497
|
-
if (stale !== null) {
|
|
498
|
-
options.onWarning?.(
|
|
499
|
-
`Warning: using stale cached GraphQL schema because introspection failed: ${formatError(error)}`
|
|
500
|
-
);
|
|
501
|
-
return buildClientSchema(stale);
|
|
502
|
-
}
|
|
2
|
+
import { realpathSync } from 'node:fs';
|
|
3
|
+
import { homedir } from 'node:os';
|
|
4
|
+
import { join, resolve } from 'node:path';
|
|
5
|
+
import { fileURLToPath } from 'node:url';
|
|
6
|
+
import { parseArgs } from 'node:util';
|
|
7
|
+
import { runInstallCommand } from '../commands/install.js';
|
|
8
|
+
import { cacheKeyFor, loadCached, saveCache } from '../core/cache.js';
|
|
9
|
+
import { coerceAndValidateValue } from '../core/coerce.js';
|
|
10
|
+
import { resolveSecret } from '../core/secrets.js';
|
|
11
|
+
import { executeGraphql } from '../graphql/execute.js';
|
|
12
|
+
import { extractGraphqlCommands } from '../graphql/extract.js';
|
|
13
|
+
import { loadGraphqlSchema } from '../graphql/load.js';
|
|
14
|
+
import { extractMcpCommands } from '../mcp/extract.js';
|
|
15
|
+
import { callHttpTool, listHttpTools } from '../mcp/http.js';
|
|
16
|
+
import { callStdioTool, listStdioTools } from '../mcp/stdio.js';
|
|
17
|
+
import { executeOpenApi } from '../openapi/execute.js';
|
|
18
|
+
import { extractOpenApiCommands } from '../openapi/extract.js';
|
|
19
|
+
import { loadOpenApiSpec } from '../openapi/load.js';
|
|
20
|
+
import { runGenerate } from '../skills/generate.js';
|
|
21
|
+
import { runDynamicMode } from './dynamic.js';
|
|
22
|
+
import { splitAtSubcommand } from './parse.js';
|
|
23
|
+
const GLOBAL_OPTION_SPEC = {
|
|
24
|
+
valueOptions: [
|
|
25
|
+
'--spec',
|
|
26
|
+
'--mcp',
|
|
27
|
+
'--mcp-stdio',
|
|
28
|
+
'--graphql',
|
|
29
|
+
'--graphql-schema',
|
|
30
|
+
'--base-url',
|
|
31
|
+
'--auth-header',
|
|
32
|
+
'--transport',
|
|
33
|
+
'--cache-key',
|
|
34
|
+
'--cache-ttl',
|
|
35
|
+
'--search',
|
|
36
|
+
'--include',
|
|
37
|
+
'--exclude',
|
|
38
|
+
'--methods',
|
|
39
|
+
'--fields',
|
|
40
|
+
'--selection-depth',
|
|
41
|
+
'--head',
|
|
42
|
+
],
|
|
43
|
+
boolOptions: ['--list', '--pretty', '--raw', '--refresh', '--stdin', '--version', '--help', '-h'],
|
|
44
|
+
};
|
|
45
|
+
export async function run(argv = process.argv.slice(2)) {
|
|
46
|
+
if (argv[0] === '--')
|
|
47
|
+
argv = argv.slice(1);
|
|
48
|
+
if (argv[0] === 'generate') {
|
|
49
|
+
try {
|
|
50
|
+
await runGenerate(argv.slice(1));
|
|
51
|
+
return 0;
|
|
52
|
+
}
|
|
53
|
+
catch (error) {
|
|
54
|
+
console.error(`Error: ${error instanceof Error ? error.message : String(error)}`);
|
|
55
|
+
return 1;
|
|
56
|
+
}
|
|
503
57
|
}
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
}
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
const introspection = await client.request(getIntrospectionQuery());
|
|
514
|
-
return buildClientSchema(introspection);
|
|
515
|
-
}
|
|
516
|
-
async function loadProvidedGraphqlSchema(source, authHeaders) {
|
|
517
|
-
try {
|
|
518
|
-
const schema = await loadSchema(source, {
|
|
519
|
-
loaders: [new UrlLoader(), new GraphQLFileLoader(), new JsonFileLoader()],
|
|
520
|
-
headers: Object.fromEntries(authHeaders)
|
|
521
|
-
});
|
|
522
|
-
return buildClientSchema(introspectionFromSchema(schema));
|
|
523
|
-
} catch {
|
|
524
|
-
return loadProvidedGraphqlSchemaFallback(source, authHeaders);
|
|
525
|
-
}
|
|
526
|
-
}
|
|
527
|
-
async function loadProvidedGraphqlSchemaFallback(source, authHeaders) {
|
|
528
|
-
const text = await readSchemaSource(source, authHeaders);
|
|
529
|
-
const trimmed = text.trim();
|
|
530
|
-
if (trimmed.startsWith("{") || trimmed.startsWith("[")) {
|
|
531
|
-
const parsed = JSON.parse(trimmed);
|
|
532
|
-
return buildClientSchema(extractIntrospection(parsed));
|
|
533
|
-
}
|
|
534
|
-
return buildSchema(text);
|
|
535
|
-
}
|
|
536
|
-
async function readSchemaSource(source, authHeaders) {
|
|
537
|
-
if (source.startsWith("http://") || source.startsWith("https://")) {
|
|
538
|
-
const response = await ky.get(source, {
|
|
539
|
-
headers: Object.fromEntries(authHeaders),
|
|
540
|
-
throwHttpErrors: false
|
|
541
|
-
});
|
|
542
|
-
if (!response.ok) throw new Error(`failed to fetch GraphQL schema: HTTP ${response.status}`);
|
|
543
|
-
return response.text();
|
|
544
|
-
}
|
|
545
|
-
return readFile2(source, "utf8");
|
|
546
|
-
}
|
|
547
|
-
function extractIntrospection(value) {
|
|
548
|
-
if (!isRecord2(value)) throw new Error("GraphQL schema JSON must be an object");
|
|
549
|
-
if (isRecord2(value.data)) return extractIntrospection(value.data);
|
|
550
|
-
if (isRecord2(value.__schema)) return value;
|
|
551
|
-
throw new Error("GraphQL schema JSON must contain an introspection __schema object");
|
|
552
|
-
}
|
|
553
|
-
function formatError(error) {
|
|
554
|
-
return error instanceof Error ? error.message : String(error);
|
|
555
|
-
}
|
|
556
|
-
function isRecord2(value) {
|
|
557
|
-
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
558
|
-
}
|
|
559
|
-
|
|
560
|
-
// src/openapi/load.ts
|
|
561
|
-
import { readFile as readFile3 } from "fs/promises";
|
|
562
|
-
import ky2 from "ky";
|
|
563
|
-
import YAML from "yaml";
|
|
564
|
-
|
|
565
|
-
// src/openapi/refs.ts
|
|
566
|
-
import $RefParser from "@apidevtools/json-schema-ref-parser";
|
|
567
|
-
async function resolveRefs(input) {
|
|
568
|
-
return await $RefParser.dereference(input, {
|
|
569
|
-
mutateInputSchema: false,
|
|
570
|
-
dereference: { circular: "ignore" }
|
|
571
|
-
});
|
|
572
|
-
}
|
|
573
|
-
|
|
574
|
-
// src/openapi/load.ts
|
|
575
|
-
async function loadOpenApiSpec(source, options = {}) {
|
|
576
|
-
const isUrl = source.startsWith("http://") || source.startsWith("https://");
|
|
577
|
-
const ttlSeconds = options.ttlSeconds ?? 3600;
|
|
578
|
-
const key = options.cacheKey ?? cacheKeyFor({ source, authHeaders: options.authHeaders ?? [] });
|
|
579
|
-
if (isUrl && options.cacheDir !== void 0 && !options.refresh) {
|
|
580
|
-
const cached = await loadCached(options.cacheDir, key, ttlSeconds);
|
|
581
|
-
if (cached !== null) return cached;
|
|
582
|
-
}
|
|
583
|
-
const raw = isUrl ? await fetchRemoteSpec(source, options.authHeaders ?? []) : await readFile3(source, "utf8");
|
|
584
|
-
const parsed = parseSpec(raw);
|
|
585
|
-
const spec = await resolveRefs(parsed);
|
|
586
|
-
if (!isOpenApiSpec(spec)) {
|
|
587
|
-
throw new Error("spec must contain 'paths'");
|
|
588
|
-
}
|
|
589
|
-
if (isUrl && options.cacheDir !== void 0) {
|
|
590
|
-
await saveCache(options.cacheDir, key, spec);
|
|
591
|
-
}
|
|
592
|
-
return spec;
|
|
593
|
-
}
|
|
594
|
-
async function fetchRemoteSpec(source, authHeaders) {
|
|
595
|
-
const response = await ky2.get(source, {
|
|
596
|
-
headers: Object.fromEntries(authHeaders),
|
|
597
|
-
throwHttpErrors: false
|
|
598
|
-
});
|
|
599
|
-
if (!response.ok) throw new Error(`failed to fetch spec: HTTP ${response.status}`);
|
|
600
|
-
return response.text();
|
|
601
|
-
}
|
|
602
|
-
function parseSpec(raw) {
|
|
603
|
-
try {
|
|
604
|
-
return JSON.parse(raw);
|
|
605
|
-
} catch {
|
|
606
|
-
return YAML.parse(raw);
|
|
607
|
-
}
|
|
608
|
-
}
|
|
609
|
-
function isOpenApiSpec(value) {
|
|
610
|
-
return typeof value === "object" && value !== null && !Array.isArray(value) && "paths" in value && typeof value.paths === "object";
|
|
611
|
-
}
|
|
612
|
-
|
|
613
|
-
// src/openapi/extract.ts
|
|
614
|
-
var HTTP_METHODS = /* @__PURE__ */ new Set(["get", "post", "put", "delete", "patch"]);
|
|
615
|
-
function extractOpenApiCommands(spec) {
|
|
616
|
-
const commands = [];
|
|
617
|
-
const seenNames = /* @__PURE__ */ new Map();
|
|
618
|
-
const paths = isObject(spec) && isObject(spec.paths) ? spec.paths : {};
|
|
619
|
-
for (const [path, methods] of Object.entries(paths)) {
|
|
620
|
-
if (!isObject(methods)) continue;
|
|
621
|
-
for (const [method, operation] of Object.entries(methods)) {
|
|
622
|
-
if (!HTTP_METHODS.has(method) || !isObject(operation)) continue;
|
|
623
|
-
let name = operation.operationId !== void 0 ? toKebab(String(operation.operationId)) : fallbackName(method, path);
|
|
624
|
-
const seen = seenNames.get(name) ?? 0;
|
|
625
|
-
seenNames.set(name, seen + 1);
|
|
626
|
-
if (seen > 0) name = `${name}-${method}`;
|
|
627
|
-
const params = extractParameters(operation);
|
|
628
|
-
const body = extractRequestBodyParams(operation);
|
|
629
|
-
params.push(...body.params);
|
|
630
|
-
commands.push({
|
|
631
|
-
name,
|
|
632
|
-
description: String(
|
|
633
|
-
operation.summary ?? operation.description ?? `${method.toUpperCase()} ${path}`
|
|
634
|
-
),
|
|
635
|
-
params,
|
|
636
|
-
hasBody: body.params.length > 0,
|
|
637
|
-
method,
|
|
638
|
-
path,
|
|
639
|
-
...body.contentType === void 0 ? {} : { contentType: body.contentType }
|
|
640
|
-
});
|
|
58
|
+
if (argv[0] === 'command' || argv[0] === 'commands' || argv[0] === 'install-command') {
|
|
59
|
+
try {
|
|
60
|
+
await runInstallCommand(argv.slice(1));
|
|
61
|
+
return 0;
|
|
62
|
+
}
|
|
63
|
+
catch (error) {
|
|
64
|
+
console.error(`Error: ${error instanceof Error ? error.message : String(error)}`);
|
|
65
|
+
return 1;
|
|
66
|
+
}
|
|
641
67
|
}
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
const schema = getSchema(param.schema);
|
|
649
|
-
const { type, suffix } = schemaTypeToCliType(schema);
|
|
650
|
-
const choices = Array.isArray(schema.enum) ? { choices: schema.enum } : {};
|
|
651
|
-
return {
|
|
652
|
-
name: toKebab(String(param.name)),
|
|
653
|
-
originalName: String(param.name),
|
|
654
|
-
type,
|
|
655
|
-
required: Boolean(param.required),
|
|
656
|
-
description: `${String(param.description ?? param.name)}${suffix}`,
|
|
657
|
-
...choices,
|
|
658
|
-
location: normalizeLocation(param.in),
|
|
659
|
-
schema
|
|
660
|
-
};
|
|
661
|
-
});
|
|
662
|
-
}
|
|
663
|
-
function extractRequestBodyParams(operation) {
|
|
664
|
-
const requestBody = isObject(operation.requestBody) ? operation.requestBody : void 0;
|
|
665
|
-
const content = requestBody !== void 0 && isObject(requestBody.content) ? requestBody.content : {};
|
|
666
|
-
const multipartSchema = getContentSchema(content, "multipart/form-data");
|
|
667
|
-
const jsonSchema = getContentSchema(content, "application/json");
|
|
668
|
-
const multipartProps = getProperties(multipartSchema);
|
|
669
|
-
const hasBinary = Object.values(multipartProps).some((schema2) => schema2.format === "binary");
|
|
670
|
-
let schema = {};
|
|
671
|
-
let contentType;
|
|
672
|
-
if (hasBinary) {
|
|
673
|
-
schema = multipartSchema;
|
|
674
|
-
contentType = "multipart/form-data";
|
|
675
|
-
} else if (Object.keys(getProperties(jsonSchema)).length > 0) {
|
|
676
|
-
schema = jsonSchema;
|
|
677
|
-
} else if (Object.keys(multipartProps).length > 0) {
|
|
678
|
-
schema = multipartSchema;
|
|
679
|
-
contentType = "multipart/form-data";
|
|
680
|
-
}
|
|
681
|
-
const required = new Set(Array.isArray(schema.required) ? schema.required : []);
|
|
682
|
-
const properties = getProperties(schema);
|
|
683
|
-
const params = Object.entries(properties).map(([propName, propSchema]) => {
|
|
684
|
-
const isBinary = contentType === "multipart/form-data" && propSchema.format === "binary";
|
|
685
|
-
const { type, suffix } = isBinary ? { type: "string", suffix: " (file path)" } : schemaTypeToCliType(propSchema);
|
|
686
|
-
const choices = Array.isArray(propSchema.enum) ? { choices: propSchema.enum } : {};
|
|
687
|
-
return {
|
|
688
|
-
name: toKebab(propName),
|
|
689
|
-
originalName: propName,
|
|
690
|
-
type,
|
|
691
|
-
required: required.has(propName),
|
|
692
|
-
description: `${String(propSchema.description ?? propName)}${suffix}`,
|
|
693
|
-
...choices,
|
|
694
|
-
location: isBinary ? "file" : "body",
|
|
695
|
-
schema: propSchema
|
|
696
|
-
};
|
|
697
|
-
});
|
|
698
|
-
return contentType === void 0 ? { params } : { params, contentType };
|
|
699
|
-
}
|
|
700
|
-
function getContentSchema(content, contentType) {
|
|
701
|
-
const item = content[contentType];
|
|
702
|
-
return isObject(item) ? getSchema(item.schema) : {};
|
|
703
|
-
}
|
|
704
|
-
function getProperties(schema) {
|
|
705
|
-
return isObject(schema.properties) ? Object.fromEntries(
|
|
706
|
-
Object.entries(schema.properties).filter(
|
|
707
|
-
(entry) => isObject(entry[1])
|
|
708
|
-
)
|
|
709
|
-
) : {};
|
|
710
|
-
}
|
|
711
|
-
function getSchema(value) {
|
|
712
|
-
return isObject(value) ? value : {};
|
|
713
|
-
}
|
|
714
|
-
function normalizeLocation(value) {
|
|
715
|
-
return value === "path" || value === "header" || value === "body" || value === "file" ? value : "query";
|
|
716
|
-
}
|
|
717
|
-
function fallbackName(method, path) {
|
|
718
|
-
const slug = path.replace(/^\/+|\/+$/g, "").replace(/[{}]/g, "").replace(/\//g, "-");
|
|
719
|
-
return slug ? `${method}-${slug}` : method;
|
|
720
|
-
}
|
|
721
|
-
function isObject(value) {
|
|
722
|
-
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
723
|
-
}
|
|
724
|
-
|
|
725
|
-
// src/openapi/execute.ts
|
|
726
|
-
import ky3 from "ky";
|
|
727
|
-
|
|
728
|
-
// src/openapi/params.ts
|
|
729
|
-
function collectOpenApiParams(command, values, options = {}) {
|
|
730
|
-
let path = command.path ?? "";
|
|
731
|
-
const queryParams = {};
|
|
732
|
-
const headers = {};
|
|
733
|
-
let body = null;
|
|
734
|
-
const files = null;
|
|
735
|
-
for (const param of command.params) {
|
|
736
|
-
if (param.location !== "path") continue;
|
|
737
|
-
const value = values[param.name];
|
|
738
|
-
if (value !== void 0 && value !== null) {
|
|
739
|
-
path = path.replace(`{${param.originalName}}`, encodeURIComponent(String(value)));
|
|
68
|
+
const { globalArgv, commandArgv } = splitAtSubcommand(argv, GLOBAL_OPTION_SPEC);
|
|
69
|
+
const globals = parseGlobalArgs(globalArgv);
|
|
70
|
+
if (globals.version) {
|
|
71
|
+
const pkg = await import('../../package.json', { with: { type: 'json' } });
|
|
72
|
+
writeStdout(`skill-creator ${pkg.default.version}\n`);
|
|
73
|
+
return 0;
|
|
740
74
|
}
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
|
|
744
|
-
if (value === void 0 || value === null) continue;
|
|
745
|
-
if (param.location === "query") {
|
|
746
|
-
queryParams[param.originalName] = coerceAndValidateValue(
|
|
747
|
-
value,
|
|
748
|
-
param.schema ?? {},
|
|
749
|
-
`--${param.name}`
|
|
750
|
-
);
|
|
751
|
-
} else if (param.location === "header") {
|
|
752
|
-
headers[param.originalName] = String(value);
|
|
75
|
+
if (globals.help && commandArgv.length === 0) {
|
|
76
|
+
printHelp();
|
|
77
|
+
return 0;
|
|
753
78
|
}
|
|
754
|
-
|
|
755
|
-
|
|
756
|
-
|
|
757
|
-
|
|
758
|
-
|
|
759
|
-
|
|
760
|
-
const collectedBody = {};
|
|
761
|
-
for (const param of command.params) {
|
|
762
|
-
if (param.location !== "body") continue;
|
|
763
|
-
const value = values[param.name];
|
|
764
|
-
if (value !== void 0 && value !== null) {
|
|
765
|
-
collectedBody[param.originalName] = coerceAndValidateValue(
|
|
766
|
-
value,
|
|
767
|
-
param.schema ?? {},
|
|
768
|
-
`--${param.name}`
|
|
769
|
-
);
|
|
79
|
+
try {
|
|
80
|
+
validateSourceModes(globals);
|
|
81
|
+
globals.authHeaders = await resolveAuthHeaders(globals.authHeaders);
|
|
82
|
+
if (globals.spec !== undefined) {
|
|
83
|
+
await handleOpenApiMode(globals, commandArgv);
|
|
84
|
+
return 0;
|
|
770
85
|
}
|
|
771
|
-
|
|
772
|
-
|
|
86
|
+
if (globals.mcpStdio !== undefined) {
|
|
87
|
+
await handleMcpStdioMode(globals, commandArgv);
|
|
88
|
+
return 0;
|
|
89
|
+
}
|
|
90
|
+
if (globals.mcp !== undefined) {
|
|
91
|
+
await handleMcpHttpMode(globals, commandArgv);
|
|
92
|
+
return 0;
|
|
93
|
+
}
|
|
94
|
+
if (globals.graphql !== undefined) {
|
|
95
|
+
await handleGraphqlMode(globals, commandArgv);
|
|
96
|
+
return 0;
|
|
97
|
+
}
|
|
98
|
+
console.error('Error: only --spec, --mcp-stdio, --mcp, and --graphql modes are implemented in this TypeScript port so far.');
|
|
99
|
+
return 1;
|
|
773
100
|
}
|
|
774
|
-
|
|
775
|
-
|
|
776
|
-
|
|
777
|
-
|
|
778
|
-
// src/openapi/execute.ts
|
|
779
|
-
async function executeOpenApi(command, values, options) {
|
|
780
|
-
const collected = collectOpenApiParams(command, values, {
|
|
781
|
-
stdinBody: options.stdinBody
|
|
782
|
-
});
|
|
783
|
-
const url = buildUrl(options.baseUrl, collected.path, collected.queryParams);
|
|
784
|
-
const method = (command.method ?? "get").toUpperCase();
|
|
785
|
-
const headers = {
|
|
786
|
-
...Object.fromEntries(options.authHeaders ?? []),
|
|
787
|
-
...collected.headers
|
|
788
|
-
};
|
|
789
|
-
const body = method !== "GET" && collected.body !== null ? JSON.stringify(collected.body) : void 0;
|
|
790
|
-
if (body !== void 0) headers["Content-Type"] ??= "application/json";
|
|
791
|
-
const response = await ky3(url, {
|
|
792
|
-
method,
|
|
793
|
-
headers,
|
|
794
|
-
...body === void 0 ? {} : { body },
|
|
795
|
-
throwHttpErrors: false
|
|
796
|
-
});
|
|
797
|
-
return {
|
|
798
|
-
status: response.status,
|
|
799
|
-
ok: response.ok,
|
|
800
|
-
text: await response.text(),
|
|
801
|
-
contentType: response.headers.get("content-type") ?? ""
|
|
802
|
-
};
|
|
803
|
-
}
|
|
804
|
-
function buildUrl(baseUrl, path, queryParams) {
|
|
805
|
-
const url = new URL(`${baseUrl.replace(/\/$/, "")}${path}`);
|
|
806
|
-
for (const [key, value] of Object.entries(queryParams)) {
|
|
807
|
-
if (value === void 0 || value === null) continue;
|
|
808
|
-
if (Array.isArray(value)) {
|
|
809
|
-
for (const item of value) url.searchParams.append(key, String(item));
|
|
810
|
-
} else {
|
|
811
|
-
url.searchParams.set(key, String(value));
|
|
101
|
+
catch (error) {
|
|
102
|
+
console.error(`Error: ${error instanceof Error ? error.message : String(error)}`);
|
|
103
|
+
return 1;
|
|
812
104
|
}
|
|
813
|
-
}
|
|
814
|
-
return url.toString();
|
|
815
105
|
}
|
|
816
|
-
|
|
817
|
-
|
|
818
|
-
|
|
819
|
-
|
|
820
|
-
const
|
|
821
|
-
|
|
822
|
-
|
|
823
|
-
|
|
824
|
-
|
|
825
|
-
|
|
826
|
-
return {
|
|
827
|
-
name: toKebab(name),
|
|
828
|
-
originalName: name,
|
|
829
|
-
type,
|
|
830
|
-
required: required.has(name),
|
|
831
|
-
description: `${String(propSchema.description ?? name)}${suffix}`,
|
|
832
|
-
...choices,
|
|
833
|
-
location: "tool_input",
|
|
834
|
-
schema: propSchema
|
|
835
|
-
};
|
|
106
|
+
async function handleOpenApiMode(globals, commandArgv) {
|
|
107
|
+
if (globals.spec === undefined)
|
|
108
|
+
throw new Error('--spec is required');
|
|
109
|
+
const source = globals.spec;
|
|
110
|
+
const spec = await loadOpenApiSpec(source, {
|
|
111
|
+
authHeaders: globals.authHeaders,
|
|
112
|
+
cacheDir: defaultCacheDir(),
|
|
113
|
+
...(globals.cacheKey === undefined ? {} : { cacheKey: globals.cacheKey }),
|
|
114
|
+
ttlSeconds: globals.cacheTtl,
|
|
115
|
+
refresh: globals.refresh,
|
|
836
116
|
});
|
|
837
|
-
|
|
838
|
-
|
|
839
|
-
|
|
840
|
-
|
|
841
|
-
|
|
842
|
-
|
|
843
|
-
|
|
844
|
-
|
|
845
|
-
}
|
|
846
|
-
|
|
847
|
-
|
|
848
|
-
|
|
849
|
-
|
|
850
|
-
|
|
851
|
-
|
|
852
|
-
|
|
853
|
-
|
|
854
|
-
|
|
855
|
-
|
|
856
|
-
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
857
|
-
}
|
|
858
|
-
|
|
859
|
-
// src/mcp/http.ts
|
|
860
|
-
import { Client as Client2 } from "@modelcontextprotocol/sdk/client/index.js";
|
|
861
|
-
import { SSEClientTransport } from "@modelcontextprotocol/sdk/client/sse.js";
|
|
862
|
-
import { StreamableHTTPClientTransport } from "@modelcontextprotocol/sdk/client/streamableHttp.js";
|
|
863
|
-
|
|
864
|
-
// src/mcp/stdio.ts
|
|
865
|
-
import { Client } from "@modelcontextprotocol/sdk/client/index.js";
|
|
866
|
-
import { StdioClientTransport } from "@modelcontextprotocol/sdk/client/stdio.js";
|
|
867
|
-
import stringArgv from "string-argv";
|
|
868
|
-
async function listStdioTools(commandLine) {
|
|
869
|
-
return withStdioClient(commandLine, async (client) => {
|
|
870
|
-
const result = await client.listTools();
|
|
871
|
-
return result.tools.map((tool) => ({
|
|
872
|
-
name: tool.name,
|
|
873
|
-
...tool.description === void 0 ? {} : { description: tool.description },
|
|
874
|
-
inputSchema: tool.inputSchema
|
|
875
|
-
}));
|
|
876
|
-
});
|
|
877
|
-
}
|
|
878
|
-
async function callStdioTool(commandLine, toolName, args) {
|
|
879
|
-
return withStdioClient(commandLine, async (client) => {
|
|
880
|
-
const result = await client.callTool({ name: toolName, arguments: args });
|
|
881
|
-
return extractMcpContent(result.content);
|
|
882
|
-
});
|
|
883
|
-
}
|
|
884
|
-
async function withStdioClient(commandLine, fn) {
|
|
885
|
-
const [command, ...args] = splitCommandLine(commandLine);
|
|
886
|
-
if (command === void 0) throw new Error("--mcp-stdio command cannot be empty");
|
|
887
|
-
const client = new Client({ name: "skill-creator", version: "0.1.0" });
|
|
888
|
-
const transport = new StdioClientTransport({ command, args, stderr: "pipe" });
|
|
889
|
-
await client.connect(transport);
|
|
890
|
-
try {
|
|
891
|
-
return await fn(client);
|
|
892
|
-
} finally {
|
|
893
|
-
await client.close();
|
|
894
|
-
}
|
|
895
|
-
}
|
|
896
|
-
function splitCommandLine(commandLine) {
|
|
897
|
-
return stringArgv(commandLine);
|
|
898
|
-
}
|
|
899
|
-
function extractMcpContent(content) {
|
|
900
|
-
if (!Array.isArray(content)) return content;
|
|
901
|
-
const parts = content.map((part) => extractContentPart(part)).filter((part) => part !== void 0);
|
|
902
|
-
if (parts.length === 0) return "";
|
|
903
|
-
if (parts.every((part) => typeof part === "string")) return parts.join("\n");
|
|
904
|
-
return parts;
|
|
905
|
-
}
|
|
906
|
-
function extractContentPart(part) {
|
|
907
|
-
if (!isObject2(part)) return void 0;
|
|
908
|
-
if (part.type === "text" && typeof part.text === "string") return part.text;
|
|
909
|
-
if (typeof part.data === "string") return part.data;
|
|
910
|
-
if (part.type === "resource" && isObject2(part.resource)) {
|
|
911
|
-
if (typeof part.resource.text === "string") return part.resource.text;
|
|
912
|
-
if (typeof part.resource.blob === "string") return part.resource.blob;
|
|
913
|
-
}
|
|
914
|
-
return part;
|
|
915
|
-
}
|
|
916
|
-
function isObject2(value) {
|
|
917
|
-
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
918
|
-
}
|
|
919
|
-
|
|
920
|
-
// src/mcp/http.ts
|
|
921
|
-
async function listHttpTools(url, optionsOrHeaders = {}) {
|
|
922
|
-
const options = normalizeOptions(optionsOrHeaders);
|
|
923
|
-
return withHttpClient(url, options, async (client) => {
|
|
924
|
-
const result = await client.listTools();
|
|
925
|
-
return result.tools.map((tool) => ({
|
|
926
|
-
name: tool.name,
|
|
927
|
-
...tool.description === void 0 ? {} : { description: tool.description },
|
|
928
|
-
inputSchema: tool.inputSchema
|
|
929
|
-
}));
|
|
930
|
-
});
|
|
931
|
-
}
|
|
932
|
-
async function callHttpTool(url, toolName, args, optionsOrHeaders = {}) {
|
|
933
|
-
const options = normalizeOptions(optionsOrHeaders);
|
|
934
|
-
return withHttpClient(url, options, async (client) => {
|
|
935
|
-
const result = await client.callTool({ name: toolName, arguments: args });
|
|
936
|
-
return extractMcpContent(result.content);
|
|
937
|
-
});
|
|
938
|
-
}
|
|
939
|
-
async function withHttpClient(url, options, fn) {
|
|
940
|
-
if (options.transport === "auto") {
|
|
941
|
-
try {
|
|
942
|
-
return await withSingleHttpClient(url, { ...options, transport: "streamable" }, fn);
|
|
943
|
-
} catch (streamableError) {
|
|
944
|
-
try {
|
|
945
|
-
return await withSingleHttpClient(url, { ...options, transport: "sse" }, fn);
|
|
946
|
-
} catch (sseError) {
|
|
947
|
-
throw new Error(
|
|
948
|
-
`failed to connect using streamable HTTP or SSE (${formatError2(streamableError)}; ${formatError2(sseError)})`
|
|
949
|
-
);
|
|
950
|
-
}
|
|
951
|
-
}
|
|
952
|
-
}
|
|
953
|
-
return withSingleHttpClient(url, options, fn);
|
|
954
|
-
}
|
|
955
|
-
async function withSingleHttpClient(url, options, fn) {
|
|
956
|
-
const client = new Client2({ name: "skill-creator", version: "0.1.0" });
|
|
957
|
-
const transport = createTransport(url, options);
|
|
958
|
-
await client.connect(transport);
|
|
959
|
-
try {
|
|
960
|
-
return await fn(client);
|
|
961
|
-
} finally {
|
|
962
|
-
await client.close();
|
|
963
|
-
}
|
|
964
|
-
}
|
|
965
|
-
function createTransport(url, options) {
|
|
966
|
-
const headers = Object.fromEntries(options.headers);
|
|
967
|
-
if (options.transport === "sse") {
|
|
968
|
-
const fetchWithHeaders = (input, init) => fetch(input, { ...init, headers: { ...headers, ...headersToObject(init?.headers) } });
|
|
969
|
-
return new SSEClientTransport(new URL(url), {
|
|
970
|
-
eventSourceInit: { fetch: fetchWithHeaders },
|
|
971
|
-
requestInit: { headers }
|
|
117
|
+
await runDynamicMode({
|
|
118
|
+
globals,
|
|
119
|
+
commandArgv,
|
|
120
|
+
loadCommands: () => extractOpenApiCommands(spec),
|
|
121
|
+
renderCommands: renderOpenApiCommands,
|
|
122
|
+
onEmptyCommand: () => {
|
|
123
|
+
printHelp();
|
|
124
|
+
throw new Error('provide a subcommand, or use --list to see available commands');
|
|
125
|
+
},
|
|
126
|
+
executeCommand: async (command, values) => {
|
|
127
|
+
const baseUrl = determineBaseUrl(spec, source, globals.baseUrl);
|
|
128
|
+
const response = await executeOpenApi(command, values, {
|
|
129
|
+
baseUrl,
|
|
130
|
+
authHeaders: globals.authHeaders,
|
|
131
|
+
});
|
|
132
|
+
if (!response.ok)
|
|
133
|
+
throw new Error(`HTTP ${response.status}: ${response.text}`);
|
|
134
|
+
return response.text;
|
|
135
|
+
},
|
|
972
136
|
});
|
|
973
|
-
}
|
|
974
|
-
return new StreamableHTTPClientTransport(new URL(url), {
|
|
975
|
-
requestInit: { headers }
|
|
976
|
-
});
|
|
977
|
-
}
|
|
978
|
-
function normalizeOptions(optionsOrHeaders) {
|
|
979
|
-
if (Array.isArray(optionsOrHeaders)) {
|
|
980
|
-
return { headers: optionsOrHeaders, transport: "auto" };
|
|
981
|
-
}
|
|
982
|
-
return {
|
|
983
|
-
headers: optionsOrHeaders.headers ?? [],
|
|
984
|
-
transport: optionsOrHeaders.transport ?? "auto"
|
|
985
|
-
};
|
|
986
|
-
}
|
|
987
|
-
function headersToObject(headers) {
|
|
988
|
-
if (headers === void 0) return {};
|
|
989
|
-
return Object.fromEntries(new Headers(headers).entries());
|
|
990
|
-
}
|
|
991
|
-
function formatError2(error) {
|
|
992
|
-
return error instanceof Error ? error.message : String(error);
|
|
993
|
-
}
|
|
994
|
-
|
|
995
|
-
// src/cli/dynamic.ts
|
|
996
|
-
import { parseArgs } from "util";
|
|
997
|
-
|
|
998
|
-
// src/core/filter.ts
|
|
999
|
-
import { minimatch } from "minimatch";
|
|
1000
|
-
function filterCommands(commands, filters = {}) {
|
|
1001
|
-
let result = commands;
|
|
1002
|
-
if (filters.methods?.length) {
|
|
1003
|
-
const allowed = new Set(filters.methods.map((method) => method.toUpperCase()));
|
|
1004
|
-
result = result.filter(
|
|
1005
|
-
(command) => command.method === void 0 || allowed.has(command.method.toUpperCase())
|
|
1006
|
-
);
|
|
1007
|
-
}
|
|
1008
|
-
if (filters.include?.length) {
|
|
1009
|
-
result = result.filter(
|
|
1010
|
-
(command) => filters.include?.some((pattern) => minimatch(command.name, pattern)) ?? false
|
|
1011
|
-
);
|
|
1012
|
-
}
|
|
1013
|
-
if (filters.exclude?.length) {
|
|
1014
|
-
result = result.filter(
|
|
1015
|
-
(command) => !(filters.exclude?.some((pattern) => minimatch(command.name, pattern)) ?? false)
|
|
1016
|
-
);
|
|
1017
|
-
}
|
|
1018
|
-
return result;
|
|
1019
|
-
}
|
|
1020
|
-
|
|
1021
|
-
// src/core/output.ts
|
|
1022
|
-
function applyHead(data, n) {
|
|
1023
|
-
if (Array.isArray(data)) return data.slice(0, n);
|
|
1024
|
-
return data;
|
|
1025
|
-
}
|
|
1026
|
-
function formatOutput(data, options = {}) {
|
|
1027
|
-
if (options.raw) {
|
|
1028
|
-
return {
|
|
1029
|
-
stdout: `${typeof data === "string" ? data : JSON.stringify(data)}
|
|
1030
|
-
`,
|
|
1031
|
-
stderr: ""
|
|
1032
|
-
};
|
|
1033
|
-
}
|
|
1034
|
-
let value = parseJsonStringIfPossible(data);
|
|
1035
|
-
if (options.head !== void 0) value = applyHead(value, options.head);
|
|
1036
|
-
if (typeof value === "string") {
|
|
1037
|
-
return { stdout: `${value}
|
|
1038
|
-
`, stderr: "" };
|
|
1039
|
-
}
|
|
1040
|
-
return {
|
|
1041
|
-
stdout: `${JSON.stringify(value, null, options.pretty ? 2 : 0)}
|
|
1042
|
-
`,
|
|
1043
|
-
stderr: ""
|
|
1044
|
-
};
|
|
1045
|
-
}
|
|
1046
|
-
function parseJsonStringIfPossible(data) {
|
|
1047
|
-
if (typeof data !== "string") return data;
|
|
1048
|
-
try {
|
|
1049
|
-
return JSON.parse(data);
|
|
1050
|
-
} catch {
|
|
1051
|
-
return data;
|
|
1052
|
-
}
|
|
1053
|
-
}
|
|
1054
|
-
|
|
1055
|
-
// src/cli/dynamic.ts
|
|
1056
|
-
async function runDynamicMode(options) {
|
|
1057
|
-
let commands = filterCommands(await options.loadCommands(), options.globals);
|
|
1058
|
-
if (options.globals.search !== void 0) {
|
|
1059
|
-
commands = searchCommands(commands, options.globals.search);
|
|
1060
|
-
}
|
|
1061
|
-
if (options.globals.list || options.globals.search !== void 0) {
|
|
1062
|
-
writeStdout(options.renderCommands(commands));
|
|
1063
|
-
return;
|
|
1064
|
-
}
|
|
1065
|
-
if (options.commandArgv.length === 0) {
|
|
1066
|
-
if (options.onEmptyCommand !== void 0) {
|
|
1067
|
-
await options.onEmptyCommand(commands);
|
|
1068
|
-
return;
|
|
1069
|
-
}
|
|
1070
|
-
writeStdout(options.renderCommands(commands));
|
|
1071
|
-
return;
|
|
1072
|
-
}
|
|
1073
|
-
const commandName2 = options.commandArgv[0];
|
|
1074
|
-
if (commandName2 === void 0) throw new Error("missing subcommand");
|
|
1075
|
-
const command = commands.find((candidate) => candidate.name === commandName2);
|
|
1076
|
-
if (command === void 0) throw new Error(`unknown subcommand: ${commandName2}`);
|
|
1077
|
-
if (options.commandArgv.includes("--help") || options.commandArgv.includes("-h")) {
|
|
1078
|
-
writeStdout(renderCommandHelp(command));
|
|
1079
|
-
return;
|
|
1080
|
-
}
|
|
1081
|
-
const prepared = await prepareCommandArgs(options, options.commandArgv.slice(1));
|
|
1082
|
-
const values = parseCommandValues(command, prepared.argv, prepared.initialValues ?? {});
|
|
1083
|
-
const result = await options.executeCommand(command, values, prepared.argv);
|
|
1084
|
-
writeFormattedOutput(result, options.globals);
|
|
1085
|
-
}
|
|
1086
|
-
function searchCommands(commands, search) {
|
|
1087
|
-
const pattern = search.toLowerCase();
|
|
1088
|
-
return commands.filter(
|
|
1089
|
-
(command) => command.name.toLowerCase().includes(pattern) || (command.description ?? "").toLowerCase().includes(pattern)
|
|
1090
|
-
);
|
|
1091
|
-
}
|
|
1092
|
-
async function prepareCommandArgs(options, argv) {
|
|
1093
|
-
return options.prepareCommandArgs === void 0 ? { argv } : await options.prepareCommandArgs(argv);
|
|
1094
|
-
}
|
|
1095
|
-
function parseCommandValues(command, argv, initialValues = {}) {
|
|
1096
|
-
const values = { ...initialValues };
|
|
1097
|
-
const params = new Map(command.params.map((param) => [param.name, param]));
|
|
1098
|
-
validateCommandOptions(command, argv, params);
|
|
1099
|
-
const { values: parsedValues } = parseArgs({
|
|
1100
|
-
args: normalizeStringOptionValues(argv, stringParamNames(command)),
|
|
1101
|
-
options: commandOptionSpec(command),
|
|
1102
|
-
strict: true,
|
|
1103
|
-
allowPositionals: true
|
|
1104
|
-
});
|
|
1105
|
-
for (const param of command.params) {
|
|
1106
|
-
const value = parsedValues[param.name];
|
|
1107
|
-
if (value !== void 0) values[param.name] = value;
|
|
1108
|
-
}
|
|
1109
|
-
for (const param of command.params) {
|
|
1110
|
-
if (param.required && values[param.name] === void 0 && values[param.originalName] === void 0 && param.location !== "body") {
|
|
1111
|
-
throw new Error(`missing required option --${param.name}`);
|
|
1112
|
-
}
|
|
1113
|
-
}
|
|
1114
|
-
return values;
|
|
1115
|
-
}
|
|
1116
|
-
function validateCommandOptions(command, argv, params) {
|
|
1117
|
-
for (let index = 0; index < argv.length; index += 1) {
|
|
1118
|
-
const token = argv[index];
|
|
1119
|
-
if (token === void 0 || !token.startsWith("--")) continue;
|
|
1120
|
-
const rawFlag = token.slice(2);
|
|
1121
|
-
const flag = rawFlag.includes("=") ? rawFlag.slice(0, rawFlag.indexOf("=")) : rawFlag;
|
|
1122
|
-
const param = params.get(flag);
|
|
1123
|
-
if (param === void 0) throw new Error(`unknown option for ${command.name}: --${flag}`);
|
|
1124
|
-
if (param.type !== "boolean" && !rawFlag.includes("=") && argv[index + 1] === void 0) {
|
|
1125
|
-
throw new Error(`missing value for --${flag}`);
|
|
1126
|
-
}
|
|
1127
|
-
}
|
|
1128
|
-
}
|
|
1129
|
-
function commandOptionSpec(command) {
|
|
1130
|
-
return Object.fromEntries(
|
|
1131
|
-
command.params.map((param) => [
|
|
1132
|
-
param.name,
|
|
1133
|
-
{ type: param.type === "boolean" ? "boolean" : "string" }
|
|
1134
|
-
])
|
|
1135
|
-
);
|
|
1136
|
-
}
|
|
1137
|
-
function stringParamNames(command) {
|
|
1138
|
-
return new Set(
|
|
1139
|
-
command.params.filter((param) => param.type !== "boolean").map((param) => param.name)
|
|
1140
|
-
);
|
|
1141
|
-
}
|
|
1142
|
-
function normalizeStringOptionValues(argv, optionNames) {
|
|
1143
|
-
const result = [];
|
|
1144
|
-
for (let index = 0; index < argv.length; index += 1) {
|
|
1145
|
-
const token = argv[index];
|
|
1146
|
-
const next = argv[index + 1];
|
|
1147
|
-
if (token !== void 0 && next !== void 0 && token.startsWith("--") && !token.includes("=") && optionNames.has(token.slice(2)) && next.startsWith("-")) {
|
|
1148
|
-
result.push(`${token}=${next}`);
|
|
1149
|
-
index += 1;
|
|
1150
|
-
} else if (token !== void 0) {
|
|
1151
|
-
result.push(token);
|
|
1152
|
-
}
|
|
1153
|
-
}
|
|
1154
|
-
return result;
|
|
1155
|
-
}
|
|
1156
|
-
function renderCommandHelp(command) {
|
|
1157
|
-
const lines = [`${command.name}: ${command.description ?? ""}`, "", "Options:"];
|
|
1158
|
-
for (const param of command.params) {
|
|
1159
|
-
const required = param.required ? " (required)" : "";
|
|
1160
|
-
lines.push(
|
|
1161
|
-
` --${param.name.padEnd(24)} ${param.description ?? param.originalName}${required}`
|
|
1162
|
-
);
|
|
1163
|
-
}
|
|
1164
|
-
return `${lines.join("\n")}
|
|
1165
|
-
`;
|
|
1166
|
-
}
|
|
1167
|
-
function writeFormattedOutput(data, globals) {
|
|
1168
|
-
const output = formatOutput(data, {
|
|
1169
|
-
pretty: globals.pretty,
|
|
1170
|
-
raw: globals.raw,
|
|
1171
|
-
...globals.head === void 0 ? {} : { head: globals.head }
|
|
1172
|
-
});
|
|
1173
|
-
if (output.stderr) console.error(output.stderr.replace(/\n$/, ""));
|
|
1174
|
-
writeStdout(output.stdout);
|
|
1175
|
-
}
|
|
1176
|
-
function writeStdout(text) {
|
|
1177
|
-
console.log(text.replace(/\n$/, ""));
|
|
1178
|
-
}
|
|
1179
|
-
|
|
1180
|
-
// src/cli/parse.ts
|
|
1181
|
-
function splitAtSubcommand(argv, spec) {
|
|
1182
|
-
const valueOptions = new Set(spec.valueOptions);
|
|
1183
|
-
const boolOptions = new Set(spec.boolOptions);
|
|
1184
|
-
let index = 0;
|
|
1185
|
-
while (index < argv.length) {
|
|
1186
|
-
const arg = argv[index];
|
|
1187
|
-
if (arg === void 0) break;
|
|
1188
|
-
if (arg === "--") {
|
|
1189
|
-
return {
|
|
1190
|
-
globalArgv: argv.slice(0, index),
|
|
1191
|
-
commandArgv: argv.slice(index + 1)
|
|
1192
|
-
};
|
|
1193
|
-
}
|
|
1194
|
-
if (arg.startsWith("-")) {
|
|
1195
|
-
const optionName = arg.startsWith("--") && arg.includes("=") ? arg.slice(0, arg.indexOf("=")) : arg;
|
|
1196
|
-
if (arg.startsWith("--") && arg.includes("=")) {
|
|
1197
|
-
index += 1;
|
|
1198
|
-
} else if (valueOptions.has(optionName)) {
|
|
1199
|
-
index += 2;
|
|
1200
|
-
} else if (boolOptions.has(optionName)) {
|
|
1201
|
-
index += 1;
|
|
1202
|
-
} else {
|
|
1203
|
-
index += 1;
|
|
1204
|
-
}
|
|
1205
|
-
continue;
|
|
1206
|
-
}
|
|
1207
|
-
return {
|
|
1208
|
-
globalArgv: argv.slice(0, index),
|
|
1209
|
-
commandArgv: argv.slice(index)
|
|
1210
|
-
};
|
|
1211
|
-
}
|
|
1212
|
-
return { globalArgv: argv, commandArgv: [] };
|
|
1213
|
-
}
|
|
1214
|
-
|
|
1215
|
-
// src/cli/main.ts
|
|
1216
|
-
var GLOBAL_OPTION_SPEC = {
|
|
1217
|
-
valueOptions: [
|
|
1218
|
-
"--spec",
|
|
1219
|
-
"--mcp",
|
|
1220
|
-
"--mcp-stdio",
|
|
1221
|
-
"--graphql",
|
|
1222
|
-
"--graphql-schema",
|
|
1223
|
-
"--base-url",
|
|
1224
|
-
"--auth-header",
|
|
1225
|
-
"--transport",
|
|
1226
|
-
"--cache-key",
|
|
1227
|
-
"--cache-ttl",
|
|
1228
|
-
"--search",
|
|
1229
|
-
"--include",
|
|
1230
|
-
"--exclude",
|
|
1231
|
-
"--methods",
|
|
1232
|
-
"--fields",
|
|
1233
|
-
"--selection-depth",
|
|
1234
|
-
"--head"
|
|
1235
|
-
],
|
|
1236
|
-
boolOptions: ["--list", "--pretty", "--raw", "--refresh", "--stdin", "--version", "--help", "-h"]
|
|
1237
|
-
};
|
|
1238
|
-
async function run(argv = process.argv.slice(2)) {
|
|
1239
|
-
if (argv[0] === "--") argv = argv.slice(1);
|
|
1240
|
-
const { globalArgv, commandArgv } = splitAtSubcommand(argv, GLOBAL_OPTION_SPEC);
|
|
1241
|
-
const globals = parseGlobalArgs(globalArgv);
|
|
1242
|
-
if (globals.version) {
|
|
1243
|
-
const pkg = await import("./package-SLCRJ4QY.js");
|
|
1244
|
-
writeStdout2(`skill-creator ${pkg.default.version}
|
|
1245
|
-
`);
|
|
1246
|
-
return 0;
|
|
1247
|
-
}
|
|
1248
|
-
if (globals.help && commandArgv.length === 0) {
|
|
1249
|
-
printHelp();
|
|
1250
|
-
return 0;
|
|
1251
|
-
}
|
|
1252
|
-
try {
|
|
1253
|
-
validateSourceModes(globals);
|
|
1254
|
-
globals.authHeaders = await resolveAuthHeaders(globals.authHeaders);
|
|
1255
|
-
if (globals.spec !== void 0) {
|
|
1256
|
-
await handleOpenApiMode(globals, commandArgv);
|
|
1257
|
-
return 0;
|
|
1258
|
-
}
|
|
1259
|
-
if (globals.mcpStdio !== void 0) {
|
|
1260
|
-
await handleMcpStdioMode(globals, commandArgv);
|
|
1261
|
-
return 0;
|
|
1262
|
-
}
|
|
1263
|
-
if (globals.mcp !== void 0) {
|
|
1264
|
-
await handleMcpHttpMode(globals, commandArgv);
|
|
1265
|
-
return 0;
|
|
1266
|
-
}
|
|
1267
|
-
if (globals.graphql !== void 0) {
|
|
1268
|
-
await handleGraphqlMode(globals, commandArgv);
|
|
1269
|
-
return 0;
|
|
1270
|
-
}
|
|
1271
|
-
console.error(
|
|
1272
|
-
"Error: only --spec, --mcp-stdio, --mcp, and --graphql modes are implemented in this TypeScript port so far."
|
|
1273
|
-
);
|
|
1274
|
-
return 1;
|
|
1275
|
-
} catch (error) {
|
|
1276
|
-
console.error(`Error: ${error instanceof Error ? error.message : String(error)}`);
|
|
1277
|
-
return 1;
|
|
1278
|
-
}
|
|
1279
|
-
}
|
|
1280
|
-
async function handleOpenApiMode(globals, commandArgv) {
|
|
1281
|
-
if (globals.spec === void 0) throw new Error("--spec is required");
|
|
1282
|
-
const source = globals.spec;
|
|
1283
|
-
const spec = await loadOpenApiSpec(source, {
|
|
1284
|
-
authHeaders: globals.authHeaders,
|
|
1285
|
-
cacheDir: defaultCacheDir(),
|
|
1286
|
-
...globals.cacheKey === void 0 ? {} : { cacheKey: globals.cacheKey },
|
|
1287
|
-
ttlSeconds: globals.cacheTtl,
|
|
1288
|
-
refresh: globals.refresh
|
|
1289
|
-
});
|
|
1290
|
-
await runDynamicMode({
|
|
1291
|
-
globals,
|
|
1292
|
-
commandArgv,
|
|
1293
|
-
loadCommands: () => extractOpenApiCommands(spec),
|
|
1294
|
-
renderCommands: renderOpenApiCommands,
|
|
1295
|
-
onEmptyCommand: () => {
|
|
1296
|
-
printHelp();
|
|
1297
|
-
throw new Error("provide a subcommand, or use --list to see available commands");
|
|
1298
|
-
},
|
|
1299
|
-
executeCommand: async (command, values) => {
|
|
1300
|
-
const baseUrl = determineBaseUrl(spec, source, globals.baseUrl);
|
|
1301
|
-
const response = await executeOpenApi(command, values, {
|
|
1302
|
-
baseUrl,
|
|
1303
|
-
authHeaders: globals.authHeaders
|
|
1304
|
-
});
|
|
1305
|
-
if (!response.ok) throw new Error(`HTTP ${response.status}: ${response.text}`);
|
|
1306
|
-
return response.text;
|
|
1307
|
-
}
|
|
1308
|
-
});
|
|
1309
137
|
}
|
|
1310
138
|
async function handleGraphqlMode(globals, commandArgv) {
|
|
1311
|
-
|
|
1312
|
-
|
|
1313
|
-
|
|
1314
|
-
|
|
1315
|
-
|
|
1316
|
-
|
|
1317
|
-
|
|
1318
|
-
|
|
1319
|
-
|
|
1320
|
-
|
|
1321
|
-
|
|
1322
|
-
|
|
1323
|
-
|
|
1324
|
-
|
|
1325
|
-
|
|
1326
|
-
|
|
1327
|
-
|
|
1328
|
-
|
|
1329
|
-
|
|
1330
|
-
|
|
1331
|
-
|
|
1332
|
-
|
|
1333
|
-
|
|
1334
|
-
|
|
1335
|
-
|
|
1336
|
-
|
|
1337
|
-
|
|
1338
|
-
|
|
139
|
+
if (globals.graphql === undefined)
|
|
140
|
+
throw new Error('--graphql is required');
|
|
141
|
+
const endpoint = globals.graphql;
|
|
142
|
+
const schema = await loadGraphqlSchema(endpoint, {
|
|
143
|
+
authHeaders: globals.authHeaders,
|
|
144
|
+
cacheDir: defaultCacheDir(),
|
|
145
|
+
...(globals.cacheKey === undefined ? {} : { cacheKey: globals.cacheKey }),
|
|
146
|
+
ttlSeconds: globals.cacheTtl,
|
|
147
|
+
refresh: globals.refresh,
|
|
148
|
+
...(globals.graphqlSchema === undefined ? {} : { schemaSource: globals.graphqlSchema }),
|
|
149
|
+
onWarning: (message) => console.error(message),
|
|
150
|
+
});
|
|
151
|
+
await runDynamicMode({
|
|
152
|
+
globals,
|
|
153
|
+
commandArgv,
|
|
154
|
+
loadCommands: () => extractGraphqlCommands(schema),
|
|
155
|
+
renderCommands: renderGraphqlCommands,
|
|
156
|
+
prepareCommandArgs: async (argv) => {
|
|
157
|
+
const stdinFlag = stripFlag(argv, '--stdin');
|
|
158
|
+
const stdinValues = globals.stdin || stdinFlag.enabled ? await readStdinJson() : {};
|
|
159
|
+
return { argv: stdinFlag.argv, initialValues: stdinValues };
|
|
160
|
+
},
|
|
161
|
+
executeCommand: (command, values) => executeGraphql(command, values, {
|
|
162
|
+
endpoint,
|
|
163
|
+
authHeaders: globals.authHeaders,
|
|
164
|
+
...(globals.fields === undefined ? {} : { fields: globals.fields }),
|
|
165
|
+
selectionDepth: globals.selectionDepth,
|
|
166
|
+
}),
|
|
167
|
+
});
|
|
1339
168
|
}
|
|
1340
169
|
async function handleMcpHttpMode(globals, commandArgv) {
|
|
1341
|
-
|
|
1342
|
-
|
|
1343
|
-
|
|
1344
|
-
|
|
1345
|
-
|
|
1346
|
-
|
|
1347
|
-
|
|
1348
|
-
|
|
1349
|
-
|
|
1350
|
-
|
|
1351
|
-
|
|
1352
|
-
|
|
1353
|
-
|
|
1354
|
-
|
|
1355
|
-
|
|
170
|
+
if (globals.mcp === undefined)
|
|
171
|
+
throw new Error('--mcp is required');
|
|
172
|
+
const endpoint = globals.mcp;
|
|
173
|
+
await runDynamicMode({
|
|
174
|
+
globals,
|
|
175
|
+
commandArgv,
|
|
176
|
+
loadCommands: async () => extractMcpCommands(await loadMcpHttpTools(globals)),
|
|
177
|
+
renderCommands: renderMcpCommands,
|
|
178
|
+
executeCommand: async (command, values) => {
|
|
179
|
+
const toolArgs = collectMcpToolArgs(command, values);
|
|
180
|
+
return callHttpTool(endpoint, command.toolName ?? command.name, toolArgs, {
|
|
181
|
+
headers: globals.authHeaders,
|
|
182
|
+
transport: globals.transport,
|
|
183
|
+
});
|
|
184
|
+
},
|
|
185
|
+
});
|
|
1356
186
|
}
|
|
1357
187
|
async function loadMcpHttpTools(globals) {
|
|
1358
|
-
|
|
1359
|
-
|
|
1360
|
-
|
|
1361
|
-
|
|
1362
|
-
|
|
1363
|
-
|
|
1364
|
-
|
|
1365
|
-
|
|
1366
|
-
const
|
|
1367
|
-
if (
|
|
1368
|
-
|
|
1369
|
-
|
|
1370
|
-
|
|
1371
|
-
|
|
1372
|
-
|
|
1373
|
-
|
|
1374
|
-
|
|
188
|
+
if (globals.mcp === undefined)
|
|
189
|
+
throw new Error('--mcp is required');
|
|
190
|
+
const cacheKey = `mcp-${globals.cacheKey ??
|
|
191
|
+
cacheKeyFor({
|
|
192
|
+
source: globals.mcp,
|
|
193
|
+
authHeaders: globals.authHeaders,
|
|
194
|
+
transport: globals.transport,
|
|
195
|
+
})}`;
|
|
196
|
+
const cacheDir = defaultCacheDir();
|
|
197
|
+
if (!globals.refresh) {
|
|
198
|
+
const cached = await loadCached(cacheDir, cacheKey, globals.cacheTtl);
|
|
199
|
+
if (cached !== null)
|
|
200
|
+
return cached;
|
|
201
|
+
}
|
|
202
|
+
const tools = await listHttpTools(globals.mcp, {
|
|
203
|
+
headers: globals.authHeaders,
|
|
204
|
+
transport: globals.transport,
|
|
205
|
+
});
|
|
206
|
+
await saveCache(cacheDir, cacheKey, tools);
|
|
207
|
+
return tools;
|
|
1375
208
|
}
|
|
1376
209
|
async function handleMcpStdioMode(globals, commandArgv) {
|
|
1377
|
-
|
|
1378
|
-
|
|
1379
|
-
|
|
1380
|
-
|
|
1381
|
-
|
|
1382
|
-
|
|
1383
|
-
|
|
1384
|
-
|
|
1385
|
-
|
|
1386
|
-
|
|
1387
|
-
|
|
1388
|
-
|
|
210
|
+
if (globals.mcpStdio === undefined)
|
|
211
|
+
throw new Error('--mcp-stdio is required');
|
|
212
|
+
const commandLine = globals.mcpStdio;
|
|
213
|
+
await runDynamicMode({
|
|
214
|
+
globals,
|
|
215
|
+
commandArgv,
|
|
216
|
+
loadCommands: async () => extractMcpCommands(await listStdioTools(commandLine)),
|
|
217
|
+
renderCommands: renderMcpCommands,
|
|
218
|
+
executeCommand: async (command, values) => {
|
|
219
|
+
const toolArgs = collectMcpToolArgs(command, values);
|
|
220
|
+
return callStdioTool(commandLine, command.toolName ?? command.name, toolArgs);
|
|
221
|
+
},
|
|
222
|
+
});
|
|
1389
223
|
}
|
|
1390
224
|
function collectMcpToolArgs(command, values) {
|
|
1391
|
-
|
|
1392
|
-
|
|
1393
|
-
|
|
1394
|
-
|
|
1395
|
-
|
|
1396
|
-
param.schema ?? {},
|
|
1397
|
-
`--${param.name}`
|
|
1398
|
-
);
|
|
225
|
+
const args = {};
|
|
226
|
+
for (const param of command.params) {
|
|
227
|
+
if (values[param.name] !== undefined) {
|
|
228
|
+
args[param.originalName] = coerceAndValidateValue(values[param.name], param.schema ?? {}, `--${param.name}`);
|
|
229
|
+
}
|
|
1399
230
|
}
|
|
1400
|
-
|
|
1401
|
-
return args;
|
|
231
|
+
return args;
|
|
1402
232
|
}
|
|
1403
233
|
function parseGlobalArgs(argv) {
|
|
1404
|
-
|
|
1405
|
-
|
|
1406
|
-
|
|
1407
|
-
|
|
1408
|
-
|
|
1409
|
-
|
|
1410
|
-
|
|
1411
|
-
|
|
1412
|
-
|
|
1413
|
-
|
|
1414
|
-
|
|
1415
|
-
|
|
1416
|
-
|
|
1417
|
-
|
|
1418
|
-
|
|
1419
|
-
|
|
1420
|
-
|
|
1421
|
-
|
|
1422
|
-
|
|
1423
|
-
|
|
1424
|
-
|
|
1425
|
-
|
|
1426
|
-
|
|
1427
|
-
|
|
1428
|
-
|
|
1429
|
-
|
|
1430
|
-
|
|
1431
|
-
|
|
1432
|
-
|
|
1433
|
-
|
|
1434
|
-
|
|
1435
|
-
|
|
1436
|
-
|
|
1437
|
-
|
|
1438
|
-
|
|
1439
|
-
|
|
1440
|
-
|
|
1441
|
-
|
|
1442
|
-
|
|
1443
|
-
|
|
1444
|
-
|
|
1445
|
-
|
|
1446
|
-
|
|
1447
|
-
|
|
1448
|
-
|
|
1449
|
-
|
|
1450
|
-
|
|
1451
|
-
|
|
1452
|
-
|
|
1453
|
-
|
|
1454
|
-
|
|
1455
|
-
|
|
1456
|
-
|
|
1457
|
-
|
|
1458
|
-
|
|
1459
|
-
|
|
1460
|
-
|
|
1461
|
-
|
|
1462
|
-
|
|
1463
|
-
|
|
234
|
+
assertKnownValueOptionsHaveValues(argv, GLOBAL_OPTION_SPEC.valueOptions);
|
|
235
|
+
const { values } = parseArgs({
|
|
236
|
+
args: argv,
|
|
237
|
+
options: {
|
|
238
|
+
spec: { type: 'string' },
|
|
239
|
+
mcp: { type: 'string' },
|
|
240
|
+
'mcp-stdio': { type: 'string' },
|
|
241
|
+
graphql: { type: 'string' },
|
|
242
|
+
'graphql-schema': { type: 'string' },
|
|
243
|
+
'base-url': { type: 'string' },
|
|
244
|
+
'auth-header': { type: 'string', multiple: true },
|
|
245
|
+
transport: { type: 'string' },
|
|
246
|
+
'cache-key': { type: 'string' },
|
|
247
|
+
'cache-ttl': { type: 'string' },
|
|
248
|
+
search: { type: 'string' },
|
|
249
|
+
include: { type: 'string' },
|
|
250
|
+
exclude: { type: 'string' },
|
|
251
|
+
methods: { type: 'string' },
|
|
252
|
+
fields: { type: 'string' },
|
|
253
|
+
'selection-depth': { type: 'string' },
|
|
254
|
+
head: { type: 'string' },
|
|
255
|
+
list: { type: 'boolean' },
|
|
256
|
+
pretty: { type: 'boolean' },
|
|
257
|
+
raw: { type: 'boolean' },
|
|
258
|
+
refresh: { type: 'boolean' },
|
|
259
|
+
stdin: { type: 'boolean' },
|
|
260
|
+
version: { type: 'boolean' },
|
|
261
|
+
help: { type: 'boolean', short: 'h' },
|
|
262
|
+
},
|
|
263
|
+
strict: false,
|
|
264
|
+
allowPositionals: false,
|
|
265
|
+
});
|
|
266
|
+
const authHeaders = stringListOption(values['auth-header']).map(parseHeader);
|
|
267
|
+
const search = stringOption(values.search);
|
|
268
|
+
return {
|
|
269
|
+
...optionalStringProperty('spec', stringOption(values.spec)),
|
|
270
|
+
...optionalStringProperty('mcp', stringOption(values.mcp)),
|
|
271
|
+
...optionalStringProperty('mcpStdio', stringOption(values['mcp-stdio'])),
|
|
272
|
+
...optionalStringProperty('graphql', stringOption(values.graphql)),
|
|
273
|
+
...optionalStringProperty('graphqlSchema', stringOption(values['graphql-schema'])),
|
|
274
|
+
...optionalStringProperty('baseUrl', stringOption(values['base-url'])),
|
|
275
|
+
authHeaders,
|
|
276
|
+
transport: parseTransport(stringOption(values.transport) ?? 'auto'),
|
|
277
|
+
...optionalStringProperty('cacheKey', stringOption(values['cache-key'])),
|
|
278
|
+
cacheTtl: Number.parseInt(stringOption(values['cache-ttl']) ?? '3600', 10),
|
|
279
|
+
refresh: values.refresh === true,
|
|
280
|
+
list: values.list === true || search !== undefined,
|
|
281
|
+
...optionalStringProperty('search', search),
|
|
282
|
+
...optionalStringArrayProperty('include', parseOptionalCommaList(values.include)),
|
|
283
|
+
...optionalStringArrayProperty('exclude', parseOptionalCommaList(values.exclude)),
|
|
284
|
+
...optionalStringArrayProperty('methods', parseOptionalCommaList(values.methods)),
|
|
285
|
+
...optionalStringProperty('fields', stringOption(values.fields)),
|
|
286
|
+
selectionDepth: Number.parseInt(stringOption(values['selection-depth']) ?? '2', 10),
|
|
287
|
+
stdin: values.stdin === true,
|
|
288
|
+
pretty: values.pretty === true,
|
|
289
|
+
raw: values.raw === true,
|
|
290
|
+
...optionalNumberProperty('head', parseOptionalInteger(values.head)),
|
|
291
|
+
help: values.help === true,
|
|
292
|
+
version: values.version === true,
|
|
293
|
+
};
|
|
1464
294
|
}
|
|
1465
295
|
function renderOpenApiCommands(commands) {
|
|
1466
|
-
|
|
1467
|
-
|
|
1468
|
-
|
|
1469
|
-
|
|
1470
|
-
`;
|
|
296
|
+
if (commands.length === 0)
|
|
297
|
+
return 'No commands found.\n';
|
|
298
|
+
return `${commands
|
|
299
|
+
.map((command) => `${command.name.padEnd(32)} ${(command.method ?? '').toUpperCase().padEnd(6)} ${command.description ?? ''}`.trimEnd())
|
|
300
|
+
.join('\n')}\n`;
|
|
1471
301
|
}
|
|
1472
302
|
function renderMcpCommands(commands) {
|
|
1473
|
-
|
|
1474
|
-
|
|
1475
|
-
|
|
303
|
+
if (commands.length === 0)
|
|
304
|
+
return 'No tools found.\n';
|
|
305
|
+
return `${commands
|
|
306
|
+
.map((command) => `${command.name.padEnd(32)} ${command.description ?? ''}`.trimEnd())
|
|
307
|
+
.join('\n')}\n`;
|
|
1476
308
|
}
|
|
1477
309
|
function renderGraphqlCommands(commands) {
|
|
1478
|
-
|
|
1479
|
-
|
|
1480
|
-
|
|
1481
|
-
|
|
1482
|
-
`;
|
|
310
|
+
if (commands.length === 0)
|
|
311
|
+
return 'No GraphQL operations found.\n';
|
|
312
|
+
return `${commands
|
|
313
|
+
.map((command) => `${command.name.padEnd(32)} ${(command.graphqlOperationType ?? '').padEnd(8)} ${command.description ?? ''}`.trimEnd())
|
|
314
|
+
.join('\n')}\n`;
|
|
1483
315
|
}
|
|
1484
316
|
function determineBaseUrl(spec, source, override) {
|
|
1485
|
-
|
|
1486
|
-
|
|
1487
|
-
|
|
1488
|
-
|
|
1489
|
-
|
|
1490
|
-
|
|
1491
|
-
|
|
1492
|
-
|
|
1493
|
-
|
|
1494
|
-
|
|
317
|
+
if (override !== undefined)
|
|
318
|
+
return override;
|
|
319
|
+
const servers = Array.isArray(spec.servers) ? spec.servers : [];
|
|
320
|
+
const firstServer = servers[0];
|
|
321
|
+
const serverUrl = typeof firstServer === 'object' && firstServer !== null && 'url' in firstServer
|
|
322
|
+
? String(firstServer.url)
|
|
323
|
+
: '';
|
|
324
|
+
if (serverUrl.startsWith('http://') || serverUrl.startsWith('https://'))
|
|
325
|
+
return serverUrl;
|
|
326
|
+
if (source.startsWith('http://') || source.startsWith('https://')) {
|
|
327
|
+
const origin = new URL(source).origin;
|
|
328
|
+
return serverUrl ? `${origin}${serverUrl}` : origin;
|
|
329
|
+
}
|
|
330
|
+
throw new Error('cannot determine base URL. Use --base-url.');
|
|
1495
331
|
}
|
|
1496
332
|
function validateSourceModes(globals) {
|
|
1497
|
-
|
|
1498
|
-
(
|
|
1499
|
-
|
|
1500
|
-
|
|
1501
|
-
|
|
1502
|
-
|
|
1503
|
-
|
|
1504
|
-
if (active > 1)
|
|
1505
|
-
throw new Error("--spec, --mcp, --mcp-stdio, and --graphql are mutually exclusive.");
|
|
333
|
+
const active = [globals.spec, globals.mcp, globals.mcpStdio, globals.graphql].filter((value) => value !== undefined).length;
|
|
334
|
+
if (active === 0) {
|
|
335
|
+
printHelp();
|
|
336
|
+
throw new Error('one of --spec, --mcp, --mcp-stdio, or --graphql is required.');
|
|
337
|
+
}
|
|
338
|
+
if (active > 1)
|
|
339
|
+
throw new Error('--spec, --mcp, --mcp-stdio, and --graphql are mutually exclusive.');
|
|
1506
340
|
}
|
|
1507
341
|
function assertKnownValueOptionsHaveValues(argv, options) {
|
|
1508
|
-
|
|
1509
|
-
|
|
1510
|
-
|
|
1511
|
-
|
|
1512
|
-
|
|
1513
|
-
|
|
1514
|
-
|
|
1515
|
-
|
|
342
|
+
const valueOptions = new Set(options);
|
|
343
|
+
for (let index = 0; index < argv.length; index += 1) {
|
|
344
|
+
const token = argv[index];
|
|
345
|
+
if (token === undefined || !token.startsWith('--'))
|
|
346
|
+
continue;
|
|
347
|
+
const option = token.includes('=') ? token.slice(0, token.indexOf('=')) : token;
|
|
348
|
+
if (!valueOptions.has(option) || token.includes('='))
|
|
349
|
+
continue;
|
|
350
|
+
if (argv[index + 1] === undefined)
|
|
351
|
+
throw new Error(`missing value for ${option}`);
|
|
352
|
+
}
|
|
1516
353
|
}
|
|
1517
354
|
function stringOption(value) {
|
|
1518
|
-
|
|
355
|
+
return typeof value === 'string' ? value : undefined;
|
|
1519
356
|
}
|
|
1520
357
|
function stringListOption(value) {
|
|
1521
|
-
|
|
1522
|
-
|
|
1523
|
-
|
|
358
|
+
if (Array.isArray(value))
|
|
359
|
+
return value.filter((item) => typeof item === 'string');
|
|
360
|
+
const single = stringOption(value);
|
|
361
|
+
return single === undefined ? [] : [single];
|
|
1524
362
|
}
|
|
1525
363
|
function optionalStringProperty(key, value) {
|
|
1526
|
-
|
|
364
|
+
return value === undefined ? {} : { [key]: value };
|
|
1527
365
|
}
|
|
1528
366
|
function optionalStringArrayProperty(key, value) {
|
|
1529
|
-
|
|
367
|
+
return value === undefined ? {} : { [key]: value };
|
|
1530
368
|
}
|
|
1531
369
|
function optionalNumberProperty(key, value) {
|
|
1532
|
-
|
|
370
|
+
return value === undefined ? {} : { [key]: value };
|
|
1533
371
|
}
|
|
1534
372
|
function parseOptionalCommaList(value) {
|
|
1535
|
-
|
|
1536
|
-
|
|
373
|
+
const raw = stringOption(value);
|
|
374
|
+
return raw === undefined ? undefined : parseCommaList(raw);
|
|
1537
375
|
}
|
|
1538
376
|
function parseOptionalInteger(value) {
|
|
1539
|
-
|
|
1540
|
-
|
|
377
|
+
const raw = stringOption(value);
|
|
378
|
+
return raw === undefined ? undefined : Number.parseInt(raw, 10);
|
|
1541
379
|
}
|
|
1542
380
|
function stripFlag(argv, flag) {
|
|
1543
|
-
|
|
1544
|
-
|
|
1545
|
-
|
|
1546
|
-
|
|
1547
|
-
|
|
1548
|
-
|
|
1549
|
-
|
|
381
|
+
let enabled = false;
|
|
382
|
+
const filtered = argv.filter((token) => {
|
|
383
|
+
if (token !== flag)
|
|
384
|
+
return true;
|
|
385
|
+
enabled = true;
|
|
386
|
+
return false;
|
|
387
|
+
});
|
|
388
|
+
return { argv: filtered, enabled };
|
|
1550
389
|
}
|
|
1551
390
|
async function readStdinJson() {
|
|
1552
|
-
|
|
1553
|
-
|
|
1554
|
-
|
|
1555
|
-
|
|
1556
|
-
|
|
1557
|
-
|
|
1558
|
-
|
|
1559
|
-
|
|
391
|
+
let raw = '';
|
|
392
|
+
for await (const chunk of process.stdin)
|
|
393
|
+
raw += String(chunk);
|
|
394
|
+
if (raw.trim().length === 0)
|
|
395
|
+
return {};
|
|
396
|
+
const parsed = JSON.parse(raw);
|
|
397
|
+
if (typeof parsed !== 'object' || parsed === null || Array.isArray(parsed)) {
|
|
398
|
+
throw new Error('--stdin must contain a JSON object');
|
|
399
|
+
}
|
|
400
|
+
return parsed;
|
|
1560
401
|
}
|
|
1561
402
|
function parseCommaList(value) {
|
|
1562
|
-
|
|
403
|
+
return value
|
|
404
|
+
.split(',')
|
|
405
|
+
.map((part) => part.trim())
|
|
406
|
+
.filter((part) => part.length > 0);
|
|
1563
407
|
}
|
|
1564
408
|
function parseTransport(value) {
|
|
1565
|
-
|
|
1566
|
-
|
|
409
|
+
if (value === 'auto' || value === 'streamable' || value === 'sse')
|
|
410
|
+
return value;
|
|
411
|
+
throw new Error('--transport must be one of: auto, streamable, sse');
|
|
1567
412
|
}
|
|
1568
413
|
function parseHeader(header) {
|
|
1569
|
-
|
|
1570
|
-
|
|
1571
|
-
|
|
414
|
+
const colon = header.indexOf(':');
|
|
415
|
+
if (colon === -1)
|
|
416
|
+
throw new Error(`invalid auth header format: ${header}`);
|
|
417
|
+
return [header.slice(0, colon).trim(), header.slice(colon + 1).trim()];
|
|
1572
418
|
}
|
|
1573
419
|
async function resolveAuthHeaders(headers) {
|
|
1574
|
-
|
|
420
|
+
return Promise.all(headers.map(async ([key, value]) => [key, await resolveSecret(value)]));
|
|
1575
421
|
}
|
|
1576
422
|
function defaultCacheDir() {
|
|
1577
|
-
|
|
423
|
+
return process.env.SKILL_CREATOR_CACHE_DIR ?? join(homedir(), '.cache', 'skill-creator');
|
|
1578
424
|
}
|
|
1579
425
|
function printHelp() {
|
|
1580
|
-
|
|
426
|
+
writeStdout(`npx @asnd/skill-creator [global options] <subcommand> [command options]
|
|
427
|
+
npx @asnd/skill-creator generate --template openapi --name NAME --spec URL|FILE --agent AGENT --scope project|global
|
|
428
|
+
npx @asnd/skill-creator command install --agent AGENT --scope project|global
|
|
1581
429
|
|
|
1582
430
|
Source (mutually exclusive, one required):
|
|
1583
431
|
--spec URL|FILE OpenAPI spec (JSON or YAML, local or remote)
|
|
@@ -1608,24 +456,22 @@ Options:
|
|
|
1608
456
|
--version Show version
|
|
1609
457
|
`);
|
|
1610
458
|
}
|
|
1611
|
-
function
|
|
1612
|
-
|
|
459
|
+
function writeStdout(text) {
|
|
460
|
+
console.log(text.replace(/\n$/, ''));
|
|
1613
461
|
}
|
|
1614
|
-
function isCliEntrypoint(metaUrl, argv1 = process.argv[1]) {
|
|
1615
|
-
|
|
1616
|
-
|
|
1617
|
-
|
|
1618
|
-
|
|
1619
|
-
|
|
1620
|
-
|
|
1621
|
-
|
|
462
|
+
export function isCliEntrypoint(metaUrl, argv1 = process.argv[1]) {
|
|
463
|
+
if (!argv1)
|
|
464
|
+
return false;
|
|
465
|
+
const modulePath = fileURLToPath(metaUrl);
|
|
466
|
+
try {
|
|
467
|
+
return realpathSync(argv1) === realpathSync(modulePath);
|
|
468
|
+
}
|
|
469
|
+
catch {
|
|
470
|
+
return resolve(argv1) === modulePath;
|
|
471
|
+
}
|
|
1622
472
|
}
|
|
1623
473
|
if (isCliEntrypoint(import.meta.url)) {
|
|
1624
|
-
|
|
1625
|
-
|
|
474
|
+
const code = await run();
|
|
475
|
+
process.exitCode = code;
|
|
1626
476
|
}
|
|
1627
|
-
export {
|
|
1628
|
-
isCliEntrypoint,
|
|
1629
|
-
run
|
|
1630
|
-
};
|
|
1631
477
|
//# sourceMappingURL=main.js.map
|