api-spec-cli 0.2.3 → 0.2.4
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 +343 -282
- package/package.json +53 -41
- package/src/cli.js +183 -172
- package/src/commands/add.js +161 -78
- package/src/commands/auth.js +32 -0
- package/src/commands/call.js +220 -207
- package/src/commands/fetch.js +344 -308
- package/src/commands/grep.js +67 -64
- package/src/commands/list.js +78 -80
- package/src/commands/show.js +224 -215
- package/src/commands/specs.js +82 -69
- package/src/commands/types.js +167 -163
- package/src/commands/validate.js +295 -269
- package/src/glob.js +34 -29
- package/src/mcp-client.js +88 -63
- package/src/oauth/auth-flow.js +59 -0
- package/src/oauth/provider.js +192 -0
- package/src/oauth/tokens.js +53 -0
- package/src/registry.js +79 -53
- package/src/resolve.js +64 -65
package/src/commands/grep.js
CHANGED
|
@@ -1,64 +1,67 @@
|
|
|
1
|
-
import { out } from "../output.js";
|
|
2
|
-
import { parseArgs } from "../args.js";
|
|
3
|
-
import { getRegistry, getEntry, getCachedSpec, saveCachedSpec } from "../registry.js";
|
|
4
|
-
import { fetchSpec } from "./fetch.js";
|
|
5
|
-
import { matchGlob } from "../glob.js";
|
|
6
|
-
|
|
7
|
-
export async function grepCmd(args) {
|
|
8
|
-
const { flags, positional } = parseArgs(args);
|
|
9
|
-
const pattern = positional[0];
|
|
10
|
-
if (!pattern)
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
const
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
1
|
+
import { out } from "../output.js";
|
|
2
|
+
import { parseArgs } from "../args.js";
|
|
3
|
+
import { getRegistry, getEntry, getCachedSpec, saveCachedSpec, allEntries } from "../registry.js";
|
|
4
|
+
import { fetchSpec } from "./fetch.js";
|
|
5
|
+
import { matchGlob } from "../glob.js";
|
|
6
|
+
|
|
7
|
+
export async function grepCmd(args) {
|
|
8
|
+
const { flags, positional } = parseArgs(args);
|
|
9
|
+
const pattern = positional[0];
|
|
10
|
+
if (!pattern)
|
|
11
|
+
throw new Error(
|
|
12
|
+
"Usage: spec grep <pattern> [--spec <name>]\n" +
|
|
13
|
+
" Glob patterns: * matches anything, ? matches one char\n" +
|
|
14
|
+
" Plain text: substring match across name and description"
|
|
15
|
+
);
|
|
16
|
+
|
|
17
|
+
const entries = flags.spec
|
|
18
|
+
? [getEntry(flags.spec)]
|
|
19
|
+
: allEntries(getRegistry()).filter((e) => e.enabled);
|
|
20
|
+
|
|
21
|
+
if (entries.length === 0) throw new Error("No registered specs. Run 'spec add' first.");
|
|
22
|
+
|
|
23
|
+
const results = [];
|
|
24
|
+
|
|
25
|
+
for (const entry of entries) {
|
|
26
|
+
let spec = getCachedSpec(entry.name);
|
|
27
|
+
if (!spec) {
|
|
28
|
+
spec = await fetchSpec(entry);
|
|
29
|
+
saveCachedSpec(entry.name, spec);
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
const matches = [];
|
|
33
|
+
|
|
34
|
+
if (spec.type === "mcp") {
|
|
35
|
+
for (const tool of spec.tools) {
|
|
36
|
+
const nameMatch = matchGlob(pattern, tool.name);
|
|
37
|
+
const descMatch = tool.description && matchGlob(pattern, tool.description);
|
|
38
|
+
if (nameMatch || descMatch) {
|
|
39
|
+
matches.push({ id: tool.name, description: tool.description });
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
} else if (spec.type === "openapi") {
|
|
43
|
+
for (const op of spec.operations) {
|
|
44
|
+
if (
|
|
45
|
+
matchGlob(pattern, op.id) ||
|
|
46
|
+
matchGlob(pattern, op.path) ||
|
|
47
|
+
(op.summary && matchGlob(pattern, op.summary))
|
|
48
|
+
) {
|
|
49
|
+
matches.push({ id: op.id, method: op.method, path: op.path });
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
} else if (spec.type === "graphql") {
|
|
53
|
+
for (const op of spec.operations) {
|
|
54
|
+
if (matchGlob(pattern, op.name) || (op.description && matchGlob(pattern, op.description))) {
|
|
55
|
+
matches.push({ id: op.name, kind: op.kind });
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
if (matches.length > 0) {
|
|
61
|
+
results.push({ spec: entry.name, type: spec.type, matches });
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
const total = results.reduce((s, r) => s + r.matches.length, 0);
|
|
66
|
+
out({ pattern, total, results });
|
|
67
|
+
}
|
package/src/commands/list.js
CHANGED
|
@@ -1,80 +1,78 @@
|
|
|
1
|
-
import { out } from "../output.js";
|
|
2
|
-
import { parseArgs } from "../args.js";
|
|
3
|
-
import { resolveSpec } from "../resolve.js";
|
|
4
|
-
|
|
5
|
-
export async function listOperations(args) {
|
|
6
|
-
const opts = parseArgs(args);
|
|
7
|
-
const { flags } = opts;
|
|
8
|
-
|
|
9
|
-
const { spec } = await resolveSpec(flags);
|
|
10
|
-
|
|
11
|
-
const filter = flags.filter?.toLowerCase();
|
|
12
|
-
const compact = flags.compact !== "false";
|
|
13
|
-
const limit = parseInt(flags.limit) || 0;
|
|
14
|
-
const offset = parseInt(flags.offset) || 0;
|
|
15
|
-
const tag = flags.tag?.toLowerCase();
|
|
16
|
-
|
|
17
|
-
let operations;
|
|
18
|
-
|
|
19
|
-
if (spec.type === "openapi") {
|
|
20
|
-
let source = spec.operations;
|
|
21
|
-
if (tag) {
|
|
22
|
-
source = source.filter((op) => op.tags?.some((t) => t.toLowerCase().includes(tag)));
|
|
23
|
-
}
|
|
24
|
-
operations = source.map((op) =>
|
|
25
|
-
compact
|
|
26
|
-
? { id: op.id, method: op.method, path: op.path }
|
|
27
|
-
: {
|
|
28
|
-
id: op.id,
|
|
29
|
-
method: op.method,
|
|
30
|
-
path: op.path,
|
|
31
|
-
summary: op.summary,
|
|
32
|
-
tags: op.tags,
|
|
33
|
-
deprecated: op.deprecated,
|
|
34
|
-
}
|
|
35
|
-
);
|
|
36
|
-
} else if (spec.type === "mcp") {
|
|
37
|
-
operations = spec.tools.map((t) =>
|
|
38
|
-
compact
|
|
39
|
-
? { id: t.name, description: t.description }
|
|
40
|
-
: { id: t.name, description: t.description, inputSchema: t.inputSchema }
|
|
41
|
-
);
|
|
42
|
-
} else {
|
|
43
|
-
// graphql
|
|
44
|
-
operations = spec.operations.map((op) =>
|
|
45
|
-
compact
|
|
46
|
-
? { id: op.name, kind: op.kind }
|
|
47
|
-
: {
|
|
48
|
-
id: op.name,
|
|
49
|
-
kind: op.kind,
|
|
50
|
-
description: op.description,
|
|
51
|
-
args: op.args.map((a) => a.name),
|
|
52
|
-
returnType: op.returnType,
|
|
53
|
-
isDeprecated: op.isDeprecated,
|
|
54
|
-
}
|
|
55
|
-
);
|
|
56
|
-
|
|
57
|
-
if (tag) {
|
|
58
|
-
operations = operations.filter((op) => op.kind === tag);
|
|
59
|
-
}
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
if (filter) {
|
|
63
|
-
operations = operations.filter((op) =>
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
});
|
|
80
|
-
}
|
|
1
|
+
import { out } from "../output.js";
|
|
2
|
+
import { parseArgs } from "../args.js";
|
|
3
|
+
import { resolveSpec } from "../resolve.js";
|
|
4
|
+
|
|
5
|
+
export async function listOperations(args) {
|
|
6
|
+
const opts = parseArgs(args);
|
|
7
|
+
const { flags } = opts;
|
|
8
|
+
|
|
9
|
+
const { spec } = await resolveSpec(flags);
|
|
10
|
+
|
|
11
|
+
const filter = flags.filter?.toLowerCase();
|
|
12
|
+
const compact = flags.compact !== "false";
|
|
13
|
+
const limit = parseInt(flags.limit) || 0;
|
|
14
|
+
const offset = parseInt(flags.offset) || 0;
|
|
15
|
+
const tag = flags.tag?.toLowerCase();
|
|
16
|
+
|
|
17
|
+
let operations;
|
|
18
|
+
|
|
19
|
+
if (spec.type === "openapi") {
|
|
20
|
+
let source = spec.operations;
|
|
21
|
+
if (tag) {
|
|
22
|
+
source = source.filter((op) => op.tags?.some((t) => t.toLowerCase().includes(tag)));
|
|
23
|
+
}
|
|
24
|
+
operations = source.map((op) =>
|
|
25
|
+
compact
|
|
26
|
+
? { id: op.id, method: op.method, path: op.path }
|
|
27
|
+
: {
|
|
28
|
+
id: op.id,
|
|
29
|
+
method: op.method,
|
|
30
|
+
path: op.path,
|
|
31
|
+
summary: op.summary,
|
|
32
|
+
tags: op.tags,
|
|
33
|
+
deprecated: op.deprecated,
|
|
34
|
+
}
|
|
35
|
+
);
|
|
36
|
+
} else if (spec.type === "mcp") {
|
|
37
|
+
operations = spec.tools.map((t) =>
|
|
38
|
+
compact
|
|
39
|
+
? { id: t.name, description: t.description }
|
|
40
|
+
: { id: t.name, description: t.description, inputSchema: t.inputSchema }
|
|
41
|
+
);
|
|
42
|
+
} else {
|
|
43
|
+
// graphql
|
|
44
|
+
operations = spec.operations.map((op) =>
|
|
45
|
+
compact
|
|
46
|
+
? { id: op.name, kind: op.kind }
|
|
47
|
+
: {
|
|
48
|
+
id: op.name,
|
|
49
|
+
kind: op.kind,
|
|
50
|
+
description: op.description,
|
|
51
|
+
args: op.args.map((a) => a.name),
|
|
52
|
+
returnType: op.returnType,
|
|
53
|
+
isDeprecated: op.isDeprecated,
|
|
54
|
+
}
|
|
55
|
+
);
|
|
56
|
+
|
|
57
|
+
if (tag) {
|
|
58
|
+
operations = operations.filter((op) => op.kind === tag);
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
if (filter) {
|
|
63
|
+
operations = operations.filter((op) => JSON.stringify(op).toLowerCase().includes(filter));
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
const total = operations.length;
|
|
67
|
+
|
|
68
|
+
if (offset > 0) operations = operations.slice(offset);
|
|
69
|
+
if (limit > 0) operations = operations.slice(0, limit);
|
|
70
|
+
|
|
71
|
+
out({
|
|
72
|
+
type: spec.type,
|
|
73
|
+
total,
|
|
74
|
+
showing: operations.length,
|
|
75
|
+
offset: offset || 0,
|
|
76
|
+
operations,
|
|
77
|
+
});
|
|
78
|
+
}
|