@leeguoo/yapi-mcp 0.3.20 → 0.3.22
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 +35 -14
- package/dist/config.d.ts +2 -0
- package/dist/config.js +22 -0
- package/dist/config.js.map +1 -1
- package/dist/index.js +1 -0
- package/dist/index.js.map +1 -1
- package/dist/server.d.ts +1 -0
- package/dist/server.js +5 -1
- package/dist/server.js.map +1 -1
- package/dist/services/yapi/api.d.ts +14 -0
- package/dist/services/yapi/api.js +154 -57
- package/dist/services/yapi/api.js.map +1 -1
- package/dist/yapi-cli.js +631 -7
- package/dist/yapi-cli.js.map +1 -1
- package/package.json +2 -2
- package/skill-template/SKILL.md +48 -79
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;
|
|
@@ -551,6 +633,10 @@ function usage() {
|
|
|
551
633
|
return [
|
|
552
634
|
"Usage:",
|
|
553
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]",
|
|
554
640
|
" yapi docs-sync [options] [dir...]",
|
|
555
641
|
" yapi docs-sync bind <action> [options]",
|
|
556
642
|
" yapi login [options]",
|
|
@@ -561,7 +647,7 @@ function usage() {
|
|
|
561
647
|
" --config <path> config file path (default: ~/.yapi/config.toml)",
|
|
562
648
|
" --base-url <url> YApi base URL",
|
|
563
649
|
" --token <token> project token (supports projectId:token)",
|
|
564
|
-
" --project-id <id> select token for project",
|
|
650
|
+
" --project-id <id> select token for project (filters search results)",
|
|
565
651
|
" --auth-mode <mode> token or global",
|
|
566
652
|
" --email <email> login email for global mode",
|
|
567
653
|
" --password <pwd> login password for global mode",
|
|
@@ -575,6 +661,16 @@ function usage() {
|
|
|
575
661
|
" --data <payload> request body (JSON or text)",
|
|
576
662
|
" --data-file <file> request body file",
|
|
577
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",
|
|
578
674
|
" --no-pretty print raw response",
|
|
579
675
|
"Docs-sync options:",
|
|
580
676
|
" --dir <path> docs directory (repeatable; default: docs/release-notes)",
|
|
@@ -689,6 +785,105 @@ function searchUsage() {
|
|
|
689
785
|
" -h, --help show help",
|
|
690
786
|
].join("\n");
|
|
691
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
|
+
}
|
|
692
887
|
function escapeTomlValue(value) {
|
|
693
888
|
return String(value || "")
|
|
694
889
|
.replace(/\\/g, "\\\\")
|
|
@@ -726,8 +921,12 @@ function promptHidden(question) {
|
|
|
726
921
|
output: process.stdout,
|
|
727
922
|
terminal: true,
|
|
728
923
|
});
|
|
924
|
+
const output = rl.output || process.stdout;
|
|
729
925
|
const originalWrite = rl
|
|
730
926
|
._writeToOutput;
|
|
927
|
+
if (question) {
|
|
928
|
+
output.write(question);
|
|
929
|
+
}
|
|
731
930
|
rl.stdoutMuted = true;
|
|
732
931
|
rl._writeToOutput =
|
|
733
932
|
function writeToOutput(value) {
|
|
@@ -737,12 +936,15 @@ function promptHidden(question) {
|
|
|
737
936
|
originalWrite.call(this, value);
|
|
738
937
|
}
|
|
739
938
|
else {
|
|
740
|
-
|
|
939
|
+
output.write(value);
|
|
741
940
|
}
|
|
742
941
|
};
|
|
743
942
|
return new Promise((resolve) => {
|
|
744
|
-
rl.question(
|
|
943
|
+
rl.question("", (answer) => {
|
|
745
944
|
rl.stdoutMuted = false;
|
|
945
|
+
if (question) {
|
|
946
|
+
output.write("\n");
|
|
947
|
+
}
|
|
746
948
|
rl.close();
|
|
747
949
|
resolve(answer);
|
|
748
950
|
});
|
|
@@ -789,7 +991,7 @@ function readVersion() {
|
|
|
789
991
|
return "unknown";
|
|
790
992
|
}
|
|
791
993
|
}
|
|
792
|
-
async function runSimpleRequest(rawArgs, usageFn, endpoint, requireBaseUrl, buildQueryItems) {
|
|
994
|
+
async function runSimpleRequest(rawArgs, usageFn, endpoint, requireBaseUrl, buildQueryItems, transform) {
|
|
793
995
|
const options = parseArgs(rawArgs);
|
|
794
996
|
if (options.help) {
|
|
795
997
|
console.log(usageFn());
|
|
@@ -859,17 +1061,32 @@ async function runSimpleRequest(rawArgs, usageFn, endpoint, requireBaseUrl, buil
|
|
|
859
1061
|
}
|
|
860
1062
|
}
|
|
861
1063
|
let queryItems = [];
|
|
1064
|
+
let method = "GET";
|
|
1065
|
+
let data = undefined;
|
|
862
1066
|
if (buildQueryItems) {
|
|
863
1067
|
const result = buildQueryItems(options);
|
|
864
1068
|
if (!result.ok)
|
|
865
1069
|
return 2;
|
|
866
1070
|
queryItems = result.queryItems || [];
|
|
1071
|
+
if (result.method) {
|
|
1072
|
+
method = result.method;
|
|
1073
|
+
}
|
|
1074
|
+
if (result.data !== undefined) {
|
|
1075
|
+
data = result.data;
|
|
1076
|
+
}
|
|
867
1077
|
}
|
|
868
1078
|
const url = buildUrl(baseUrl, endpoint, queryItems, authMode === "token" ? token : "", options.tokenParam || "token");
|
|
869
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
|
+
}
|
|
870
1086
|
const response = await fetchWithTimeout(url, {
|
|
871
|
-
method
|
|
872
|
-
headers,
|
|
1087
|
+
method,
|
|
1088
|
+
headers: requestHeaders,
|
|
1089
|
+
body,
|
|
873
1090
|
}, options.timeout || 30000);
|
|
874
1091
|
const text = await response.text();
|
|
875
1092
|
return { response, text, json: parseJsonMaybe(text) };
|
|
@@ -899,7 +1116,8 @@ async function runSimpleRequest(rawArgs, usageFn, endpoint, requireBaseUrl, buil
|
|
|
899
1116
|
}
|
|
900
1117
|
try {
|
|
901
1118
|
const payload = result.json ?? JSON.parse(text);
|
|
902
|
-
|
|
1119
|
+
const nextPayload = transform ? transform(payload, options) : payload;
|
|
1120
|
+
console.log(JSON.stringify(nextPayload ?? payload, null, 2));
|
|
903
1121
|
}
|
|
904
1122
|
catch {
|
|
905
1123
|
console.log(text);
|
|
@@ -984,8 +1202,257 @@ async function runSearch(rawArgs) {
|
|
|
984
1202
|
return { ok: false };
|
|
985
1203
|
}
|
|
986
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 };
|
|
987
1229
|
});
|
|
988
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
|
+
}
|
|
989
1456
|
function findDocsSyncHome(startDir) {
|
|
990
1457
|
let current = path_1.default.resolve(startDir);
|
|
991
1458
|
while (true) {
|
|
@@ -1124,6 +1591,17 @@ function normalizeBindingDir(rootDir, bindingDir) {
|
|
|
1124
1591
|
return resolved;
|
|
1125
1592
|
return relative;
|
|
1126
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
|
+
}
|
|
1127
1605
|
function loadMapping(dirPath) {
|
|
1128
1606
|
const mappingPath = path_1.default.join(dirPath, ".yapi.json");
|
|
1129
1607
|
if (!fs_1.default.existsSync(mappingPath)) {
|
|
@@ -1217,6 +1695,111 @@ function normalizeProjectEnvs(raw) {
|
|
|
1217
1695
|
});
|
|
1218
1696
|
return result;
|
|
1219
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
|
+
}
|
|
1220
1803
|
async function fetchProjectInfo(projectId, baseUrl, request) {
|
|
1221
1804
|
if (!projectId)
|
|
1222
1805
|
return null;
|
|
@@ -1665,7 +2248,29 @@ async function runDocsSync(rawArgs) {
|
|
|
1665
2248
|
console.error("no docs-sync bindings found (run docs-sync bind add or use --dir)");
|
|
1666
2249
|
return 2;
|
|
1667
2250
|
}
|
|
2251
|
+
const usingDefaultDir = !useBindings && !options.dirs.length;
|
|
1668
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
|
+
}
|
|
1669
2274
|
let config = {};
|
|
1670
2275
|
let configPath = options.config || "";
|
|
1671
2276
|
if (options.config) {
|
|
@@ -1870,6 +2475,13 @@ async function runDocsSync(rawArgs) {
|
|
|
1870
2475
|
}
|
|
1871
2476
|
async function main() {
|
|
1872
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 });
|
|
1873
2485
|
if (rawArgs[0] === "install-skill") {
|
|
1874
2486
|
await (0, install_1.runInstallSkill)(rawArgs.slice(1));
|
|
1875
2487
|
return 0;
|
|
@@ -1883,6 +2495,18 @@ async function main() {
|
|
|
1883
2495
|
if (rawArgs[0] === "search") {
|
|
1884
2496
|
return await runSearch(rawArgs.slice(1));
|
|
1885
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
|
+
}
|
|
1886
2510
|
if (rawArgs[0] === "docs-sync") {
|
|
1887
2511
|
return await runDocsSync(rawArgs.slice(1));
|
|
1888
2512
|
}
|