writedock-mcp 0.4.4 → 0.4.7
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 +21 -20
- package/extension/LICENSE-MULTIPOST.txt +201 -0
- package/extension/LICENSE.txt +674 -0
- package/extension/NOTICE.txt +13 -0
- package/extension/assets/editor-ClIT9WYa.js +76 -0
- package/extension/assets/editor-VBFqqvXW.css +1 -0
- package/extension/assets/icon-128.png +0 -0
- package/extension/assets/icon-16.png +0 -0
- package/extension/assets/icon-48.png +0 -0
- package/extension/assets/index.browser-Hs3ZYqOv.js +157 -0
- package/extension/assets/index.ts-BEVXnN62.js +42 -0
- package/extension/assets/logo.svg +5 -0
- package/extension/assets/offscreen-3BECPbQX.js +63 -0
- package/extension/assets/purify.es-1yEQDPgc.js +3 -0
- package/extension/assets/react-vendor-BrXlnJmF.js +40 -0
- package/extension/assets/task-utils-DwzGcJkm.js +1 -0
- package/extension/manifest.json +40 -0
- package/extension/service-worker-loader.js +1 -0
- package/extension/src/editor/index.html +16 -0
- package/extension/src/offscreen/index.html +6 -0
- package/extension/zhihu-publication-observer.js +1 -0
- package/extension//345/256/211/350/243/205/344/270/216/346/233/264/346/226/260.txt +19 -0
- package/index.cjs +290 -131
- package/package.json +3 -2
- package/source/package.json +1 -1
- package/source/src/__tests__/bridge.test.ts +22 -0
- package/source/src/__tests__/extension-update.test.ts +54 -0
- package/source/src/__tests__/setup.test.ts +50 -0
- package/source/src/bridge.ts +43 -61
- package/source/src/extension-id.ts +2 -0
- package/source/src/extension-update.ts +82 -0
- package/source/src/identity.ts +2 -2
- package/source/src/index.ts +3 -0
- package/source/src/release-info.ts +3 -0
- package/source/src/server.ts +2 -6
- package/source/src/setup.ts +88 -0
package/index.cjs
CHANGED
|
@@ -6820,14 +6820,14 @@ var require_utils = __commonJS({
|
|
|
6820
6820
|
BYTE_HEX[i] = "%" + HEX_DIGITS[i >> 4] + HEX_DIGITS[i & 15];
|
|
6821
6821
|
}
|
|
6822
6822
|
}
|
|
6823
|
-
function percentEncodeNonAscii(
|
|
6824
|
-
if (
|
|
6825
|
-
return BYTE_HEX[192 |
|
|
6823
|
+
function percentEncodeNonAscii(cp2) {
|
|
6824
|
+
if (cp2 < 2048) {
|
|
6825
|
+
return BYTE_HEX[192 | cp2 >> 6] + BYTE_HEX[128 | cp2 & 63];
|
|
6826
6826
|
}
|
|
6827
|
-
if (
|
|
6828
|
-
return BYTE_HEX[224 |
|
|
6827
|
+
if (cp2 < 65536) {
|
|
6828
|
+
return BYTE_HEX[224 | cp2 >> 12] + BYTE_HEX[128 | cp2 >> 6 & 63] + BYTE_HEX[128 | cp2 & 63];
|
|
6829
6829
|
}
|
|
6830
|
-
return BYTE_HEX[240 |
|
|
6830
|
+
return BYTE_HEX[240 | cp2 >> 18] + BYTE_HEX[128 | cp2 >> 12 & 63] + BYTE_HEX[128 | cp2 >> 6 & 63] + BYTE_HEX[128 | cp2 & 63];
|
|
6831
6831
|
}
|
|
6832
6832
|
function stringArrayToHexStripped(input) {
|
|
6833
6833
|
let acc = "";
|
|
@@ -7222,8 +7222,8 @@ var require_utils = __commonJS({
|
|
|
7222
7222
|
function encodeFragment(input) {
|
|
7223
7223
|
return encodeComponent(input, isQueryFragmentCharacter);
|
|
7224
7224
|
}
|
|
7225
|
-
function isEscapeSafe(
|
|
7226
|
-
return
|
|
7225
|
+
function isEscapeSafe(cp2) {
|
|
7226
|
+
return cp2 >= 48 && cp2 <= 57 || cp2 >= 65 && cp2 <= 90 || cp2 >= 97 && cp2 <= 122 || cp2 === 42 || cp2 === 43 || cp2 === 45 || cp2 === 46 || cp2 === 47 || cp2 === 64 || cp2 === 95;
|
|
7227
7227
|
}
|
|
7228
7228
|
function normalizeQueryFragmentEncoding(input) {
|
|
7229
7229
|
let output = "";
|
|
@@ -29739,7 +29739,7 @@ var MediaStore = class {
|
|
|
29739
29739
|
// package.json
|
|
29740
29740
|
var package_default = {
|
|
29741
29741
|
name: "@writedock/mcp-server",
|
|
29742
|
-
version: "0.4.
|
|
29742
|
+
version: "0.4.7",
|
|
29743
29743
|
private: true,
|
|
29744
29744
|
type: "module",
|
|
29745
29745
|
description: "WriteDock local MCP server for the browser extension",
|
|
@@ -29772,6 +29772,91 @@ var distribution_default = {
|
|
|
29772
29772
|
registry: "https://registry.npmjs.org/"
|
|
29773
29773
|
};
|
|
29774
29774
|
|
|
29775
|
+
// src/extension-update.ts
|
|
29776
|
+
var import_node_fs = require("fs");
|
|
29777
|
+
var import_promises2 = require("fs/promises");
|
|
29778
|
+
var import_node_os = require("os");
|
|
29779
|
+
var import_node_path2 = require("path");
|
|
29780
|
+
|
|
29781
|
+
// src/extension-id.ts
|
|
29782
|
+
var EXTENSION_ORIGIN = "chrome-extension://himncajjeipncfpoajkcmfieonfpjfhb";
|
|
29783
|
+
|
|
29784
|
+
// src/extension-update.ts
|
|
29785
|
+
var EXTENSION_ID = EXTENSION_ORIGIN.replace("chrome-extension://", "");
|
|
29786
|
+
var readManifest = async (directory) => JSON.parse(await (0, import_promises2.readFile)((0, import_node_path2.join)(directory, "manifest.json"), "utf8"));
|
|
29787
|
+
var isWriteDock = (manifest) => manifest.name === "\u5199\u4F5C\u53F0 WriteDock";
|
|
29788
|
+
function bundledExtension(base = __dirname) {
|
|
29789
|
+
try {
|
|
29790
|
+
const directory = (0, import_node_path2.join)(base, "extension");
|
|
29791
|
+
const manifest = JSON.parse((0, import_node_fs.readFileSync)((0, import_node_path2.join)(directory, "manifest.json"), "utf8"));
|
|
29792
|
+
return typeof manifest.version === "string" ? { directory, version: manifest.version } : void 0;
|
|
29793
|
+
} catch {
|
|
29794
|
+
return void 0;
|
|
29795
|
+
}
|
|
29796
|
+
}
|
|
29797
|
+
var defaultExtensionDirectory = (home = (0, import_node_os.homedir)()) => (0, import_node_path2.join)(home, "WriteDock", "extension");
|
|
29798
|
+
function browserRoots(user = (0, import_node_os.homedir)()) {
|
|
29799
|
+
const names = process.platform === "win32" ? ["Google/Chrome/User Data", "Microsoft/Edge/User Data", "Chromium/User Data", "BraveSoftware/Brave-Browser/User Data"].map((name) => (0, import_node_path2.join)(process.env.LOCALAPPDATA || (0, import_node_path2.join)(user, "AppData/Local"), name)) : process.platform === "darwin" ? ["Google/Chrome", "Microsoft Edge", "Chromium", "BraveSoftware/Brave-Browser"].map((name) => (0, import_node_path2.join)(user, "Library/Application Support", name)) : ["google-chrome", "microsoft-edge", "chromium", "BraveSoftware/Brave-Browser"].map((name) => (0, import_node_path2.join)(process.env.XDG_CONFIG_HOME || (0, import_node_path2.join)(user, ".config"), name));
|
|
29800
|
+
return names;
|
|
29801
|
+
}
|
|
29802
|
+
async function findInstalledExtension(roots = browserRoots(), id = EXTENSION_ID) {
|
|
29803
|
+
for (const root of roots) {
|
|
29804
|
+
let profiles;
|
|
29805
|
+
try {
|
|
29806
|
+
profiles = await (0, import_promises2.readdir)(root, { withFileTypes: true });
|
|
29807
|
+
} catch {
|
|
29808
|
+
continue;
|
|
29809
|
+
}
|
|
29810
|
+
for (const profile of profiles.filter((item) => item.isDirectory() && /^(Default|Profile \d+)$/.test(item.name))) {
|
|
29811
|
+
for (const filename of ["Secure Preferences", "Preferences"]) {
|
|
29812
|
+
try {
|
|
29813
|
+
const preferences = JSON.parse(await (0, import_promises2.readFile)((0, import_node_path2.join)(root, profile.name, filename), "utf8"));
|
|
29814
|
+
const path = preferences.extensions?.settings?.[id]?.path;
|
|
29815
|
+
if (typeof path === "string" && (0, import_node_path2.isAbsolute)(path) && isWriteDock(await readManifest(path))) return (0, import_promises2.realpath)(path);
|
|
29816
|
+
} catch {
|
|
29817
|
+
}
|
|
29818
|
+
}
|
|
29819
|
+
}
|
|
29820
|
+
}
|
|
29821
|
+
return void 0;
|
|
29822
|
+
}
|
|
29823
|
+
async function installExtensionFiles(source2, target) {
|
|
29824
|
+
const next = await readManifest(source2);
|
|
29825
|
+
if (!isWriteDock(next)) throw new Error("\u5185\u7F6E\u6269\u5C55\u6587\u4EF6\u4E0D\u6B63\u786E");
|
|
29826
|
+
let previous;
|
|
29827
|
+
try {
|
|
29828
|
+
previous = await readManifest(target);
|
|
29829
|
+
} catch {
|
|
29830
|
+
}
|
|
29831
|
+
if (previous) {
|
|
29832
|
+
if (!isWriteDock(previous)) throw new Error("\u76EE\u6807\u76EE\u5F55\u4E0D\u662F WriteDock \u6269\u5C55\uFF0C\u672A\u8986\u76D6");
|
|
29833
|
+
if (previous.key !== next.key) throw new Error("\u65E7\u7248\u6269\u5C55\u7684\u8EAB\u4EFD\u4E0D\u540C\uFF0C\u65E0\u6CD5\u539F\u4F4D\u66F4\u65B0\uFF0C\u8BF7\u91CD\u65B0\u52A0\u8F7D\u4E00\u6B21\u65B0\u7248");
|
|
29834
|
+
}
|
|
29835
|
+
await (0, import_promises2.mkdir)(target, { recursive: true });
|
|
29836
|
+
const backup = previous ? await (0, import_promises2.mkdtemp)((0, import_node_path2.join)((0, import_node_os.tmpdir)(), "writedock-extension-backup-")) : void 0;
|
|
29837
|
+
if (backup) await (0, import_promises2.cp)(target, backup, { recursive: true, force: true });
|
|
29838
|
+
try {
|
|
29839
|
+
await (0, import_promises2.cp)(source2, target, { recursive: true, force: true, filter: (path) => path !== (0, import_node_path2.join)(source2, "manifest.json") });
|
|
29840
|
+
const staged = (0, import_node_path2.join)(target, ".writedock-manifest-update.tmp");
|
|
29841
|
+
await (0, import_promises2.writeFile)(staged, await (0, import_promises2.readFile)((0, import_node_path2.join)(source2, "manifest.json")));
|
|
29842
|
+
await (0, import_promises2.rename)(staged, (0, import_node_path2.join)(target, "manifest.json"));
|
|
29843
|
+
} catch (error2) {
|
|
29844
|
+
if (backup) await (0, import_promises2.cp)(backup, target, { recursive: true, force: true });
|
|
29845
|
+
throw error2;
|
|
29846
|
+
} finally {
|
|
29847
|
+
if (backup) await (0, import_promises2.rm)(backup, { recursive: true, force: true }).catch(() => {
|
|
29848
|
+
});
|
|
29849
|
+
}
|
|
29850
|
+
return { directory: target, version: next.version };
|
|
29851
|
+
}
|
|
29852
|
+
async function updateInstalledExtension() {
|
|
29853
|
+
const bundle = bundledExtension();
|
|
29854
|
+
if (!bundle) throw new Error("\u5F53\u524D\u670D\u52A1\u6CA1\u6709\u5185\u7F6E\u6269\u5C55\uFF0C\u8BF7\u91CD\u542F agent \u540E\u518D\u8BD5");
|
|
29855
|
+
const target = await findInstalledExtension();
|
|
29856
|
+
if (!target) throw new Error("\u6CA1\u6709\u627E\u5230\u5DF2\u52A0\u8F7D\u7684\u6269\u5C55\u76EE\u5F55\uFF0C\u8BF7\u624B\u52A8\u66F4\u65B0");
|
|
29857
|
+
return installExtensionFiles(bundle.directory, target);
|
|
29858
|
+
}
|
|
29859
|
+
|
|
29775
29860
|
// src/release-info.ts
|
|
29776
29861
|
var CHECK_INTERVAL = 6 * 60 * 6e4;
|
|
29777
29862
|
var RETRY_INTERVAL = 10 * 6e4;
|
|
@@ -29827,8 +29912,10 @@ function refresh() {
|
|
|
29827
29912
|
}
|
|
29828
29913
|
function mcpReleaseInfo() {
|
|
29829
29914
|
refresh();
|
|
29915
|
+
const bundle = bundledExtension();
|
|
29830
29916
|
return {
|
|
29831
29917
|
mcp_version: package_default.version,
|
|
29918
|
+
...bundle ? { extension_bundle_version: bundle.version } : {},
|
|
29832
29919
|
...latestVersion ? { latest_version: latestVersion } : {},
|
|
29833
29920
|
update_available: !!latestVersion && newerVersion(latestVersion, package_default.version),
|
|
29834
29921
|
...checkedAt ? { update_checked_at: checkedAt } : {},
|
|
@@ -29866,7 +29953,6 @@ var LocalBridge = class {
|
|
|
29866
29953
|
primary = false;
|
|
29867
29954
|
extensionId;
|
|
29868
29955
|
seenNonces = /* @__PURE__ */ new Map();
|
|
29869
|
-
pairing;
|
|
29870
29956
|
extensionToken;
|
|
29871
29957
|
media;
|
|
29872
29958
|
pending = /* @__PURE__ */ new Map();
|
|
@@ -29895,13 +29981,13 @@ var LocalBridge = class {
|
|
|
29895
29981
|
server.on("upgrade", (req, socket, head) => {
|
|
29896
29982
|
const origin = req.headers.origin;
|
|
29897
29983
|
const extensionOrigin = typeof origin === "string" && /^chrome-extension:\/\/[a-p]{32}$/.test(origin);
|
|
29898
|
-
if (!this.validHost(req.headers.host) || !["/extension", "/
|
|
29984
|
+
if (!this.validHost(req.headers.host) || !["/extension", "/connect"].includes(req.url || "") || origin && !extensionOrigin || req.url === "/connect" && origin !== EXTENSION_ORIGIN) {
|
|
29899
29985
|
socket.write("HTTP/1.1 403 Forbidden\r\n\r\n");
|
|
29900
29986
|
socket.destroy();
|
|
29901
29987
|
return;
|
|
29902
29988
|
}
|
|
29903
29989
|
sockets.handleUpgrade(req, socket, head, (ws) => {
|
|
29904
|
-
if (req.url === "/
|
|
29990
|
+
if (req.url === "/connect") void this.acceptConnect(ws);
|
|
29905
29991
|
else this.accept(ws);
|
|
29906
29992
|
});
|
|
29907
29993
|
});
|
|
@@ -29923,76 +30009,44 @@ var LocalBridge = class {
|
|
|
29923
30009
|
validHost(host) {
|
|
29924
30010
|
return host === `127.0.0.1:${this.port}` || host === `localhost:${this.port}`;
|
|
29925
30011
|
}
|
|
29926
|
-
|
|
29927
|
-
|
|
29928
|
-
|
|
29929
|
-
|
|
29930
|
-
|
|
29931
|
-
const
|
|
29932
|
-
|
|
29933
|
-
this.
|
|
29934
|
-
|
|
29935
|
-
|
|
29936
|
-
|
|
29937
|
-
|
|
29938
|
-
|
|
29939
|
-
|
|
29940
|
-
|
|
30012
|
+
/** Replaces the extension credential and drops whatever was using the old one. */
|
|
30013
|
+
async replaceExtensionToken(reason) {
|
|
30014
|
+
const token = (0, import_node_crypto2.randomBytes)(32).toString("hex");
|
|
30015
|
+
await this.options.credentials?.setExtensionToken(token);
|
|
30016
|
+
this.extensionToken = token;
|
|
30017
|
+
const previousClient = this.client;
|
|
30018
|
+
this.client = void 0;
|
|
30019
|
+
this.extensionId = void 0;
|
|
30020
|
+
for (const value of this.pending.values()) {
|
|
30021
|
+
clearTimeout(value.timer);
|
|
30022
|
+
value.reject(new Error(reason));
|
|
30023
|
+
}
|
|
30024
|
+
this.pending.clear();
|
|
30025
|
+
previousClient?.close(1e3, "Extension connection replaced");
|
|
30026
|
+
return token;
|
|
30027
|
+
}
|
|
30028
|
+
/** One-click connection: only this extension's fixed origin can reach here, and the user has just clicked 连接. */
|
|
30029
|
+
async acceptConnect(ws) {
|
|
30030
|
+
try {
|
|
30031
|
+
const token = await this.replaceExtensionToken("\u6269\u5C55\u8FDE\u63A5\u5DF2\u66F4\u6362\uFF1B\u8BF7\u67E5\u8BE2\u539F\u540C\u6B65\u4EFB\u52A1\uFF0C\u4E0D\u8981\u81EA\u52A8\u91CD\u65B0\u63D0\u4EA4\u3002");
|
|
30032
|
+
ws.send(JSON.stringify({ type: "connect_result", token, port: this.port, protocol: "writedock/1" }));
|
|
30033
|
+
ws.close(1e3);
|
|
30034
|
+
} catch {
|
|
30035
|
+
ws.close(1011, "Connect failed");
|
|
29941
30036
|
}
|
|
29942
|
-
pairing.attempts++;
|
|
29943
|
-
const nonce = (0, import_node_crypto2.randomBytes)(32).toString("hex");
|
|
29944
|
-
const timer = setTimeout(() => ws.close(1008, "Pairing timed out"), 1e4);
|
|
29945
|
-
ws.send(JSON.stringify({ type: "pairing_challenge", nonce, proof: proof(pairing.code, "pairing-server", nonce) }));
|
|
29946
|
-
let consumed = false;
|
|
29947
|
-
ws.on("message", (raw) => {
|
|
29948
|
-
void (async () => {
|
|
29949
|
-
if (consumed) return;
|
|
29950
|
-
consumed = true;
|
|
29951
|
-
const body = raw.toString();
|
|
29952
|
-
if (body.length > 2048) throw new Error("Invalid pairing message");
|
|
29953
|
-
const message = JSON.parse(body);
|
|
29954
|
-
if (this.pairing !== pairing || Date.now() > pairing.expiresAt || message.type !== "pairing_hello" || typeof message.proof !== "string" || !equal(message.proof, proof(pairing.code, "pairing-extension", nonce))) throw new Error("Pairing failed");
|
|
29955
|
-
this.pairing = void 0;
|
|
29956
|
-
const token = (0, import_node_crypto2.randomBytes)(32).toString("hex");
|
|
29957
|
-
await this.options.credentials?.setExtensionToken(token);
|
|
29958
|
-
this.extensionToken = token;
|
|
29959
|
-
const previousClient = this.client;
|
|
29960
|
-
this.client = void 0;
|
|
29961
|
-
this.extensionId = void 0;
|
|
29962
|
-
for (const value of this.pending.values()) {
|
|
29963
|
-
clearTimeout(value.timer);
|
|
29964
|
-
value.reject(new Error("\u6269\u5C55\u914D\u5BF9\u5DF2\u66F4\u6362\uFF1B\u8BF7\u67E5\u8BE2\u539F\u540C\u6B65\u4EFB\u52A1\uFF0C\u4E0D\u8981\u81EA\u52A8\u91CD\u65B0\u63D0\u4EA4\u3002"));
|
|
29965
|
-
}
|
|
29966
|
-
this.pending.clear();
|
|
29967
|
-
previousClient?.close(1e3, "Extension pairing replaced");
|
|
29968
|
-
const key2 = (0, import_node_crypto2.createHmac)("sha256", pairing.code).update(`writedock-pairing-key:${nonce}`).digest();
|
|
29969
|
-
const iv = (0, import_node_crypto2.randomBytes)(12);
|
|
29970
|
-
const cipher = (0, import_node_crypto2.createCipheriv)("aes-256-gcm", key2, iv);
|
|
29971
|
-
cipher.setAAD(Buffer.from("writedock-pairing-v1"));
|
|
29972
|
-
const ciphertext = Buffer.concat([cipher.update(JSON.stringify({ token, port: this.port, protocol: "writedock/1" })), cipher.final(), cipher.getAuthTag()]);
|
|
29973
|
-
clearTimeout(timer);
|
|
29974
|
-
if (ws.readyState === import_websocket.default.OPEN) ws.send(JSON.stringify({ type: "pairing_result", iv: iv.toString("base64"), ciphertext: ciphertext.toString("base64") }));
|
|
29975
|
-
ws.close(1e3);
|
|
29976
|
-
})().catch(() => {
|
|
29977
|
-
ws.close(1008, "Pairing failed");
|
|
29978
|
-
});
|
|
29979
|
-
});
|
|
29980
|
-
ws.on("close", () => clearTimeout(timer));
|
|
29981
|
-
ws.on("error", () => {
|
|
29982
|
-
});
|
|
29983
30037
|
}
|
|
29984
30038
|
accept(ws) {
|
|
29985
30039
|
const nonce = (0, import_node_crypto2.randomBytes)(32).toString("hex");
|
|
29986
30040
|
const token = this.extensionToken;
|
|
29987
30041
|
let authenticated = false;
|
|
29988
|
-
const timer = setTimeout(() => ws.close(1008, "
|
|
30042
|
+
const timer = setTimeout(() => ws.close(1008, "Handshake timed out"), 8e3);
|
|
29989
30043
|
ws.send(JSON.stringify({ type: "challenge", nonce, proof: proof(token, "server", nonce) }));
|
|
29990
30044
|
ws.on("message", (raw) => {
|
|
29991
30045
|
try {
|
|
29992
30046
|
const message = JSON.parse(raw.toString());
|
|
29993
30047
|
if (!authenticated) {
|
|
29994
30048
|
if (token !== this.extensionToken || message.type !== "hello" || typeof message.proof !== "string" || !equal(message.proof, proof(token, "extension", nonce))) {
|
|
29995
|
-
ws.close(1008, "
|
|
30049
|
+
ws.close(1008, "Handshake failed");
|
|
29996
30050
|
return;
|
|
29997
30051
|
}
|
|
29998
30052
|
if (this.client?.readyState === import_websocket.default.OPEN) {
|
|
@@ -30007,6 +30061,13 @@ var LocalBridge = class {
|
|
|
30007
30061
|
return;
|
|
30008
30062
|
}
|
|
30009
30063
|
if (message.type === "pong") return;
|
|
30064
|
+
if (message.type === "update_extension") {
|
|
30065
|
+
void updateInstalledExtension().then(
|
|
30066
|
+
(result) => ws.send(JSON.stringify({ type: "update_extension_result", ok: true, version: result.version })),
|
|
30067
|
+
(error2) => ws.send(JSON.stringify({ type: "update_extension_result", ok: false, error: messageOf(error2) }))
|
|
30068
|
+
);
|
|
30069
|
+
return;
|
|
30070
|
+
}
|
|
30010
30071
|
const pending2 = this.pending.get(message.id);
|
|
30011
30072
|
if (!pending2) return;
|
|
30012
30073
|
this.pending.delete(message.id);
|
|
@@ -30041,6 +30102,10 @@ var LocalBridge = class {
|
|
|
30041
30102
|
res.writeHead(status);
|
|
30042
30103
|
res.end(body);
|
|
30043
30104
|
};
|
|
30105
|
+
if (req.url === "/hello" && req.method === "GET" && this.validHost(req.headers.host) && (!req.headers.origin || req.headers.origin === EXTENSION_ORIGIN)) {
|
|
30106
|
+
send(200, { service: "writedock-mcp", protocol: "writedock/1", version: mcpReleaseInfo().mcp_version });
|
|
30107
|
+
return;
|
|
30108
|
+
}
|
|
30044
30109
|
const assetPath = /^\/assets\/([a-f0-9-]{36})$/.exec(req.url || "");
|
|
30045
30110
|
if (assetPath && this.validHost(req.headers.host) && (!req.headers.origin || /^chrome-extension:\/\/[a-p]{32}$/.test(req.headers.origin))) {
|
|
30046
30111
|
await this.media.serve(req, res, assetPath[1]);
|
|
@@ -30074,11 +30139,7 @@ var LocalBridge = class {
|
|
|
30074
30139
|
this.seenNonces.set(nonce, now + 3e4);
|
|
30075
30140
|
responseNonce = nonce;
|
|
30076
30141
|
if (req.method === "GET" && req.url === "/status") {
|
|
30077
|
-
send(200, { connected: this.client?.readyState === import_websocket.default.OPEN, extensionId: this.extensionId, protocol: "writedock/1",
|
|
30078
|
-
return;
|
|
30079
|
-
}
|
|
30080
|
-
if (req.method === "POST" && req.url === "/pairing") {
|
|
30081
|
-
send(200, await this.beginPairing());
|
|
30142
|
+
send(200, { connected: this.client?.readyState === import_websocket.default.OPEN, extensionId: this.extensionId, protocol: "writedock/1", ...mcpReleaseInfo() });
|
|
30082
30143
|
return;
|
|
30083
30144
|
}
|
|
30084
30145
|
if (req.method === "POST" && req.url === "/assets/register") {
|
|
@@ -30117,14 +30178,14 @@ var LocalBridge = class {
|
|
|
30117
30178
|
}
|
|
30118
30179
|
async status() {
|
|
30119
30180
|
await this.start();
|
|
30120
|
-
if (this.primary) return { connected: this.client?.readyState === import_websocket.default.OPEN, extensionId: this.extensionId, protocol: "writedock/1",
|
|
30181
|
+
if (this.primary) return { connected: this.client?.readyState === import_websocket.default.OPEN, extensionId: this.extensionId, protocol: "writedock/1", ...mcpReleaseInfo() };
|
|
30121
30182
|
try {
|
|
30122
30183
|
return await this.httpCall("/status");
|
|
30123
30184
|
} catch (error2) {
|
|
30124
30185
|
if (!(error2 instanceof TypeError)) throw error2;
|
|
30125
30186
|
this.starting = void 0;
|
|
30126
30187
|
await this.start();
|
|
30127
|
-
if (this.primary) return { connected: false, protocol: "writedock/1",
|
|
30188
|
+
if (this.primary) return { connected: false, protocol: "writedock/1", ...mcpReleaseInfo() };
|
|
30128
30189
|
throw error2;
|
|
30129
30190
|
}
|
|
30130
30191
|
}
|
|
@@ -30144,7 +30205,7 @@ var LocalBridge = class {
|
|
|
30144
30205
|
return await this.httpCall("/assets/register", { path, kind });
|
|
30145
30206
|
}
|
|
30146
30207
|
async requestConnected(method, params) {
|
|
30147
|
-
if (this.client?.readyState !== import_websocket.default.OPEN) throw new Error("\u6269\u5C55\u672A\u8FDE\u63A5\u3002\u8BF7\
|
|
30208
|
+
if (this.client?.readyState !== import_websocket.default.OPEN) throw new Error("\u6269\u5C55\u672A\u8FDE\u63A5\u3002\u8BF7\u8BA9\u7528\u6237\u6253\u5F00\u6D4F\u89C8\u5668\u7684 WriteDock \u6269\u5C55\uFF0C\u5728\u300C\u8FDE\u63A5\u300D\u9875\u70B9\u201C\u8FDE\u63A5\u201D\u3002\u65E0\u9700\u91CD\u88C5 MCP\u3002");
|
|
30148
30209
|
const id = (0, import_node_crypto2.randomUUID)();
|
|
30149
30210
|
return new Promise((resolve4, reject) => {
|
|
30150
30211
|
const timer = setTimeout(() => {
|
|
@@ -30161,7 +30222,6 @@ var LocalBridge = class {
|
|
|
30161
30222
|
});
|
|
30162
30223
|
}
|
|
30163
30224
|
async close() {
|
|
30164
|
-
this.pairing = void 0;
|
|
30165
30225
|
clearInterval(this.heartbeat);
|
|
30166
30226
|
for (const value of this.pending.values()) {
|
|
30167
30227
|
clearTimeout(value.timer);
|
|
@@ -38347,8 +38407,8 @@ var EMPTY_COMPLETION_RESULT = {
|
|
|
38347
38407
|
};
|
|
38348
38408
|
|
|
38349
38409
|
// src/article.ts
|
|
38350
|
-
var
|
|
38351
|
-
var
|
|
38410
|
+
var import_promises3 = require("fs/promises");
|
|
38411
|
+
var import_node_path3 = require("path");
|
|
38352
38412
|
var import_node_url = require("url");
|
|
38353
38413
|
|
|
38354
38414
|
// ../../node_modules/.pnpm/marked@17.0.6/node_modules/marked/lib/marked.esm.js
|
|
@@ -40186,7 +40246,7 @@ var Tokenizer = class {
|
|
|
40186
40246
|
this.sequenceIndex = 0;
|
|
40187
40247
|
this.xmlMode = xmlMode;
|
|
40188
40248
|
this.decodeEntities = decodeEntities;
|
|
40189
|
-
this.entityDecoder = new EntityDecoder(xmlMode ? xmlDecodeTree : htmlDecodeTree, (
|
|
40249
|
+
this.entityDecoder = new EntityDecoder(xmlMode ? xmlDecodeTree : htmlDecodeTree, (cp2, consumed) => this.emitCodePoint(cp2, consumed));
|
|
40190
40250
|
}
|
|
40191
40251
|
reset() {
|
|
40192
40252
|
this.state = State.Text;
|
|
@@ -40751,21 +40811,21 @@ var Tokenizer = class {
|
|
|
40751
40811
|
this.cbs.ontext(this.sectionStart, endIndex);
|
|
40752
40812
|
}
|
|
40753
40813
|
}
|
|
40754
|
-
emitCodePoint(
|
|
40814
|
+
emitCodePoint(cp2, consumed) {
|
|
40755
40815
|
if (this.baseState !== State.Text && this.baseState !== State.InSpecialTag) {
|
|
40756
40816
|
if (this.sectionStart < this.entityStart) {
|
|
40757
40817
|
this.cbs.onattribdata(this.sectionStart, this.entityStart);
|
|
40758
40818
|
}
|
|
40759
40819
|
this.sectionStart = this.entityStart + consumed;
|
|
40760
40820
|
this.index = this.sectionStart - 1;
|
|
40761
|
-
this.cbs.onattribentity(
|
|
40821
|
+
this.cbs.onattribentity(cp2);
|
|
40762
40822
|
} else {
|
|
40763
40823
|
if (this.sectionStart < this.entityStart) {
|
|
40764
40824
|
this.cbs.ontext(this.sectionStart, this.entityStart);
|
|
40765
40825
|
}
|
|
40766
40826
|
this.sectionStart = this.entityStart + consumed;
|
|
40767
40827
|
this.index = this.sectionStart - 1;
|
|
40768
|
-
this.cbs.ontextentity(
|
|
40828
|
+
this.cbs.ontextentity(cp2, this.sectionStart);
|
|
40769
40829
|
}
|
|
40770
40830
|
}
|
|
40771
40831
|
};
|
|
@@ -40902,10 +40962,10 @@ var Parser = class {
|
|
|
40902
40962
|
this.startIndex = endIndex;
|
|
40903
40963
|
}
|
|
40904
40964
|
/** @internal */
|
|
40905
|
-
ontextentity(
|
|
40965
|
+
ontextentity(cp2, endIndex) {
|
|
40906
40966
|
var _a3, _b;
|
|
40907
40967
|
this.endIndex = endIndex - 1;
|
|
40908
|
-
(_b = (_a3 = this.cbs).ontext) === null || _b === void 0 ? void 0 : _b.call(_a3, fromCodePoint(
|
|
40968
|
+
(_b = (_a3 = this.cbs).ontext) === null || _b === void 0 ? void 0 : _b.call(_a3, fromCodePoint(cp2));
|
|
40909
40969
|
this.startIndex = endIndex;
|
|
40910
40970
|
}
|
|
40911
40971
|
/**
|
|
@@ -41026,8 +41086,8 @@ var Parser = class {
|
|
|
41026
41086
|
this.attribvalue += this.getSlice(start, endIndex);
|
|
41027
41087
|
}
|
|
41028
41088
|
/** @internal */
|
|
41029
|
-
onattribentity(
|
|
41030
|
-
this.attribvalue += fromCodePoint(
|
|
41089
|
+
onattribentity(cp2) {
|
|
41090
|
+
this.attribvalue += fromCodePoint(cp2);
|
|
41031
41091
|
}
|
|
41032
41092
|
/** @internal */
|
|
41033
41093
|
onattribend(quote, endIndex) {
|
|
@@ -44916,9 +44976,9 @@ function encodeXML2(input) {
|
|
|
44916
44976
|
last = index += 1;
|
|
44917
44977
|
continue;
|
|
44918
44978
|
}
|
|
44919
|
-
const
|
|
44920
|
-
out += `&#x${
|
|
44921
|
-
if (
|
|
44979
|
+
const cp2 = input.codePointAt(index);
|
|
44980
|
+
out += `&#x${cp2.toString(16)};`;
|
|
44981
|
+
if (cp2 !== char)
|
|
44922
44982
|
index++;
|
|
44923
44983
|
last = index += 1;
|
|
44924
44984
|
}
|
|
@@ -50136,17 +50196,17 @@ async function prepareSource(source2, registerVideo) {
|
|
|
50136
50196
|
let markdown = source2.markdown;
|
|
50137
50197
|
let html = source2.html;
|
|
50138
50198
|
let title = source2.title?.trim() || "";
|
|
50139
|
-
let baseDirectory = source2.base_directory ? await (0,
|
|
50199
|
+
let baseDirectory = source2.base_directory ? await (0, import_promises3.realpath)(source2.base_directory) : void 0;
|
|
50140
50200
|
if (source2.file_path) {
|
|
50141
|
-
if (!(0,
|
|
50142
|
-
const filename = await (0,
|
|
50143
|
-
const info = await (0,
|
|
50201
|
+
if (!(0, import_node_path3.isAbsolute)(source2.file_path)) throw new Error("file_path \u5FC5\u987B\u662F\u7EDD\u5BF9\u8DEF\u5F84");
|
|
50202
|
+
const filename = await (0, import_promises3.realpath)(source2.file_path);
|
|
50203
|
+
const info = await (0, import_promises3.stat)(filename);
|
|
50144
50204
|
if (!info.isFile() || info.size > TEXT_LIMIT) throw new Error("\u7A3F\u4EF6\u5FC5\u987B\u662F 2 MB \u4EE5\u5185\u7684 Markdown \u6216 HTML \u6587\u4EF6");
|
|
50145
|
-
const text = (await (0,
|
|
50205
|
+
const text = (await (0, import_promises3.readFile)(filename, "utf8")).replace(/^\uFEFF/, "");
|
|
50146
50206
|
if (/\.(md|markdown|mdown)$/i.test(filename)) markdown = text;
|
|
50147
50207
|
else if (/\.html?$/i.test(filename)) html = text;
|
|
50148
50208
|
else throw new Error("\u7A3F\u4EF6\u6587\u4EF6\u4EC5\u652F\u6301 .md\u3001.markdown\u3001.html\u3001.htm");
|
|
50149
|
-
baseDirectory ??= (0,
|
|
50209
|
+
baseDirectory ??= (0, import_node_path3.dirname)(filename);
|
|
50150
50210
|
}
|
|
50151
50211
|
const embedded = /* @__PURE__ */ new Map();
|
|
50152
50212
|
let imageBytes = 0;
|
|
@@ -50164,13 +50224,13 @@ async function prepareSource(source2, registerVideo) {
|
|
|
50164
50224
|
} else {
|
|
50165
50225
|
if (!baseDirectory) throw new Error("\u7A3F\u4EF6\u5305\u542B\u672C\u5730\u56FE\u7247\uFF0C\u8BF7\u63D0\u4F9B base_directory \u6216\u4F7F\u7528 file_path");
|
|
50166
50226
|
const path = src.startsWith("file:") ? (0, import_node_url.fileURLToPath)(src) : decodeURIComponent(src);
|
|
50167
|
-
if (/^[a-z][a-z0-9+.-]*:/i.test(path) && !(0,
|
|
50168
|
-
const filename = await (0,
|
|
50169
|
-
const inside = (0,
|
|
50170
|
-
if (inside === ".." || inside.startsWith("../") || inside.startsWith("..\\") || (0,
|
|
50171
|
-
const info = await (0,
|
|
50227
|
+
if (/^[a-z][a-z0-9+.-]*:/i.test(path) && !(0, import_node_path3.isAbsolute)(path)) throw new Error("\u56FE\u7247\u5730\u5740\u53EA\u652F\u6301 HTTP(S)\u3001\u672C\u5730\u56FE\u7247\u6216 data URI");
|
|
50228
|
+
const filename = await (0, import_promises3.realpath)((0, import_node_path3.resolve)(baseDirectory, path));
|
|
50229
|
+
const inside = (0, import_node_path3.relative)(baseDirectory, filename);
|
|
50230
|
+
if (inside === ".." || inside.startsWith("../") || inside.startsWith("..\\") || (0, import_node_path3.isAbsolute)(inside)) throw new Error("\u672C\u5730\u56FE\u7247\u5FC5\u987B\u4F4D\u4E8E\u7A3F\u4EF6\u76EE\u5F55\u6216\u660E\u786E\u6307\u5B9A\u7684 base_directory \u5185");
|
|
50231
|
+
const info = await (0, import_promises3.stat)(filename);
|
|
50172
50232
|
if (!info.isFile() || info.size > IMAGE_LIMIT) throw new Error("\u5355\u5F20\u672C\u5730\u56FE\u7247\u4E0D\u80FD\u8D85\u8FC7 8 MB");
|
|
50173
|
-
bytes = await (0,
|
|
50233
|
+
bytes = await (0, import_promises3.readFile)(filename);
|
|
50174
50234
|
}
|
|
50175
50235
|
if (!bytes.length || bytes.length > IMAGE_LIMIT) throw new Error("\u5355\u5F20\u56FE\u7247\u5FC5\u987B\u5728 8 MB \u4EE5\u5185");
|
|
50176
50236
|
const mime = imageMime(bytes);
|
|
@@ -50271,22 +50331,17 @@ var postSource = {
|
|
|
50271
50331
|
var agentWorkflow = "Agent \u53EA\u901A\u8FC7 WriteDock MCP \u6295\u7A3F\u548C\u67E5\u8BE2\u56DE\u6267\uFF0C\u4E0D\u4F9D\u8D56\u4E5F\u4E0D\u8981\u8C03\u7528\u6D4F\u89C8\u5668\u3001Playwright \u6216\u5176\u4ED6\u7F51\u9875\u5DE5\u5177\u3002\u6309\u7ED3\u679C\u7684 next_action \u64CD\u4F5C\uFF1Apoll_task \u7528\u539F task_id/request_id \u67E5\u8BE2 get_sync_task\uFF1Breport_result \u76F4\u63A5\u5411\u7528\u6237\u6C47\u62A5\u9010\u5E73\u53F0\u72B6\u6001\u548C\u94FE\u63A5\u3002submitted/published \u5DF2\u6709\u6269\u5C55\u786E\u8BA4\uFF0C\u4E0D\u518D\u6253\u5F00\u7F51\u9875\u9A8C\u8BC1\uFF1B\u9A8C\u8BC1\u7801\u3001\u56FE\u5F62\u9A8C\u8BC1\u3001\u534F\u8BAE\u3001\u53D1\u5E03\u8BBE\u7F6E\u6216 unknown/interrupted \u90FD\u5C06\u539F\u56E0\u548C\u94FE\u63A5\u4EA4\u7ED9\u7528\u6237\u81EA\u884C\u70B9\u51FB\u5904\u7406\uFF0C\u4E0D\u81EA\u52A8\u91CD\u53D1\u3001\u4E0D\u6362 request_id\u3002\u4EBA\u5DE5\u63A5\u7BA1\u4E0D\u4EE3\u8868\u5DF2\u7ECF\u53D1\u5E03\u3002";
|
|
50272
50332
|
function createMcpServer(bridge) {
|
|
50273
50333
|
const server = new McpServer({ name: "writedock", version: package_default.version }, {
|
|
50274
|
-
instructions: agentWorkflow + 'WriteDock \u901A\u8FC7\u672C\u673A\u6269\u5C55\u6267\u884C\u6295\u7A3F\u3002\u5148\u68C0\u67E5\u8FDE\u63A5\u548C\u5E73\u53F0\u767B\u5F55\u3002\u5B9E\u9645\u5E73\u53F0\u80FD\u529B\u4EE5 list_platforms \u8FD4\u56DE\u7684 action\u3001publishModes\u3001tools\u3001modeOptions\u3001confirmation\u3001capabilities\u3001coverRequired \u4E3A\u51C6\uFF0C\u6269\u5C55\u66F4\u65B0\u540E\u4F18\u5148\u6309\u8BE5\u5143\u4FE1\u606F\u9009\u62E9\u5DE5\u5177\u3002\u77E5\u4E4E\u3001\u767E\u5BB6\u53F7\u3001\u5FAE\u535A\u957F\u6587\u7AE0\u3001B\u7AD9\u4E13\u680F\u3001\u5C0F\u7EA2\u4E66\u957F\u6587\u4F7F\u7528 publish_article\uFF0CAgent \u76F4\u63A5\u63D0\u4F9B Markdown/HTML \u6216\u6587\u4EF6\u8DEF\u5F84\u5373\u53EF\u53D1\u5E03\uFF0C\u65E0\u9700\u7528\u6237\u5728\u6269\u5C55\u7F16\u8F91\u3002\u767E\u5BB6\u53F7\u5FC5\u987B\u63D0\u4F9B cover \u5C01\u9762\uFF1B\u77E5\u4E4E\u53E6\u652F\u6301 <video src="\u672C\u5730MP4\u8DEF\u5F84"></video> \u6587\u7AE0\u5185\u89C6\u9891\uFF0C\u72EC\u7ACB\u5C01\u9762\u6682\u672A\u63A5\u5165\u3002prepare_article \u4EC5\u4F9B\u53EF\u9009\u672C\u673A\u9884\u89C8\uFF0C\u4E0D\u662F\u53D1\u5E03\u7684\u524D\u7F6E\u8981\u6C42\u3002\u5C0F\u7EA2\u4E66 ID \u4E3A xiaohongshu\uFF0C\u4E09\u79CD\u6A21\u5F0F\u5171\u7528\u6B64\u5E73\u53F0\uFF1A\u5199\u957F\u6587\u7528 publish_article\uFF0C\u652F\u6301\u6B63\u6587\u56FE\u7247\u3001\u539F\u751F\u81EA\u52A8\u6392\u7248\u540E\u53D1\u5E03\uFF0C\u6807\u9898\u6700\u591A 64 \u5B57\u3001\u6B63\u6587\u6700\u591A 10000 \u5B57\uFF0C\u4E0D\u63A5\u72EC\u7ACB cover \u6216\u6587\u7AE0\u5185\u89C6\u9891\uFF1B\u666E\u901A\u56FE\u6587\u7528 publish_post(kind=image)\uFF0C\u72EC\u7ACB\u89C6\u9891\u7528 publish_post(kind=video)\uFF0C\u672A\u4F20 cover_path \u65F6\u4FDD\u7559\u5E73\u53F0\u81EA\u52A8\u751F\u6210\u7684\u7B2C\u4E00\u5E27\u5C01\u9762\u3002\u5C0F\u7EA2\u4E66\u6700\u7EC8\u5747\u987B\u5339\u914D POST /web_api/sns/v2/note \u7684 success=true\u3001result=0 \u548C\u7B14\u8BB0 ID \u56DE\u6267\u624D\u786E\u8BA4 submitted\u3002\u6296\u97F3\u5E73\u53F0 ID \u4E3A douyin\uFF0C\u56FE\u6587\u548C\u89C6\u9891\u540C\u6837\u7528 publish_post(kind=image|video)\uFF0C\u53EF\u9009 cover_path\uFF1B\u56FE\u6587\u8BDD\u9898\u6700\u591A 4 \u4E2A\u3001\u89C6\u9891\u6700\u591A 5 \u4E2A\uFF0C\u5F53\u524D\u53EA\u63A5\u5373\u65F6\u53D1\u5E03\u3002\u6296\u97F3 confirmation=native_page_message\uFF0C\u4EE5\u672C\u6B21\u64CD\u4F5C\u65B0\u589E\u7684\u539F\u751F\u6210\u529F\u63D0\u793A\u786E\u8BA4 submitted\uFF0C\u672A\u6838\u5BF9\u6700\u7EC8 HTTP \u4E1A\u52A1\u56DE\u6267\uFF1B\u4EC5\u8DF3\u8F6C\u4F5C\u54C1\u7BA1\u7406\u65F6\u4ECD\u4E3A unknown\uFF0CAgent \u4E0D\u81EA\u52A8\u91CD\u53D1\u3002\u5C0F\u7EA2\u4E66\u3001\u6296\u97F3\u3001\u5FAE\u535A\u666E\u901A\u56FE\u6587\u4E0E\u72EC\u7ACB\u89C6\u9891\u4F7F\u7528 prepare_post / publish_post\uFF0C\u5FAE\u535A\u5E73\u53F0 ID \u4ECD\u4E3A weibo\uFF0C\u4E0D\u53E6\u5EFA\u5E73\u53F0\u3002\u5FAE\u535A\u56FE\u6587\u6807\u9898\u653E\u5165\u6B63\u6587\u9996\u884C\uFF0C\u6700\u591A 18 \u5F20\u56FE\u7247\uFF1B\u72EC\u7ACB\u89C6\u9891\u6807\u9898\u6700\u591A 30 \u4E2A\u6C49\u5B57\uFF0860 \u4E2A\u534A\u89D2\u5B57\u7B26\uFF09\uFF0Cweibo_video_creation \u5FC5\u586B\uFF0C\u987B\u4F9D\u636E\u7528\u6237\u5B9E\u9645\u5185\u5BB9\u4F20 original\u3001secondary \u6216 repost\uFF0C\u4E0D\u80FD\u9ED8\u8BA4\u539F\u521B\u3002\u5FAE\u535A\u540C\u4E00\u6B21\u53EA\u80FD\u9009\u56FE\u6587\u6216\u89C6\u9891\uFF0C\u957F\u6587\u7AE0\u7EE7\u7EED\u7528 publish_article\u3002\u53EA\u6709\u7528\u6237\u660E\u786E\u8981\u6C42\u53D1\u5E03\u5E76\u6307\u5B9A\u5E73\u53F0\u65F6\u624D\u8C03\u7528\u53D1\u5E03\u5DE5\u5177\u3002\u5FAE\u4FE1\u516C\u4F17\u53F7\u3001\u641C\u72D0\u53F7\u76EE\u524D\u7528 sync_article \u4FDD\u5B58\u6587\u7AE0\u8349\u7A3F\u3002B\u7AD9\u4E13\u680F ID \u4E3A bilibili\uFF0C\u4F7F\u7528 publish_article \u76F4\u63A5\u53D1\u5E03\u6587\u5B57\u548C\u6B63\u6587\u56FE\u7247\uFF0C\u4E0D\u63A5\u89C6\u9891\u3001\u97F3\u9891\u3001\u9644\u52A0\u5185\u5BB9\u5361\u7247\u6216\u72EC\u7ACB\u5C01\u9762\uFF1B\u901A\u8FC7\u65B0\u7248\u539F\u751F\u7F16\u8F91\u5668\u4E0A\u4F20\u5E76\u53D1\u5E03\uFF0Ccode=-352 \u65F6\u505C\u6B62\u81EA\u52A8\u63D0\u4EA4\u5E76\u8F6C\u4EBA\u5DE5\u9A8C\u8BC1\u3002\u9047\u5230 needs_user_action \u6216 requires_user_action \u65F6\uFF0C\u628A user_actions \u4E2D\u7684\u539F\u56E0\u548C\u7A3F\u4EF6\u94FE\u63A5\u544A\u8BC9\u7528\u6237\uFF0C\u4F18\u5148\u8BA9\u7528\u6237\u5207\u6362\u5230\u6269\u5C55\u5DF2\u6253\u5F00\u7684\u9875\u9762\u5904\u7406\u9A8C\u8BC1\u7801\u3001\u5C01\u9762\u3001\u5185\u5BB9\u58F0\u660E\u3001\u6765\u6E90\u3001\u539F\u521B\u534F\u8BAE\u6216\u53D1\u5E03\u8BBE\u7F6E\uFF0C\u5E76\u624B\u52A8\u53D1\u5E03\uFF1B\u4E0D\u5F97\u81EA\u52A8\u89E3\u51B3\u9A8C\u8BC1\u7801\u3001\u91CD\u65B0\u63D0\u4EA4\u6216\u6362 request_id\u3002\u8BE5\u72B6\u6001\u53EA\u8BB0\u5F55\u4EBA\u5DE5\u63A5\u7BA1\uFF0C\u4E0D\u4EE3\u8868\u7528\u6237\u4E4B\u540E\u7684\u64CD\u4F5C\u7ED3\u679C\u5DF2\u88AB\u786E\u8BA4\u3002\u6269\u5C55\u66F4\u65B0\u590D\u7528\u5DF2\u6709 MCP\uFF1B\
|
|
50334
|
+
instructions: agentWorkflow + 'WriteDock \u901A\u8FC7\u672C\u673A\u6269\u5C55\u6267\u884C\u6295\u7A3F\u3002\u5148\u68C0\u67E5\u8FDE\u63A5\u548C\u5E73\u53F0\u767B\u5F55\u3002\u5B9E\u9645\u5E73\u53F0\u80FD\u529B\u4EE5 list_platforms \u8FD4\u56DE\u7684 action\u3001publishModes\u3001tools\u3001modeOptions\u3001confirmation\u3001capabilities\u3001coverRequired \u4E3A\u51C6\uFF0C\u6269\u5C55\u66F4\u65B0\u540E\u4F18\u5148\u6309\u8BE5\u5143\u4FE1\u606F\u9009\u62E9\u5DE5\u5177\u3002\u77E5\u4E4E\u3001\u767E\u5BB6\u53F7\u3001\u5FAE\u535A\u957F\u6587\u7AE0\u3001B\u7AD9\u4E13\u680F\u3001\u5C0F\u7EA2\u4E66\u957F\u6587\u4F7F\u7528 publish_article\uFF0CAgent \u76F4\u63A5\u63D0\u4F9B Markdown/HTML \u6216\u6587\u4EF6\u8DEF\u5F84\u5373\u53EF\u53D1\u5E03\uFF0C\u65E0\u9700\u7528\u6237\u5728\u6269\u5C55\u7F16\u8F91\u3002\u767E\u5BB6\u53F7\u5FC5\u987B\u63D0\u4F9B cover \u5C01\u9762\uFF1B\u77E5\u4E4E\u53E6\u652F\u6301 <video src="\u672C\u5730MP4\u8DEF\u5F84"></video> \u6587\u7AE0\u5185\u89C6\u9891\uFF0C\u72EC\u7ACB\u5C01\u9762\u6682\u672A\u63A5\u5165\u3002prepare_article \u4EC5\u4F9B\u53EF\u9009\u672C\u673A\u9884\u89C8\uFF0C\u4E0D\u662F\u53D1\u5E03\u7684\u524D\u7F6E\u8981\u6C42\u3002\u5C0F\u7EA2\u4E66 ID \u4E3A xiaohongshu\uFF0C\u4E09\u79CD\u6A21\u5F0F\u5171\u7528\u6B64\u5E73\u53F0\uFF1A\u5199\u957F\u6587\u7528 publish_article\uFF0C\u652F\u6301\u6B63\u6587\u56FE\u7247\u3001\u539F\u751F\u81EA\u52A8\u6392\u7248\u540E\u53D1\u5E03\uFF0C\u6807\u9898\u6700\u591A 64 \u5B57\u3001\u6B63\u6587\u6700\u591A 10000 \u5B57\uFF0C\u4E0D\u63A5\u72EC\u7ACB cover \u6216\u6587\u7AE0\u5185\u89C6\u9891\uFF1B\u666E\u901A\u56FE\u6587\u7528 publish_post(kind=image)\uFF0C\u72EC\u7ACB\u89C6\u9891\u7528 publish_post(kind=video)\uFF0C\u672A\u4F20 cover_path \u65F6\u4FDD\u7559\u5E73\u53F0\u81EA\u52A8\u751F\u6210\u7684\u7B2C\u4E00\u5E27\u5C01\u9762\u3002\u5C0F\u7EA2\u4E66\u6700\u7EC8\u5747\u987B\u5339\u914D POST /web_api/sns/v2/note \u7684 success=true\u3001result=0 \u548C\u7B14\u8BB0 ID \u56DE\u6267\u624D\u786E\u8BA4 submitted\u3002\u6296\u97F3\u5E73\u53F0 ID \u4E3A douyin\uFF0C\u56FE\u6587\u548C\u89C6\u9891\u540C\u6837\u7528 publish_post(kind=image|video)\uFF0C\u53EF\u9009 cover_path\uFF1B\u56FE\u6587\u8BDD\u9898\u6700\u591A 4 \u4E2A\u3001\u89C6\u9891\u6700\u591A 5 \u4E2A\uFF0C\u5F53\u524D\u53EA\u63A5\u5373\u65F6\u53D1\u5E03\u3002\u6296\u97F3 confirmation=native_page_message\uFF0C\u4EE5\u672C\u6B21\u64CD\u4F5C\u65B0\u589E\u7684\u539F\u751F\u6210\u529F\u63D0\u793A\u786E\u8BA4 submitted\uFF0C\u672A\u6838\u5BF9\u6700\u7EC8 HTTP \u4E1A\u52A1\u56DE\u6267\uFF1B\u4EC5\u8DF3\u8F6C\u4F5C\u54C1\u7BA1\u7406\u65F6\u4ECD\u4E3A unknown\uFF0CAgent \u4E0D\u81EA\u52A8\u91CD\u53D1\u3002\u5C0F\u7EA2\u4E66\u3001\u6296\u97F3\u3001\u5FAE\u535A\u666E\u901A\u56FE\u6587\u4E0E\u72EC\u7ACB\u89C6\u9891\u4F7F\u7528 prepare_post / publish_post\uFF0C\u5FAE\u535A\u5E73\u53F0 ID \u4ECD\u4E3A weibo\uFF0C\u4E0D\u53E6\u5EFA\u5E73\u53F0\u3002\u5FAE\u535A\u56FE\u6587\u6807\u9898\u653E\u5165\u6B63\u6587\u9996\u884C\uFF0C\u6700\u591A 18 \u5F20\u56FE\u7247\uFF1B\u72EC\u7ACB\u89C6\u9891\u6807\u9898\u6700\u591A 30 \u4E2A\u6C49\u5B57\uFF0860 \u4E2A\u534A\u89D2\u5B57\u7B26\uFF09\uFF0Cweibo_video_creation \u5FC5\u586B\uFF0C\u987B\u4F9D\u636E\u7528\u6237\u5B9E\u9645\u5185\u5BB9\u4F20 original\u3001secondary \u6216 repost\uFF0C\u4E0D\u80FD\u9ED8\u8BA4\u539F\u521B\u3002\u5FAE\u535A\u540C\u4E00\u6B21\u53EA\u80FD\u9009\u56FE\u6587\u6216\u89C6\u9891\uFF0C\u957F\u6587\u7AE0\u7EE7\u7EED\u7528 publish_article\u3002\u53EA\u6709\u7528\u6237\u660E\u786E\u8981\u6C42\u53D1\u5E03\u5E76\u6307\u5B9A\u5E73\u53F0\u65F6\u624D\u8C03\u7528\u53D1\u5E03\u5DE5\u5177\u3002\u5FAE\u4FE1\u516C\u4F17\u53F7\u3001\u641C\u72D0\u53F7\u76EE\u524D\u7528 sync_article \u4FDD\u5B58\u6587\u7AE0\u8349\u7A3F\u3002B\u7AD9\u4E13\u680F ID \u4E3A bilibili\uFF0C\u4F7F\u7528 publish_article \u76F4\u63A5\u53D1\u5E03\u6587\u5B57\u548C\u6B63\u6587\u56FE\u7247\uFF0C\u4E0D\u63A5\u89C6\u9891\u3001\u97F3\u9891\u3001\u9644\u52A0\u5185\u5BB9\u5361\u7247\u6216\u72EC\u7ACB\u5C01\u9762\uFF1B\u901A\u8FC7\u65B0\u7248\u539F\u751F\u7F16\u8F91\u5668\u4E0A\u4F20\u5E76\u53D1\u5E03\uFF0Ccode=-352 \u65F6\u505C\u6B62\u81EA\u52A8\u63D0\u4EA4\u5E76\u8F6C\u4EBA\u5DE5\u9A8C\u8BC1\u3002\u9047\u5230 needs_user_action \u6216 requires_user_action \u65F6\uFF0C\u628A user_actions \u4E2D\u7684\u539F\u56E0\u548C\u7A3F\u4EF6\u94FE\u63A5\u544A\u8BC9\u7528\u6237\uFF0C\u4F18\u5148\u8BA9\u7528\u6237\u5207\u6362\u5230\u6269\u5C55\u5DF2\u6253\u5F00\u7684\u9875\u9762\u5904\u7406\u9A8C\u8BC1\u7801\u3001\u5C01\u9762\u3001\u5185\u5BB9\u58F0\u660E\u3001\u6765\u6E90\u3001\u539F\u521B\u534F\u8BAE\u6216\u53D1\u5E03\u8BBE\u7F6E\uFF0C\u5E76\u624B\u52A8\u53D1\u5E03\uFF1B\u4E0D\u5F97\u81EA\u52A8\u89E3\u51B3\u9A8C\u8BC1\u7801\u3001\u91CD\u65B0\u63D0\u4EA4\u6216\u6362 request_id\u3002\u8BE5\u72B6\u6001\u53EA\u8BB0\u5F55\u4EBA\u5DE5\u63A5\u7BA1\uFF0C\u4E0D\u4EE3\u8868\u7528\u6237\u4E4B\u540E\u7684\u64CD\u4F5C\u7ED3\u679C\u5DF2\u88AB\u786E\u8BA4\u3002\u6269\u5C55\u66F4\u65B0\u590D\u7528\u5DF2\u6709 MCP\uFF1B\u672A\u8FDE\u63A5\u65F6\uFF0C\u8BA9\u7528\u6237\u6253\u5F00 WriteDock \u6269\u5C55\u7684\u300C\u8FDE\u63A5\u300D\u9875\u70B9\u201C\u8FDE\u63A5\u201D\u3002\u63D0\u4EA4\u8FD4\u56DE\u4EFB\u52A1 ID\uFF0C\u968F\u540E\u7528 get_sync_task \u67E5\u8BE2\u7ED3\u679C\u3002\u4FDD\u6301\u540C\u4E00\u64CD\u4F5C\u7684 request_id \u4E0D\u53D8\u3002publicationState=unknown \u6216\u4EFB\u52A1\u4E2D\u65AD\u65F6\uFF0C\u5411\u7528\u6237\u7ED9\u51FA\u94FE\u63A5\u8BA9\u5176\u81EA\u884C\u6838\u5BF9\u5E73\u53F0\uFF0CAgent \u4E0D\u6253\u5F00\u7F51\u9875\uFF0C\u4E0D\u8981\u81EA\u52A8\u751F\u6210\u65B0 request_id \u91CD\u53D1\u3002\u76F4\u63A5\u53D1\u5E03\u6210\u529F\u8868\u793A\u5E73\u53F0\u5DF2\u63A5\u53D7\u63D0\u4EA4\uFF0C\u662F\u5426\u516C\u5F00\u5C55\u793A\u4EE5\u5E73\u53F0\u4E3A\u51C6\u3002'
|
|
50275
50335
|
});
|
|
50276
50336
|
const wrap = (operation) => operation().then((result) => ({
|
|
50277
50337
|
content: [{ type: "text", text: JSON.stringify(result, null, 2) }]
|
|
50278
50338
|
})).catch((error2) => ({ content: [{ type: "text", text: error2 instanceof Error ? error2.message : String(error2) }], isError: true }));
|
|
50279
50339
|
const readOnly = { readOnlyHint: true, destructiveHint: false, openWorldHint: false };
|
|
50280
50340
|
server.registerTool("get_connection_status", {
|
|
50281
|
-
description: "\u68C0\u67E5\u672C\u673A MCP \u4E0E\u6269\u5C55\u7684\u8FDE\u63A5\u3001\u8FD0\u884C\u7248\u672C\u548C npm \u66F4\u65B0\u63D0\u793A\u3002update_available=true \u65F6\u544A\u77E5\u7528\u6237\u91CD\u65B0\u542F\u52A8 WriteDock MCP\uFF0C\u4FDD\u7559\u914D\u7F6E\
|
|
50341
|
+
description: "\u68C0\u67E5\u672C\u673A MCP \u4E0E\u6269\u5C55\u7684\u8FDE\u63A5\u3001\u8FD0\u884C\u7248\u672C\u548C npm \u66F4\u65B0\u63D0\u793A\u3002update_available=true \u65F6\u544A\u77E5\u7528\u6237\u91CD\u65B0\u542F\u52A8 WriteDock MCP\uFF0C\u4FDD\u7559\u914D\u7F6E\uFF1B\u53D1\u5E03\u4EFB\u52A1\u8FDB\u884C\u4E2D\u4E0D\u8981\u81EA\u52A8\u91CD\u542F\u3002\u65E7\u7248\u670D\u52A1\u53EF\u80FD\u4E0D\u8FD4\u56DE\u7248\u672C\u5B57\u6BB5\uFF1Blatest_version \u7F3A\u5931\u6216 update_checking=true \u65F6\uFF0C\u4E0D\u5F97\u58F0\u79F0\u5DF2\u662F\u6700\u65B0\u7248\u3002",
|
|
50282
50342
|
inputSchema: {},
|
|
50283
50343
|
annotations: { ...readOnly, openWorldHint: true }
|
|
50284
50344
|
}, () => wrap(() => bridge.status()));
|
|
50285
|
-
server.registerTool("begin_pairing", {
|
|
50286
|
-
description: "\u9996\u6B21\u8FDE\u63A5\u6216\u6269\u5C55\u5378\u8F7D\u91CD\u88C5\u540E\uFF0C\u751F\u6210 3 \u5206\u949F\u6709\u6548\u7684\u4E00\u6B21\u6027\u914D\u5BF9\u7801\u3002\u8BA9\u7528\u6237\u7C98\u8D34\u5230\u6269\u5C55\u7684 MCP \u8FDE\u63A5\u9875\u5373\u53EF\uFF0C\u590D\u7528\u5DF2\u6709 MCP \u548C agent \u914D\u7F6E\u3002\u65B0\u914D\u5BF9\u4F1A\u66FF\u6362\u6B64\u524D\u6D4F\u89C8\u5668\u7684\u8FDE\u63A5\u6388\u6743\uFF0C\u8BF7\u5728\u7528\u6237\u8981\u6C42\u8FDE\u63A5\u6269\u5C55\u65F6\u8C03\u7528\u3002",
|
|
50287
|
-
inputSchema: {},
|
|
50288
|
-
annotations: { readOnlyHint: false, destructiveHint: false, openWorldHint: false }
|
|
50289
|
-
}, () => wrap(() => bridge.beginPairing()));
|
|
50290
50345
|
server.registerTool("list_platforms", {
|
|
50291
50346
|
description: "\u5217\u51FA\u6269\u5C55\u5B9E\u9645\u652F\u6301\u7684\u5E73\u53F0\u3001\u767B\u5F55\u72B6\u6001\u3001publishModes\u3001\u5BF9\u5E94 tools \u53CA modeOptions \u9650\u5236\uFF1B\u5C0F\u7EA2\u4E66 article \u5BF9\u5E94 publish_article\uFF0Cimage/video \u5BF9\u5E94 publish_post\u3002\u4E0D\u4F1A\u8FD4\u56DE Cookie\u3002",
|
|
50292
50347
|
inputSchema: { force_refresh: external_exports.boolean().optional() },
|
|
@@ -50397,25 +50452,25 @@ function createMcpServer(bridge) {
|
|
|
50397
50452
|
}
|
|
50398
50453
|
|
|
50399
50454
|
// src/identity.ts
|
|
50400
|
-
var
|
|
50455
|
+
var import_promises4 = require("fs/promises");
|
|
50401
50456
|
var import_node_crypto3 = require("crypto");
|
|
50402
|
-
var
|
|
50403
|
-
var
|
|
50457
|
+
var import_node_os2 = require("os");
|
|
50458
|
+
var import_node_path4 = require("path");
|
|
50404
50459
|
var validToken = (value) => typeof value === "string" && /^[A-Za-z0-9_-]{24,128}$/.test(value);
|
|
50405
50460
|
function dataDirectory() {
|
|
50406
|
-
if (process.env.WRITEDOCK_DATA_DIR) return (0,
|
|
50407
|
-
if (process.platform === "darwin") return (0,
|
|
50408
|
-
if (process.platform === "win32") return (0,
|
|
50409
|
-
return (0,
|
|
50461
|
+
if (process.env.WRITEDOCK_DATA_DIR) return (0, import_node_path4.resolve)(process.env.WRITEDOCK_DATA_DIR);
|
|
50462
|
+
if (process.platform === "darwin") return (0, import_node_path4.join)((0, import_node_os2.homedir)(), "Library/Application Support/WriteDock/mcp");
|
|
50463
|
+
if (process.platform === "win32") return (0, import_node_path4.join)(process.env.LOCALAPPDATA || (0, import_node_path4.join)((0, import_node_os2.homedir)(), "AppData/Local"), "WriteDock/mcp");
|
|
50464
|
+
return (0, import_node_path4.join)(process.env.XDG_DATA_HOME || (0, import_node_path4.join)((0, import_node_os2.homedir)(), ".local/share"), "writedock/mcp");
|
|
50410
50465
|
}
|
|
50411
50466
|
async function loadIdentity(port, legacyToken) {
|
|
50412
50467
|
if (!Number.isInteger(port) || port < 1024 || port > 65535) throw new Error("WRITEDOCK_PORT \u5FC5\u987B\u662F 1024\u201365535 \u7684\u6574\u6570");
|
|
50413
50468
|
const directory = dataDirectory();
|
|
50414
|
-
await (0,
|
|
50415
|
-
const filename = (0,
|
|
50469
|
+
await (0, import_promises4.mkdir)(directory, { recursive: true, mode: 448 });
|
|
50470
|
+
const filename = (0, import_node_path4.join)(directory, `identity-${port}.json`);
|
|
50416
50471
|
const read = async () => {
|
|
50417
|
-
const value = JSON.parse(await (0,
|
|
50418
|
-
if (value.version !== 1 || !validToken(value.token) || !validToken(value.extensionToken)) throw new Error("MCP \
|
|
50472
|
+
const value = JSON.parse(await (0, import_promises4.readFile)(filename, "utf8"));
|
|
50473
|
+
if (value.version !== 1 || !validToken(value.token) || !validToken(value.extensionToken)) throw new Error("MCP \u8FDE\u63A5\u8D44\u6599\u683C\u5F0F\u4E0D\u6B63\u786E\uFF1B\u8BF7\u4FDD\u7559\u539F\u6587\u4EF6\u5E76\u68C0\u67E5\u5907\u4EFD\uFF0C\u4E0D\u4F1A\u81EA\u52A8\u8986\u76D6");
|
|
50419
50474
|
return value;
|
|
50420
50475
|
};
|
|
50421
50476
|
let identity;
|
|
@@ -50426,16 +50481,16 @@ async function loadIdentity(port, legacyToken) {
|
|
|
50426
50481
|
if (legacyToken && !validToken(legacyToken)) throw new Error("\u65E7 WRITEDOCK_TOKEN \u683C\u5F0F\u4E0D\u6B63\u786E\uFF0C\u8BF7\u68C0\u67E5\u539F MCP \u914D\u7F6E");
|
|
50427
50482
|
const token = legacyToken || (0, import_node_crypto3.randomBytes)(32).toString("hex");
|
|
50428
50483
|
const initial = { version: 1, token, extensionToken: token };
|
|
50429
|
-
const temporary = (0,
|
|
50430
|
-
await (0,
|
|
50484
|
+
const temporary = (0, import_node_path4.join)(directory, `.identity-${(0, import_node_crypto3.randomUUID)()}.tmp`);
|
|
50485
|
+
await (0, import_promises4.writeFile)(temporary, JSON.stringify(initial), { flag: "wx", mode: 384 });
|
|
50431
50486
|
try {
|
|
50432
50487
|
try {
|
|
50433
|
-
await (0,
|
|
50488
|
+
await (0, import_promises4.link)(temporary, filename);
|
|
50434
50489
|
} catch (cause) {
|
|
50435
50490
|
if (cause.code !== "EEXIST") throw cause;
|
|
50436
50491
|
}
|
|
50437
50492
|
} finally {
|
|
50438
|
-
await (0,
|
|
50493
|
+
await (0, import_promises4.unlink)(temporary);
|
|
50439
50494
|
}
|
|
50440
50495
|
identity = await read();
|
|
50441
50496
|
}
|
|
@@ -50443,21 +50498,120 @@ async function loadIdentity(port, legacyToken) {
|
|
|
50443
50498
|
token: identity.token,
|
|
50444
50499
|
getExtensionToken: async () => (await read()).extensionToken,
|
|
50445
50500
|
setExtensionToken: async (extensionToken) => {
|
|
50446
|
-
if (!validToken(extensionToken)) throw new Error("\u6269\u5C55\
|
|
50501
|
+
if (!validToken(extensionToken)) throw new Error("\u6269\u5C55\u8FDE\u63A5\u5BC6\u94A5\u683C\u5F0F\u4E0D\u6B63\u786E");
|
|
50447
50502
|
const current = await read();
|
|
50448
50503
|
if (current.token !== identity.token) throw new Error("MCP \u8EAB\u4EFD\u5DF2\u53D8\u5316\uFF0C\u8BF7\u91CD\u65B0\u542F\u52A8\u670D\u52A1");
|
|
50449
|
-
const temporary = (0,
|
|
50504
|
+
const temporary = (0, import_node_path4.join)(directory, `.identity-${(0, import_node_crypto3.randomUUID)()}.tmp`);
|
|
50450
50505
|
try {
|
|
50451
|
-
await (0,
|
|
50452
|
-
await (0,
|
|
50506
|
+
await (0, import_promises4.writeFile)(temporary, JSON.stringify({ ...current, extensionToken }), { flag: "wx", mode: 384 });
|
|
50507
|
+
await (0, import_promises4.rename)(temporary, filename);
|
|
50453
50508
|
} finally {
|
|
50454
|
-
await (0,
|
|
50509
|
+
await (0, import_promises4.unlink)(temporary).catch(() => {
|
|
50455
50510
|
});
|
|
50456
50511
|
}
|
|
50457
50512
|
}
|
|
50458
50513
|
};
|
|
50459
50514
|
}
|
|
50460
50515
|
|
|
50516
|
+
// src/setup.ts
|
|
50517
|
+
var import_node_child_process = require("child_process");
|
|
50518
|
+
var import_promises5 = require("fs/promises");
|
|
50519
|
+
var import_node_os3 = require("os");
|
|
50520
|
+
var import_node_path5 = require("path");
|
|
50521
|
+
var COMMAND = "npx";
|
|
50522
|
+
var ARGS = ["-y", "--prefer-online", "writedock-mcp@latest"];
|
|
50523
|
+
async function readIfPresent(path) {
|
|
50524
|
+
try {
|
|
50525
|
+
return await (0, import_promises5.readFile)(path, "utf8");
|
|
50526
|
+
} catch (error2) {
|
|
50527
|
+
if (error2.code === "ENOENT") return void 0;
|
|
50528
|
+
throw error2;
|
|
50529
|
+
}
|
|
50530
|
+
}
|
|
50531
|
+
async function save(path, content, previous) {
|
|
50532
|
+
await (0, import_promises5.mkdir)((0, import_node_path5.dirname)(path), { recursive: true });
|
|
50533
|
+
if (previous !== void 0) await (0, import_promises5.copyFile)(path, `${path}.writedock-backup`);
|
|
50534
|
+
await (0, import_promises5.writeFile)(path, content);
|
|
50535
|
+
}
|
|
50536
|
+
async function configureCodex(path) {
|
|
50537
|
+
const previous = await readIfPresent(path);
|
|
50538
|
+
if (previous !== void 0 && /^\s*\[mcp_servers\.writedock\]/m.test(previous)) return "present";
|
|
50539
|
+
const entry = `[mcp_servers.writedock]
|
|
50540
|
+
command = "${COMMAND}"
|
|
50541
|
+
args = ${JSON.stringify(ARGS)}
|
|
50542
|
+
startup_timeout_sec = 120
|
|
50543
|
+
`;
|
|
50544
|
+
await save(path, previous ? `${previous.replace(/\s*$/, "")}
|
|
50545
|
+
|
|
50546
|
+
${entry}` : entry, previous);
|
|
50547
|
+
return "added";
|
|
50548
|
+
}
|
|
50549
|
+
async function configureJsonClient(path) {
|
|
50550
|
+
const previous = await readIfPresent(path);
|
|
50551
|
+
const config2 = previous?.trim() ? JSON.parse(previous) : {};
|
|
50552
|
+
if (config2.mcpServers?.writedock) return "present";
|
|
50553
|
+
config2.mcpServers = { ...config2.mcpServers, writedock: { command: COMMAND, args: ARGS } };
|
|
50554
|
+
await save(path, `${JSON.stringify(config2, null, 2)}
|
|
50555
|
+
`, previous);
|
|
50556
|
+
return "added";
|
|
50557
|
+
}
|
|
50558
|
+
var exists = async (path) => await readIfPresent(path) !== void 0;
|
|
50559
|
+
function revealFolder(path) {
|
|
50560
|
+
const command = process.platform === "darwin" ? "open" : process.platform === "win32" ? "explorer" : "xdg-open";
|
|
50561
|
+
try {
|
|
50562
|
+
const child = (0, import_node_child_process.spawn)(command, [path], { detached: true, stdio: "ignore" });
|
|
50563
|
+
child.on("error", () => {
|
|
50564
|
+
});
|
|
50565
|
+
child.unref();
|
|
50566
|
+
} catch {
|
|
50567
|
+
}
|
|
50568
|
+
}
|
|
50569
|
+
async function runSetup(home = (0, import_node_os3.homedir)(), log = console.log) {
|
|
50570
|
+
const clients = [
|
|
50571
|
+
{ name: "Codex", path: (0, import_node_path5.join)(home, ".codex/config.toml"), detect: (0, import_node_path5.join)(home, ".codex/config.toml"), configure: configureCodex },
|
|
50572
|
+
{ name: "Claude Code", path: (0, import_node_path5.join)(home, ".claude.json"), detect: (0, import_node_path5.join)(home, ".claude.json"), configure: configureJsonClient },
|
|
50573
|
+
{ name: "Cursor", path: (0, import_node_path5.join)(home, ".cursor/mcp.json"), detect: (0, import_node_path5.join)(home, ".cursor/mcp.json"), configure: configureJsonClient },
|
|
50574
|
+
{ name: "Claude Desktop", path: (0, import_node_path5.join)(home, "Library/Application Support/Claude/claude_desktop_config.json"), detect: (0, import_node_path5.join)(home, "Library/Application Support/Claude/claude_desktop_config.json"), configure: configureJsonClient }
|
|
50575
|
+
];
|
|
50576
|
+
let found = 0;
|
|
50577
|
+
for (const client of clients) {
|
|
50578
|
+
if (!await exists(client.detect)) continue;
|
|
50579
|
+
found++;
|
|
50580
|
+
try {
|
|
50581
|
+
const state = await client.configure(client.path);
|
|
50582
|
+
log(state === "added" ? `\u2713 ${client.name}\uFF1A\u5DF2\u6DFB\u52A0 WriteDock\uFF08\u539F\u914D\u7F6E\u5907\u4EFD\u4E3A .writedock-backup\uFF09` : `\u2713 ${client.name}\uFF1A\u5DF2\u6709 WriteDock \u914D\u7F6E\uFF0C\u672A\u6539\u52A8`);
|
|
50583
|
+
} catch (error2) {
|
|
50584
|
+
log(`\u2717 ${client.name}\uFF1A\u914D\u7F6E\u5931\u8D25\uFF08${error2 instanceof Error ? error2.message : String(error2)}\uFF09\uFF0C\u539F\u6587\u4EF6\u672A\u6539\u52A8`);
|
|
50585
|
+
}
|
|
50586
|
+
}
|
|
50587
|
+
if (!found) {
|
|
50588
|
+
log("\u6CA1\u6709\u627E\u5230\u5DF2\u77E5\u7684 agent \u5BA2\u6237\u7AEF\u3002\u8BF7\u5728\u4F60\u7684\u5BA2\u6237\u7AEF\u91CC\u6DFB\u52A0\u8FD9\u4E2A MCP \u670D\u52A1\uFF1A");
|
|
50589
|
+
log(JSON.stringify({ mcpServers: { writedock: { command: COMMAND, args: ARGS } } }, null, 2));
|
|
50590
|
+
}
|
|
50591
|
+
const bundle = bundledExtension();
|
|
50592
|
+
const loaded = bundle ? await findInstalledExtension().catch(() => void 0) : void 0;
|
|
50593
|
+
let extensionFolder;
|
|
50594
|
+
if (bundle) {
|
|
50595
|
+
try {
|
|
50596
|
+
extensionFolder = (await installExtensionFiles(bundle.directory, loaded || defaultExtensionDirectory(home))).directory;
|
|
50597
|
+
} catch (error2) {
|
|
50598
|
+
log(`\u2717 \u6269\u5C55\uFF1A\u5B89\u88C5\u5931\u8D25\uFF08${error2 instanceof Error ? error2.message : String(error2)}\uFF09`);
|
|
50599
|
+
}
|
|
50600
|
+
}
|
|
50601
|
+
log("");
|
|
50602
|
+
log("\u63A5\u4E0B\u6765\uFF1A");
|
|
50603
|
+
log("1. \u91CD\u542F\u4F60\u7684 agent \u5BA2\u6237\u7AEF\uFF0C\u8BA9\u5B83\u52A0\u8F7D WriteDock\u3002");
|
|
50604
|
+
if (extensionFolder && !loaded) {
|
|
50605
|
+
log("2. \u5728 Chrome \u6216 Edge \u6253\u5F00 chrome://extensions\uFF0C\u6253\u5F00\u53F3\u4E0A\u89D2\u7684\u201C\u5F00\u53D1\u8005\u6A21\u5F0F\u201D\uFF0C\u70B9\u201C\u52A0\u8F7D\u5DF2\u89E3\u538B\u7684\u6269\u5C55\u7A0B\u5E8F\u201D\uFF0C\u9009\u62E9\u8FD9\u4E2A\u6587\u4EF6\u5939\uFF1A");
|
|
50606
|
+
log(` ${extensionFolder}`);
|
|
50607
|
+
revealFolder(extensionFolder);
|
|
50608
|
+
log("3. \u6253\u5F00 WriteDock \u6269\u5C55\uFF0C\u70B9\u201C\u8FDE\u63A5\u201D\u3002");
|
|
50609
|
+
} else if (extensionFolder) {
|
|
50610
|
+
log(`2. \u6269\u5C55\u5DF2\u66F4\u65B0\u3002\u5728 chrome://extensions \u70B9\u4E00\u6B21 WriteDock \u7684\u5237\u65B0\u56FE\u6807\uFF0C\u7136\u540E\u70B9\u201C\u8FDE\u63A5\u201D\u3002`);
|
|
50611
|
+
} else log("2. \u6253\u5F00\u6D4F\u89C8\u5668\u7684 WriteDock \u6269\u5C55\uFF0C\u70B9\u201C\u8FDE\u63A5\u201D\u3002");
|
|
50612
|
+
return found;
|
|
50613
|
+
}
|
|
50614
|
+
|
|
50461
50615
|
// src/index.ts
|
|
50462
50616
|
async function main() {
|
|
50463
50617
|
const port = Number(process.env.WRITEDOCK_PORT || 9647);
|
|
@@ -50488,6 +50642,11 @@ async function main() {
|
|
|
50488
50642
|
}
|
|
50489
50643
|
if (process.argv.slice(2).some((argument) => argument === "--version" || argument === "-v")) {
|
|
50490
50644
|
console.log(package_default.version);
|
|
50645
|
+
} else if (process.argv[2] === "setup") {
|
|
50646
|
+
runSetup().catch((error2) => {
|
|
50647
|
+
console.error("[WriteDock MCP]", error2 instanceof Error ? error2.message : String(error2));
|
|
50648
|
+
process.exit(1);
|
|
50649
|
+
});
|
|
50491
50650
|
} else {
|
|
50492
50651
|
main().catch((error2) => {
|
|
50493
50652
|
console.error("[WriteDock MCP]", error2 instanceof Error ? error2.message : String(error2));
|