@opentiny/webmcp-cli 0.0.1-alpha.5 → 0.0.1-alpha.6

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/dist/bin.cjs CHANGED
@@ -26,25 +26,197 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
26
26
  // src/bin.ts
27
27
  var import_commander = require("commander");
28
28
  var import_picocolors2 = __toESM(require("picocolors"), 1);
29
- var import_fs2 = require("fs");
30
- var import_path2 = require("path");
29
+ var import_fs3 = require("fs");
30
+ var import_path3 = require("path");
31
+
32
+ // src/expand-file-refs.ts
33
+ var import_fs = require("fs");
34
+ var import_path = require("path");
35
+ var import_url = require("url");
36
+ var JSON_STRING_LITERAL = /"(?:[^"\\]|\\.)*"/g;
37
+ var UNQUOTED_FILE_REF = /(?<![\\"])@(base64)?file:([^\s"',}\]]+)/g;
38
+ var FILE_REF_PREFIX = /^@(base64)?file:(.+)$/s;
39
+ function resolveFilePath(filePath, baseDir = process.cwd()) {
40
+ let normalizedPath = filePath.trim();
41
+ if (normalizedPath.startsWith('"') && normalizedPath.endsWith('"') || normalizedPath.startsWith("'") && normalizedPath.endsWith("'")) {
42
+ normalizedPath = normalizedPath.slice(1, -1).trim();
43
+ }
44
+ if (normalizedPath.startsWith("file://")) {
45
+ normalizedPath = (0, import_url.fileURLToPath)(normalizedPath);
46
+ }
47
+ if ((0, import_path.isAbsolute)(normalizedPath)) {
48
+ return (0, import_path.normalize)(normalizedPath);
49
+ }
50
+ return (0, import_path.resolve)(baseDir, normalizedPath);
51
+ }
52
+ function decodeFilePath(rawPath) {
53
+ return rawPath.replace(/\\\\/g, "\\");
54
+ }
55
+ function readFileRefContent(filePath, baseDir, base64) {
56
+ const absPath = resolveFilePath(filePath, baseDir);
57
+ let content;
58
+ try {
59
+ content = (0, import_fs.readFileSync)(absPath, "utf-8");
60
+ } catch (e) {
61
+ throw new Error(`\u65E0\u6CD5\u8BFB\u53D6\u6587\u4EF6\u5F15\u7528 "${filePath}" (${absPath}): ${e.message}`);
62
+ }
63
+ return base64 ? Buffer.from(content, "utf-8").toString("base64") : content;
64
+ }
65
+ function expandFileRefValue(rawInner, baseDir) {
66
+ const match = rawInner.match(FILE_REF_PREFIX);
67
+ if (!match) {
68
+ return null;
69
+ }
70
+ const [, base64Flag, rawPath] = match;
71
+ const filePath = decodeFilePath(rawPath);
72
+ const value = readFileRefContent(filePath, baseDir, Boolean(base64Flag));
73
+ return JSON.stringify(value);
74
+ }
75
+ function expandQuotedFileRefs(argsJson, baseDir) {
76
+ return argsJson.replace(JSON_STRING_LITERAL, (jsonStringLiteral) => {
77
+ const rawInner = jsonStringLiteral.slice(1, -1);
78
+ const replacement = expandFileRefValue(rawInner, baseDir);
79
+ return replacement ?? jsonStringLiteral;
80
+ });
81
+ }
82
+ function expandUnquotedFileRefs(argsJson, baseDir) {
83
+ return argsJson.replace(UNQUOTED_FILE_REF, (match, base64Flag, filePath) => {
84
+ const value = readFileRefContent(filePath, baseDir, Boolean(base64Flag));
85
+ return JSON.stringify(value);
86
+ });
87
+ }
88
+ function expandFileRefs(argsJson, baseDir = process.cwd()) {
89
+ const withQuotedExpanded = expandQuotedFileRefs(argsJson, baseDir);
90
+ return expandUnquotedFileRefs(withQuotedExpanded, baseDir);
91
+ }
92
+ function getFileBaseDir(fileOption) {
93
+ if (!fileOption) {
94
+ return process.cwd();
95
+ }
96
+ return (0, import_path.dirname)((0, import_path.resolve)(process.cwd(), fileOption));
97
+ }
98
+
99
+ // src/parse-run-args.ts
100
+ var import_json5 = __toESM(require("json5"), 1);
101
+ function cleanJsonString(str) {
102
+ let cleaned = str.trim();
103
+ if (cleaned.startsWith("'") && cleaned.endsWith("'")) {
104
+ cleaned = cleaned.slice(1, -1).trim();
105
+ }
106
+ return cleaned;
107
+ }
108
+ function looksLikeObjectBody(raw) {
109
+ return /^("?[\w-]+"?\s*:)/.test(raw);
110
+ }
111
+ function joinShellArgs(args) {
112
+ const cleaned = args.map(cleanJsonString);
113
+ const first = cleaned[0] ?? "";
114
+ if (first.startsWith("{")) {
115
+ return cleaned.join(" ");
116
+ }
117
+ if (looksLikeObjectBody(first)) {
118
+ let joined = cleaned.join(" ");
119
+ if (!joined.startsWith("{")) {
120
+ joined = `{${joined}}`;
121
+ }
122
+ if (!joined.endsWith("}")) {
123
+ joined = `${joined}}`;
124
+ }
125
+ return joined;
126
+ }
127
+ return cleaned.join(" ");
128
+ }
129
+ function insertMissingCommas(raw) {
130
+ return raw.replace(/"(\s+)("?[\w-]+"?\s*:)/g, '",$2');
131
+ }
132
+ function quoteUnquotedStringValues(raw) {
133
+ return raw.replace(
134
+ /([{,]\s*(?:"([^"]+)"|([\w-]+))\s*:\s*)(?!"@(?:base64)?file:|"|true|false|null|-?\d|\{|\[)([^",}\]]+?)(?=\s*[,}])/g,
135
+ (_match, prefix, _quotedKey, _unquotedKey, value) => {
136
+ const trimmed = value.trim();
137
+ if (/^-?\d+(\.\d+)?([eE][+-]?\d+)?$/.test(trimmed)) {
138
+ return prefix + trimmed;
139
+ }
140
+ return prefix + JSON.stringify(trimmed);
141
+ }
142
+ );
143
+ }
144
+ function normalizeShellDamagedJson(raw) {
145
+ let normalized = raw.trim();
146
+ normalized = insertMissingCommas(normalized);
147
+ normalized = quoteUnquotedStringValues(normalized);
148
+ return normalized;
149
+ }
150
+ function buildArgsFromKeyValuePairs(args) {
151
+ const obj = {};
152
+ for (const pair of args) {
153
+ const index = pair.indexOf("=");
154
+ if (index === -1) {
155
+ throw new Error(`\u65E0\u6548\u7684\u53C2\u6570\u683C\u5F0F "${pair}"\u3002\u8BF7\u4F7F\u7528 key=value\uFF0C\u6216 JSON / -f \u6587\u4EF6\u3002`);
156
+ }
157
+ const key = pair.substring(0, index).trim();
158
+ let val = cleanJsonString(pair.substring(index + 1).trim());
159
+ try {
160
+ obj[key] = JSON.parse(val);
161
+ } catch {
162
+ obj[key] = val;
163
+ }
164
+ }
165
+ return JSON.stringify(obj);
166
+ }
167
+ function parseArgsJson(raw) {
168
+ const trimmed = cleanJsonString(raw);
169
+ try {
170
+ return JSON.stringify(import_json5.default.parse(trimmed));
171
+ } catch (firstError) {
172
+ const repaired = normalizeShellDamagedJson(trimmed);
173
+ try {
174
+ return JSON.stringify(import_json5.default.parse(repaired));
175
+ } catch {
176
+ const hint = `\u63D0\u793A\uFF1Abash \u4E2D JSON \u5FC5\u987B\u7528\u5355\u5F15\u53F7\u5305\u88F9\uFF0C\u4F8B\u5982 '{"title":"\u6807\u9898","content":"@base64file:./article.md"}'\uFF1B\u6216\u4F7F\u7528 key=value\uFF1Atitle=\u6807\u9898 content=@base64file:./article.md\uFF1B\u6216\u4F7F\u7528 -f args.json\u3002`;
177
+ const message = firstError instanceof Error ? firstError.message : String(firstError);
178
+ throw new Error(`\u53C2\u6570\u4E0D\u662F\u6709\u6548\u7684 JSON \u6216 JS \u5BF9\u8C61: ${message}
179
+ ${hint}`);
180
+ }
181
+ }
182
+ }
183
+ function prepareRunArgsJson(args, fileContent, baseDir) {
184
+ let raw = "";
185
+ if (fileContent !== void 0) {
186
+ raw = fileContent.trim();
187
+ } else if (args.length > 0) {
188
+ const joined = joinShellArgs(args);
189
+ if (joined.startsWith("{") || looksLikeObjectBody(joined)) {
190
+ raw = joined;
191
+ } else if (args.every((arg) => arg.includes("="))) {
192
+ raw = buildArgsFromKeyValuePairs(args);
193
+ } else {
194
+ raw = joined;
195
+ }
196
+ }
197
+ if (!raw) {
198
+ throw new Error("\u5FC5\u987B\u63D0\u4F9B\u53C2\u6570\u6216\u901A\u8FC7 -f/--file \u6307\u5B9A\u53C2\u6570\u6587\u4EF6");
199
+ }
200
+ const expanded = expandFileRefs(raw, baseDir);
201
+ return parseArgsJson(expanded);
202
+ }
31
203
 
32
204
  // src/browser.ts
33
205
  var import_puppeteer_core = __toESM(require("puppeteer-core"), 1);
34
206
  var import_picocolors = __toESM(require("picocolors"), 1);
35
- var import_fs = __toESM(require("fs"), 1);
207
+ var import_fs2 = __toESM(require("fs"), 1);
36
208
  var import_os = __toESM(require("os"), 1);
37
- var import_path = __toESM(require("path"), 1);
209
+ var import_path2 = __toESM(require("path"), 1);
38
210
  var import_http = __toESM(require("http"), 1);
39
211
  var import_child_process = require("child_process");
40
- var import_url = require("url");
212
+ var import_url2 = require("url");
41
213
  var import_meta = {};
42
- var __filename = (0, import_url.fileURLToPath)(import_meta.url);
43
- var __dirname = import_path.default.dirname(__filename);
214
+ var __filename = (0, import_url2.fileURLToPath)(import_meta.url);
215
+ var __dirname = import_path2.default.dirname(__filename);
44
216
  var CDP_PORT = 9222;
45
217
  var CDP_URL = `http://localhost:${CDP_PORT}`;
46
218
  function httpGet(url, timeoutMs = 1500) {
47
- return new Promise((resolve2, reject) => {
219
+ return new Promise((resolve3, reject) => {
48
220
  const req = import_http.default.get(url, { timeout: timeoutMs }, (res) => {
49
221
  let data = "";
50
222
  res.on("data", (chunk) => {
@@ -52,7 +224,7 @@ function httpGet(url, timeoutMs = 1500) {
52
224
  });
53
225
  res.on("end", () => {
54
226
  if (res.statusCode && res.statusCode >= 200 && res.statusCode < 300) {
55
- resolve2(data);
227
+ resolve3(data);
56
228
  } else {
57
229
  reject(new Error(`HTTP ${res.statusCode}`));
58
230
  }
@@ -93,9 +265,9 @@ function getDefaultBrowserPath() {
93
265
  const platform = import_os.default.platform();
94
266
  if (platform === "darwin") {
95
267
  const chromePath = "/Applications/Google Chrome.app/Contents/MacOS/Google Chrome";
96
- if (import_fs.default.existsSync(chromePath)) return { path: chromePath, name: "Chrome" };
268
+ if (import_fs2.default.existsSync(chromePath)) return { path: chromePath, name: "Chrome" };
97
269
  const edgePath = "/Applications/Microsoft Edge.app/Contents/MacOS/Microsoft Edge";
98
- if (import_fs.default.existsSync(edgePath)) return { path: edgePath, name: "Edge" };
270
+ if (import_fs2.default.existsSync(edgePath)) return { path: edgePath, name: "Edge" };
99
271
  return null;
100
272
  } else if (platform === "win32") {
101
273
  const chromePaths = [
@@ -103,7 +275,7 @@ function getDefaultBrowserPath() {
103
275
  process.env.PROGRAMFILES + "\\Google\\Chrome\\Application\\chrome.exe",
104
276
  process.env["PROGRAMFILES(X86)"] + "\\Google\\Chrome\\Application\\chrome.exe"
105
277
  ];
106
- const foundChrome = chromePaths.find((p) => import_fs.default.existsSync(p));
278
+ const foundChrome = chromePaths.find((p) => import_fs2.default.existsSync(p));
107
279
  if (foundChrome) return { path: foundChrome, name: "Chrome" };
108
280
  const edgePaths = [
109
281
  process.env.LOCALAPPDATA + "\\Microsoft\\Edge\\Application\\msedge.exe",
@@ -112,7 +284,7 @@ function getDefaultBrowserPath() {
112
284
  "C:\\Program Files (x86)\\Microsoft\\Edge\\Application\\msedge.exe",
113
285
  "C:\\Program Files\\Microsoft\\Edge\\Application\\msedge.exe"
114
286
  ];
115
- const foundEdge = edgePaths.find((p) => import_fs.default.existsSync(p));
287
+ const foundEdge = edgePaths.find((p) => import_fs2.default.existsSync(p));
116
288
  if (foundEdge) return { path: foundEdge, name: "Edge" };
117
289
  return null;
118
290
  } else {
@@ -124,17 +296,17 @@ function getDefaultBrowserPath() {
124
296
  { p: "/usr/bin/microsoft-edge", name: "Edge" },
125
297
  { p: "/usr/bin/microsoft-edge-stable", name: "Edge" }
126
298
  ];
127
- const found = paths.find(({ p }) => import_fs.default.existsSync(p));
299
+ const found = paths.find(({ p }) => import_fs2.default.existsSync(p));
128
300
  return found ? { path: found.p, name: found.name } : null;
129
301
  }
130
302
  }
131
303
  async function startBrowserInBackground() {
132
304
  const browserInfo = getDefaultBrowserPath();
133
- if (!browserInfo || !import_fs.default.existsSync(browserInfo.path)) {
305
+ if (!browserInfo || !import_fs2.default.existsSync(browserInfo.path)) {
134
306
  throw new Error("\u65E0\u6CD5\u5728\u7CFB\u7EDF\u4E2D\u627E\u5230 Chrome \u6216 Edge \u6D4F\u89C8\u5668\u7684\u9ED8\u8BA4\u5B89\u88C5\u8DEF\u5F84\u3002");
135
307
  }
136
308
  console.log(import_picocolors.default.yellow(`\u6B63\u5728\u542F\u52A8\u540E\u53F0 ${browserInfo.name} \u5B9E\u4F8B (\u7AEF\u53E3: ${CDP_PORT})...`));
137
- const userDataDir = process.env.WEBMCP_WORKSPACE || import_path.default.join(import_os.default.homedir(), ".webmcp_chrome_profile");
309
+ const userDataDir = process.env.WEBMCP_WORKSPACE || import_path2.default.join(import_os.default.homedir(), ".webmcp_chrome_profile");
138
310
  const child = (0, import_child_process.spawn)(
139
311
  browserInfo.path,
140
312
  [
@@ -162,7 +334,7 @@ async function startBrowserInBackground() {
162
334
  }
163
335
  } catch (e) {
164
336
  }
165
- await new Promise((resolve2) => setTimeout(resolve2, 500));
337
+ await new Promise((resolve3) => setTimeout(resolve3, 500));
166
338
  }
167
339
  throw new Error(`${browserInfo.name} \u542F\u52A8\u8D85\u65F6\uFF0C\u65E0\u6CD5\u8FDE\u63A5\u5230 CDP \u7AEF\u53E3\u3002`);
168
340
  }
@@ -345,18 +517,18 @@ async function injectWebMCPPolyfillAndTools(page2, force = false) {
345
517
  }).catch(() => false);
346
518
  if (!polyfillReady) {
347
519
  console.log(import_picocolors.default.cyan("\u5F53\u524D\u9875\u9762\u5C1A\u672A\u6CE8\u5165 WebMCP \u73AF\u5883\uFF0C\u6B63\u5728\u6267\u884C\u81EA\u52A8\u6CE8\u5165..."));
348
- const injectScriptPath = import_path.default.resolve(__dirname, "inject-bundle.js");
349
- if (!import_fs.default.existsSync(injectScriptPath)) {
520
+ const injectScriptPath = import_path2.default.resolve(__dirname, "inject-bundle.js");
521
+ if (!import_fs2.default.existsSync(injectScriptPath)) {
350
522
  throw new Error(`Cannot find inject-bundle.js at ${injectScriptPath}. Please ensure you run 'pnpm build:inject' first.`);
351
523
  }
352
- const scriptContent = import_fs.default.readFileSync(injectScriptPath, "utf-8");
524
+ const scriptContent = import_fs2.default.readFileSync(injectScriptPath, "utf-8");
353
525
  try {
354
526
  await page2.evaluate(scriptContent);
355
527
  } catch (err) {
356
528
  const msg = err instanceof Error ? err.message : String(err);
357
529
  throw new Error("\u81EA\u52A8\u6CE8\u5165\u811A\u672C\u6267\u884C\u5931\u8D25: " + msg);
358
530
  }
359
- await new Promise((resolve2) => setTimeout(resolve2, 300));
531
+ await new Promise((resolve3) => setTimeout(resolve3, 300));
360
532
  }
361
533
  await injectDomainTools(page2);
362
534
  }
@@ -368,12 +540,12 @@ async function injectDomainTools(page2) {
368
540
  } catch {
369
541
  return;
370
542
  }
371
- const toolBundlePath = import_path.default.resolve(__dirname, "webmcp-tools", `${hostname}.js`);
372
- if (!import_fs.default.existsSync(toolBundlePath)) {
543
+ const toolBundlePath = import_path2.default.resolve(__dirname, "webmcp-tools", `${hostname}.js`);
544
+ if (!import_fs2.default.existsSync(toolBundlePath)) {
373
545
  return;
374
546
  }
375
547
  console.log(import_picocolors.default.cyan(`\u68C0\u6D4B\u5230\u57DF\u540D ${hostname} \u6709\u9884\u7F6E\u5DE5\u5177\uFF0C\u6B63\u5728\u6CE8\u5165...`));
376
- const toolScript = import_fs.default.readFileSync(toolBundlePath, "utf-8");
548
+ const toolScript = import_fs2.default.readFileSync(toolBundlePath, "utf-8");
377
549
  try {
378
550
  await page2.evaluate(toolScript);
379
551
  console.log(import_picocolors.default.green(`\u5DF2\u4E3A ${hostname} \u6CE8\u5165\u9884\u7F6E\u5DE5\u5177`));
@@ -444,7 +616,7 @@ async function stateCommand({ tabid: tabid2 }) {
444
616
  if (pUrl.startsWith("devtools://")) return null;
445
617
  const pTitle = await Promise.race([
446
618
  p.title().catch(() => "Unknown"),
447
- new Promise((resolve2) => setTimeout(() => resolve2("Unknown"), 500))
619
+ new Promise((resolve3) => setTimeout(() => resolve3("Unknown"), 500))
448
620
  ]);
449
621
  return {
450
622
  // 使用真实的 Chrome target ID,而非数组下标
@@ -464,7 +636,6 @@ async function stateCommand({ tabid: tabid2 }) {
464
636
  }
465
637
 
466
638
  // src/commands/run.ts
467
- var import_json5 = __toESM(require("json5"), 1);
468
639
  async function runCommand({
469
640
  toolName,
470
641
  argsJson,
@@ -473,16 +644,7 @@ async function runCommand({
473
644
  const browser2 = await connectBrowser();
474
645
  try {
475
646
  const page2 = await getTargetPage(browser2, tabid2);
476
- let cleanedArgs = argsJson.trim();
477
- if (cleanedArgs.startsWith("'") && cleanedArgs.endsWith("'")) {
478
- cleanedArgs = cleanedArgs.slice(1, -1).trim();
479
- }
480
- try {
481
- const obj = import_json5.default.parse(cleanedArgs);
482
- cleanedArgs = JSON.stringify(obj);
483
- } catch (e) {
484
- throw new Error(`\u53C2\u6570\u4E0D\u662F\u6709\u6548\u7684 JSON \u6216 JS \u5BF9\u8C61: ${e.message}`);
485
- }
647
+ const cleanedArgs = argsJson.trim();
486
648
  const result2 = await page2.evaluate(async (name, inputString) => {
487
649
  const mcp = navigator.modelContextTesting || navigator.modelContext;
488
650
  if (!mcp || typeof mcp.executeTool !== "function") {
@@ -492,9 +654,17 @@ async function runCommand({
492
654
  if (typeof res === "string") {
493
655
  try {
494
656
  res = JSON.parse(res);
495
- } catch (e) {
657
+ } catch {
496
658
  }
497
659
  }
660
+ if (res === void 0 || res === null) {
661
+ throw new Error("\u5DE5\u5177 execute \u672A\u8FD4\u56DE\u7ED3\u679C\uFF0C\u8BF7\u68C0\u67E5\u5DE5\u5177\u5B9E\u73B0\u662F\u5426\u7F3A\u5C11 return");
662
+ }
663
+ if (typeof res === "object" && res.success === false) {
664
+ const failed = res;
665
+ throw new Error(failed.error || failed.message || "\u5DE5\u5177\u6267\u884C\u5931\u8D25");
666
+ }
667
+ return res;
498
668
  }, toolName, cleanedArgs);
499
669
  return result2;
500
670
  } finally {
@@ -555,7 +725,7 @@ async function tabsOpenCommand(url) {
555
725
  url = normalizeUrl(url);
556
726
  await page2.goto(url, { waitUntil: "domcontentloaded" });
557
727
  await injectIntoPage(page2);
558
- await new Promise((resolve2) => setTimeout(resolve2, 500));
728
+ await new Promise((resolve3) => setTimeout(resolve3, 500));
559
729
  return {
560
730
  success: true,
561
731
  tabid: await getPageTargetId(page2),
@@ -656,7 +826,7 @@ async function tabsForwardCommand(tabid2) {
656
826
  // package.json
657
827
  var package_default = {
658
828
  name: "@opentiny/webmcp-cli",
659
- version: "0.0.1-alpha.5",
829
+ version: "0.0.1-alpha.6",
660
830
  type: "module",
661
831
  description: "WebMCP CLI for AI Agents to interact with browser via Puppeteer",
662
832
  main: "dist/index.js",
@@ -710,13 +880,6 @@ var package_default = {
710
880
 
711
881
  // src/bin.ts
712
882
  var program = new import_commander.Command();
713
- function cleanJsonString(str) {
714
- let cleaned = str.trim();
715
- if (cleaned.startsWith("'") && cleaned.endsWith("'")) {
716
- cleaned = cleaned.slice(1, -1).trim();
717
- }
718
- return cleaned;
719
- }
720
883
  function parseTabId(id) {
721
884
  if (!id) return void 0;
722
885
  return id;
@@ -726,19 +889,6 @@ function handleCommandError(error, commandName) {
726
889
  console.error(import_picocolors2.default.red(`Error executing ${commandName} command: ${msg}`));
727
890
  process.exit(1);
728
891
  }
729
- function expandFileRefs(argsJson) {
730
- return argsJson.replace(/"@(base64)?file:([^"]+)"/g, (_match, base64Flag, filePath) => {
731
- const absPath = (0, import_path2.resolve)(process.cwd(), filePath);
732
- let content;
733
- try {
734
- content = (0, import_fs2.readFileSync)(absPath, "utf-8");
735
- } catch (e) {
736
- throw new Error(`\u65E0\u6CD5\u8BFB\u53D6\u6587\u4EF6\u5F15\u7528 "${filePath}": ${e.message}`);
737
- }
738
- const value = base64Flag ? Buffer.from(content, "utf-8").toString("base64") : content;
739
- return JSON.stringify(value);
740
- });
741
- }
742
892
  program.name("webmcp-cli").description("WebMCP CLI for interacting with browser via CDP").version(package_default.version).option("-w, --workspace <path>", "\u6307\u5B9A\u81EA\u5B9A\u4E49\u7684\u6D4F\u89C8\u5668\u5DE5\u4F5C\u7A7A\u95F4\uFF08\u7528\u6237\u914D\u7F6E\u76EE\u5F55\uFF09\u8DEF\u5F84").hook("preAction", (thisCommand) => {
743
893
  const opts = thisCommand.opts();
744
894
  if (opts.workspace) {
@@ -762,42 +912,18 @@ program.command("run <toolName> [args...]").description("\u5411\u6307\u5B9A\u987
762
912
  \u793A\u4F8B\uFF1Awebmcp-cli run mytool '{"content":"@base64file:./doc.md"}'`
763
913
  ).action(async (toolName, args, options) => {
764
914
  try {
765
- let finalArgsJson = "";
915
+ let fileContent;
916
+ let fileBaseDir = process.cwd();
766
917
  if (options.file) {
767
- const filePath = (0, import_path2.resolve)(process.cwd(), options.file);
918
+ const filePath = (0, import_path3.resolve)(process.cwd(), options.file);
919
+ fileBaseDir = getFileBaseDir(options.file);
768
920
  try {
769
- finalArgsJson = (0, import_fs2.readFileSync)(filePath, "utf-8").trim();
921
+ fileContent = (0, import_fs3.readFileSync)(filePath, "utf-8");
770
922
  } catch (e) {
771
923
  throw new Error(`\u65E0\u6CD5\u8BFB\u53D6\u6587\u4EF6 "${filePath}": ${e.message}`);
772
924
  }
773
- } else if (args && args.length > 0) {
774
- const firstArg = args[0].trim();
775
- const cleanedFirstArg = cleanJsonString(firstArg);
776
- if (cleanedFirstArg.startsWith("{") && cleanedFirstArg.endsWith("}")) {
777
- finalArgsJson = args.map(cleanJsonString).join(" ");
778
- } else {
779
- const obj = {};
780
- for (const pair of args) {
781
- const index = pair.indexOf("=");
782
- if (index === -1) {
783
- throw new Error(`\u65E0\u6548\u7684\u53C2\u6570\u683C\u5F0F "${pair}"\u3002\u5FC5\u987B\u662F JSON \u5B57\u7B26\u4E32\u6216 key=value \u683C\u5F0F\u3002`);
784
- }
785
- const key = pair.substring(0, index).trim();
786
- let val = pair.substring(index + 1).trim();
787
- val = cleanJsonString(val);
788
- try {
789
- obj[key] = JSON.parse(val);
790
- } catch {
791
- obj[key] = val;
792
- }
793
- }
794
- finalArgsJson = JSON.stringify(obj);
795
- }
796
- }
797
- if (!finalArgsJson) {
798
- throw new Error("\u5FC5\u987B\u63D0\u4F9B\u53C2\u6570\u6216\u901A\u8FC7 -f/--file \u6307\u5B9A\u53C2\u6570\u6587\u4EF6");
799
925
  }
800
- finalArgsJson = expandFileRefs(finalArgsJson);
926
+ const finalArgsJson = prepareRunArgsJson(args ?? [], fileContent, fileBaseDir);
801
927
  const result2 = await runCommand({
802
928
  toolName,
803
929
  argsJson: finalArgsJson,