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.
Files changed (36) hide show
  1. package/README.md +21 -20
  2. package/extension/LICENSE-MULTIPOST.txt +201 -0
  3. package/extension/LICENSE.txt +674 -0
  4. package/extension/NOTICE.txt +13 -0
  5. package/extension/assets/editor-ClIT9WYa.js +76 -0
  6. package/extension/assets/editor-VBFqqvXW.css +1 -0
  7. package/extension/assets/icon-128.png +0 -0
  8. package/extension/assets/icon-16.png +0 -0
  9. package/extension/assets/icon-48.png +0 -0
  10. package/extension/assets/index.browser-Hs3ZYqOv.js +157 -0
  11. package/extension/assets/index.ts-BEVXnN62.js +42 -0
  12. package/extension/assets/logo.svg +5 -0
  13. package/extension/assets/offscreen-3BECPbQX.js +63 -0
  14. package/extension/assets/purify.es-1yEQDPgc.js +3 -0
  15. package/extension/assets/react-vendor-BrXlnJmF.js +40 -0
  16. package/extension/assets/task-utils-DwzGcJkm.js +1 -0
  17. package/extension/manifest.json +40 -0
  18. package/extension/service-worker-loader.js +1 -0
  19. package/extension/src/editor/index.html +16 -0
  20. package/extension/src/offscreen/index.html +6 -0
  21. package/extension/zhihu-publication-observer.js +1 -0
  22. package/extension//345/256/211/350/243/205/344/270/216/346/233/264/346/226/260.txt +19 -0
  23. package/index.cjs +290 -131
  24. package/package.json +3 -2
  25. package/source/package.json +1 -1
  26. package/source/src/__tests__/bridge.test.ts +22 -0
  27. package/source/src/__tests__/extension-update.test.ts +54 -0
  28. package/source/src/__tests__/setup.test.ts +50 -0
  29. package/source/src/bridge.ts +43 -61
  30. package/source/src/extension-id.ts +2 -0
  31. package/source/src/extension-update.ts +82 -0
  32. package/source/src/identity.ts +2 -2
  33. package/source/src/index.ts +3 -0
  34. package/source/src/release-info.ts +3 -0
  35. package/source/src/server.ts +2 -6
  36. 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(cp) {
6824
- if (cp < 2048) {
6825
- return BYTE_HEX[192 | cp >> 6] + BYTE_HEX[128 | cp & 63];
6823
+ function percentEncodeNonAscii(cp2) {
6824
+ if (cp2 < 2048) {
6825
+ return BYTE_HEX[192 | cp2 >> 6] + BYTE_HEX[128 | cp2 & 63];
6826
6826
  }
6827
- if (cp < 65536) {
6828
- return BYTE_HEX[224 | cp >> 12] + BYTE_HEX[128 | cp >> 6 & 63] + BYTE_HEX[128 | cp & 63];
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 | cp >> 18] + BYTE_HEX[128 | cp >> 12 & 63] + BYTE_HEX[128 | cp >> 6 & 63] + BYTE_HEX[128 | cp & 63];
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(cp) {
7226
- return cp >= 48 && cp <= 57 || cp >= 65 && cp <= 90 || cp >= 97 && cp <= 122 || cp === 42 || cp === 43 || cp === 45 || cp === 46 || cp === 47 || cp === 64 || cp === 95;
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.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", "/pair"].includes(req.url || "") || origin && !extensionOrigin || req.url === "/pair" && !extensionOrigin) {
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 === "/pair") this.acceptPairing(ws);
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
- async beginPairing() {
29927
- const state = await this.status();
29928
- if (!this.primary && state.pairing_available !== true) throw new Error("\u672C\u673A\u7AEF\u53E3\u4ECD\u7531\u65E7\u7248 MCP \u5360\u7528\u3002\u8BF7\u91CD\u542F\u4ECD\u4F7F\u7528\u65E7\u7248\u672C\u7684 agent/MCP \u8FDB\u7A0B\uFF0C\u4FDD\u7559\u539F\u914D\u7F6E\u540E\u518D\u751F\u6210\u914D\u5BF9\u7801\u3002");
29929
- if (!this.primary) return this.httpCall("/pairing", {});
29930
- const secret = (0, import_node_crypto2.randomBytes)(8).toString("hex").toUpperCase().match(/.{4}/g).join("-");
29931
- const code = `WD1-${this.port}-${secret}`;
29932
- const expiresAt = Date.now() + 18e4;
29933
- this.pairing = { code, expiresAt, attempts: 0 };
29934
- return { pairing_code: code, expires_at: new Date(expiresAt).toISOString(), expires_in_seconds: 180, instructions: "\u5C06\u6B64\u4E00\u6B21\u6027\u914D\u5BF9\u7801\u7C98\u8D34\u5230\u6269\u5C55\u7684\u300CMCP \u8FDE\u63A5 \u2192 \u8F93\u5165\u4E00\u6B21\u6027\u914D\u5BF9\u7801\u300D\u3002\u6709\u6548\u671F 3 \u5206\u949F\uFF0C\u4F7F\u7528\u4E00\u6B21\u540E\u5931\u6548\u3002\u65E0\u9700\u91CD\u65B0\u5B89\u88C5 MCP \u6216\u4FEE\u6539 agent \u914D\u7F6E\u3002\u786E\u8BA4\u7528\u6237\u8981\u8FDE\u63A5\u5F53\u524D\u6269\u5C55\u540E\u518D\u751F\u6210\u3002" };
29935
- }
29936
- acceptPairing(ws) {
29937
- const pairing = this.pairing;
29938
- if (!pairing || Date.now() > pairing.expiresAt || pairing.attempts >= 8) {
29939
- ws.close(1008, "Pairing code expired");
29940
- return;
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, "Pairing timed out"), 8e3);
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, "Pairing failed");
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", pairing_available: true, ...mcpReleaseInfo() });
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", pairing_available: true, ...mcpReleaseInfo() };
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", pairing_available: true, ...mcpReleaseInfo() };
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\u8C03\u7528 begin_pairing\uFF0C\u5C06\u4E00\u6B21\u6027\u914D\u5BF9\u7801\u7C98\u8D34\u5230\u6269\u5C55\u7684 MCP \u8FDE\u63A5\u9875\uFF1B\u65E0\u9700\u91CD\u88C5 MCP\u3002");
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 import_promises2 = require("fs/promises");
38351
- var import_node_path2 = require("path");
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, (cp, consumed) => this.emitCodePoint(cp, consumed));
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(cp, consumed) {
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(cp);
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(cp, this.sectionStart);
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(cp, endIndex) {
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(cp));
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(cp) {
41030
- this.attribvalue += fromCodePoint(cp);
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 cp = input.codePointAt(index);
44920
- out += `&#x${cp.toString(16)};`;
44921
- if (cp !== char)
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, import_promises2.realpath)(source2.base_directory) : void 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, import_node_path2.isAbsolute)(source2.file_path)) throw new Error("file_path \u5FC5\u987B\u662F\u7EDD\u5BF9\u8DEF\u5F84");
50142
- const filename = await (0, import_promises2.realpath)(source2.file_path);
50143
- const info = await (0, import_promises2.stat)(filename);
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, import_promises2.readFile)(filename, "utf8")).replace(/^\uFEFF/, "");
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, import_node_path2.dirname)(filename);
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, import_node_path2.isAbsolute)(path)) throw new Error("\u56FE\u7247\u5730\u5740\u53EA\u652F\u6301 HTTP(S)\u3001\u672C\u5730\u56FE\u7247\u6216 data URI");
50168
- const filename = await (0, import_promises2.realpath)((0, import_node_path2.resolve)(baseDirectory, path));
50169
- const inside = (0, import_node_path2.relative)(baseDirectory, filename);
50170
- if (inside === ".." || inside.startsWith("../") || inside.startsWith("..\\") || (0, import_node_path2.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");
50171
- const info = await (0, import_promises2.stat)(filename);
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, import_promises2.readFile)(filename);
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\u9700\u8981\u6062\u590D\u8FDE\u63A5\u65F6\u8C03\u7528 begin_pairing\uFF0C\u8BA9\u7528\u6237\u5728\u6269\u5C55\u8F93\u5165\u914D\u5BF9\u7801\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'
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\u548C\u914D\u5BF9\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",
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 import_promises3 = require("fs/promises");
50455
+ var import_promises4 = require("fs/promises");
50401
50456
  var import_node_crypto3 = require("crypto");
50402
- var import_node_os = require("os");
50403
- var import_node_path3 = require("path");
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, import_node_path3.resolve)(process.env.WRITEDOCK_DATA_DIR);
50407
- if (process.platform === "darwin") return (0, import_node_path3.join)((0, import_node_os.homedir)(), "Library/Application Support/WriteDock/mcp");
50408
- if (process.platform === "win32") return (0, import_node_path3.join)(process.env.LOCALAPPDATA || (0, import_node_path3.join)((0, import_node_os.homedir)(), "AppData/Local"), "WriteDock/mcp");
50409
- return (0, import_node_path3.join)(process.env.XDG_DATA_HOME || (0, import_node_path3.join)((0, import_node_os.homedir)(), ".local/share"), "writedock/mcp");
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, import_promises3.mkdir)(directory, { recursive: true, mode: 448 });
50415
- const filename = (0, import_node_path3.join)(directory, `identity-${port}.json`);
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, import_promises3.readFile)(filename, "utf8"));
50418
- if (value.version !== 1 || !validToken(value.token) || !validToken(value.extensionToken)) throw new Error("MCP \u914D\u5BF9\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");
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, import_node_path3.join)(directory, `.identity-${(0, import_node_crypto3.randomUUID)()}.tmp`);
50430
- await (0, import_promises3.writeFile)(temporary, JSON.stringify(initial), { flag: "wx", mode: 384 });
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, import_promises3.link)(temporary, filename);
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, import_promises3.unlink)(temporary);
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\u914D\u5BF9\u5BC6\u94A5\u683C\u5F0F\u4E0D\u6B63\u786E");
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, import_node_path3.join)(directory, `.identity-${(0, import_node_crypto3.randomUUID)()}.tmp`);
50504
+ const temporary = (0, import_node_path4.join)(directory, `.identity-${(0, import_node_crypto3.randomUUID)()}.tmp`);
50450
50505
  try {
50451
- await (0, import_promises3.writeFile)(temporary, JSON.stringify({ ...current, extensionToken }), { flag: "wx", mode: 384 });
50452
- await (0, import_promises3.rename)(temporary, filename);
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, import_promises3.unlink)(temporary).catch(() => {
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));