@opentiny/webmcp-cli 0.0.1-alpha.4 → 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,而非数组下标
@@ -472,11 +644,7 @@ async function runCommand({
472
644
  const browser2 = await connectBrowser();
473
645
  try {
474
646
  const page2 = await getTargetPage(browser2, tabid2);
475
- try {
476
- JSON.parse(argsJson);
477
- } catch (e) {
478
- throw new Error(`\u53C2\u6570\u4E0D\u662F\u6709\u6548\u7684 JSON: ${e.message}`);
479
- }
647
+ const cleanedArgs = argsJson.trim();
480
648
  const result2 = await page2.evaluate(async (name, inputString) => {
481
649
  const mcp = navigator.modelContextTesting || navigator.modelContext;
482
650
  if (!mcp || typeof mcp.executeTool !== "function") {
@@ -486,11 +654,18 @@ async function runCommand({
486
654
  if (typeof res === "string") {
487
655
  try {
488
656
  res = JSON.parse(res);
489
- } catch (e) {
657
+ } catch {
490
658
  }
491
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
+ }
492
667
  return res;
493
- }, toolName, argsJson);
668
+ }, toolName, cleanedArgs);
494
669
  return result2;
495
670
  } finally {
496
671
  await browser2.disconnect();
@@ -550,7 +725,7 @@ async function tabsOpenCommand(url) {
550
725
  url = normalizeUrl(url);
551
726
  await page2.goto(url, { waitUntil: "domcontentloaded" });
552
727
  await injectIntoPage(page2);
553
- await new Promise((resolve2) => setTimeout(resolve2, 500));
728
+ await new Promise((resolve3) => setTimeout(resolve3, 500));
554
729
  return {
555
730
  success: true,
556
731
  tabid: await getPageTargetId(page2),
@@ -651,7 +826,7 @@ async function tabsForwardCommand(tabid2) {
651
826
  // package.json
652
827
  var package_default = {
653
828
  name: "@opentiny/webmcp-cli",
654
- version: "0.0.1-alpha.4",
829
+ version: "0.0.1-alpha.6",
655
830
  type: "module",
656
831
  description: "WebMCP CLI for AI Agents to interact with browser via Puppeteer",
657
832
  main: "dist/index.js",
@@ -690,6 +865,7 @@ var package_default = {
690
865
  "@page-agent/page-controller": "catalog:",
691
866
  clipboardy: "^5.3.1",
692
867
  commander: "^11.1.0",
868
+ json5: "^2.2.3",
693
869
  picocolors: "^1.0.0",
694
870
  "puppeteer-core": "^22.0.0"
695
871
  },
@@ -713,19 +889,6 @@ function handleCommandError(error, commandName) {
713
889
  console.error(import_picocolors2.default.red(`Error executing ${commandName} command: ${msg}`));
714
890
  process.exit(1);
715
891
  }
716
- function expandFileRefs(argsJson) {
717
- return argsJson.replace(/"@(base64)?file:([^"]+)"/g, (_match, base64Flag, filePath) => {
718
- const absPath = (0, import_path2.resolve)(process.cwd(), filePath);
719
- let content;
720
- try {
721
- content = (0, import_fs2.readFileSync)(absPath, "utf-8");
722
- } catch (e) {
723
- throw new Error(`\u65E0\u6CD5\u8BFB\u53D6\u6587\u4EF6\u5F15\u7528 "${filePath}": ${e.message}`);
724
- }
725
- const value = base64Flag ? Buffer.from(content, "utf-8").toString("base64") : content;
726
- return JSON.stringify(value);
727
- });
728
- }
729
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) => {
730
893
  const opts = thisCommand.opts();
731
894
  if (opts.workspace) {
@@ -740,28 +903,27 @@ program.command("state").description("\u83B7\u53D6\u6D4F\u89C8\u5668\u5F53\u524D
740
903
  handleCommandError(error, "state");
741
904
  }
742
905
  });
743
- program.command("run <toolName> [argsJson]").description("\u5411\u6307\u5B9A\u9875\u7B7E\u8C03\u7528\u6307\u5B9A\u7684 WebMCP \u5DE5\u5177\u6267\u884C\u64CD\u4F5C").option("-t, --tabid <id>", "\u6307\u5B9A\u9875\u7B7E\u7684 ID").option(
906
+ program.command("run <toolName> [args...]").description("\u5411\u6307\u5B9A\u9875\u7B7E\u8C03\u7528\u6307\u5B9A\u7684 WebMCP \u5DE5\u5177\u6267\u884C\u64CD\u4F5C").option("-t, --tabid <id>", "\u6307\u5B9A\u9875\u7B7E\u7684 ID").option(
744
907
  "-f, --file <path>",
745
- `\u4ECE\u6307\u5B9A .json \u6587\u4EF6\u8BFB\u53D6\u6574\u4E2A argsJson\uFF08\u6587\u4EF6\u5185\u5BB9\u5373\u4E3A\u53C2\u6570 JSON\uFF09\u3002
746
- \u5982\u9700\u5728 argsJson \u4E2D\u5185\u8054\u5F15\u7528\u6587\u4EF6\uFF0C\u4F7F\u7528\u5360\u4F4D\u7B26\u8BED\u6CD5\uFF1A
908
+ `\u4ECE\u6307\u5B9A .json \u6587\u4EF6\u8BFB\u53D6\u6574\u4E2A\u53C2\u6570\uFF08\u6587\u4EF6\u5185\u5BB9\u5373\u4E3A\u53C2\u6570 JSON\uFF09\u3002
909
+ \u5982\u9700\u5728\u53C2\u6570\u4E2D\u5185\u8054\u5F15\u7528\u6587\u4EF6\uFF0C\u4F7F\u7528\u5360\u4F4D\u7B26\u8BED\u6CD5\uFF1A
747
910
  @file:<path> \u8BFB\u53D6\u6587\u4EF6\u539F\u59CB\u6587\u672C
748
911
  @base64file:<path> \u8BFB\u53D6\u6587\u4EF6\u5E76 Base64 \u7F16\u7801
749
912
  \u793A\u4F8B\uFF1Awebmcp-cli run mytool '{"content":"@base64file:./doc.md"}'`
750
- ).action(async (toolName, argsJson, options) => {
913
+ ).action(async (toolName, args, options) => {
751
914
  try {
752
- let finalArgsJson = argsJson;
915
+ let fileContent;
916
+ let fileBaseDir = process.cwd();
753
917
  if (options.file) {
754
- 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);
755
920
  try {
756
- finalArgsJson = (0, import_fs2.readFileSync)(filePath, "utf-8").trim();
921
+ fileContent = (0, import_fs3.readFileSync)(filePath, "utf-8");
757
922
  } catch (e) {
758
923
  throw new Error(`\u65E0\u6CD5\u8BFB\u53D6\u6587\u4EF6 "${filePath}": ${e.message}`);
759
924
  }
760
925
  }
761
- if (!finalArgsJson) {
762
- throw new Error("\u5FC5\u987B\u63D0\u4F9B argsJson \u53C2\u6570\u6216\u901A\u8FC7 -f/--file \u6307\u5B9A\u53C2\u6570\u6587\u4EF6");
763
- }
764
- finalArgsJson = expandFileRefs(finalArgsJson);
926
+ const finalArgsJson = prepareRunArgsJson(args ?? [], fileContent, fileBaseDir);
765
927
  const result2 = await runCommand({
766
928
  toolName,
767
929
  argsJson: finalArgsJson,