@leeguoo/yapi-mcp 0.3.16 → 0.3.21
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 +22 -4
- package/dist/docs/markdown.d.ts +0 -1
- package/dist/docs/markdown.js +3 -8
- package/dist/docs/markdown.js.map +1 -1
- package/dist/yapi-cli.js +632 -18
- package/dist/yapi-cli.js.map +1 -1
- package/package.json +3 -2
package/dist/yapi-cli.js
CHANGED
|
@@ -164,6 +164,10 @@ function parseArgs(argv) {
|
|
|
164
164
|
options.version = true;
|
|
165
165
|
continue;
|
|
166
166
|
}
|
|
167
|
+
if (arg === "--no-update" || arg === "--no-update-check") {
|
|
168
|
+
options.noUpdate = true;
|
|
169
|
+
continue;
|
|
170
|
+
}
|
|
167
171
|
if (arg === "--config") {
|
|
168
172
|
options.config = argv[++i];
|
|
169
173
|
continue;
|
|
@@ -308,6 +312,84 @@ function parseArgs(argv) {
|
|
|
308
312
|
options.timeout = Number(arg.slice(10));
|
|
309
313
|
continue;
|
|
310
314
|
}
|
|
315
|
+
if (arg === "--name") {
|
|
316
|
+
options.name = argv[++i];
|
|
317
|
+
continue;
|
|
318
|
+
}
|
|
319
|
+
if (arg.startsWith("--name=")) {
|
|
320
|
+
options.name = arg.slice(7);
|
|
321
|
+
continue;
|
|
322
|
+
}
|
|
323
|
+
if (arg === "--desc") {
|
|
324
|
+
options.desc = argv[++i];
|
|
325
|
+
continue;
|
|
326
|
+
}
|
|
327
|
+
if (arg.startsWith("--desc=")) {
|
|
328
|
+
options.desc = arg.slice(7);
|
|
329
|
+
continue;
|
|
330
|
+
}
|
|
331
|
+
if (arg === "--cat-id" || arg === "--catid") {
|
|
332
|
+
options.catId = argv[++i];
|
|
333
|
+
continue;
|
|
334
|
+
}
|
|
335
|
+
if (arg.startsWith("--cat-id=")) {
|
|
336
|
+
options.catId = arg.slice(9);
|
|
337
|
+
continue;
|
|
338
|
+
}
|
|
339
|
+
if (arg.startsWith("--catid=")) {
|
|
340
|
+
options.catId = arg.slice(8);
|
|
341
|
+
continue;
|
|
342
|
+
}
|
|
343
|
+
if (arg === "--id") {
|
|
344
|
+
options.id = argv[++i];
|
|
345
|
+
continue;
|
|
346
|
+
}
|
|
347
|
+
if (arg.startsWith("--id=")) {
|
|
348
|
+
options.id = arg.slice(5);
|
|
349
|
+
continue;
|
|
350
|
+
}
|
|
351
|
+
if (arg === "--group-id") {
|
|
352
|
+
options.groupId = argv[++i];
|
|
353
|
+
continue;
|
|
354
|
+
}
|
|
355
|
+
if (arg.startsWith("--group-id=")) {
|
|
356
|
+
options.groupId = arg.slice(11);
|
|
357
|
+
continue;
|
|
358
|
+
}
|
|
359
|
+
if (arg === "--type") {
|
|
360
|
+
options.type = argv[++i];
|
|
361
|
+
continue;
|
|
362
|
+
}
|
|
363
|
+
if (arg.startsWith("--type=")) {
|
|
364
|
+
options.type = arg.slice(7);
|
|
365
|
+
continue;
|
|
366
|
+
}
|
|
367
|
+
if (arg === "--type-id") {
|
|
368
|
+
options.typeId = argv[++i];
|
|
369
|
+
continue;
|
|
370
|
+
}
|
|
371
|
+
if (arg.startsWith("--type-id=")) {
|
|
372
|
+
options.typeId = arg.slice(10);
|
|
373
|
+
continue;
|
|
374
|
+
}
|
|
375
|
+
if (arg === "--page") {
|
|
376
|
+
options.page = Number(argv[++i]);
|
|
377
|
+
continue;
|
|
378
|
+
}
|
|
379
|
+
if (arg.startsWith("--page=")) {
|
|
380
|
+
options.page = Number(arg.slice(7));
|
|
381
|
+
continue;
|
|
382
|
+
}
|
|
383
|
+
if (arg === "--limit") {
|
|
384
|
+
const raw = String(argv[++i] ?? "").trim();
|
|
385
|
+
options.limit = raw.toLowerCase() === "all" ? "all" : Number(raw);
|
|
386
|
+
continue;
|
|
387
|
+
}
|
|
388
|
+
if (arg.startsWith("--limit=")) {
|
|
389
|
+
const raw = String(arg.slice(8)).trim();
|
|
390
|
+
options.limit = raw.toLowerCase() === "all" ? "all" : Number(raw);
|
|
391
|
+
continue;
|
|
392
|
+
}
|
|
311
393
|
if (arg === "--no-pretty") {
|
|
312
394
|
options.noPretty = true;
|
|
313
395
|
continue;
|
|
@@ -462,10 +544,6 @@ function parseDocsSyncArgs(argv) {
|
|
|
462
544
|
options.mermaidLook = "handDrawn";
|
|
463
545
|
continue;
|
|
464
546
|
}
|
|
465
|
-
if (arg === "--d2-sketch") {
|
|
466
|
-
options.d2Sketch = true;
|
|
467
|
-
continue;
|
|
468
|
-
}
|
|
469
547
|
if (arg === "--force") {
|
|
470
548
|
options.force = true;
|
|
471
549
|
continue;
|
|
@@ -555,6 +633,10 @@ function usage() {
|
|
|
555
633
|
return [
|
|
556
634
|
"Usage:",
|
|
557
635
|
" yapi --path /api/interface/get --query id=123",
|
|
636
|
+
" yapi group [options]",
|
|
637
|
+
" yapi project [options]",
|
|
638
|
+
" yapi interface [options]",
|
|
639
|
+
" yapi log [options]",
|
|
558
640
|
" yapi docs-sync [options] [dir...]",
|
|
559
641
|
" yapi docs-sync bind <action> [options]",
|
|
560
642
|
" yapi login [options]",
|
|
@@ -565,7 +647,7 @@ function usage() {
|
|
|
565
647
|
" --config <path> config file path (default: ~/.yapi/config.toml)",
|
|
566
648
|
" --base-url <url> YApi base URL",
|
|
567
649
|
" --token <token> project token (supports projectId:token)",
|
|
568
|
-
" --project-id <id> select token for project",
|
|
650
|
+
" --project-id <id> select token for project (filters search results)",
|
|
569
651
|
" --auth-mode <mode> token or global",
|
|
570
652
|
" --email <email> login email for global mode",
|
|
571
653
|
" --password <pwd> login password for global mode",
|
|
@@ -579,6 +661,16 @@ function usage() {
|
|
|
579
661
|
" --data <payload> request body (JSON or text)",
|
|
580
662
|
" --data-file <file> request body file",
|
|
581
663
|
" --timeout <ms> request timeout in ms",
|
|
664
|
+
" --id <id> resource id (for group/project/interface)",
|
|
665
|
+
" --name <name> category name (for interface cat)",
|
|
666
|
+
" --desc <desc> category description (for interface cat)",
|
|
667
|
+
" --cat-id <id> category id (for interface cat)",
|
|
668
|
+
" --group-id <id> group id (for project list)",
|
|
669
|
+
" --type <type> log type (for log list)",
|
|
670
|
+
" --type-id <id> log type id (for log list)",
|
|
671
|
+
" --page <n> page number (for list)",
|
|
672
|
+
" --limit <n|all> page size (for list)",
|
|
673
|
+
" --no-update disable update check",
|
|
582
674
|
" --no-pretty print raw response",
|
|
583
675
|
"Docs-sync options:",
|
|
584
676
|
" --dir <path> docs directory (repeatable; default: docs/release-notes)",
|
|
@@ -588,7 +680,6 @@ function usage() {
|
|
|
588
680
|
" --mermaid-hand-drawn force mermaid hand-drawn look (default)",
|
|
589
681
|
" --mermaid-classic render mermaid with classic look",
|
|
590
682
|
" --mermaid-hand-drawn-seed <n> hand-drawn seed (implies hand-drawn look)",
|
|
591
|
-
" --d2-sketch render D2 diagrams in sketch style",
|
|
592
683
|
" --force sync all files even if unchanged",
|
|
593
684
|
"Docs-sync bind actions:",
|
|
594
685
|
" list, get, add, update, remove",
|
|
@@ -619,7 +710,6 @@ function docsSyncUsage() {
|
|
|
619
710
|
" --mermaid-hand-drawn force mermaid hand-drawn look (default)",
|
|
620
711
|
" --mermaid-classic render mermaid with classic look",
|
|
621
712
|
" --mermaid-hand-drawn-seed <n> hand-drawn seed (implies hand-drawn look)",
|
|
622
|
-
" --d2-sketch render D2 diagrams in sketch style",
|
|
623
713
|
" --force sync all files even if unchanged",
|
|
624
714
|
" -h, --help show help",
|
|
625
715
|
].join("\n");
|
|
@@ -695,6 +785,105 @@ function searchUsage() {
|
|
|
695
785
|
" -h, --help show help",
|
|
696
786
|
].join("\n");
|
|
697
787
|
}
|
|
788
|
+
function groupUsage() {
|
|
789
|
+
return [
|
|
790
|
+
"Usage:",
|
|
791
|
+
" yapi group list",
|
|
792
|
+
" yapi group get --id <group_id>",
|
|
793
|
+
"Options:",
|
|
794
|
+
" --config <path> config file path (default: ~/.yapi/config.toml)",
|
|
795
|
+
" --base-url <url> YApi base URL",
|
|
796
|
+
" --token <token> project token (supports projectId:token)",
|
|
797
|
+
" --project-id <id> select token for project",
|
|
798
|
+
" --auth-mode <mode> token or global",
|
|
799
|
+
" --email <email> login email for global mode",
|
|
800
|
+
" --password <pwd> login password for global mode",
|
|
801
|
+
" --cookie <cookie> cookie for global mode",
|
|
802
|
+
" --token-param <name> token query param name (default: token)",
|
|
803
|
+
" --timeout <ms> request timeout in ms",
|
|
804
|
+
" --id <id> group id (for get)",
|
|
805
|
+
" --no-pretty print raw response",
|
|
806
|
+
" -h, --help show help",
|
|
807
|
+
].join("\n");
|
|
808
|
+
}
|
|
809
|
+
function projectUsage() {
|
|
810
|
+
return [
|
|
811
|
+
"Usage:",
|
|
812
|
+
" yapi project list --group-id <group_id> [--page <n>] [--limit <n>]",
|
|
813
|
+
" yapi project get --id <project_id>",
|
|
814
|
+
" yapi project token --project-id <project_id>",
|
|
815
|
+
"Options:",
|
|
816
|
+
" --config <path> config file path (default: ~/.yapi/config.toml)",
|
|
817
|
+
" --base-url <url> YApi base URL",
|
|
818
|
+
" --token <token> project token (supports projectId:token)",
|
|
819
|
+
" --project-id <id> select token for project",
|
|
820
|
+
" --auth-mode <mode> token or global",
|
|
821
|
+
" --email <email> login email for global mode",
|
|
822
|
+
" --password <pwd> login password for global mode",
|
|
823
|
+
" --cookie <cookie> cookie for global mode",
|
|
824
|
+
" --token-param <name> token query param name (default: token)",
|
|
825
|
+
" --timeout <ms> request timeout in ms",
|
|
826
|
+
" --group-id <id> group id (for list)",
|
|
827
|
+
" --id <id> project id (for get)",
|
|
828
|
+
" --page <n> page number (default: 1)",
|
|
829
|
+
" --limit <n> page size (default: 10)",
|
|
830
|
+
" --no-pretty print raw response",
|
|
831
|
+
" -h, --help show help",
|
|
832
|
+
].join("\n");
|
|
833
|
+
}
|
|
834
|
+
function interfaceUsage() {
|
|
835
|
+
return [
|
|
836
|
+
"Usage:",
|
|
837
|
+
" yapi interface list --project-id <project_id> [--page <n>] [--limit <n>]",
|
|
838
|
+
" yapi interface list-menu --project-id <project_id>",
|
|
839
|
+
" yapi interface get --id <api_id>",
|
|
840
|
+
" yapi interface cat add --project-id <project_id> --name <name> [--desc <desc>]",
|
|
841
|
+
" yapi interface cat update --cat-id <cat_id> --name <name> [--desc <desc>]",
|
|
842
|
+
" yapi interface cat delete --cat-id <cat_id>",
|
|
843
|
+
"Options:",
|
|
844
|
+
" --config <path> config file path (default: ~/.yapi/config.toml)",
|
|
845
|
+
" --base-url <url> YApi base URL",
|
|
846
|
+
" --token <token> project token (supports projectId:token)",
|
|
847
|
+
" --project-id <id> select token for project",
|
|
848
|
+
" --auth-mode <mode> token or global",
|
|
849
|
+
" --email <email> login email for global mode",
|
|
850
|
+
" --password <pwd> login password for global mode",
|
|
851
|
+
" --cookie <cookie> cookie for global mode",
|
|
852
|
+
" --token-param <name> token query param name (default: token)",
|
|
853
|
+
" --timeout <ms> request timeout in ms",
|
|
854
|
+
" --id <id> interface id (for get)",
|
|
855
|
+
" --cat-id <id> category id (for cat update/delete)",
|
|
856
|
+
" --name <name> category name (for cat add/update)",
|
|
857
|
+
" --desc <desc> category description (for cat add/update)",
|
|
858
|
+
" --page <n> page number (default: 1)",
|
|
859
|
+
" --limit <n|all> page size (default: 20)",
|
|
860
|
+
" --no-pretty print raw response",
|
|
861
|
+
" -h, --help show help",
|
|
862
|
+
].join("\n");
|
|
863
|
+
}
|
|
864
|
+
function logUsage() {
|
|
865
|
+
return [
|
|
866
|
+
"Usage:",
|
|
867
|
+
" yapi log list --type <type> --type-id <id> [--page <n>] [--limit <n>]",
|
|
868
|
+
"Options:",
|
|
869
|
+
" --config <path> config file path (default: ~/.yapi/config.toml)",
|
|
870
|
+
" --base-url <url> YApi base URL",
|
|
871
|
+
" --token <token> project token (supports projectId:token)",
|
|
872
|
+
" --project-id <id> select token for project",
|
|
873
|
+
" --auth-mode <mode> token or global",
|
|
874
|
+
" --email <email> login email for global mode",
|
|
875
|
+
" --password <pwd> login password for global mode",
|
|
876
|
+
" --cookie <cookie> cookie for global mode",
|
|
877
|
+
" --token-param <name> token query param name (default: token)",
|
|
878
|
+
" --timeout <ms> request timeout in ms",
|
|
879
|
+
" --type <type> log type (e.g., group/project)",
|
|
880
|
+
" --type-id <id> log type id",
|
|
881
|
+
" --page <n> page number (default: 1)",
|
|
882
|
+
" --limit <n> page size (default: 10)",
|
|
883
|
+
" --no-pretty print raw response",
|
|
884
|
+
" -h, --help show help",
|
|
885
|
+
].join("\n");
|
|
886
|
+
}
|
|
698
887
|
function escapeTomlValue(value) {
|
|
699
888
|
return String(value || "")
|
|
700
889
|
.replace(/\\/g, "\\\\")
|
|
@@ -732,8 +921,12 @@ function promptHidden(question) {
|
|
|
732
921
|
output: process.stdout,
|
|
733
922
|
terminal: true,
|
|
734
923
|
});
|
|
924
|
+
const output = rl.output || process.stdout;
|
|
735
925
|
const originalWrite = rl
|
|
736
926
|
._writeToOutput;
|
|
927
|
+
if (question) {
|
|
928
|
+
output.write(question);
|
|
929
|
+
}
|
|
737
930
|
rl.stdoutMuted = true;
|
|
738
931
|
rl._writeToOutput =
|
|
739
932
|
function writeToOutput(value) {
|
|
@@ -743,12 +936,15 @@ function promptHidden(question) {
|
|
|
743
936
|
originalWrite.call(this, value);
|
|
744
937
|
}
|
|
745
938
|
else {
|
|
746
|
-
|
|
939
|
+
output.write(value);
|
|
747
940
|
}
|
|
748
941
|
};
|
|
749
942
|
return new Promise((resolve) => {
|
|
750
|
-
rl.question(
|
|
943
|
+
rl.question("", (answer) => {
|
|
751
944
|
rl.stdoutMuted = false;
|
|
945
|
+
if (question) {
|
|
946
|
+
output.write("\n");
|
|
947
|
+
}
|
|
752
948
|
rl.close();
|
|
753
949
|
resolve(answer);
|
|
754
950
|
});
|
|
@@ -795,7 +991,7 @@ function readVersion() {
|
|
|
795
991
|
return "unknown";
|
|
796
992
|
}
|
|
797
993
|
}
|
|
798
|
-
async function runSimpleRequest(rawArgs, usageFn, endpoint, requireBaseUrl, buildQueryItems) {
|
|
994
|
+
async function runSimpleRequest(rawArgs, usageFn, endpoint, requireBaseUrl, buildQueryItems, transform) {
|
|
799
995
|
const options = parseArgs(rawArgs);
|
|
800
996
|
if (options.help) {
|
|
801
997
|
console.log(usageFn());
|
|
@@ -865,17 +1061,32 @@ async function runSimpleRequest(rawArgs, usageFn, endpoint, requireBaseUrl, buil
|
|
|
865
1061
|
}
|
|
866
1062
|
}
|
|
867
1063
|
let queryItems = [];
|
|
1064
|
+
let method = "GET";
|
|
1065
|
+
let data = undefined;
|
|
868
1066
|
if (buildQueryItems) {
|
|
869
1067
|
const result = buildQueryItems(options);
|
|
870
1068
|
if (!result.ok)
|
|
871
1069
|
return 2;
|
|
872
1070
|
queryItems = result.queryItems || [];
|
|
1071
|
+
if (result.method) {
|
|
1072
|
+
method = result.method;
|
|
1073
|
+
}
|
|
1074
|
+
if (result.data !== undefined) {
|
|
1075
|
+
data = result.data;
|
|
1076
|
+
}
|
|
873
1077
|
}
|
|
874
1078
|
const url = buildUrl(baseUrl, endpoint, queryItems, authMode === "token" ? token : "", options.tokenParam || "token");
|
|
875
1079
|
const sendOnce = async () => {
|
|
1080
|
+
let body;
|
|
1081
|
+
const requestHeaders = { ...headers };
|
|
1082
|
+
if (data !== undefined) {
|
|
1083
|
+
body = JSON.stringify(data);
|
|
1084
|
+
requestHeaders["Content-Type"] = "application/json;charset=UTF-8";
|
|
1085
|
+
}
|
|
876
1086
|
const response = await fetchWithTimeout(url, {
|
|
877
|
-
method
|
|
878
|
-
headers,
|
|
1087
|
+
method,
|
|
1088
|
+
headers: requestHeaders,
|
|
1089
|
+
body,
|
|
879
1090
|
}, options.timeout || 30000);
|
|
880
1091
|
const text = await response.text();
|
|
881
1092
|
return { response, text, json: parseJsonMaybe(text) };
|
|
@@ -905,7 +1116,8 @@ async function runSimpleRequest(rawArgs, usageFn, endpoint, requireBaseUrl, buil
|
|
|
905
1116
|
}
|
|
906
1117
|
try {
|
|
907
1118
|
const payload = result.json ?? JSON.parse(text);
|
|
908
|
-
|
|
1119
|
+
const nextPayload = transform ? transform(payload, options) : payload;
|
|
1120
|
+
console.log(JSON.stringify(nextPayload ?? payload, null, 2));
|
|
909
1121
|
}
|
|
910
1122
|
catch {
|
|
911
1123
|
console.log(text);
|
|
@@ -990,8 +1202,257 @@ async function runSearch(rawArgs) {
|
|
|
990
1202
|
return { ok: false };
|
|
991
1203
|
}
|
|
992
1204
|
return { ok: true, queryItems: [["q", keyword]] };
|
|
1205
|
+
}, (payload, options) => {
|
|
1206
|
+
const filterProjectId = String(options.projectId || "").trim();
|
|
1207
|
+
if (!filterProjectId)
|
|
1208
|
+
return payload;
|
|
1209
|
+
if (!payload || typeof payload !== "object")
|
|
1210
|
+
return payload;
|
|
1211
|
+
const record = payload;
|
|
1212
|
+
const data = record.data;
|
|
1213
|
+
if (!data || typeof data !== "object")
|
|
1214
|
+
return payload;
|
|
1215
|
+
const nextData = { ...data };
|
|
1216
|
+
if (Array.isArray(data.interface)) {
|
|
1217
|
+
nextData.interface = data.interface.filter((item) => {
|
|
1218
|
+
const projectId = item?.projectId ?? item?.project_id ?? item?.projectID ?? "";
|
|
1219
|
+
return String(projectId) === filterProjectId;
|
|
1220
|
+
});
|
|
1221
|
+
}
|
|
1222
|
+
if (Array.isArray(data.project)) {
|
|
1223
|
+
nextData.project = data.project.filter((item) => {
|
|
1224
|
+
const projectId = item?._id ?? item?.id ?? item?.project_id ?? item?.projectId ?? "";
|
|
1225
|
+
return String(projectId) === filterProjectId;
|
|
1226
|
+
});
|
|
1227
|
+
}
|
|
1228
|
+
return { ...record, data: nextData };
|
|
993
1229
|
});
|
|
994
1230
|
}
|
|
1231
|
+
async function runGroup(rawArgs) {
|
|
1232
|
+
const action = (rawArgs[0] || "list").toLowerCase();
|
|
1233
|
+
const options = parseArgs(rawArgs.slice(1));
|
|
1234
|
+
if (options.help) {
|
|
1235
|
+
console.log(groupUsage());
|
|
1236
|
+
return 0;
|
|
1237
|
+
}
|
|
1238
|
+
if (options.version) {
|
|
1239
|
+
console.log(readVersion());
|
|
1240
|
+
return 0;
|
|
1241
|
+
}
|
|
1242
|
+
if (action === "list") {
|
|
1243
|
+
return await runSimpleRequest(rawArgs.slice(1), groupUsage, "/api/group/list", true);
|
|
1244
|
+
}
|
|
1245
|
+
if (action === "get") {
|
|
1246
|
+
return await runSimpleRequest(rawArgs.slice(1), groupUsage, "/api/group/get", true, (opts) => {
|
|
1247
|
+
const id = String(opts.id || "").trim();
|
|
1248
|
+
if (!id) {
|
|
1249
|
+
console.error("missing --id for group get");
|
|
1250
|
+
return { ok: false };
|
|
1251
|
+
}
|
|
1252
|
+
return { ok: true, queryItems: [["id", id]] };
|
|
1253
|
+
});
|
|
1254
|
+
}
|
|
1255
|
+
console.error(`unknown group action: ${action}`);
|
|
1256
|
+
console.error(groupUsage());
|
|
1257
|
+
return 2;
|
|
1258
|
+
}
|
|
1259
|
+
async function runProject(rawArgs) {
|
|
1260
|
+
const action = (rawArgs[0] || "list").toLowerCase();
|
|
1261
|
+
const options = parseArgs(rawArgs.slice(1));
|
|
1262
|
+
if (options.help) {
|
|
1263
|
+
console.log(projectUsage());
|
|
1264
|
+
return 0;
|
|
1265
|
+
}
|
|
1266
|
+
if (options.version) {
|
|
1267
|
+
console.log(readVersion());
|
|
1268
|
+
return 0;
|
|
1269
|
+
}
|
|
1270
|
+
if (action === "list") {
|
|
1271
|
+
return await runSimpleRequest(rawArgs.slice(1), projectUsage, "/api/project/list", true, (opts) => {
|
|
1272
|
+
const groupId = String(opts.groupId || "").trim();
|
|
1273
|
+
if (!groupId) {
|
|
1274
|
+
console.error("missing --group-id for project list");
|
|
1275
|
+
return { ok: false };
|
|
1276
|
+
}
|
|
1277
|
+
const page = Number.isFinite(opts.page ?? NaN) ? String(opts.page) : "1";
|
|
1278
|
+
const limit = resolveLimit(opts.limit, "10");
|
|
1279
|
+
return {
|
|
1280
|
+
ok: true,
|
|
1281
|
+
queryItems: [
|
|
1282
|
+
["group_id", groupId],
|
|
1283
|
+
["page", page],
|
|
1284
|
+
["limit", limit],
|
|
1285
|
+
],
|
|
1286
|
+
};
|
|
1287
|
+
});
|
|
1288
|
+
}
|
|
1289
|
+
if (action === "get") {
|
|
1290
|
+
return await runSimpleRequest(rawArgs.slice(1), projectUsage, "/api/project/get", true, (opts) => {
|
|
1291
|
+
const id = String(opts.id || "").trim();
|
|
1292
|
+
if (!id) {
|
|
1293
|
+
console.error("missing --id for project get");
|
|
1294
|
+
return { ok: false };
|
|
1295
|
+
}
|
|
1296
|
+
return { ok: true, queryItems: [["id", id]] };
|
|
1297
|
+
});
|
|
1298
|
+
}
|
|
1299
|
+
if (action === "token") {
|
|
1300
|
+
return await runSimpleRequest(rawArgs.slice(1), projectUsage, "/api/project/token", true, (opts) => {
|
|
1301
|
+
const projectId = String(opts.projectId || opts.id || "").trim();
|
|
1302
|
+
if (!projectId) {
|
|
1303
|
+
console.error("missing --project-id for project token");
|
|
1304
|
+
return { ok: false };
|
|
1305
|
+
}
|
|
1306
|
+
return { ok: true, queryItems: [["project_id", projectId]] };
|
|
1307
|
+
});
|
|
1308
|
+
}
|
|
1309
|
+
console.error(`unknown project action: ${action}`);
|
|
1310
|
+
console.error(projectUsage());
|
|
1311
|
+
return 2;
|
|
1312
|
+
}
|
|
1313
|
+
async function runInterface(rawArgs) {
|
|
1314
|
+
const action = (rawArgs[0] || "").toLowerCase();
|
|
1315
|
+
const options = parseArgs(rawArgs.slice(1));
|
|
1316
|
+
if (options.help) {
|
|
1317
|
+
console.log(interfaceUsage());
|
|
1318
|
+
return 0;
|
|
1319
|
+
}
|
|
1320
|
+
if (options.version) {
|
|
1321
|
+
console.log(readVersion());
|
|
1322
|
+
return 0;
|
|
1323
|
+
}
|
|
1324
|
+
if (action === "list") {
|
|
1325
|
+
return await runSimpleRequest(rawArgs.slice(1), interfaceUsage, "/api/interface/list", true, (opts) => {
|
|
1326
|
+
const projectId = String(opts.projectId || "").trim();
|
|
1327
|
+
if (!projectId) {
|
|
1328
|
+
console.error("missing --project-id for interface list");
|
|
1329
|
+
return { ok: false };
|
|
1330
|
+
}
|
|
1331
|
+
const page = Number.isFinite(opts.page ?? NaN) ? String(opts.page) : "1";
|
|
1332
|
+
const limit = resolveLimit(opts.limit, "20");
|
|
1333
|
+
return {
|
|
1334
|
+
ok: true,
|
|
1335
|
+
queryItems: [
|
|
1336
|
+
["project_id", projectId],
|
|
1337
|
+
["page", page],
|
|
1338
|
+
["limit", limit],
|
|
1339
|
+
],
|
|
1340
|
+
};
|
|
1341
|
+
});
|
|
1342
|
+
}
|
|
1343
|
+
if (action === "list-menu" || action === "menu" || action === "list_menu") {
|
|
1344
|
+
return await runSimpleRequest(rawArgs.slice(1), interfaceUsage, "/api/interface/list_menu", true, (opts) => {
|
|
1345
|
+
const projectId = String(opts.projectId || "").trim();
|
|
1346
|
+
if (!projectId) {
|
|
1347
|
+
console.error("missing --project-id for interface list-menu");
|
|
1348
|
+
return { ok: false };
|
|
1349
|
+
}
|
|
1350
|
+
return { ok: true, queryItems: [["project_id", projectId]] };
|
|
1351
|
+
});
|
|
1352
|
+
}
|
|
1353
|
+
if (action === "get") {
|
|
1354
|
+
return await runSimpleRequest(rawArgs.slice(1), interfaceUsage, "/api/interface/get", true, (opts) => {
|
|
1355
|
+
const id = String(opts.id || "").trim();
|
|
1356
|
+
if (!id) {
|
|
1357
|
+
console.error("missing --id for interface get");
|
|
1358
|
+
return { ok: false };
|
|
1359
|
+
}
|
|
1360
|
+
return { ok: true, queryItems: [["id", id]] };
|
|
1361
|
+
});
|
|
1362
|
+
}
|
|
1363
|
+
if (action === "cat" || action === "category") {
|
|
1364
|
+
const subAction = (rawArgs[1] || "").toLowerCase();
|
|
1365
|
+
const subArgs = rawArgs.slice(2);
|
|
1366
|
+
if (subAction === "add") {
|
|
1367
|
+
return await runSimpleRequest(subArgs, interfaceUsage, "/api/interface/add_cat", true, (opts) => {
|
|
1368
|
+
const projectId = String(opts.projectId || "").trim();
|
|
1369
|
+
const name = String(opts.name || "").trim();
|
|
1370
|
+
if (!projectId || !name) {
|
|
1371
|
+
console.error("missing --project-id/--name for interface cat add");
|
|
1372
|
+
return { ok: false };
|
|
1373
|
+
}
|
|
1374
|
+
const payload = {
|
|
1375
|
+
project_id: projectId,
|
|
1376
|
+
name,
|
|
1377
|
+
};
|
|
1378
|
+
if (opts.desc !== undefined) {
|
|
1379
|
+
payload.desc = opts.desc;
|
|
1380
|
+
}
|
|
1381
|
+
return { ok: true, method: "POST", data: payload };
|
|
1382
|
+
});
|
|
1383
|
+
}
|
|
1384
|
+
if (subAction === "update" || subAction === "up") {
|
|
1385
|
+
return await runSimpleRequest(subArgs, interfaceUsage, "/api/interface/up_cat", true, (opts) => {
|
|
1386
|
+
const catId = String(opts.catId || "").trim();
|
|
1387
|
+
const name = String(opts.name || "").trim();
|
|
1388
|
+
if (!catId || !name) {
|
|
1389
|
+
console.error("missing --cat-id/--name for interface cat update");
|
|
1390
|
+
return { ok: false };
|
|
1391
|
+
}
|
|
1392
|
+
const payload = {
|
|
1393
|
+
catid: catId,
|
|
1394
|
+
name,
|
|
1395
|
+
};
|
|
1396
|
+
if (opts.desc !== undefined) {
|
|
1397
|
+
payload.desc = opts.desc;
|
|
1398
|
+
}
|
|
1399
|
+
return { ok: true, method: "POST", data: payload };
|
|
1400
|
+
});
|
|
1401
|
+
}
|
|
1402
|
+
if (subAction === "delete" || subAction === "del" || subAction === "remove") {
|
|
1403
|
+
return await runSimpleRequest(subArgs, interfaceUsage, "/api/interface/del_cat", true, (opts) => {
|
|
1404
|
+
const catId = String(opts.catId || "").trim();
|
|
1405
|
+
if (!catId) {
|
|
1406
|
+
console.error("missing --cat-id for interface cat delete");
|
|
1407
|
+
return { ok: false };
|
|
1408
|
+
}
|
|
1409
|
+
return { ok: true, method: "POST", data: { catid: catId } };
|
|
1410
|
+
});
|
|
1411
|
+
}
|
|
1412
|
+
console.error(`unknown interface cat action: ${subAction || "(missing)"}`);
|
|
1413
|
+
console.error(interfaceUsage());
|
|
1414
|
+
return 2;
|
|
1415
|
+
}
|
|
1416
|
+
console.error(`unknown interface action: ${action || "(missing)"}`);
|
|
1417
|
+
console.error(interfaceUsage());
|
|
1418
|
+
return 2;
|
|
1419
|
+
}
|
|
1420
|
+
async function runLog(rawArgs) {
|
|
1421
|
+
const action = (rawArgs[0] || "list").toLowerCase();
|
|
1422
|
+
const options = parseArgs(rawArgs.slice(1));
|
|
1423
|
+
if (options.help) {
|
|
1424
|
+
console.log(logUsage());
|
|
1425
|
+
return 0;
|
|
1426
|
+
}
|
|
1427
|
+
if (options.version) {
|
|
1428
|
+
console.log(readVersion());
|
|
1429
|
+
return 0;
|
|
1430
|
+
}
|
|
1431
|
+
if (action === "list") {
|
|
1432
|
+
return await runSimpleRequest(rawArgs.slice(1), logUsage, "/api/log/list", true, (opts) => {
|
|
1433
|
+
const type = String(opts.type || "").trim();
|
|
1434
|
+
const typeId = String(opts.typeId || "").trim();
|
|
1435
|
+
if (!type || !typeId) {
|
|
1436
|
+
console.error("missing --type/--type-id for log list");
|
|
1437
|
+
return { ok: false };
|
|
1438
|
+
}
|
|
1439
|
+
const page = Number.isFinite(opts.page ?? NaN) ? String(opts.page) : "1";
|
|
1440
|
+
const limit = resolveLimit(opts.limit, "10");
|
|
1441
|
+
return {
|
|
1442
|
+
ok: true,
|
|
1443
|
+
queryItems: [
|
|
1444
|
+
["type", type],
|
|
1445
|
+
["typeid", typeId],
|
|
1446
|
+
["page", page],
|
|
1447
|
+
["limit", limit],
|
|
1448
|
+
],
|
|
1449
|
+
};
|
|
1450
|
+
});
|
|
1451
|
+
}
|
|
1452
|
+
console.error(`unknown log action: ${action}`);
|
|
1453
|
+
console.error(logUsage());
|
|
1454
|
+
return 2;
|
|
1455
|
+
}
|
|
995
1456
|
function findDocsSyncHome(startDir) {
|
|
996
1457
|
let current = path_1.default.resolve(startDir);
|
|
997
1458
|
while (true) {
|
|
@@ -1130,6 +1591,17 @@ function normalizeBindingDir(rootDir, bindingDir) {
|
|
|
1130
1591
|
return resolved;
|
|
1131
1592
|
return relative;
|
|
1132
1593
|
}
|
|
1594
|
+
function suggestDocsSyncDir(startDir) {
|
|
1595
|
+
const candidates = ["docs", "doc", "documentation", "release-notes"];
|
|
1596
|
+
for (const candidate of candidates) {
|
|
1597
|
+
const candidatePath = path_1.default.resolve(startDir, candidate);
|
|
1598
|
+
if (fs_1.default.existsSync(candidatePath) && fs_1.default.statSync(candidatePath).isDirectory()) {
|
|
1599
|
+
const relative = path_1.default.relative(startDir, candidatePath);
|
|
1600
|
+
return relative && relative !== "." ? relative : candidate;
|
|
1601
|
+
}
|
|
1602
|
+
}
|
|
1603
|
+
return null;
|
|
1604
|
+
}
|
|
1133
1605
|
function loadMapping(dirPath) {
|
|
1134
1606
|
const mappingPath = path_1.default.join(dirPath, ".yapi.json");
|
|
1135
1607
|
if (!fs_1.default.existsSync(mappingPath)) {
|
|
@@ -1153,9 +1625,6 @@ function buildDocsSyncHash(markdown, options) {
|
|
|
1153
1625
|
hash.update(`mermaid-seed:${options.mermaidHandDrawnSeed}\n`);
|
|
1154
1626
|
}
|
|
1155
1627
|
}
|
|
1156
|
-
if (options.d2Sketch) {
|
|
1157
|
-
hash.update("d2-sketch\n");
|
|
1158
|
-
}
|
|
1159
1628
|
hash.update(markdown);
|
|
1160
1629
|
return hash.digest("hex");
|
|
1161
1630
|
}
|
|
@@ -1226,6 +1695,111 @@ function normalizeProjectEnvs(raw) {
|
|
|
1226
1695
|
});
|
|
1227
1696
|
return result;
|
|
1228
1697
|
}
|
|
1698
|
+
function resolveLimit(value, fallback) {
|
|
1699
|
+
if (typeof value === "string") {
|
|
1700
|
+
const trimmed = value.trim();
|
|
1701
|
+
if (trimmed)
|
|
1702
|
+
return trimmed;
|
|
1703
|
+
}
|
|
1704
|
+
if (Number.isFinite(value ?? NaN)) {
|
|
1705
|
+
return String(value);
|
|
1706
|
+
}
|
|
1707
|
+
return fallback;
|
|
1708
|
+
}
|
|
1709
|
+
const UPDATE_CHECK_TTL_MS = 12 * 60 * 60 * 1000;
|
|
1710
|
+
function updateCachePath() {
|
|
1711
|
+
const yapiHome = process.env.YAPI_HOME || path_1.default.join(os_1.default.homedir(), ".yapi");
|
|
1712
|
+
return path_1.default.join(yapiHome, "update.json");
|
|
1713
|
+
}
|
|
1714
|
+
function readUpdateCache() {
|
|
1715
|
+
try {
|
|
1716
|
+
const cachePath = updateCachePath();
|
|
1717
|
+
if (!fs_1.default.existsSync(cachePath))
|
|
1718
|
+
return {};
|
|
1719
|
+
const raw = fs_1.default.readFileSync(cachePath, "utf8");
|
|
1720
|
+
const parsed = JSON.parse(raw);
|
|
1721
|
+
return parsed && typeof parsed === "object" ? parsed : {};
|
|
1722
|
+
}
|
|
1723
|
+
catch {
|
|
1724
|
+
return {};
|
|
1725
|
+
}
|
|
1726
|
+
}
|
|
1727
|
+
function writeUpdateCache(cache) {
|
|
1728
|
+
try {
|
|
1729
|
+
const cachePath = updateCachePath();
|
|
1730
|
+
fs_1.default.mkdirSync(path_1.default.dirname(cachePath), { recursive: true });
|
|
1731
|
+
fs_1.default.writeFileSync(cachePath, `${JSON.stringify(cache, null, 2)}\n`, "utf8");
|
|
1732
|
+
}
|
|
1733
|
+
catch {
|
|
1734
|
+
}
|
|
1735
|
+
}
|
|
1736
|
+
function compareSemver(a, b) {
|
|
1737
|
+
const toParts = (value) => String(value || "")
|
|
1738
|
+
.trim()
|
|
1739
|
+
.split(".")
|
|
1740
|
+
.map((part) => Number(part.replace(/\D/g, "")) || 0);
|
|
1741
|
+
const aParts = toParts(a);
|
|
1742
|
+
const bParts = toParts(b);
|
|
1743
|
+
const len = Math.max(aParts.length, bParts.length);
|
|
1744
|
+
for (let i = 0; i < len; i += 1) {
|
|
1745
|
+
const diff = (aParts[i] || 0) - (bParts[i] || 0);
|
|
1746
|
+
if (diff !== 0)
|
|
1747
|
+
return diff;
|
|
1748
|
+
}
|
|
1749
|
+
return 0;
|
|
1750
|
+
}
|
|
1751
|
+
function isNewerVersion(latest, current) {
|
|
1752
|
+
return compareSemver(latest, current) > 0;
|
|
1753
|
+
}
|
|
1754
|
+
async function fetchLatestVersion(timeoutMs) {
|
|
1755
|
+
try {
|
|
1756
|
+
const encoded = encodeURIComponent("@leeguoo/yapi-mcp");
|
|
1757
|
+
const url = `https://registry.npmjs.org/${encoded}/latest`;
|
|
1758
|
+
const response = await fetchWithTimeout(url, { method: "GET" }, timeoutMs);
|
|
1759
|
+
if (!response.ok)
|
|
1760
|
+
return null;
|
|
1761
|
+
const payload = (await response.json());
|
|
1762
|
+
return typeof payload?.version === "string" ? payload.version : null;
|
|
1763
|
+
}
|
|
1764
|
+
catch {
|
|
1765
|
+
return null;
|
|
1766
|
+
}
|
|
1767
|
+
}
|
|
1768
|
+
async function checkForUpdates(options) {
|
|
1769
|
+
if (options.skip || options.noUpdate)
|
|
1770
|
+
return;
|
|
1771
|
+
if (process.env.YAPI_NO_UPDATE_CHECK === "1")
|
|
1772
|
+
return;
|
|
1773
|
+
if (process.env.CI === "1")
|
|
1774
|
+
return;
|
|
1775
|
+
const currentVersion = readVersion();
|
|
1776
|
+
if (!currentVersion || currentVersion === "unknown")
|
|
1777
|
+
return;
|
|
1778
|
+
const cache = readUpdateCache();
|
|
1779
|
+
const now = Date.now();
|
|
1780
|
+
let latest = cache.latest;
|
|
1781
|
+
const shouldCheck = !cache.lastChecked || now - cache.lastChecked > UPDATE_CHECK_TTL_MS || !latest;
|
|
1782
|
+
if (shouldCheck) {
|
|
1783
|
+
const fetched = await fetchLatestVersion(2000);
|
|
1784
|
+
if (fetched) {
|
|
1785
|
+
latest = fetched;
|
|
1786
|
+
cache.latest = fetched;
|
|
1787
|
+
}
|
|
1788
|
+
cache.lastChecked = now;
|
|
1789
|
+
}
|
|
1790
|
+
if (!latest || !isNewerVersion(latest, currentVersion)) {
|
|
1791
|
+
writeUpdateCache(cache);
|
|
1792
|
+
return;
|
|
1793
|
+
}
|
|
1794
|
+
const shouldNotify = cache.lastNotified !== latest || !cache.lastNotifiedAt || now - cache.lastNotifiedAt > UPDATE_CHECK_TTL_MS;
|
|
1795
|
+
if (shouldNotify) {
|
|
1796
|
+
console.warn(`update available: ${currentVersion} -> ${latest}. Run: npm install -g @leeguoo/yapi-mcp@latest`);
|
|
1797
|
+
console.warn("or: pnpm add -g @leeguoo/yapi-mcp@latest");
|
|
1798
|
+
cache.lastNotified = latest;
|
|
1799
|
+
cache.lastNotifiedAt = now;
|
|
1800
|
+
}
|
|
1801
|
+
writeUpdateCache(cache);
|
|
1802
|
+
}
|
|
1229
1803
|
async function fetchProjectInfo(projectId, baseUrl, request) {
|
|
1230
1804
|
if (!projectId)
|
|
1231
1805
|
return null;
|
|
@@ -1346,7 +1920,7 @@ async function syncDocsDir(dirPath, mapping, options, request) {
|
|
|
1346
1920
|
if (!mapping.template_id && envTemplateId)
|
|
1347
1921
|
mapping.template_id = Number(envTemplateId);
|
|
1348
1922
|
if (!mapping.project_id || !mapping.catid) {
|
|
1349
|
-
throw new Error("project_id/catid
|
|
1923
|
+
throw new Error("缺少 project_id/catid。请先绑定或配置:yapi docs-sync bind add --name <binding> --dir <path> --project-id <id> --catid <id>,或在目录下添加 .yapi.json,或设置环境变量 YAPI_PROJECT_ID/YAPI_CATID。");
|
|
1350
1924
|
}
|
|
1351
1925
|
const { byPath, byTitle, byId } = await listExistingInterfaces(Number(mapping.catid), request);
|
|
1352
1926
|
let updated = 0;
|
|
@@ -1402,7 +1976,6 @@ async function syncDocsDir(dirPath, mapping, options, request) {
|
|
|
1402
1976
|
logMermaid: true,
|
|
1403
1977
|
mermaidLook: options.mermaidLook,
|
|
1404
1978
|
mermaidHandDrawnSeed: options.mermaidHandDrawnSeed,
|
|
1405
|
-
d2Sketch: options.d2Sketch,
|
|
1406
1979
|
logger: (message) => console.log(`${logPrefix} ${message}`),
|
|
1407
1980
|
onMermaidError: () => {
|
|
1408
1981
|
mermaidFailed = true;
|
|
@@ -1675,7 +2248,29 @@ async function runDocsSync(rawArgs) {
|
|
|
1675
2248
|
console.error("no docs-sync bindings found (run docs-sync bind add or use --dir)");
|
|
1676
2249
|
return 2;
|
|
1677
2250
|
}
|
|
2251
|
+
const usingDefaultDir = !useBindings && !options.dirs.length;
|
|
1678
2252
|
const dirs = useBindings ? [] : options.dirs.length ? options.dirs : ["docs/release-notes"];
|
|
2253
|
+
if (!useBindings) {
|
|
2254
|
+
const missingDirs = dirs.filter((dir) => {
|
|
2255
|
+
const dirPath = path_1.default.resolve(dir);
|
|
2256
|
+
return !fs_1.default.existsSync(dirPath) || !fs_1.default.statSync(dirPath).isDirectory();
|
|
2257
|
+
});
|
|
2258
|
+
if (missingDirs.length) {
|
|
2259
|
+
const firstMissing = path_1.default.resolve(missingDirs[0]);
|
|
2260
|
+
console.error(`dir not found: ${firstMissing}`);
|
|
2261
|
+
if (usingDefaultDir) {
|
|
2262
|
+
const suggestion = suggestDocsSyncDir(process.cwd());
|
|
2263
|
+
if (suggestion) {
|
|
2264
|
+
console.error(`hint: pass --dir ${suggestion}`);
|
|
2265
|
+
}
|
|
2266
|
+
console.error("hint: or bind a directory with yapi docs-sync bind add");
|
|
2267
|
+
}
|
|
2268
|
+
else {
|
|
2269
|
+
console.error("hint: pass --dir <existing_dir> or bind a directory");
|
|
2270
|
+
}
|
|
2271
|
+
return 2;
|
|
2272
|
+
}
|
|
2273
|
+
}
|
|
1679
2274
|
let config = {};
|
|
1680
2275
|
let configPath = options.config || "";
|
|
1681
2276
|
if (options.config) {
|
|
@@ -1880,6 +2475,13 @@ async function runDocsSync(rawArgs) {
|
|
|
1880
2475
|
}
|
|
1881
2476
|
async function main() {
|
|
1882
2477
|
const rawArgs = process.argv.slice(2);
|
|
2478
|
+
const parsedForUpdate = parseArgs(rawArgs);
|
|
2479
|
+
const skipUpdateCheck = parsedForUpdate.version ||
|
|
2480
|
+
parsedForUpdate.help ||
|
|
2481
|
+
parsedForUpdate.noUpdate ||
|
|
2482
|
+
rawArgs.includes("-h") ||
|
|
2483
|
+
rawArgs.includes("--help");
|
|
2484
|
+
await checkForUpdates({ noUpdate: parsedForUpdate.noUpdate, skip: skipUpdateCheck });
|
|
1883
2485
|
if (rawArgs[0] === "install-skill") {
|
|
1884
2486
|
await (0, install_1.runInstallSkill)(rawArgs.slice(1));
|
|
1885
2487
|
return 0;
|
|
@@ -1893,6 +2495,18 @@ async function main() {
|
|
|
1893
2495
|
if (rawArgs[0] === "search") {
|
|
1894
2496
|
return await runSearch(rawArgs.slice(1));
|
|
1895
2497
|
}
|
|
2498
|
+
if (rawArgs[0] === "group") {
|
|
2499
|
+
return await runGroup(rawArgs.slice(1));
|
|
2500
|
+
}
|
|
2501
|
+
if (rawArgs[0] === "project") {
|
|
2502
|
+
return await runProject(rawArgs.slice(1));
|
|
2503
|
+
}
|
|
2504
|
+
if (rawArgs[0] === "interface") {
|
|
2505
|
+
return await runInterface(rawArgs.slice(1));
|
|
2506
|
+
}
|
|
2507
|
+
if (rawArgs[0] === "log") {
|
|
2508
|
+
return await runLog(rawArgs.slice(1));
|
|
2509
|
+
}
|
|
1896
2510
|
if (rawArgs[0] === "docs-sync") {
|
|
1897
2511
|
return await runDocsSync(rawArgs.slice(1));
|
|
1898
2512
|
}
|