@leeguoo/yapi-mcp 0.3.9 → 0.3.10
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 +4 -0
- package/dist/docs/markdown.d.ts +1 -0
- package/dist/docs/markdown.js +32 -6
- package/dist/docs/markdown.js.map +1 -1
- package/dist/skill/install.js +161 -140
- package/dist/skill/install.js.map +1 -1
- package/dist/yapi-cli.js +270 -31
- package/dist/yapi-cli.js.map +1 -1
- package/package.json +4 -1
package/dist/skill/install.js
CHANGED
|
@@ -26,10 +26,16 @@ const SKILL_MD = [
|
|
|
26
26
|
"3. Call YApi endpoints with `scripts/yapi_request.js` to fetch raw JSON.",
|
|
27
27
|
"4. Summarize method, path, headers, query/body schema, response schema, and examples.",
|
|
28
28
|
"",
|
|
29
|
+
"## CLI",
|
|
30
|
+
"- Install & run: `npx -y @leeguoo/yapi-mcp yapi -h` (or install globally and use `yapi`).",
|
|
31
|
+
"- Use the same config as the skill: `~/.yapi/config.toml`.",
|
|
32
|
+
"",
|
|
29
33
|
"## Docs sync",
|
|
30
34
|
"- Bind local docs to YApi category with `yapi docs-sync bind add --name <binding> --dir <path> --project-id <id> --catid <id>` (stored in `.yapi/docs-sync.json`).",
|
|
31
35
|
"- Sync with `yapi docs-sync --binding <binding>` or run all bindings with `yapi docs-sync`.",
|
|
32
36
|
"- Default syncs only changed files; use `--force` to sync everything.",
|
|
37
|
+
"- Mermaid rendering depends on `mmdc` (auto-installed if possible; failures do not block sync).",
|
|
38
|
+
"- For full Markdown render, install `pandoc` (manual install required).",
|
|
33
39
|
"- Extra mappings (generated after docs-sync run in binding mode):",
|
|
34
40
|
" - `.yapi/docs-sync.links.json`: local docs to YApi doc URLs.",
|
|
35
41
|
" - `.yapi/docs-sync.projects.json`: cached project metadata/envs.",
|
|
@@ -44,45 +50,45 @@ const SKILL_MD = [
|
|
|
44
50
|
"- The script reads `~/.yapi/config.toml` first, then `<skill-dir>/config.toml`; pass `--config` for a custom path.",
|
|
45
51
|
"- Use `--base-url`, `--token`, `--project-id` to override config.",
|
|
46
52
|
"- For `auth_mode=global`, the script logs in via `/api/user/login` and uses the returned cookie automatically.",
|
|
47
|
-
|
|
53
|
+
'- For POST: `--method POST --data \'{"key":"value"}\'`',
|
|
48
54
|
"- If your instance differs, pass a full URL with `--url`.",
|
|
49
55
|
"",
|
|
50
56
|
].join("\n");
|
|
51
57
|
const REQUEST_SCRIPT = [
|
|
52
58
|
"#!/usr/bin/env node",
|
|
53
|
-
"
|
|
59
|
+
'"use strict";',
|
|
54
60
|
"",
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
61
|
+
'const fs = require("fs");',
|
|
62
|
+
'const os = require("os");',
|
|
63
|
+
'const path = require("path");',
|
|
64
|
+
'const readline = require("readline");',
|
|
59
65
|
"",
|
|
60
66
|
"function parseKeyValue(raw) {",
|
|
61
|
-
|
|
62
|
-
|
|
67
|
+
' if (!raw || !raw.includes("=")) throw new Error("expected key=value");',
|
|
68
|
+
' const idx = raw.indexOf("=");',
|
|
63
69
|
" return [raw.slice(0, idx), raw.slice(idx + 1)];",
|
|
64
70
|
"}",
|
|
65
71
|
"",
|
|
66
72
|
"function parseHeader(raw) {",
|
|
67
|
-
|
|
68
|
-
|
|
73
|
+
' if (!raw || !raw.includes(":")) throw new Error("expected Header:Value");',
|
|
74
|
+
' const idx = raw.indexOf(":");',
|
|
69
75
|
" return [raw.slice(0, idx).trim(), raw.slice(idx + 1).trim()];",
|
|
70
76
|
"}",
|
|
71
77
|
"",
|
|
72
78
|
"function joinUrl(baseUrl, endpoint) {",
|
|
73
79
|
" if (!baseUrl) return endpoint;",
|
|
74
|
-
|
|
75
|
-
|
|
80
|
+
' if (baseUrl.endsWith("/") && endpoint.startsWith("/")) return baseUrl.slice(0, -1) + endpoint;',
|
|
81
|
+
' if (!baseUrl.endsWith("/") && !endpoint.startsWith("/")) return baseUrl + "/" + endpoint;',
|
|
76
82
|
" return baseUrl + endpoint;",
|
|
77
83
|
"}",
|
|
78
84
|
"",
|
|
79
85
|
"function globalConfigPath() {",
|
|
80
|
-
|
|
81
|
-
|
|
86
|
+
' const yapiHome = process.env.YAPI_HOME || path.join(os.homedir(), ".yapi");',
|
|
87
|
+
' return path.join(yapiHome, "config.toml");',
|
|
82
88
|
"}",
|
|
83
89
|
"",
|
|
84
90
|
"function localConfigPath() {",
|
|
85
|
-
|
|
91
|
+
' return path.resolve(__dirname, "..", "config.toml");',
|
|
86
92
|
"}",
|
|
87
93
|
"",
|
|
88
94
|
"function ensureDir(dirPath) {",
|
|
@@ -94,22 +100,22 @@ const REQUEST_SCRIPT = [
|
|
|
94
100
|
"}",
|
|
95
101
|
"",
|
|
96
102
|
"function escapeTomlValue(value) {",
|
|
97
|
-
|
|
103
|
+
' return String(value || "").replace(/\\\\/g, "\\\\\\\\").replace(/\\"/g, "\\\\\\"");',
|
|
98
104
|
"}",
|
|
99
105
|
"",
|
|
100
106
|
"function formatToml(config) {",
|
|
101
|
-
|
|
102
|
-
|
|
107
|
+
' const orderedKeys = ["base_url", "auth_mode", "email", "password", "token", "project_id"];',
|
|
108
|
+
' const lines = ["# YApi CLI config"];',
|
|
103
109
|
" for (const key of orderedKeys) {",
|
|
104
|
-
|
|
105
|
-
|
|
110
|
+
' const value = config[key] || "";',
|
|
111
|
+
' lines.push(`${key} = \\"${escapeTomlValue(value)}\\"`);',
|
|
106
112
|
" }",
|
|
107
|
-
|
|
113
|
+
' return lines.join("\\n") + "\\n";',
|
|
108
114
|
"}",
|
|
109
115
|
"",
|
|
110
116
|
"function writeConfig(filePath, config) {",
|
|
111
117
|
" ensureDir(path.dirname(filePath));",
|
|
112
|
-
|
|
118
|
+
' fs.writeFileSync(filePath, formatToml(config), "utf8");',
|
|
113
119
|
"}",
|
|
114
120
|
"",
|
|
115
121
|
"function promptText(question) {",
|
|
@@ -127,7 +133,7 @@ const REQUEST_SCRIPT = [
|
|
|
127
133
|
" const originalWrite = rl._writeToOutput;",
|
|
128
134
|
" rl._writeToOutput = function writeToOutput(stringToWrite) {",
|
|
129
135
|
" if (rl.stdoutMuted) return;",
|
|
130
|
-
|
|
136
|
+
' if (typeof originalWrite === "function") {',
|
|
131
137
|
" originalWrite.call(this, stringToWrite);",
|
|
132
138
|
" } else {",
|
|
133
139
|
" rl.output.write(stringToWrite);",
|
|
@@ -146,7 +152,7 @@ const REQUEST_SCRIPT = [
|
|
|
146
152
|
"async function promptRequired(question, hidden) {",
|
|
147
153
|
" while (true) {",
|
|
148
154
|
" const answer = hidden ? await promptHidden(question) : await promptText(question);",
|
|
149
|
-
|
|
155
|
+
' const trimmed = String(answer || "").trim();',
|
|
150
156
|
" if (trimmed) return trimmed;",
|
|
151
157
|
" }",
|
|
152
158
|
"}",
|
|
@@ -158,16 +164,16 @@ const REQUEST_SCRIPT = [
|
|
|
158
164
|
" if (!hasBaseUrl || !hasEmail || !hasPassword) {",
|
|
159
165
|
" if (!process.stdin.isTTY || !process.stdout.isTTY) return null;",
|
|
160
166
|
" }",
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
167
|
+
' const baseUrl = hasBaseUrl ? options.baseUrl : await promptRequired("YApi base URL: ", false);',
|
|
168
|
+
' const email = hasEmail ? options.email : await promptRequired("YApi email: ", false);',
|
|
169
|
+
' const password = hasPassword ? options.password : await promptRequired("YApi password: ", true);',
|
|
164
170
|
" const config = {",
|
|
165
171
|
" base_url: baseUrl,",
|
|
166
|
-
|
|
172
|
+
' auth_mode: "global",',
|
|
167
173
|
" email,",
|
|
168
174
|
" password,",
|
|
169
|
-
|
|
170
|
-
|
|
175
|
+
' token: options.token || "",',
|
|
176
|
+
' project_id: options.projectId || "",',
|
|
171
177
|
" };",
|
|
172
178
|
" const configPath = globalConfigPath();",
|
|
173
179
|
" writeConfig(configPath, config);",
|
|
@@ -176,11 +182,11 @@ const REQUEST_SCRIPT = [
|
|
|
176
182
|
"",
|
|
177
183
|
"function parseSimpleToml(text) {",
|
|
178
184
|
" const data = {};",
|
|
179
|
-
|
|
185
|
+
' const lines = String(text || "").split(/\\r?\\n/);',
|
|
180
186
|
" for (const rawLine of lines) {",
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
187
|
+
' const line = rawLine.split("#", 1)[0].split(";", 1)[0].trim();',
|
|
188
|
+
' if (!line || line.startsWith("[")) continue;',
|
|
189
|
+
' const idx = line.indexOf("=");',
|
|
184
190
|
" if (idx === -1) continue;",
|
|
185
191
|
" const key = line.slice(0, idx).trim();",
|
|
186
192
|
" let value = line.slice(idx + 1).trim();",
|
|
@@ -194,14 +200,14 @@ const REQUEST_SCRIPT = [
|
|
|
194
200
|
"}",
|
|
195
201
|
"",
|
|
196
202
|
"function resolveToken(tokenValue, projectId) {",
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
203
|
+
' if (!tokenValue) return "";',
|
|
204
|
+
' if (tokenValue.includes(",") || tokenValue.includes(":")) {',
|
|
205
|
+
' let defaultToken = "";',
|
|
200
206
|
" const mapping = {};",
|
|
201
|
-
|
|
207
|
+
' tokenValue.split(",").forEach((rawPair) => {',
|
|
202
208
|
" const pair = rawPair.trim();",
|
|
203
209
|
" if (!pair) return;",
|
|
204
|
-
|
|
210
|
+
' const idx = pair.indexOf(":");',
|
|
205
211
|
" if (idx === -1) {",
|
|
206
212
|
" defaultToken = pair;",
|
|
207
213
|
" return;",
|
|
@@ -220,21 +226,21 @@ const REQUEST_SCRIPT = [
|
|
|
220
226
|
"",
|
|
221
227
|
"function getSetCookie(headers) {",
|
|
222
228
|
" if (!headers) return [];",
|
|
223
|
-
|
|
224
|
-
|
|
229
|
+
' if (typeof headers.getSetCookie === "function") return headers.getSetCookie();',
|
|
230
|
+
' const value = headers.get("set-cookie");',
|
|
225
231
|
" return value ? [value] : [];",
|
|
226
232
|
"}",
|
|
227
233
|
"",
|
|
228
234
|
"function extractCookieValue(setCookies, name) {",
|
|
229
|
-
|
|
235
|
+
' if (!setCookies || !setCookies.length) return "";',
|
|
230
236
|
" for (const entry of setCookies) {",
|
|
231
|
-
|
|
237
|
+
' const parts = String(entry || "").split(";");',
|
|
232
238
|
" for (const part of parts) {",
|
|
233
239
|
" const item = part.trim();",
|
|
234
|
-
|
|
240
|
+
' if (item.startsWith(name + "=")) return item.split("=").slice(1).join("=");',
|
|
235
241
|
" }",
|
|
236
242
|
" }",
|
|
237
|
-
|
|
243
|
+
' return "";',
|
|
238
244
|
"}",
|
|
239
245
|
"",
|
|
240
246
|
"async function fetchWithTimeout(url, options, timeoutMs) {",
|
|
@@ -248,22 +254,22 @@ const REQUEST_SCRIPT = [
|
|
|
248
254
|
"}",
|
|
249
255
|
"",
|
|
250
256
|
"async function loginGetCookie(baseUrl, email, password, timeoutMs) {",
|
|
251
|
-
|
|
257
|
+
' const url = joinUrl(baseUrl, "/api/user/login");',
|
|
252
258
|
" const payload = JSON.stringify({ email, password });",
|
|
253
259
|
" const response = await fetchWithTimeout(url, {",
|
|
254
|
-
|
|
255
|
-
|
|
260
|
+
' method: "POST",',
|
|
261
|
+
' headers: { "Content-Type": "application/json;charset=UTF-8" },',
|
|
256
262
|
" body: payload,",
|
|
257
263
|
" }, timeoutMs);",
|
|
258
264
|
" const bodyText = await response.text();",
|
|
259
265
|
" const setCookies = getSetCookie(response.headers);",
|
|
260
|
-
|
|
261
|
-
|
|
266
|
+
' const yapiToken = extractCookieValue(setCookies, "_yapi_token");',
|
|
267
|
+
' const yapiUid = extractCookieValue(setCookies, "_yapi_uid");',
|
|
262
268
|
" if (!yapiToken) {",
|
|
263
|
-
|
|
269
|
+
' let message = "login failed: missing _yapi_token cookie";',
|
|
264
270
|
" try {",
|
|
265
271
|
" const payload = JSON.parse(bodyText);",
|
|
266
|
-
|
|
272
|
+
' if (payload && typeof payload === "object" && payload.errmsg) message = payload.errmsg;',
|
|
267
273
|
" } catch {",
|
|
268
274
|
" // ignore",
|
|
269
275
|
" }",
|
|
@@ -279,7 +285,7 @@ const REQUEST_SCRIPT = [
|
|
|
279
285
|
" const parsed = new URL(url);",
|
|
280
286
|
" if (Array.isArray(queryItems)) {",
|
|
281
287
|
" for (const [key, value] of queryItems) {",
|
|
282
|
-
|
|
288
|
+
' if (key) parsed.searchParams.append(key, value ?? "");',
|
|
283
289
|
" }",
|
|
284
290
|
" }",
|
|
285
291
|
" if (token) {",
|
|
@@ -294,75 +300,75 @@ const REQUEST_SCRIPT = [
|
|
|
294
300
|
" const options = {",
|
|
295
301
|
" query: [],",
|
|
296
302
|
" header: [],",
|
|
297
|
-
|
|
298
|
-
|
|
303
|
+
' method: "GET",',
|
|
304
|
+
' tokenParam: "token",',
|
|
299
305
|
" timeout: 30000,",
|
|
300
306
|
" };",
|
|
301
307
|
" for (let i = 0; i < argv.length; i += 1) {",
|
|
302
308
|
" const arg = argv[i];",
|
|
303
309
|
" if (!arg) continue;",
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
310
|
+
' if (arg === "--config") { options.config = argv[++i]; continue; }',
|
|
311
|
+
' if (arg.startsWith("--config=")) { options.config = arg.slice(9); continue; }',
|
|
312
|
+
' if (arg === "--base-url") { options.baseUrl = argv[++i]; continue; }',
|
|
313
|
+
' if (arg.startsWith("--base-url=")) { options.baseUrl = arg.slice(11); continue; }',
|
|
314
|
+
' if (arg === "--token") { options.token = argv[++i]; continue; }',
|
|
315
|
+
' if (arg.startsWith("--token=")) { options.token = arg.slice(8); continue; }',
|
|
316
|
+
' if (arg === "--project-id") { options.projectId = argv[++i]; continue; }',
|
|
317
|
+
' if (arg.startsWith("--project-id=")) { options.projectId = arg.slice(13); continue; }',
|
|
318
|
+
' if (arg === "--auth-mode") { options.authMode = argv[++i]; continue; }',
|
|
319
|
+
' if (arg.startsWith("--auth-mode=")) { options.authMode = arg.slice(12); continue; }',
|
|
320
|
+
' if (arg === "--email") { options.email = argv[++i]; continue; }',
|
|
321
|
+
' if (arg.startsWith("--email=")) { options.email = arg.slice(8); continue; }',
|
|
322
|
+
' if (arg === "--password") { options.password = argv[++i]; continue; }',
|
|
323
|
+
' if (arg.startsWith("--password=")) { options.password = arg.slice(11); continue; }',
|
|
324
|
+
' if (arg === "--cookie") { options.cookie = argv[++i]; continue; }',
|
|
325
|
+
' if (arg.startsWith("--cookie=")) { options.cookie = arg.slice(9); continue; }',
|
|
326
|
+
' if (arg === "--token-param") { options.tokenParam = argv[++i]; continue; }',
|
|
327
|
+
' if (arg.startsWith("--token-param=")) { options.tokenParam = arg.slice(14); continue; }',
|
|
328
|
+
' if (arg === "--method") { options.method = argv[++i]; continue; }',
|
|
329
|
+
' if (arg.startsWith("--method=")) { options.method = arg.slice(9); continue; }',
|
|
330
|
+
' if (arg === "--path") { options.path = argv[++i]; continue; }',
|
|
331
|
+
' if (arg.startsWith("--path=")) { options.path = arg.slice(7); continue; }',
|
|
332
|
+
' if (arg === "--url") { options.url = argv[++i]; continue; }',
|
|
333
|
+
' if (arg.startsWith("--url=")) { options.url = arg.slice(6); continue; }',
|
|
334
|
+
' if (arg === "--query") { options.query.push(argv[++i]); continue; }',
|
|
335
|
+
' if (arg.startsWith("--query=")) { options.query.push(arg.slice(8)); continue; }',
|
|
336
|
+
' if (arg === "--header") { options.header.push(argv[++i]); continue; }',
|
|
337
|
+
' if (arg.startsWith("--header=")) { options.header.push(arg.slice(9)); continue; }',
|
|
338
|
+
' if (arg === "--data") { options.data = argv[++i]; continue; }',
|
|
339
|
+
' if (arg.startsWith("--data=")) { options.data = arg.slice(7); continue; }',
|
|
340
|
+
' if (arg === "--data-file") { options.dataFile = argv[++i]; continue; }',
|
|
341
|
+
' if (arg.startsWith("--data-file=")) { options.dataFile = arg.slice(12); continue; }',
|
|
342
|
+
' if (arg === "--timeout") { options.timeout = Number(argv[++i]); continue; }',
|
|
343
|
+
' if (arg.startsWith("--timeout=")) { options.timeout = Number(arg.slice(10)); continue; }',
|
|
344
|
+
' if (arg === "--no-pretty") { options.noPretty = true; continue; }',
|
|
345
|
+
' if (arg === "--help" || arg === "-h") { options.help = true; continue; }',
|
|
340
346
|
" }",
|
|
341
347
|
" return options;",
|
|
342
348
|
"}",
|
|
343
349
|
"",
|
|
344
350
|
"function usage() {",
|
|
345
351
|
" return [",
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
352
|
+
' "Usage:",',
|
|
353
|
+
' " node yapi_request.js --path /api/interface/get --query id=123",',
|
|
354
|
+
' "Options:",',
|
|
355
|
+
' " --config <path> config file path (default: ~/.yapi/config.toml)",',
|
|
356
|
+
' " --base-url <url> YApi base URL",',
|
|
357
|
+
' " --token <token> project token (supports projectId:token)",',
|
|
358
|
+
' " --project-id <id> select token for project",',
|
|
359
|
+
' " --auth-mode <mode> token or global",',
|
|
360
|
+
' " --email <email> login email for global mode",',
|
|
361
|
+
' " --password <pwd> login password for global mode",',
|
|
362
|
+
' " --path <path> API path (e.g., /api/interface/get)",',
|
|
363
|
+
' " --url <url> full URL (overrides base-url/path)",',
|
|
364
|
+
' " --query key=value query param (repeatable)",',
|
|
365
|
+
' " --header Header:Value request header (repeatable)",',
|
|
366
|
+
' " --method <method> HTTP method",',
|
|
367
|
+
' " --data <payload> request body (JSON or text)",',
|
|
368
|
+
' " --data-file <file> request body file",',
|
|
369
|
+
' " --timeout <ms> request timeout in ms",',
|
|
370
|
+
' " --no-pretty print raw response",',
|
|
371
|
+
' ].join("\\n");',
|
|
366
372
|
"}",
|
|
367
373
|
"",
|
|
368
374
|
"async function main() {",
|
|
@@ -373,21 +379,21 @@ const REQUEST_SCRIPT = [
|
|
|
373
379
|
" }",
|
|
374
380
|
"",
|
|
375
381
|
" if (options.url && options.path) {",
|
|
376
|
-
|
|
382
|
+
' console.error("use --url or --path, not both");',
|
|
377
383
|
" return 2;",
|
|
378
384
|
" }",
|
|
379
385
|
"",
|
|
380
386
|
" if (!options.url && !options.path) {",
|
|
381
|
-
|
|
387
|
+
' console.error("missing --path or --url");',
|
|
382
388
|
" console.error(usage());",
|
|
383
389
|
" return 2;",
|
|
384
390
|
" }",
|
|
385
391
|
"",
|
|
386
392
|
" let config = {};",
|
|
387
|
-
|
|
393
|
+
' let configPath = options.config || "";',
|
|
388
394
|
" if (options.config) {",
|
|
389
395
|
" if (fs.existsSync(configPath)) {",
|
|
390
|
-
|
|
396
|
+
' config = parseSimpleToml(fs.readFileSync(configPath, "utf8"));',
|
|
391
397
|
" } else {",
|
|
392
398
|
" const init = await initConfigIfMissing(options);",
|
|
393
399
|
" if (init) {",
|
|
@@ -403,39 +409,39 @@ const REQUEST_SCRIPT = [
|
|
|
403
409
|
" const localPath = localConfigPath();",
|
|
404
410
|
" if (fs.existsSync(globalPath)) {",
|
|
405
411
|
" configPath = globalPath;",
|
|
406
|
-
|
|
412
|
+
' config = parseSimpleToml(fs.readFileSync(globalPath, "utf8"));',
|
|
407
413
|
" } else if (fs.existsSync(localPath)) {",
|
|
408
414
|
" configPath = localPath;",
|
|
409
|
-
|
|
415
|
+
' config = parseSimpleToml(fs.readFileSync(localPath, "utf8"));',
|
|
410
416
|
" } else {",
|
|
411
417
|
" const init = await initConfigIfMissing(options);",
|
|
412
418
|
" if (init) {",
|
|
413
419
|
" config = init.config;",
|
|
414
420
|
" configPath = init.configPath;",
|
|
415
421
|
" } else {",
|
|
416
|
-
|
|
422
|
+
' console.error("missing config: create ~/.yapi/config.toml or pass --config");',
|
|
417
423
|
" return 2;",
|
|
418
424
|
" }",
|
|
419
425
|
" }",
|
|
420
426
|
" }",
|
|
421
427
|
"",
|
|
422
|
-
|
|
428
|
+
' const baseUrl = options.url ? null : (options.baseUrl || config.base_url || "");',
|
|
423
429
|
" const endpoint = options.url || options.path;",
|
|
424
430
|
" if (!options.url && !baseUrl) {",
|
|
425
|
-
|
|
431
|
+
' console.error("missing --base-url or config base_url");',
|
|
426
432
|
" return 2;",
|
|
427
433
|
" }",
|
|
428
434
|
"",
|
|
429
|
-
|
|
430
|
-
|
|
435
|
+
' const projectId = options.projectId || config.project_id || "";',
|
|
436
|
+
' const rawToken = options.token || config.token || "";',
|
|
431
437
|
" const token = resolveToken(rawToken, projectId);",
|
|
432
438
|
"",
|
|
433
|
-
|
|
439
|
+
' let authMode = (options.authMode || config.auth_mode || "").trim().toLowerCase();',
|
|
434
440
|
" if (!authMode) {",
|
|
435
|
-
|
|
441
|
+
' authMode = token ? "token" : (options.email || options.password || config.email || config.password) ? "global" : "token";',
|
|
436
442
|
" }",
|
|
437
|
-
|
|
438
|
-
|
|
443
|
+
' if (authMode !== "token" && authMode !== "global") {',
|
|
444
|
+
' console.error("invalid --auth-mode (use token or global)");',
|
|
439
445
|
" return 2;",
|
|
440
446
|
" }",
|
|
441
447
|
"",
|
|
@@ -447,11 +453,11 @@ const REQUEST_SCRIPT = [
|
|
|
447
453
|
"",
|
|
448
454
|
" if (options.cookie) {",
|
|
449
455
|
" headers.Cookie = options.cookie;",
|
|
450
|
-
|
|
456
|
+
' } else if (authMode === "global") {',
|
|
451
457
|
" const email = options.email || config.email;",
|
|
452
458
|
" const password = options.password || config.password;",
|
|
453
459
|
" if (!email || !password) {",
|
|
454
|
-
|
|
460
|
+
' console.error("missing email/password for global auth");',
|
|
455
461
|
" return 2;",
|
|
456
462
|
" }",
|
|
457
463
|
" try {",
|
|
@@ -466,24 +472,24 @@ const REQUEST_SCRIPT = [
|
|
|
466
472
|
" for (const query of options.query || []) {",
|
|
467
473
|
" queryItems.push(parseKeyValue(query));",
|
|
468
474
|
" }",
|
|
469
|
-
|
|
475
|
+
' const url = buildUrl(baseUrl, endpoint, queryItems, authMode === "token" ? token : "", options.tokenParam);',
|
|
470
476
|
"",
|
|
471
477
|
" let dataRaw = null;",
|
|
472
478
|
" if (options.dataFile) {",
|
|
473
|
-
|
|
479
|
+
' dataRaw = fs.readFileSync(options.dataFile, "utf8");',
|
|
474
480
|
" } else if (options.data !== undefined) {",
|
|
475
481
|
" dataRaw = options.data;",
|
|
476
482
|
" }",
|
|
477
483
|
"",
|
|
478
484
|
" let body;",
|
|
479
|
-
|
|
485
|
+
' if (dataRaw !== null && options.method.toUpperCase() !== "GET" && options.method.toUpperCase() !== "HEAD") {',
|
|
480
486
|
" try {",
|
|
481
487
|
" const parsed = JSON.parse(dataRaw);",
|
|
482
488
|
" body = JSON.stringify(parsed);",
|
|
483
|
-
|
|
489
|
+
' if (!headers["Content-Type"]) headers["Content-Type"] = "application/json";',
|
|
484
490
|
" } catch {",
|
|
485
491
|
" body = String(dataRaw);",
|
|
486
|
-
|
|
492
|
+
' if (!headers["Content-Type"]) headers["Content-Type"] = "text/plain";',
|
|
487
493
|
" }",
|
|
488
494
|
" }",
|
|
489
495
|
"",
|
|
@@ -495,7 +501,7 @@ const REQUEST_SCRIPT = [
|
|
|
495
501
|
" body,",
|
|
496
502
|
" }, options.timeout);",
|
|
497
503
|
" } catch (error) {",
|
|
498
|
-
|
|
504
|
+
' console.error("request failed: " + (error && error.message ? error.message : String(error)));',
|
|
499
505
|
" return 2;",
|
|
500
506
|
" }",
|
|
501
507
|
"",
|
|
@@ -531,7 +537,8 @@ function parseSimpleToml(text) {
|
|
|
531
537
|
let value = line.slice(idx + 1).trim();
|
|
532
538
|
if (!key)
|
|
533
539
|
continue;
|
|
534
|
-
if ((value.startsWith("
|
|
540
|
+
if ((value.startsWith('"') && value.endsWith('"')) ||
|
|
541
|
+
(value.startsWith("'") && value.endsWith("'"))) {
|
|
535
542
|
value = value.slice(1, -1);
|
|
536
543
|
}
|
|
537
544
|
data[key] = value;
|
|
@@ -539,7 +546,7 @@ function parseSimpleToml(text) {
|
|
|
539
546
|
return data;
|
|
540
547
|
}
|
|
541
548
|
function escapeTomlValue(value) {
|
|
542
|
-
return value.replace(/\\/g, "\\\\").replace(/"/g, "
|
|
549
|
+
return value.replace(/\\/g, "\\\\").replace(/"/g, '\\"');
|
|
543
550
|
}
|
|
544
551
|
function formatToml(config) {
|
|
545
552
|
const orderedKeys = ["base_url", "auth_mode", "email", "password", "token", "project_id"];
|
|
@@ -548,7 +555,9 @@ function formatToml(config) {
|
|
|
548
555
|
const value = config[key] ?? "";
|
|
549
556
|
lines.push(`${key} = "${escapeTomlValue(value)}"`);
|
|
550
557
|
}
|
|
551
|
-
const extras = Object.keys(config)
|
|
558
|
+
const extras = Object.keys(config)
|
|
559
|
+
.filter((key) => !orderedKeys.includes(key))
|
|
560
|
+
.sort();
|
|
552
561
|
for (const key of extras) {
|
|
553
562
|
const value = config[key] ?? "";
|
|
554
563
|
lines.push(`${key} = "${escapeTomlValue(value)}"`);
|
|
@@ -576,7 +585,11 @@ function ensurePrivate(filePath) {
|
|
|
576
585
|
}
|
|
577
586
|
}
|
|
578
587
|
async function promptHidden(question) {
|
|
579
|
-
const rl = readline_1.default.createInterface({
|
|
588
|
+
const rl = readline_1.default.createInterface({
|
|
589
|
+
input: process.stdin,
|
|
590
|
+
output: process.stdout,
|
|
591
|
+
terminal: true,
|
|
592
|
+
});
|
|
580
593
|
const originalWrite = rl._writeToOutput;
|
|
581
594
|
rl._writeToOutput = function writeToOutput(stringToWrite) {
|
|
582
595
|
if (rl.stdoutMuted)
|
|
@@ -598,7 +611,11 @@ async function promptHidden(question) {
|
|
|
598
611
|
});
|
|
599
612
|
}
|
|
600
613
|
async function promptText(question) {
|
|
601
|
-
const rl = readline_1.default.createInterface({
|
|
614
|
+
const rl = readline_1.default.createInterface({
|
|
615
|
+
input: process.stdin,
|
|
616
|
+
output: process.stdout,
|
|
617
|
+
terminal: true,
|
|
618
|
+
});
|
|
602
619
|
return new Promise((resolve) => {
|
|
603
620
|
rl.question(question, (answer) => {
|
|
604
621
|
rl.close();
|
|
@@ -636,7 +653,11 @@ async function runInstallSkill(rawArgs) {
|
|
|
636
653
|
"yapi-home": { type: "string", describe: "Override YApi config home (default: ~/.yapi)" },
|
|
637
654
|
"codex-home": { type: "string", describe: "Override CODEX_HOME" },
|
|
638
655
|
"claude-home": { type: "string", describe: "Override Claude home (default: ~/.claude)" },
|
|
639
|
-
force: {
|
|
656
|
+
force: {
|
|
657
|
+
type: "boolean",
|
|
658
|
+
default: false,
|
|
659
|
+
describe: "Overwrite skill files if they already exist",
|
|
660
|
+
},
|
|
640
661
|
})
|
|
641
662
|
.help()
|
|
642
663
|
.parseSync();
|