@opentiny/webmcp-cli 0.0.6 → 0.0.9
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 +17 -0
- package/dist/bin.cjs +3033 -542
- package/dist/bin.cjs.map +1 -1
- package/dist/bin.js +3042 -551
- package/dist/bin.js.map +1 -1
- package/dist/index.cjs +101 -197
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +4 -1
- package/dist/index.d.ts +4 -1
- package/dist/index.js +101 -197
- package/dist/index.js.map +1 -1
- package/dist/inject-bundle.js +3580 -2990
- package/dist/webmcp-tools/editor.csdn.net.js +199 -9
- package/dist/webmcp-tools/juejin.cn.js +23047 -8
- package/dist/webmcp-tools/my.oschina.net.js +175 -33
- package/dist/webmcp-tools/segmentfault.com.js +157 -1
- package/dist/webmcp-tools/zhuanlan.zhihu.com.js +2261 -6
- package/package.json +13 -5
- package/dist/bin.d.cts +0 -1
- package/dist/bin.d.ts +0 -1
package/dist/index.d.cts
CHANGED
|
@@ -11,7 +11,10 @@ declare function stateCommand({ tabid }: {
|
|
|
11
11
|
} | null)[];
|
|
12
12
|
url: string;
|
|
13
13
|
title: string;
|
|
14
|
-
webmcpTools:
|
|
14
|
+
webmcpTools: {
|
|
15
|
+
name: string;
|
|
16
|
+
description?: string;
|
|
17
|
+
}[];
|
|
15
18
|
}>;
|
|
16
19
|
|
|
17
20
|
declare function runCommand({ toolName, argsJson, tabid }: {
|
package/dist/index.d.ts
CHANGED
|
@@ -11,7 +11,10 @@ declare function stateCommand({ tabid }: {
|
|
|
11
11
|
} | null)[];
|
|
12
12
|
url: string;
|
|
13
13
|
title: string;
|
|
14
|
-
webmcpTools:
|
|
14
|
+
webmcpTools: {
|
|
15
|
+
name: string;
|
|
16
|
+
description?: string;
|
|
17
|
+
}[];
|
|
15
18
|
}>;
|
|
16
19
|
|
|
17
20
|
declare function runCommand({ toolName, argsJson, tabid }: {
|
package/dist/index.js
CHANGED
|
@@ -9,11 +9,37 @@ import { spawn } from "child_process";
|
|
|
9
9
|
import { fileURLToPath } from "url";
|
|
10
10
|
var __filename = fileURLToPath(import.meta.url);
|
|
11
11
|
var __dirname = path.dirname(__filename);
|
|
12
|
-
|
|
13
|
-
|
|
12
|
+
function getCdpPort() {
|
|
13
|
+
const raw = process.env.WEBMCP_CDP_PORT;
|
|
14
|
+
if (raw) {
|
|
15
|
+
const port = Number.parseInt(raw, 10);
|
|
16
|
+
if (Number.isFinite(port) && port > 0) return port;
|
|
17
|
+
}
|
|
18
|
+
return 9222;
|
|
19
|
+
}
|
|
14
20
|
function getWorkspaceDir() {
|
|
15
21
|
return process.env.WEBMCP_WORKSPACE || path.join(os.homedir(), ".webmcp_chrome_profile");
|
|
16
22
|
}
|
|
23
|
+
function shouldLaunchHeadless() {
|
|
24
|
+
if (process.env.WEBMCP_HEADLESS === "0") return false;
|
|
25
|
+
return process.env.WEBMCP_HEADLESS === "1" || process.env.CI === "true" || process.env.CI === "1";
|
|
26
|
+
}
|
|
27
|
+
function readActivePortFromProfile(userDataDir) {
|
|
28
|
+
try {
|
|
29
|
+
const content = fs.readFileSync(path.join(userDataDir, "DevToolsActivePort"), "utf-8");
|
|
30
|
+
const port = parseInt(content.split("\n")[0].trim(), 10);
|
|
31
|
+
return Number.isFinite(port) && port > 0 ? port : null;
|
|
32
|
+
} catch {
|
|
33
|
+
return null;
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
function candidateCdpBaseUrls(port) {
|
|
37
|
+
return [
|
|
38
|
+
`http://127.0.0.1:${port}`,
|
|
39
|
+
`http://[::1]:${port}`,
|
|
40
|
+
`http://localhost:${port}`
|
|
41
|
+
];
|
|
42
|
+
}
|
|
17
43
|
function getLastTabIdFilePath() {
|
|
18
44
|
return path.join(getWorkspaceDir(), ".last-tab-id");
|
|
19
45
|
}
|
|
@@ -97,6 +123,13 @@ async function connectPuppeteer(browserBaseUrl, attempt = 1, maxAttempts = 3) {
|
|
|
97
123
|
}
|
|
98
124
|
}
|
|
99
125
|
function getDefaultBrowserPath() {
|
|
126
|
+
const envPath = process.env.CHROME_PATH || process.env.WEBMCP_CHROME_PATH || process.env.PUPPETEER_EXECUTABLE_PATH;
|
|
127
|
+
if (envPath && fs.existsSync(envPath)) {
|
|
128
|
+
return { path: envPath, name: "Chrome" };
|
|
129
|
+
}
|
|
130
|
+
if (envPath) {
|
|
131
|
+
console.log(pc.yellow(`\u6307\u5B9A\u7684\u6D4F\u89C8\u5668\u8DEF\u5F84\u4E0D\u5B58\u5728\uFF0C\u5C06\u56DE\u9000\u5230\u81EA\u52A8\u68C0\u6D4B: ${envPath}`));
|
|
132
|
+
}
|
|
100
133
|
const platform = os.platform();
|
|
101
134
|
if (platform === "darwin") {
|
|
102
135
|
const chromePath = "/Applications/Google Chrome.app/Contents/MacOS/Google Chrome";
|
|
@@ -140,16 +173,30 @@ async function startBrowserInBackground() {
|
|
|
140
173
|
if (!browserInfo || !fs.existsSync(browserInfo.path)) {
|
|
141
174
|
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");
|
|
142
175
|
}
|
|
143
|
-
|
|
144
|
-
|
|
176
|
+
const cdpPort = getCdpPort();
|
|
177
|
+
console.log(pc.yellow(`\u6B63\u5728\u542F\u52A8\u540E\u53F0 ${browserInfo.name} \u5B9E\u4F8B (\u7AEF\u53E3: ${cdpPort})...`));
|
|
178
|
+
const userDataDir = getWorkspaceDir();
|
|
179
|
+
if (!fs.existsSync(userDataDir)) {
|
|
180
|
+
fs.mkdirSync(userDataDir, { recursive: true });
|
|
181
|
+
}
|
|
182
|
+
try {
|
|
183
|
+
fs.unlinkSync(path.join(userDataDir, "DevToolsActivePort"));
|
|
184
|
+
} catch {
|
|
185
|
+
}
|
|
145
186
|
const launchArgs = [
|
|
146
|
-
`--remote-debugging-port=${
|
|
187
|
+
`--remote-debugging-port=${cdpPort}`,
|
|
147
188
|
`--user-data-dir=${userDataDir}`,
|
|
148
189
|
"--no-first-run",
|
|
149
190
|
"--no-default-browser-check"
|
|
150
191
|
];
|
|
151
|
-
if (
|
|
152
|
-
launchArgs.push(
|
|
192
|
+
if (shouldLaunchHeadless()) {
|
|
193
|
+
launchArgs.push(
|
|
194
|
+
"--headless=new",
|
|
195
|
+
"--no-sandbox",
|
|
196
|
+
"--disable-setuid-sandbox",
|
|
197
|
+
"--disable-gpu",
|
|
198
|
+
"--disable-dev-shm-usage"
|
|
199
|
+
);
|
|
153
200
|
}
|
|
154
201
|
const child = spawn(
|
|
155
202
|
browserInfo.path,
|
|
@@ -160,15 +207,22 @@ async function startBrowserInBackground() {
|
|
|
160
207
|
}
|
|
161
208
|
);
|
|
162
209
|
child.unref();
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
210
|
+
for (let i = 0; i < 40; i++) {
|
|
211
|
+
const ports = /* @__PURE__ */ new Set([cdpPort]);
|
|
212
|
+
const activePort = readActivePortFromProfile(userDataDir);
|
|
213
|
+
if (activePort) ports.add(activePort);
|
|
214
|
+
for (const port of ports) {
|
|
215
|
+
for (const baseUrl of candidateCdpBaseUrls(port)) {
|
|
216
|
+
try {
|
|
217
|
+
await httpGet(`${baseUrl}/json/version`, 1e3);
|
|
218
|
+
if (port !== cdpPort) {
|
|
219
|
+
console.log(pc.yellow(`\u68C0\u6D4B\u5230\u7AEF\u53E3 ${cdpPort} \u5DF2\u88AB\u5176\u4ED6\u7A0B\u5E8F\u5360\u7528\uFF0C${browserInfo.name} \u5DF2\u81EA\u52A8\u5207\u6362\u5230\u7AEF\u53E3 ${port}\u3002`));
|
|
220
|
+
}
|
|
221
|
+
console.log(pc.green(`${browserInfo.name} \u542F\u52A8\u5E76\u5C31\u7EEA\uFF08${baseUrl}\uFF09\u3002`));
|
|
222
|
+
await new Promise((resolve) => setTimeout(resolve, 500));
|
|
223
|
+
return baseUrl;
|
|
224
|
+
} catch {
|
|
225
|
+
}
|
|
172
226
|
}
|
|
173
227
|
}
|
|
174
228
|
await new Promise((resolve) => setTimeout(resolve, 500));
|
|
@@ -176,13 +230,16 @@ async function startBrowserInBackground() {
|
|
|
176
230
|
throw new Error(`${browserInfo.name} \u542F\u52A8\u8D85\u65F6\uFF0C\u65E0\u6CD5\u8FDE\u63A5\u5230 CDP \u7AEF\u53E3\u3002`);
|
|
177
231
|
}
|
|
178
232
|
async function connectBrowser() {
|
|
233
|
+
const userDataDir = getWorkspaceDir();
|
|
234
|
+
const cdpPort = getCdpPort();
|
|
235
|
+
const recordedPort = readActivePortFromProfile(userDataDir);
|
|
179
236
|
const versionAddresses = [
|
|
180
|
-
|
|
181
|
-
|
|
237
|
+
...recordedPort && recordedPort !== cdpPort ? candidateCdpBaseUrls(recordedPort).map((u) => `${u}/json/version`) : [],
|
|
238
|
+
...candidateCdpBaseUrls(cdpPort).map((u) => `${u}/json/version`)
|
|
182
239
|
];
|
|
183
240
|
const existingUrl = await findAvailableCdpUrl(versionAddresses, 2);
|
|
184
241
|
if (existingUrl) {
|
|
185
|
-
console.log(pc.yellow(`connectBrowser: \u68C0\u6D4B\u5230\u7AEF\u53E3 ${
|
|
242
|
+
console.log(pc.yellow(`connectBrowser: \u68C0\u6D4B\u5230\u7AEF\u53E3 ${cdpPort} \u5DF2\u5C31\u7EEA\uFF0C\u6B63\u5728\u8FDE\u63A5 ${existingUrl}...`));
|
|
186
243
|
try {
|
|
187
244
|
const browser = await connectPuppeteer(existingUrl);
|
|
188
245
|
console.log(pc.green(`connectBrowser: \u6210\u529F\u8FDE\u63A5 ${existingUrl}`));
|
|
@@ -194,25 +251,22 @@ async function connectBrowser() {
|
|
|
194
251
|
);
|
|
195
252
|
}
|
|
196
253
|
}
|
|
197
|
-
console.log(pc.yellow(`connectBrowser: \u7AEF\u53E3 ${
|
|
254
|
+
console.log(pc.yellow(`connectBrowser: \u7AEF\u53E3 ${cdpPort} \u65E0\u54CD\u5E94\uFF0C\u6B63\u5728\u542F\u52A8\u65B0\u7684\u6D4F\u89C8\u5668\u5B9E\u4F8B...`));
|
|
255
|
+
let launchedUrl;
|
|
198
256
|
try {
|
|
199
|
-
await startBrowserInBackground();
|
|
257
|
+
launchedUrl = await startBrowserInBackground();
|
|
200
258
|
} catch (launchError) {
|
|
201
259
|
const msg = launchError instanceof Error ? launchError.message : String(launchError);
|
|
202
260
|
console.error(pc.red(`\u65E0\u6CD5\u542F\u52A8\u6D4F\u89C8\u5668: ${msg}`));
|
|
203
261
|
throw new Error("Browser launch failed.");
|
|
204
262
|
}
|
|
205
|
-
const launchedUrl = await findAvailableCdpUrl(versionAddresses, 5);
|
|
206
|
-
if (!launchedUrl) {
|
|
207
|
-
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`);
|
|
208
|
-
}
|
|
209
263
|
try {
|
|
210
264
|
console.log(pc.yellow(`connectBrowser: \u6D4F\u89C8\u5668\u5DF2\u542F\u52A8\uFF0C\u6B63\u5728\u8FDE\u63A5 ${launchedUrl}...`));
|
|
211
265
|
const browser = await connectPuppeteer(launchedUrl);
|
|
212
266
|
console.log(pc.green(`connectBrowser: \u6210\u529F\u8FDE\u63A5 ${launchedUrl}`));
|
|
213
267
|
return browser;
|
|
214
268
|
} catch {
|
|
215
|
-
throw new Error(`\u65E0\u6CD5\u8FDE\u63A5\u5230\u6D4F\u89C8\u5668\uFF08
|
|
269
|
+
throw new Error(`\u65E0\u6CD5\u8FDE\u63A5\u5230\u6D4F\u89C8\u5668\uFF08${launchedUrl}\uFF09\uFF0C\u8BF7\u68C0\u67E5 Chrome/Edge \u662F\u5426\u5DF2\u5B89\u88C5\u3002`);
|
|
216
270
|
}
|
|
217
271
|
}
|
|
218
272
|
async function getPageTargetId(page) {
|
|
@@ -270,10 +324,11 @@ async function getTargetPage(browser, tabid) {
|
|
|
270
324
|
}
|
|
271
325
|
if (!targetPage) {
|
|
272
326
|
try {
|
|
273
|
-
const
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
];
|
|
327
|
+
const workspaceDir = getWorkspaceDir();
|
|
328
|
+
const cdpPort = getCdpPort();
|
|
329
|
+
const recordedPort = readActivePortFromProfile(workspaceDir);
|
|
330
|
+
const ports = recordedPort && recordedPort !== cdpPort ? [recordedPort, cdpPort] : [cdpPort];
|
|
331
|
+
const urls = ports.flatMap((port) => candidateCdpBaseUrls(port).map((u) => `${u}/json/list`));
|
|
277
332
|
let activeTargetId = null;
|
|
278
333
|
for (const url of urls) {
|
|
279
334
|
try {
|
|
@@ -310,10 +365,10 @@ async function getTargetPage(browser, tabid) {
|
|
|
310
365
|
return targetPage;
|
|
311
366
|
}
|
|
312
367
|
async function injectIntoPage(page) {
|
|
313
|
-
await injectWebMCPPolyfillAndTools(page
|
|
368
|
+
await injectWebMCPPolyfillAndTools(page);
|
|
314
369
|
}
|
|
315
|
-
async function injectWebMCPPolyfillAndTools(page
|
|
316
|
-
const polyfillReady =
|
|
370
|
+
async function injectWebMCPPolyfillAndTools(page) {
|
|
371
|
+
const polyfillReady = await page.evaluate(() => {
|
|
317
372
|
return !!window.__webmcpcli_init;
|
|
318
373
|
}).catch(() => false);
|
|
319
374
|
if (!polyfillReady) {
|
|
@@ -403,11 +458,19 @@ async function stateCommand({ tabid }) {
|
|
|
403
458
|
const state = await page.evaluate(async () => {
|
|
404
459
|
const url = document.URL;
|
|
405
460
|
const title = document.title;
|
|
406
|
-
const mcp = document.modelContext;
|
|
461
|
+
const mcp = document.modelContext || navigator.modelContext;
|
|
407
462
|
let webmcpTools = [];
|
|
408
|
-
if (mcp && typeof mcp.
|
|
409
|
-
|
|
410
|
-
|
|
463
|
+
if (mcp && typeof mcp.getTools === "function") {
|
|
464
|
+
try {
|
|
465
|
+
const toolsResult = await mcp.getTools();
|
|
466
|
+
const list = toolsResult?.tools || toolsResult || [];
|
|
467
|
+
webmcpTools = list.filter((t) => t && typeof t.name === "string").map((t) => ({
|
|
468
|
+
name: t.name,
|
|
469
|
+
description: typeof t.description === "string" ? t.description.slice(0, 200) : void 0
|
|
470
|
+
}));
|
|
471
|
+
} catch {
|
|
472
|
+
webmcpTools = [];
|
|
473
|
+
}
|
|
411
474
|
}
|
|
412
475
|
return { url, title, webmcpTools };
|
|
413
476
|
});
|
|
@@ -439,155 +502,6 @@ async function stateCommand({ tabid }) {
|
|
|
439
502
|
}
|
|
440
503
|
}
|
|
441
504
|
|
|
442
|
-
// src/zhihu/create-article.ts
|
|
443
|
-
import os2 from "os";
|
|
444
|
-
|
|
445
|
-
// src/zhihu/markdown.ts
|
|
446
|
-
import { Marked } from "marked";
|
|
447
|
-
function stripLeadingH1(markdown) {
|
|
448
|
-
return markdown.replace(/^#\s+[^\n]*\n+/, "");
|
|
449
|
-
}
|
|
450
|
-
function markdownToHtml(markdown) {
|
|
451
|
-
const marked = new Marked({
|
|
452
|
-
gfm: true,
|
|
453
|
-
breaks: false,
|
|
454
|
-
renderer: {
|
|
455
|
-
code({ text, lang }) {
|
|
456
|
-
const langClass = lang ? ` class="language-${lang}"` : "";
|
|
457
|
-
const escaped = text.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">");
|
|
458
|
-
return `<pre><code${langClass}>${escaped}</code></pre>`;
|
|
459
|
-
},
|
|
460
|
-
codespan({ text }) {
|
|
461
|
-
const escaped = text.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">");
|
|
462
|
-
return `<code>${escaped}</code>`;
|
|
463
|
-
}
|
|
464
|
-
}
|
|
465
|
-
});
|
|
466
|
-
return marked.parse(markdown);
|
|
467
|
-
}
|
|
468
|
-
function decodeBase64Content(content) {
|
|
469
|
-
return Buffer.from(content, "base64").toString("utf-8");
|
|
470
|
-
}
|
|
471
|
-
var ZHIHU_WRITE_PAGE_RE = /^https:\/\/zhuanlan\.zhihu\.com\/(write|p\/\d+\/edit)/;
|
|
472
|
-
function isZhihuWriteUrl(url) {
|
|
473
|
-
return ZHIHU_WRITE_PAGE_RE.test(url);
|
|
474
|
-
}
|
|
475
|
-
|
|
476
|
-
// src/zhihu/create-article.ts
|
|
477
|
-
function delay(ms) {
|
|
478
|
-
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
479
|
-
}
|
|
480
|
-
function getPasteModifier() {
|
|
481
|
-
return os2.platform() === "darwin" ? "Meta" : "Control";
|
|
482
|
-
}
|
|
483
|
-
async function fillTitle(page, title) {
|
|
484
|
-
await page.evaluate((titleText) => {
|
|
485
|
-
const input = document.querySelector('textarea[placeholder*="\u6807\u9898"]') || document.querySelector(".WriteIndex-titleInput textarea") || document.querySelector(".WriteIndex-titleInput input");
|
|
486
|
-
if (!input) throw new Error("\u672A\u627E\u5230\u6807\u9898\u8F93\u5165\u6846\uFF0C\u8BF7\u786E\u8BA4\u7F16\u8F91\u5668\u9875\u9762\u5DF2\u5B8C\u5168\u52A0\u8F7D");
|
|
487
|
-
const setter = Object.getOwnPropertyDescriptor(
|
|
488
|
-
input instanceof HTMLTextAreaElement ? HTMLTextAreaElement.prototype : HTMLInputElement.prototype,
|
|
489
|
-
"value"
|
|
490
|
-
)?.set;
|
|
491
|
-
if (setter) {
|
|
492
|
-
setter.call(input, titleText);
|
|
493
|
-
} else {
|
|
494
|
-
input.value = titleText;
|
|
495
|
-
}
|
|
496
|
-
input.dispatchEvent(new InputEvent("input", { bubbles: true, cancelable: true, data: titleText }));
|
|
497
|
-
input.dispatchEvent(new Event("change", { bubbles: true }));
|
|
498
|
-
if (input.value.trim() !== titleText.trim()) {
|
|
499
|
-
throw new Error("\u6807\u9898\u586B\u5199\u5931\u8D25\uFF0C\u8BF7\u5237\u65B0\u9875\u9762\u540E\u91CD\u8BD5");
|
|
500
|
-
}
|
|
501
|
-
}, title);
|
|
502
|
-
}
|
|
503
|
-
async function pasteHtmlToEditor(page, html) {
|
|
504
|
-
const browser = page.browser();
|
|
505
|
-
const tempPage = await browser.newPage();
|
|
506
|
-
const modifier = getPasteModifier();
|
|
507
|
-
try {
|
|
508
|
-
const fullHtml = `<!DOCTYPE html><html><head><meta charset="utf-8"></head><body>${html}</body></html>`;
|
|
509
|
-
await tempPage.setContent(fullHtml, { waitUntil: "domcontentloaded" });
|
|
510
|
-
await delay(300);
|
|
511
|
-
await tempPage.click("body");
|
|
512
|
-
await tempPage.keyboard.down(modifier);
|
|
513
|
-
await tempPage.keyboard.press("KeyA");
|
|
514
|
-
await tempPage.keyboard.up(modifier);
|
|
515
|
-
await delay(100);
|
|
516
|
-
await tempPage.keyboard.down(modifier);
|
|
517
|
-
await tempPage.keyboard.press("KeyC");
|
|
518
|
-
await tempPage.keyboard.up(modifier);
|
|
519
|
-
await delay(300);
|
|
520
|
-
} finally {
|
|
521
|
-
await tempPage.close();
|
|
522
|
-
}
|
|
523
|
-
await page.bringToFront();
|
|
524
|
-
await page.waitForSelector(".public-DraftEditor-content", { timeout: 1e4 });
|
|
525
|
-
await page.click(".public-DraftEditor-content");
|
|
526
|
-
await delay(200);
|
|
527
|
-
await page.keyboard.down(modifier);
|
|
528
|
-
await page.keyboard.press("KeyA");
|
|
529
|
-
await page.keyboard.up(modifier);
|
|
530
|
-
await delay(100);
|
|
531
|
-
await page.keyboard.press("Backspace");
|
|
532
|
-
await delay(200);
|
|
533
|
-
await page.keyboard.down(modifier);
|
|
534
|
-
await page.keyboard.press("KeyV");
|
|
535
|
-
await page.keyboard.up(modifier);
|
|
536
|
-
await delay(1500);
|
|
537
|
-
const probe = html.replace(/<[^>]+>/g, "").trim().slice(0, 15);
|
|
538
|
-
const verifyResult = await page.evaluate((probeText) => {
|
|
539
|
-
const editor = document.querySelector(".public-DraftEditor-content");
|
|
540
|
-
if (!editor) return { ok: false, reason: "no editor" };
|
|
541
|
-
const text = (editor.innerText || "").trim();
|
|
542
|
-
if (!text) return { ok: false, reason: "empty" };
|
|
543
|
-
const inner = editor.innerHTML;
|
|
544
|
-
const hasRichFormat = inner.includes("data-text") || /font-weight:\s*bold|font-style:\s*italic|code-block|blockquote/i.test(inner);
|
|
545
|
-
const normalizedText = text.replace(/\s+/g, "");
|
|
546
|
-
const normalizedProbe = probeText.replace(/\s+/g, "");
|
|
547
|
-
const hasProbe = normalizedText.includes(normalizedProbe);
|
|
548
|
-
return {
|
|
549
|
-
ok: text.length > 20 && hasProbe && (hasRichFormat || text.length > 50),
|
|
550
|
-
reason: !hasProbe ? "probe mismatch" : !hasRichFormat && text.length <= 50 ? "no rich format" : void 0
|
|
551
|
-
};
|
|
552
|
-
}, probe);
|
|
553
|
-
if (!verifyResult.ok) {
|
|
554
|
-
throw new Error(
|
|
555
|
-
`\u6B63\u6587\u586B\u5199\u5931\u8D25\uFF08Markdown \u8F6C HTML \u7C98\u8D34\u672A\u751F\u6548\uFF09${verifyResult.reason ? `: ${verifyResult.reason}` : ""}`
|
|
556
|
-
);
|
|
557
|
-
}
|
|
558
|
-
}
|
|
559
|
-
async function zhihuCreateArticle(page, args) {
|
|
560
|
-
const { title, content } = args;
|
|
561
|
-
if (!title?.trim()) throw new Error("\u53C2\u6570 title \u4E0D\u80FD\u4E3A\u7A7A");
|
|
562
|
-
if (!content?.trim()) throw new Error("\u53C2\u6570 content \u4E0D\u80FD\u4E3A\u7A7A");
|
|
563
|
-
const url = page.url();
|
|
564
|
-
if (!isZhihuWriteUrl(url)) {
|
|
565
|
-
throw new Error("\u5F53\u524D\u9875\u9762\u4E0D\u662F\u77E5\u4E4E\u4E13\u680F\u7F16\u8F91\u5668\uFF0C\u8BF7\u5148\u6253\u5F00 https://zhuanlan.zhihu.com/write");
|
|
566
|
-
}
|
|
567
|
-
let markdown;
|
|
568
|
-
try {
|
|
569
|
-
markdown = stripLeadingH1(decodeBase64Content(content));
|
|
570
|
-
} catch {
|
|
571
|
-
throw new Error("content \u4E0D\u662F\u6709\u6548\u7684 Base64 \u7F16\u7801\uFF0C\u8BF7\u68C0\u67E5\u53C2\u6570\u6216\u4F7F\u7528 @base64file: \u5F15\u7528\u6587\u4EF6");
|
|
572
|
-
}
|
|
573
|
-
if (!markdown.trim()) {
|
|
574
|
-
throw new Error("\u6B63\u6587\u5185\u5BB9\u4E3A\u7A7A\uFF0C\u8BF7\u68C0\u67E5 Markdown \u6587\u4EF6");
|
|
575
|
-
}
|
|
576
|
-
const html = markdownToHtml(markdown);
|
|
577
|
-
await fillTitle(page, title.trim());
|
|
578
|
-
await delay(500);
|
|
579
|
-
await pasteHtmlToEditor(page, html);
|
|
580
|
-
const result = {
|
|
581
|
-
success: true,
|
|
582
|
-
message: "\u6587\u7AE0\u6807\u9898\u548C\u6B63\u6587\u5DF2\u6210\u529F\u586B\u5199\u5230\u77E5\u4E4E\u4E13\u680F\u7F16\u8F91\u5668\uFF08Markdown \u5DF2\u8F6C\u4E3A\u5BCC\u6587\u672C\uFF09\uFF0C\u8349\u7A3F\u5C06\u81EA\u52A8\u4FDD\u5B58",
|
|
583
|
-
title: title.trim(),
|
|
584
|
-
contentLength: markdown.length
|
|
585
|
-
};
|
|
586
|
-
return {
|
|
587
|
-
content: [{ type: "text", text: JSON.stringify(result) }]
|
|
588
|
-
};
|
|
589
|
-
}
|
|
590
|
-
|
|
591
505
|
// src/commands/run.ts
|
|
592
506
|
async function runCommand({
|
|
593
507
|
toolName,
|
|
@@ -598,18 +512,8 @@ async function runCommand({
|
|
|
598
512
|
try {
|
|
599
513
|
const page = await getTargetPage(browser, tabid);
|
|
600
514
|
const urlBefore = page.url();
|
|
515
|
+
await injectIntoPage(page);
|
|
601
516
|
const cleanedArgs = argsJson.trim();
|
|
602
|
-
if (toolName === "create_article") {
|
|
603
|
-
let parsedArgs;
|
|
604
|
-
try {
|
|
605
|
-
parsedArgs = JSON.parse(cleanedArgs);
|
|
606
|
-
} catch {
|
|
607
|
-
throw new Error("\u53C2\u6570\u4E0D\u662F\u6709\u6548\u7684 JSON");
|
|
608
|
-
}
|
|
609
|
-
if (isZhihuWriteUrl(page.url())) {
|
|
610
|
-
return await zhihuCreateArticle(page, parsedArgs);
|
|
611
|
-
}
|
|
612
|
-
}
|
|
613
517
|
let result;
|
|
614
518
|
try {
|
|
615
519
|
result = await page.evaluate(async (name, inputString) => {
|