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

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.js CHANGED
@@ -1,10 +1,185 @@
1
1
  #!/usr/bin/env node
2
2
 
3
3
  // src/bin.ts
4
+ import fs2 from "fs";
5
+ import path2 from "path";
6
+ import os2 from "os";
4
7
  import { Command } from "commander";
5
8
  import pc2 from "picocolors";
9
+ import { readFileSync as readFileSync2 } from "fs";
10
+ import { resolve as resolve2 } from "path";
11
+
12
+ // src/expand-file-refs.ts
6
13
  import { readFileSync } from "fs";
7
- import { resolve } from "path";
14
+ import { dirname, isAbsolute, normalize, resolve } from "path";
15
+ import { fileURLToPath } from "url";
16
+ var JSON_STRING_LITERAL = /"(?:[^"\\]|\\.)*"/g;
17
+ var UNQUOTED_FILE_REF = /(?<![\\"])@(base64)?file:([^\s"',}\]]+)/g;
18
+ var FILE_REF_PREFIX = /^@(base64)?file:(.+)$/s;
19
+ function resolveFilePath(filePath, baseDir = process.cwd()) {
20
+ let normalizedPath = filePath.trim();
21
+ if (normalizedPath.startsWith('"') && normalizedPath.endsWith('"') || normalizedPath.startsWith("'") && normalizedPath.endsWith("'")) {
22
+ normalizedPath = normalizedPath.slice(1, -1).trim();
23
+ }
24
+ if (normalizedPath.startsWith("file://")) {
25
+ normalizedPath = fileURLToPath(normalizedPath);
26
+ }
27
+ if (isAbsolute(normalizedPath)) {
28
+ return normalize(normalizedPath);
29
+ }
30
+ return resolve(baseDir, normalizedPath);
31
+ }
32
+ function decodeFilePath(rawPath) {
33
+ return rawPath.replace(/\\\\/g, "\\");
34
+ }
35
+ function readFileRefContent(filePath, baseDir, base64) {
36
+ const absPath = resolveFilePath(filePath, baseDir);
37
+ let content;
38
+ try {
39
+ content = readFileSync(absPath, "utf-8");
40
+ } catch (e) {
41
+ throw new Error(`\u65E0\u6CD5\u8BFB\u53D6\u6587\u4EF6\u5F15\u7528 "${filePath}" (${absPath}): ${e.message}`);
42
+ }
43
+ return base64 ? Buffer.from(content, "utf-8").toString("base64") : content;
44
+ }
45
+ function expandFileRefValue(rawInner, baseDir) {
46
+ const match = rawInner.match(FILE_REF_PREFIX);
47
+ if (!match) {
48
+ return null;
49
+ }
50
+ const [, base64Flag, rawPath] = match;
51
+ const filePath = decodeFilePath(rawPath);
52
+ const value = readFileRefContent(filePath, baseDir, Boolean(base64Flag));
53
+ return JSON.stringify(value);
54
+ }
55
+ function expandQuotedFileRefs(argsJson, baseDir) {
56
+ return argsJson.replace(JSON_STRING_LITERAL, (jsonStringLiteral) => {
57
+ const rawInner = jsonStringLiteral.slice(1, -1);
58
+ const replacement = expandFileRefValue(rawInner, baseDir);
59
+ return replacement ?? jsonStringLiteral;
60
+ });
61
+ }
62
+ function expandUnquotedFileRefs(argsJson, baseDir) {
63
+ return argsJson.replace(UNQUOTED_FILE_REF, (match, base64Flag, filePath) => {
64
+ const value = readFileRefContent(filePath, baseDir, Boolean(base64Flag));
65
+ return JSON.stringify(value);
66
+ });
67
+ }
68
+ function expandFileRefs(argsJson, baseDir = process.cwd()) {
69
+ const withQuotedExpanded = expandQuotedFileRefs(argsJson, baseDir);
70
+ return expandUnquotedFileRefs(withQuotedExpanded, baseDir);
71
+ }
72
+ function getFileBaseDir(fileOption) {
73
+ if (!fileOption) {
74
+ return process.cwd();
75
+ }
76
+ return dirname(resolve(process.cwd(), fileOption));
77
+ }
78
+
79
+ // src/parse-run-args.ts
80
+ import JSON5 from "json5";
81
+ function cleanJsonString(str) {
82
+ let cleaned = str.trim();
83
+ if (cleaned.startsWith("'") && cleaned.endsWith("'")) {
84
+ cleaned = cleaned.slice(1, -1).trim();
85
+ }
86
+ return cleaned;
87
+ }
88
+ function looksLikeObjectBody(raw) {
89
+ return /^("?[\w-]+"?\s*:)/.test(raw);
90
+ }
91
+ function joinShellArgs(args) {
92
+ const cleaned = args.map(cleanJsonString);
93
+ const first = cleaned[0] ?? "";
94
+ if (first.startsWith("{")) {
95
+ return cleaned.join(" ");
96
+ }
97
+ if (looksLikeObjectBody(first)) {
98
+ let joined = cleaned.join(" ");
99
+ if (!joined.startsWith("{")) {
100
+ joined = `{${joined}}`;
101
+ }
102
+ if (!joined.endsWith("}")) {
103
+ joined = `${joined}}`;
104
+ }
105
+ return joined;
106
+ }
107
+ return cleaned.join(" ");
108
+ }
109
+ function insertMissingCommas(raw) {
110
+ return raw.replace(/"(\s+)("?[\w-]+"?\s*:)/g, '",$2');
111
+ }
112
+ function quoteUnquotedStringValues(raw) {
113
+ return raw.replace(
114
+ /([{,]\s*(?:"([^"]+)"|([\w-]+))\s*:\s*)(?!"@(?:base64)?file:|"|true|false|null|-?\d|\{|\[)([^",}\]]+?)(?=\s*[,}])/g,
115
+ (_match, prefix, _quotedKey, _unquotedKey, value) => {
116
+ const trimmed = value.trim();
117
+ if (/^-?\d+(\.\d+)?([eE][+-]?\d+)?$/.test(trimmed)) {
118
+ return prefix + trimmed;
119
+ }
120
+ return prefix + JSON.stringify(trimmed);
121
+ }
122
+ );
123
+ }
124
+ function normalizeShellDamagedJson(raw) {
125
+ let normalized = raw.trim();
126
+ normalized = insertMissingCommas(normalized);
127
+ normalized = quoteUnquotedStringValues(normalized);
128
+ return normalized;
129
+ }
130
+ function buildArgsFromKeyValuePairs(args) {
131
+ const obj = {};
132
+ for (const pair of args) {
133
+ const index = pair.indexOf("=");
134
+ if (index === -1) {
135
+ throw new Error(`\u65E0\u6548\u7684\u53C2\u6570\u683C\u5F0F "${pair}"\u3002\u8BF7\u4F7F\u7528 key=value\uFF0C\u6216 JSON / -f \u6587\u4EF6\u3002`);
136
+ }
137
+ const key = pair.substring(0, index).trim();
138
+ let val = cleanJsonString(pair.substring(index + 1).trim());
139
+ try {
140
+ obj[key] = JSON.parse(val);
141
+ } catch {
142
+ obj[key] = val;
143
+ }
144
+ }
145
+ return JSON.stringify(obj);
146
+ }
147
+ function parseArgsJson(raw) {
148
+ const trimmed = cleanJsonString(raw);
149
+ try {
150
+ return JSON.stringify(JSON5.parse(trimmed));
151
+ } catch (firstError) {
152
+ const repaired = normalizeShellDamagedJson(trimmed);
153
+ try {
154
+ return JSON.stringify(JSON5.parse(repaired));
155
+ } catch {
156
+ 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`;
157
+ const message = firstError instanceof Error ? firstError.message : String(firstError);
158
+ throw new Error(`\u53C2\u6570\u4E0D\u662F\u6709\u6548\u7684 JSON \u6216 JS \u5BF9\u8C61: ${message}
159
+ ${hint}`);
160
+ }
161
+ }
162
+ }
163
+ function prepareRunArgsJson(args, fileContent, baseDir) {
164
+ let raw = "";
165
+ if (fileContent !== void 0) {
166
+ raw = fileContent.trim();
167
+ } else if (args.length > 0) {
168
+ const joined = joinShellArgs(args);
169
+ if (joined.startsWith("{") || looksLikeObjectBody(joined)) {
170
+ raw = joined;
171
+ } else if (args.every((arg) => arg.includes("="))) {
172
+ raw = buildArgsFromKeyValuePairs(args);
173
+ } else {
174
+ raw = joined;
175
+ }
176
+ }
177
+ if (!raw) {
178
+ throw new Error("\u5FC5\u987B\u63D0\u4F9B\u53C2\u6570\u6216\u901A\u8FC7 -f/--file \u6307\u5B9A\u53C2\u6570\u6587\u4EF6");
179
+ }
180
+ const expanded = expandFileRefs(raw, baseDir);
181
+ return parseArgsJson(expanded);
182
+ }
8
183
 
9
184
  // src/browser.ts
10
185
  import puppeteer from "puppeteer-core";
@@ -14,13 +189,13 @@ import os from "os";
14
189
  import path from "path";
15
190
  import http from "http";
16
191
  import { spawn } from "child_process";
17
- import { fileURLToPath } from "url";
18
- var __filename = fileURLToPath(import.meta.url);
192
+ import { fileURLToPath as fileURLToPath2 } from "url";
193
+ var __filename = fileURLToPath2(import.meta.url);
19
194
  var __dirname = path.dirname(__filename);
20
195
  var CDP_PORT = 9222;
21
196
  var CDP_URL = `http://localhost:${CDP_PORT}`;
22
197
  function httpGet(url, timeoutMs = 1500) {
23
- return new Promise((resolve2, reject) => {
198
+ return new Promise((resolve3, reject) => {
24
199
  const req = http.get(url, { timeout: timeoutMs }, (res) => {
25
200
  let data = "";
26
201
  res.on("data", (chunk) => {
@@ -28,7 +203,7 @@ function httpGet(url, timeoutMs = 1500) {
28
203
  });
29
204
  res.on("end", () => {
30
205
  if (res.statusCode && res.statusCode >= 200 && res.statusCode < 300) {
31
- resolve2(data);
206
+ resolve3(data);
32
207
  } else {
33
208
  reject(new Error(`HTTP ${res.statusCode}`));
34
209
  }
@@ -138,7 +313,7 @@ async function startBrowserInBackground() {
138
313
  }
139
314
  } catch (e) {
140
315
  }
141
- await new Promise((resolve2) => setTimeout(resolve2, 500));
316
+ await new Promise((resolve3) => setTimeout(resolve3, 500));
142
317
  }
143
318
  throw new Error(`${browserInfo.name} \u542F\u52A8\u8D85\u65F6\uFF0C\u65E0\u6CD5\u8FDE\u63A5\u5230 CDP \u7AEF\u53E3\u3002`);
144
319
  }
@@ -169,13 +344,13 @@ async function connectBrowser() {
169
344
  const browserURL = addr.replace("/json/version", "");
170
345
  try {
171
346
  console.log(pc.yellow(`connectBrowser: \u68C0\u6D4B\u5230\u7AEF\u53E3 ${CDP_PORT} \u5DF2\u5C31\u7EEA\uFF0C\u6B63\u5728\u8FDE\u63A5 ${browserURL}...`));
172
- const browser2 = await promiseWithTimeout(
347
+ const browser = await promiseWithTimeout(
173
348
  puppeteer.connect({ browserURL, defaultViewport: null, targetFilter }),
174
349
  1e4,
175
350
  `puppeteer.connect to ${browserURL} timed out`
176
351
  );
177
352
  console.log(pc.green(`connectBrowser: \u6210\u529F\u8FDE\u63A5 ${browserURL}`));
178
- return browser2;
353
+ return browser;
179
354
  } catch {
180
355
  }
181
356
  }
@@ -193,20 +368,20 @@ async function connectBrowser() {
193
368
  const browserURL = addr.replace("/json/version", "");
194
369
  try {
195
370
  console.log(pc.yellow(`connectBrowser: \u6D4F\u89C8\u5668\u5DF2\u542F\u52A8\uFF0C\u6B63\u5728\u8FDE\u63A5 ${browserURL}...`));
196
- const browser2 = await promiseWithTimeout(
371
+ const browser = await promiseWithTimeout(
197
372
  puppeteer.connect({ browserURL, defaultViewport: null, targetFilter }),
198
373
  1e4,
199
374
  `puppeteer.connect to ${browserURL} after launch timed out`
200
375
  );
201
376
  console.log(pc.green(`connectBrowser: \u6210\u529F\u8FDE\u63A5 ${browserURL}`));
202
- return browser2;
377
+ return browser;
203
378
  } catch {
204
379
  }
205
380
  }
206
381
  throw new Error(`\u65E0\u6CD5\u8FDE\u63A5\u5230\u6D4F\u89C8\u5668\uFF08\u7AEF\u53E3 ${CDP_PORT}\uFF09\uFF0C\u8BF7\u68C0\u67E5 Chrome/Edge \u662F\u5426\u5DF2\u5B89\u88C5\u3002`);
207
382
  }
208
- async function getPageTargetId(page2) {
209
- const session = await page2.target().createCDPSession();
383
+ async function getPageTargetId(page) {
384
+ const session = await page.target().createCDPSession();
210
385
  try {
211
386
  const { targetInfo } = await session.send("Target.getTargetInfo");
212
387
  return targetInfo.targetId;
@@ -215,8 +390,8 @@ async function getPageTargetId(page2) {
215
390
  });
216
391
  }
217
392
  }
218
- function getPageTargets(browser2) {
219
- return browser2.targets().filter((t) => {
393
+ function getPageTargets(browser) {
394
+ return browser.targets().filter((t) => {
220
395
  try {
221
396
  const type = (typeof t.type === "function" ? t.type() : t.type) || "";
222
397
  const url = (typeof t.url === "function" ? t.url() : t.url) || "";
@@ -229,26 +404,26 @@ function getPageTargets(browser2) {
229
404
  function getTargetIdFromTarget(target) {
230
405
  return typeof target._getTargetInfo === "function" ? target._getTargetInfo().targetId : target._targetId || target.targetId || "";
231
406
  }
232
- function findPageTargetByTabId(browser2, tabid2) {
233
- const pageTargets = getPageTargets(browser2);
407
+ function findPageTargetByTabId(browser, tabid) {
408
+ const pageTargets = getPageTargets(browser);
234
409
  return pageTargets.find((t) => {
235
410
  const tid = getTargetIdFromTarget(t);
236
- return tid === tabid2 || tid.includes(tabid2);
411
+ return tid === tabid || tid.includes(tabid);
237
412
  }) ?? null;
238
413
  }
239
- async function activateTabById(browser2, tabid2) {
240
- const target = findPageTargetByTabId(browser2, tabid2);
414
+ async function activateTabById(browser, tabid) {
415
+ const target = findPageTargetByTabId(browser, tabid);
241
416
  if (!target) {
242
- throw new Error(`Tab with targetId "${tabid2}" not found.`);
417
+ throw new Error(`Tab with targetId "${tabid}" not found.`);
243
418
  }
244
419
  const realTabId = getTargetIdFromTarget(target);
245
- const pages = await browser2.pages();
420
+ const pages = await browser.pages();
246
421
  let sessionPage = pages.find((p) => !p.url().startsWith("devtools://"));
247
422
  if (!sessionPage && pages.length > 0) {
248
423
  sessionPage = pages[0];
249
424
  }
250
425
  if (!sessionPage) {
251
- sessionPage = await browser2.newPage();
426
+ sessionPage = await browser.newPage();
252
427
  }
253
428
  const session = await sessionPage.createCDPSession();
254
429
  try {
@@ -258,18 +433,18 @@ async function activateTabById(browser2, tabid2) {
258
433
  });
259
434
  }
260
435
  }
261
- async function getTargetPage(browser2, tabid2) {
262
- const pageTargets = getPageTargets(browser2);
436
+ async function getTargetPage(browser, tabid) {
437
+ const pageTargets = getPageTargets(browser);
263
438
  if (pageTargets.length === 0) {
264
- const newPage = await browser2.newPage();
439
+ const newPage = await browser.newPage();
265
440
  await injectWebMCPPolyfillAndTools(newPage);
266
441
  return newPage;
267
442
  }
268
443
  let targetPage = null;
269
- if (tabid2 !== void 0) {
270
- const target = findPageTargetByTabId(browser2, tabid2);
444
+ if (tabid !== void 0) {
445
+ const target = findPageTargetByTabId(browser, tabid);
271
446
  if (!target) {
272
- throw new Error(`Tab with targetId "${tabid2}" not found.`);
447
+ throw new Error(`Tab with targetId "${tabid}" not found.`);
273
448
  }
274
449
  targetPage = await target.page();
275
450
  } else {
@@ -312,11 +487,11 @@ async function getTargetPage(browser2, tabid2) {
312
487
  await injectWebMCPPolyfillAndTools(targetPage);
313
488
  return targetPage;
314
489
  }
315
- async function injectIntoPage(page2) {
316
- await injectWebMCPPolyfillAndTools(page2, true);
490
+ async function injectIntoPage(page) {
491
+ await injectWebMCPPolyfillAndTools(page, true);
317
492
  }
318
- async function injectWebMCPPolyfillAndTools(page2, force = false) {
319
- const polyfillReady = !force && await page2.evaluate(() => {
493
+ async function injectWebMCPPolyfillAndTools(page, force = false) {
494
+ const polyfillReady = !force && await page.evaluate(() => {
320
495
  return !!window.__webmcpcli_init;
321
496
  }).catch(() => false);
322
497
  if (!polyfillReady) {
@@ -327,31 +502,63 @@ async function injectWebMCPPolyfillAndTools(page2, force = false) {
327
502
  }
328
503
  const scriptContent = fs.readFileSync(injectScriptPath, "utf-8");
329
504
  try {
330
- await page2.evaluate(scriptContent);
505
+ await page.evaluate(scriptContent);
331
506
  } catch (err) {
332
507
  const msg = err instanceof Error ? err.message : String(err);
333
508
  throw new Error("\u81EA\u52A8\u6CE8\u5165\u811A\u672C\u6267\u884C\u5931\u8D25: " + msg);
334
509
  }
335
- await new Promise((resolve2) => setTimeout(resolve2, 300));
510
+ await page.waitForFunction(() => {
511
+ const mcp = navigator.modelContext;
512
+ return mcp && typeof mcp.registerTool === "function";
513
+ }, { timeout: 1e4 }).catch(() => {
514
+ console.warn("\u7B49\u5F85 WebMCP registerTool \u63A5\u53E3\u5C31\u7EEA\u8D85\u65F6\uFF0C\u6B63\u5728\u7EE7\u7EED\u6CE8\u5165...");
515
+ });
336
516
  }
337
- await injectDomainTools(page2);
517
+ await injectDomainTools(page);
338
518
  }
339
- async function injectDomainTools(page2) {
519
+ function getToolsBundleName(hostname) {
520
+ const normalized = hostname.split(":")[0].toLowerCase();
521
+ const toolsDir = path.resolve(__dirname, "webmcp-tools");
522
+ if (!fs.existsSync(toolsDir)) {
523
+ return null;
524
+ }
525
+ let files = [];
526
+ try {
527
+ files = fs.readdirSync(toolsDir);
528
+ } catch {
529
+ return null;
530
+ }
531
+ const supportedDomains = files.filter((f) => f.endsWith(".js")).map((f) => f.slice(0, -3));
532
+ if (supportedDomains.includes(normalized)) {
533
+ return `webmcp-tools/${normalized}.js`;
534
+ }
535
+ for (const domain of supportedDomains) {
536
+ if (normalized.endsWith("." + domain)) {
537
+ return `webmcp-tools/${domain}.js`;
538
+ }
539
+ }
540
+ return null;
541
+ }
542
+ async function injectDomainTools(page) {
340
543
  let hostname;
341
544
  try {
342
- const url = new URL(page2.url());
545
+ const url = new URL(page.url());
343
546
  hostname = url.hostname;
344
547
  } catch {
345
548
  return;
346
549
  }
347
- const toolBundlePath = path.resolve(__dirname, "webmcp-tools", `${hostname}.js`);
550
+ const bundleName = getToolsBundleName(hostname);
551
+ if (!bundleName) {
552
+ return;
553
+ }
554
+ const toolBundlePath = path.resolve(__dirname, bundleName);
348
555
  if (!fs.existsSync(toolBundlePath)) {
349
556
  return;
350
557
  }
351
558
  console.log(pc.cyan(`\u68C0\u6D4B\u5230\u57DF\u540D ${hostname} \u6709\u9884\u7F6E\u5DE5\u5177\uFF0C\u6B63\u5728\u6CE8\u5165...`));
352
559
  const toolScript = fs.readFileSync(toolBundlePath, "utf-8");
353
560
  try {
354
- await page2.evaluate(toolScript);
561
+ await page.evaluate(toolScript);
355
562
  console.log(pc.green(`\u5DF2\u4E3A ${hostname} \u6CE8\u5165\u9884\u7F6E\u5DE5\u5177`));
356
563
  } catch (err) {
357
564
  const msg = err instanceof Error ? err.message : String(err);
@@ -360,67 +567,30 @@ async function injectDomainTools(page2) {
360
567
  }
361
568
 
362
569
  // src/commands/state.ts
363
- async function stateCommand({ tabid: tabid2 }) {
364
- const browser2 = await connectBrowser();
570
+ async function stateCommand({ tabid }) {
571
+ const browser = await connectBrowser();
365
572
  try {
366
- const page2 = await getTargetPage(browser2, tabid2);
367
- await injectIntoPage(page2);
368
- const state = await page2.evaluate(async () => {
573
+ const page = await getTargetPage(browser, tabid);
574
+ await injectIntoPage(page);
575
+ const state = await page.evaluate(async () => {
369
576
  const url = document.URL;
370
577
  const title = document.title;
371
578
  const mcp = navigator.modelContextTesting || navigator.modelContext;
372
579
  let webmcpTools = [];
373
- let contentData = `\u9875\u9762\u5DF2\u51C6\u5907\u597D: ${title}`;
374
- if (mcp) {
375
- if (typeof mcp.listTools === "function") {
376
- const toolsResult = await mcp.listTools();
377
- webmcpTools = toolsResult?.tools || toolsResult || [];
378
- }
379
- if (typeof mcp.executeTool === "function") {
380
- try {
381
- const argsString = JSON.stringify({ action: "browserState" });
382
- let stateRes = await mcp.executeTool("page-agent-tool", argsString);
383
- if (typeof stateRes === "string") {
384
- try {
385
- stateRes = JSON.parse(stateRes);
386
- } catch (e) {
387
- }
388
- }
389
- if (stateRes && stateRes.content && stateRes.content.length > 0) {
390
- const textContent = stateRes.content.map((c) => c.text).join("\\n");
391
- const prefix = "\u6D4F\u89C8\u5668\u72B6\u6001: ";
392
- if (textContent.startsWith(prefix)) {
393
- try {
394
- const jsonStr = textContent.substring(prefix.length);
395
- const parsedState = JSON.parse(jsonStr);
396
- contentData = parsedState.content;
397
- } catch (e) {
398
- contentData = textContent;
399
- }
400
- } else {
401
- contentData = textContent;
402
- }
403
- }
404
- } catch (e) {
405
- console.error("Snapshot error:", e.message);
406
- }
407
- }
580
+ if (mcp && typeof mcp.listTools === "function") {
581
+ const toolsResult = await mcp.listTools();
582
+ webmcpTools = toolsResult?.tools || toolsResult || [];
408
583
  }
409
- return {
410
- content: contentData,
411
- url,
412
- title,
413
- webmcpTools
414
- };
584
+ return { url, title, webmcpTools };
415
585
  });
416
- const pages = await browser2.pages();
586
+ const pages = await browser.pages();
417
587
  const tabs2 = await Promise.all(
418
588
  pages.map(async (p) => {
419
589
  const pUrl = p.url();
420
590
  if (pUrl.startsWith("devtools://")) return null;
421
591
  const pTitle = await Promise.race([
422
592
  p.title().catch(() => "Unknown"),
423
- new Promise((resolve2) => setTimeout(() => resolve2("Unknown"), 500))
593
+ new Promise((resolve3) => setTimeout(() => resolve3("Unknown"), 500))
424
594
  ]);
425
595
  return {
426
596
  // 使用真实的 Chrome target ID,而非数组下标
@@ -435,31 +605,21 @@ async function stateCommand({ tabid: tabid2 }) {
435
605
  tabs: tabs2.filter(Boolean)
436
606
  };
437
607
  } finally {
438
- await browser2.disconnect();
608
+ await browser.disconnect();
439
609
  }
440
610
  }
441
611
 
442
612
  // src/commands/run.ts
443
- import JSON5 from "json5";
444
613
  async function runCommand({
445
614
  toolName,
446
615
  argsJson,
447
- tabid: tabid2
616
+ tabid
448
617
  }) {
449
- const browser2 = await connectBrowser();
618
+ const browser = await connectBrowser();
450
619
  try {
451
- const page2 = await getTargetPage(browser2, tabid2);
452
- let cleanedArgs = argsJson.trim();
453
- if (cleanedArgs.startsWith("'") && cleanedArgs.endsWith("'")) {
454
- cleanedArgs = cleanedArgs.slice(1, -1).trim();
455
- }
456
- try {
457
- const obj = JSON5.parse(cleanedArgs);
458
- cleanedArgs = JSON.stringify(obj);
459
- } catch (e) {
460
- throw new Error(`\u53C2\u6570\u4E0D\u662F\u6709\u6548\u7684 JSON \u6216 JS \u5BF9\u8C61: ${e.message}`);
461
- }
462
- const result2 = await page2.evaluate(async (name, inputString) => {
620
+ const page = await getTargetPage(browser, tabid);
621
+ const cleanedArgs = argsJson.trim();
622
+ const result = await page.evaluate(async (name, inputString) => {
463
623
  const mcp = navigator.modelContextTesting || navigator.modelContext;
464
624
  if (!mcp || typeof mcp.executeTool !== "function") {
465
625
  throw new Error("\u5F53\u524D\u9875\u9762\u6CA1\u6709\u6CE8\u5165 WebMCP \u73AF\u5883 (navigator.modelContextTesting.executeTool \u672A\u627E\u5230)");
@@ -468,39 +628,18 @@ async function runCommand({
468
628
  if (typeof res === "string") {
469
629
  try {
470
630
  res = JSON.parse(res);
471
- } catch (e) {
631
+ } catch {
472
632
  }
473
633
  }
474
- }, toolName, cleanedArgs);
475
- return result2;
476
- } finally {
477
- await browser2.disconnect();
478
- }
479
- }
480
-
481
- // src/commands/evaluate.ts
482
- async function evaluateCommand({
483
- jsScript,
484
- tabid
485
- }) {
486
- const browser = await connectBrowser();
487
- try {
488
- const page = await getTargetPage(browser, tabid);
489
- const result = await page.evaluate(async (script) => {
490
- try {
491
- const result = eval(script);
492
- if (result instanceof Promise) {
493
- return await result;
494
- }
495
- return result;
496
- } catch (error) {
497
- return {
498
- error: true,
499
- message: error.message,
500
- stack: error.stack
501
- };
634
+ if (res === void 0 || res === null) {
635
+ throw new Error("\u5DE5\u5177 execute \u672A\u8FD4\u56DE\u7ED3\u679C\uFF0C\u8BF7\u68C0\u67E5\u5DE5\u5177\u5B9E\u73B0\u662F\u5426\u7F3A\u5C11 return");
502
636
  }
503
- }, jsScript);
637
+ if (typeof res === "object" && res.success === false) {
638
+ const failed = res;
639
+ throw new Error(failed.error || failed.message || "\u5DE5\u5177\u6267\u884C\u5931\u8D25");
640
+ }
641
+ return res;
642
+ }, toolName, cleanedArgs);
504
643
  return result;
505
644
  } finally {
506
645
  await browser.disconnect();
@@ -525,114 +664,114 @@ function normalizeUrl(url) {
525
664
  return url;
526
665
  }
527
666
  async function tabsOpenCommand(url) {
528
- const browser2 = await connectBrowser();
667
+ const browser = await connectBrowser();
529
668
  try {
530
- const page2 = await browser2.newPage();
669
+ const page = await browser.newPage();
531
670
  url = normalizeUrl(url);
532
- await page2.goto(url, { waitUntil: "domcontentloaded" });
533
- await injectIntoPage(page2);
534
- await new Promise((resolve2) => setTimeout(resolve2, 500));
671
+ await page.goto(url, { waitUntil: "domcontentloaded" });
672
+ await injectIntoPage(page);
673
+ await new Promise((resolve3) => setTimeout(resolve3, 500));
535
674
  return {
536
675
  success: true,
537
- tabid: await getPageTargetId(page2),
538
- url: page2.url(),
539
- title: await page2.title()
676
+ tabid: await getPageTargetId(page),
677
+ url: page.url(),
678
+ title: await page.title()
540
679
  };
541
680
  } finally {
542
- await browser2.disconnect();
681
+ await browser.disconnect();
543
682
  }
544
683
  }
545
- async function tabsCloseCommand(tabid2) {
546
- const browser2 = await connectBrowser();
684
+ async function tabsCloseCommand(tabid) {
685
+ const browser = await connectBrowser();
547
686
  try {
548
- const target = findPageTargetByTabId(browser2, tabid2);
687
+ const target = findPageTargetByTabId(browser, tabid);
549
688
  if (!target) {
550
- throw new Error(`Tab with targetId "${tabid2}" not found.`);
689
+ throw new Error(`Tab with targetId "${tabid}" not found.`);
551
690
  }
552
- const page2 = await target.page();
553
- if (!page2) {
554
- throw new Error(`Tab with targetId "${tabid2}" not found.`);
691
+ const page = await target.page();
692
+ if (!page) {
693
+ throw new Error(`Tab with targetId "${tabid}" not found.`);
555
694
  }
556
- const closedTabid = await getPageTargetId(page2).catch(() => tabid2);
557
- await page2.close();
695
+ const closedTabid = await getPageTargetId(page).catch(() => tabid);
696
+ await page.close();
558
697
  return {
559
698
  success: true,
560
699
  tabid: closedTabid
561
700
  };
562
701
  } finally {
563
- await browser2.disconnect();
702
+ await browser.disconnect();
564
703
  }
565
704
  }
566
- async function tabsSwitchCommand(tabid2) {
567
- const browser2 = await connectBrowser();
705
+ async function tabsSwitchCommand(tabid) {
706
+ const browser = await connectBrowser();
568
707
  try {
569
- await activateTabById(browser2, tabid2);
570
- const page2 = await getTargetPage(browser2, tabid2);
708
+ await activateTabById(browser, tabid);
709
+ const page = await getTargetPage(browser, tabid);
571
710
  return {
572
711
  success: true,
573
- tabid: await getPageTargetId(page2),
574
- url: page2.url(),
575
- title: await page2.title()
712
+ tabid: await getPageTargetId(page),
713
+ url: page.url(),
714
+ title: await page.title()
576
715
  };
577
716
  } finally {
578
- await browser2.disconnect();
717
+ await browser.disconnect();
579
718
  }
580
719
  }
581
- async function tabsBackCommand(tabid2) {
582
- const browser2 = await connectBrowser();
720
+ async function tabsBackCommand(tabid) {
721
+ const browser = await connectBrowser();
583
722
  try {
584
- const page2 = await getTargetPage(browser2, tabid2);
585
- const response = await page2.goBack();
723
+ const page = await getTargetPage(browser, tabid);
724
+ const response = await page.goBack();
586
725
  if (!response) {
587
726
  return {
588
727
  success: false,
589
728
  error: "\u65E0\u6CD5\u540E\u9000\uFF1A\u5DF2\u5728\u5386\u53F2\u8BB0\u5F55\u8D77\u70B9",
590
- tabid: await getPageTargetId(page2).catch(() => tabid2),
591
- url: page2.url(),
592
- title: await page2.title()
729
+ tabid: await getPageTargetId(page).catch(() => tabid),
730
+ url: page.url(),
731
+ title: await page.title()
593
732
  };
594
733
  }
595
- await injectIntoPage(page2);
734
+ await injectIntoPage(page);
596
735
  return {
597
736
  success: true,
598
- tabid: await getPageTargetId(page2),
599
- url: page2.url(),
600
- title: await page2.title()
737
+ tabid: await getPageTargetId(page),
738
+ url: page.url(),
739
+ title: await page.title()
601
740
  };
602
741
  } finally {
603
- await browser2.disconnect();
742
+ await browser.disconnect();
604
743
  }
605
744
  }
606
- async function tabsForwardCommand(tabid2) {
607
- const browser2 = await connectBrowser();
745
+ async function tabsForwardCommand(tabid) {
746
+ const browser = await connectBrowser();
608
747
  try {
609
- const page2 = await getTargetPage(browser2, tabid2);
610
- const response = await page2.goForward();
748
+ const page = await getTargetPage(browser, tabid);
749
+ const response = await page.goForward();
611
750
  if (!response) {
612
751
  return {
613
752
  success: false,
614
753
  error: "\u65E0\u6CD5\u524D\u8FDB\uFF1A\u5DF2\u5728\u5386\u53F2\u8BB0\u5F55\u672B\u5C3E",
615
- tabid: await getPageTargetId(page2).catch(() => tabid2),
616
- url: page2.url(),
617
- title: await page2.title()
754
+ tabid: await getPageTargetId(page).catch(() => tabid),
755
+ url: page.url(),
756
+ title: await page.title()
618
757
  };
619
758
  }
620
- await injectIntoPage(page2);
759
+ await injectIntoPage(page);
621
760
  return {
622
761
  success: true,
623
- tabid: await getPageTargetId(page2),
624
- url: page2.url(),
625
- title: await page2.title()
762
+ tabid: await getPageTargetId(page),
763
+ url: page.url(),
764
+ title: await page.title()
626
765
  };
627
766
  } finally {
628
- await browser2.disconnect();
767
+ await browser.disconnect();
629
768
  }
630
769
  }
631
770
 
632
771
  // package.json
633
772
  var package_default = {
634
773
  name: "@opentiny/webmcp-cli",
635
- version: "0.0.1-alpha.5",
774
+ version: "0.0.2",
636
775
  type: "module",
637
776
  description: "WebMCP CLI for AI Agents to interact with browser via Puppeteer",
638
777
  main: "dist/index.js",
@@ -686,47 +825,118 @@ var package_default = {
686
825
 
687
826
  // src/bin.ts
688
827
  var program = new Command();
689
- function cleanJsonString(str) {
690
- let cleaned = str.trim();
691
- if (cleaned.startsWith("'") && cleaned.endsWith("'")) {
692
- cleaned = cleaned.slice(1, -1).trim();
693
- }
694
- return cleaned;
695
- }
696
828
  function parseTabId(id) {
697
829
  if (!id) return void 0;
698
830
  return id;
699
831
  }
700
- function handleCommandError(error, commandName) {
832
+ function cleanOldLogs(baseDir, logDir) {
833
+ try {
834
+ const oldLogFile = path2.join(baseDir, "webmcp-cli.log");
835
+ if (fs2.existsSync(oldLogFile)) {
836
+ try {
837
+ fs2.unlinkSync(oldLogFile);
838
+ } catch {
839
+ }
840
+ }
841
+ if (fs2.existsSync(baseDir)) {
842
+ const baseFiles = fs2.readdirSync(baseDir);
843
+ for (const file of baseFiles) {
844
+ if (/^webmcp-cli-\d{4}-\d{2}-\d{2}\.log$/.test(file)) {
845
+ try {
846
+ fs2.unlinkSync(path2.join(baseDir, file));
847
+ } catch {
848
+ }
849
+ }
850
+ }
851
+ }
852
+ if (!fs2.existsSync(logDir)) return;
853
+ const files = fs2.readdirSync(logDir);
854
+ const logFilePattern = /^webmcp-cli-(\d{4}-\d{2}-\d{2})\.log$/;
855
+ const now = Date.now();
856
+ const maxAgeMs = 7 * 24 * 60 * 60 * 1e3;
857
+ for (const file of files) {
858
+ const match = file.match(logFilePattern);
859
+ if (match) {
860
+ const fileDateStr = match[1];
861
+ const fileDate = new Date(fileDateStr).getTime();
862
+ if (isNaN(fileDate)) continue;
863
+ if (now - fileDate > maxAgeMs) {
864
+ try {
865
+ fs2.unlinkSync(path2.join(logDir, file));
866
+ } catch {
867
+ }
868
+ }
869
+ }
870
+ }
871
+ } catch (e) {
872
+ }
873
+ }
874
+ function writeLog(commandName, args, result, error) {
875
+ try {
876
+ const baseDir = process.env.WEBMCP_WORKSPACE || path2.join(os2.homedir(), ".webmcp_chrome_profile");
877
+ const logDir = path2.join(baseDir, "logs");
878
+ if (!fs2.existsSync(logDir)) {
879
+ fs2.mkdirSync(logDir, { recursive: true });
880
+ }
881
+ const todayStr = (/* @__PURE__ */ new Date()).toISOString().split("T")[0];
882
+ const logFile = path2.join(logDir, `webmcp-cli-${todayStr}.log`);
883
+ const timestamp = (/* @__PURE__ */ new Date()).toISOString();
884
+ let logText = `========================================
885
+ `;
886
+ logText += `[${timestamp}] COMMAND: ${commandName}
887
+ `;
888
+ logText += `ARGS:
889
+ ${JSON.stringify(args, null, 2)}
890
+
891
+ `;
892
+ if (error) {
893
+ logText += `ERROR:
894
+ ${error instanceof Error ? error.stack : String(error)}
895
+ `;
896
+ } else if (result) {
897
+ logText += `RESULT:
898
+ `;
899
+ try {
900
+ if (result && typeof result === "object") {
901
+ logText += JSON.stringify(result, null, 2).split("\n").map((line) => ` ${line}`).join("\n") + "\n";
902
+ } else {
903
+ logText += String(result).split("\n").map((line) => ` ${line}`).join("\n") + "\n";
904
+ }
905
+ } catch {
906
+ logText += JSON.stringify(result, null, 2).split("\n").map((line) => ` ${line}`).join("\n") + "\n";
907
+ }
908
+ } else {
909
+ logText += `RESULT: null
910
+ `;
911
+ }
912
+ logText += `========================================
913
+
914
+ `;
915
+ fs2.appendFileSync(logFile, logText, "utf-8");
916
+ cleanOldLogs(baseDir, logDir);
917
+ } catch (e) {
918
+ }
919
+ }
920
+ function handleCommandError(error, commandName, args) {
701
921
  const msg = error instanceof Error ? error.message : String(error);
922
+ writeLog(commandName, args, null, error);
702
923
  console.error(pc2.red(`Error executing ${commandName} command: ${msg}`));
703
924
  process.exit(1);
704
925
  }
705
- function expandFileRefs(argsJson) {
706
- return argsJson.replace(/"@(base64)?file:([^"]+)"/g, (_match, base64Flag, filePath) => {
707
- const absPath = resolve(process.cwd(), filePath);
708
- let content;
709
- try {
710
- content = readFileSync(absPath, "utf-8");
711
- } catch (e) {
712
- throw new Error(`\u65E0\u6CD5\u8BFB\u53D6\u6587\u4EF6\u5F15\u7528 "${filePath}": ${e.message}`);
713
- }
714
- const value = base64Flag ? Buffer.from(content, "utf-8").toString("base64") : content;
715
- return JSON.stringify(value);
716
- });
717
- }
718
926
  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) => {
719
927
  const opts = thisCommand.opts();
720
928
  if (opts.workspace) {
721
929
  process.env.WEBMCP_WORKSPACE = opts.workspace;
722
930
  }
723
931
  });
724
- program.command("state").description("\u83B7\u53D6\u6D4F\u89C8\u5668\u5F53\u524D\u9875\u7B7E\u6216\u6307\u5B9A\u9875\u7B7E\u7684\u72B6\u6001\uFF08\u5185\u5BB9\u3001\u6240\u6709\u9875\u7B7E\u5217\u8868\u3001\u53EF\u7528 WebMCP \u5DE5\u5177\u5217\u8868\uFF09").option("-t, --tabid <id>", "\u6307\u5B9A\u9875\u7B7E\u7684 ID").action(async (options) => {
932
+ program.command("state").description("\u83B7\u53D6\u6D4F\u89C8\u5668\u5F53\u524D\u9875\u7B7E\u6216\u6307\u5B9A\u9875\u7B7E\u7684\u5BFC\u822A\u5143\u6570\u636E\uFF08url\u3001title\u3001webmcpTools\u3001\u6240\u6709\u9875\u7B7E\u5217\u8868\uFF09").option("-t, --tabid <id>", "\u6307\u5B9A\u9875\u7B7E\u7684 ID").action(async (options) => {
933
+ const args = { tabid: parseTabId(options.tabid) };
725
934
  try {
726
- const result2 = await stateCommand({ tabid: parseTabId(options.tabid) });
727
- console.log(JSON.stringify(result2, null, 2));
935
+ const result = await stateCommand(args);
936
+ writeLog("state", args, result);
937
+ console.log(JSON.stringify(result, null, 2));
728
938
  } catch (error) {
729
- handleCommandError(error, "state");
939
+ handleCommandError(error, "state", args);
730
940
  }
731
941
  });
732
942
  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(
@@ -737,111 +947,92 @@ program.command("run <toolName> [args...]").description("\u5411\u6307\u5B9A\u987
737
947
  @base64file:<path> \u8BFB\u53D6\u6587\u4EF6\u5E76 Base64 \u7F16\u7801
738
948
  \u793A\u4F8B\uFF1Awebmcp-cli run mytool '{"content":"@base64file:./doc.md"}'`
739
949
  ).action(async (toolName, args, options) => {
950
+ let finalArgsJson = "";
951
+ const rawArgs = { toolName, args, file: options.file, tabid: options.tabid };
740
952
  try {
741
- let finalArgsJson = "";
953
+ let fileContent;
954
+ let fileBaseDir = process.cwd();
742
955
  if (options.file) {
743
- const filePath = resolve(process.cwd(), options.file);
956
+ const filePath = resolve2(process.cwd(), options.file);
957
+ fileBaseDir = getFileBaseDir(options.file);
744
958
  try {
745
- finalArgsJson = readFileSync(filePath, "utf-8").trim();
959
+ fileContent = readFileSync2(filePath, "utf-8");
746
960
  } catch (e) {
747
961
  throw new Error(`\u65E0\u6CD5\u8BFB\u53D6\u6587\u4EF6 "${filePath}": ${e.message}`);
748
962
  }
749
- } else if (args && args.length > 0) {
750
- const firstArg = args[0].trim();
751
- const cleanedFirstArg = cleanJsonString(firstArg);
752
- if (cleanedFirstArg.startsWith("{") && cleanedFirstArg.endsWith("}")) {
753
- finalArgsJson = args.map(cleanJsonString).join(" ");
754
- } else {
755
- const obj = {};
756
- for (const pair of args) {
757
- const index = pair.indexOf("=");
758
- if (index === -1) {
759
- 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`);
760
- }
761
- const key = pair.substring(0, index).trim();
762
- let val = pair.substring(index + 1).trim();
763
- val = cleanJsonString(val);
764
- try {
765
- obj[key] = JSON.parse(val);
766
- } catch {
767
- obj[key] = val;
768
- }
769
- }
770
- finalArgsJson = JSON.stringify(obj);
771
- }
772
963
  }
773
- if (!finalArgsJson) {
774
- throw new Error("\u5FC5\u987B\u63D0\u4F9B\u53C2\u6570\u6216\u901A\u8FC7 -f/--file \u6307\u5B9A\u53C2\u6570\u6587\u4EF6");
775
- }
776
- finalArgsJson = expandFileRefs(finalArgsJson);
777
- const result2 = await runCommand({
964
+ finalArgsJson = prepareRunArgsJson(args ?? [], fileContent, fileBaseDir);
965
+ const result = await runCommand({
778
966
  toolName,
779
967
  argsJson: finalArgsJson,
780
968
  tabid: parseTabId(options.tabid)
781
969
  });
782
- console.log(JSON.stringify(result2, null, 2));
783
- } catch (error) {
784
- handleCommandError(error, "run");
785
- }
786
- });
787
- program.command("evaluate <jsScript>").description("\u5411\u5F53\u524D\u7F51\u9875\u6216\u6307\u5B9A\u9875\u7B7E\u6CE8\u5165\u5E76\u6267\u884C JavaScript \u4EE3\u7801").option("-t, --tabid <id>", "\u6307\u5B9A\u9875\u7B7E\u7684 ID").action(async (jsScript2, options) => {
788
- try {
789
- const result2 = await evaluateCommand({
790
- jsScript: jsScript2,
791
- tabid: parseTabId(options.tabid)
792
- });
793
- console.log(JSON.stringify(result2, null, 2));
970
+ writeLog("run", { ...rawArgs, finalArgsJson }, result);
971
+ console.log(JSON.stringify(result, null, 2));
794
972
  } catch (error) {
795
- handleCommandError(error, "evaluate");
973
+ handleCommandError(error, "run", { ...rawArgs, finalArgsJson });
796
974
  }
797
975
  });
798
976
  program.command("clipboard <content>").description("\u5C06\u5185\u5BB9\u8BBE\u7F6E\u5230\u7CFB\u7EDF\u526A\u8D34\u677F").action(async (content) => {
977
+ const args = { content };
799
978
  try {
800
979
  await setClipboard(content);
801
- console.log(JSON.stringify({ success: true, message: "\u5185\u5BB9\u5DF2\u8BBE\u7F6E\u5230\u7CFB\u7EDF\u526A\u8D34\u677F" }, null, 2));
980
+ const result = { success: true, message: "\u5185\u5BB9\u5DF2\u8BBE\u7F6E\u5230\u7CFB\u7EDF\u526A\u8D34\u677F" };
981
+ writeLog("clipboard", args, result);
982
+ console.log(JSON.stringify(result, null, 2));
802
983
  } catch (error) {
803
- handleCommandError(error, "clipboard");
984
+ handleCommandError(error, "clipboard", args);
804
985
  }
805
986
  });
806
987
  var tabs = program.command("tabs").description("\u7BA1\u7406\u6D4F\u89C8\u5668\u6807\u7B7E\u9875");
807
988
  tabs.command("open <url>").description("\u6253\u5F00\u65B0\u7F51\u9875").action(async (url) => {
989
+ const args = { url };
808
990
  try {
809
- const result2 = await tabsOpenCommand(url);
810
- console.log(JSON.stringify(result2, null, 2));
991
+ const result = await tabsOpenCommand(url);
992
+ writeLog("tabs open", args, result);
993
+ console.log(JSON.stringify(result, null, 2));
811
994
  } catch (error) {
812
- handleCommandError(error, "tabs open");
995
+ handleCommandError(error, "tabs open", args);
813
996
  }
814
997
  });
815
- tabs.command("close <tabid>").description("\u5173\u95ED\u6307\u5B9A tabid \u7684\u6807\u7B7E\u9875").action(async (tabid2) => {
998
+ tabs.command("close <tabid>").description("\u5173\u95ED\u6307\u5B9A tabid \u7684\u6807\u7B7E\u9875").action(async (tabid) => {
999
+ const args = { tabid };
816
1000
  try {
817
- const result2 = await tabsCloseCommand(tabid2);
818
- console.log(JSON.stringify(result2, null, 2));
1001
+ const result = await tabsCloseCommand(tabid);
1002
+ writeLog("tabs close", args, result);
1003
+ console.log(JSON.stringify(result, null, 2));
819
1004
  } catch (error) {
820
- handleCommandError(error, "tabs close");
1005
+ handleCommandError(error, "tabs close", args);
821
1006
  }
822
1007
  });
823
- tabs.command("switch <tabid>").description("\u6FC0\u6D3B\u5E76\u5207\u6362\u5230\u6307\u5B9A tabid \u7684\u6807\u7B7E\u9875").action(async (tabid2) => {
1008
+ tabs.command("switch <tabid>").description("\u6FC0\u6D3B\u5E76\u5207\u6362\u5230\u6307\u5B9A tabid \u7684\u6807\u7B7E\u9875").action(async (tabid) => {
1009
+ const args = { tabid };
824
1010
  try {
825
- const result2 = await tabsSwitchCommand(tabid2);
826
- console.log(JSON.stringify(result2, null, 2));
1011
+ const result = await tabsSwitchCommand(tabid);
1012
+ writeLog("tabs switch", args, result);
1013
+ console.log(JSON.stringify(result, null, 2));
827
1014
  } catch (error) {
828
- handleCommandError(error, "tabs switch");
1015
+ handleCommandError(error, "tabs switch", args);
829
1016
  }
830
1017
  });
831
- tabs.command("back [tabid]").description("\u5C06\u5F53\u524D\u6216\u6307\u5B9A\u6807\u7B7E\u9875\u5BFC\u822A\u540E\u9000\u4E00\u6B65").action(async (tabid2) => {
1018
+ tabs.command("back [tabid]").description("\u5C06\u5F53\u524D\u6216\u6307\u5B9A\u6807\u7B7E\u9875\u5BFC\u822A\u540E\u9000\u4E00\u6B65").action(async (tabid) => {
1019
+ const args = { tabid: parseTabId(tabid) };
832
1020
  try {
833
- const result2 = await tabsBackCommand(parseTabId(tabid2));
834
- console.log(JSON.stringify(result2, null, 2));
1021
+ const result = await tabsBackCommand(args.tabid);
1022
+ writeLog("tabs back", args, result);
1023
+ console.log(JSON.stringify(result, null, 2));
835
1024
  } catch (error) {
836
- handleCommandError(error, "tabs back");
1025
+ handleCommandError(error, "tabs back", args);
837
1026
  }
838
1027
  });
839
- tabs.command("forward [tabid]").description("\u5C06\u5F53\u524D\u6216\u6307\u5B9A\u6807\u7B7E\u9875\u5BFC\u822A\u524D\u8FDB\u4E00\u6B65").action(async (tabid2) => {
1028
+ tabs.command("forward [tabid]").description("\u5C06\u5F53\u524D\u6216\u6307\u5B9A\u6807\u7B7E\u9875\u5BFC\u822A\u524D\u8FDB\u4E00\u6B65").action(async (tabid) => {
1029
+ const args = { tabid: parseTabId(tabid) };
840
1030
  try {
841
- const result2 = await tabsForwardCommand(parseTabId(tabid2));
842
- console.log(JSON.stringify(result2, null, 2));
1031
+ const result = await tabsForwardCommand(args.tabid);
1032
+ writeLog("tabs forward", args, result);
1033
+ console.log(JSON.stringify(result, null, 2));
843
1034
  } catch (error) {
844
- handleCommandError(error, "tabs forward");
1035
+ handleCommandError(error, "tabs forward", args);
845
1036
  }
846
1037
  });
847
1038
  program.parse(process.argv);