z-mcp-codesign 0.1.2 → 0.1.4

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 (3) hide show
  1. package/README.md +14 -20
  2. package/dist/index.js +133 -240
  3. package/package.json +1 -1
package/README.md CHANGED
@@ -21,46 +21,40 @@
21
21
  }
22
22
  ```
23
23
 
24
-
25
-
26
24
  # 提供的工具
27
25
 
28
- | 工具名 | 说明 |
29
- | ------ | ---- |
30
- | `codesign_open_login` | 有界面打开 CoDesign 登录页,用于在本机 Edge 用户数据目录中完成登录 |
31
- | `codesign_screen_inspect` | 打开指定 CoDesign 页面,截取最大的可见 `.screen-inspect` 区域,以 PNG base64 返回 |
32
- | `codesign_screen_inspect_save` | 打开指定 CoDesign 页面,截取最大的可见 `.screen-inspect` 区域,保存为本地 PNG 文件并返回保存路径(默认保存到项目根目录 `.codesign_screen_imgs/`) |
33
- | `codesign_screen_list` | 获取 CoDesign 设计页面左侧菜单中的所有页面名称和访问地址 |
26
+ | 工具名 | 说明 |
27
+ | -------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------- |
28
+ | `codesign_open_login` | 有界面打开 CoDesign 登录页,用于在本机 Edge 用户数据目录中完成登录 |
29
+ | `codesign_screen_inspect_save` | 打开指定 CoDesign 页面,截取最大的可见 `.screen-inspect` 区域,保存为本地 PNG 文件并返回保存路径(默认保存到项目根目录 `_z-mcp-imgs/`) |
30
+ | `codesign_screen_viewer_download_save` | 打开 CoDesign 页面,点击 `.screen-viewer__action` 选择“下载图片”,捕获下载并保存到项目根目录 `_z-mcp-imgs/` |
31
+ | `codesign_screen_list` | 获取 CoDesign 设计页面左侧菜单中的所有页面名称和访问地址 |
34
32
 
35
33
  # 使用示例
36
34
 
37
35
  **打开登录页(用于后续抓图)**
38
36
 
39
37
  ```bash
40
- 请在本机用可见浏览器打开 CoDesign 登录页
38
+ 调用MCP 打开CoDesign登录页
41
39
  ```
42
40
 
43
41
  **列出当前项目的页面列表**
44
42
 
45
43
  ```bash
46
- 请打开这个 CoDesign inspect 链接,并把左侧菜单里的页面列表整理出来(包含每个页面的名称和对应的 inspect 访问地址):https://codesign.qq.com/app/design/<projectId>/<pageId>/inspect
44
+ 调用MCP 获取CoDesign所有页面列表使用表格展示。
45
+ URL为:https://codesign.qq.com/app/design/<projectId>/<pageId>/inspect
47
46
  ```
48
47
 
49
- **抓图并返回图片(base64)**
48
+ **通过页面“下载图片”保存到本地**
50
49
 
51
50
  ```bash
52
- 请打开这个 CoDesign inspect 链接 把截图以 base64 返回给我:https://codesign.qq.com/app/design/<projectId>/<pageId>/inspect
53
- ```
54
-
55
- **抓图并保存到本地**
56
-
57
- ```bash
58
- 请打开这个 CoDesign inspect 链接,获取图片,把截图保存到本地,并告诉我保存路径:https://codesign.qq.com/app/design/<projectId>/<pageId>/inspect
51
+ 调用MCP 打开CoDesign页面,下载图片保存到本地,并告诉我保存路径。
52
+ 传入参数url为:https://codesign.qq.com/app/design/<projectId>/<pageId>/inspect
59
53
  ```
60
54
 
61
55
  **生成项目**
62
56
 
63
57
  ```bash
64
- 请打开这个 CoDesign inspect 链接,获取图片,把截图保存到本地,并识别图片内容生成Vue3+Vite项目:https://codesign.qq.com/app/design/<projectId>/<pageId>/inspect
58
+ 调用MCP 打开CoDesign页面,下载图片保存到本地,并识别图片内容生成Vue3+Vite项目。
59
+ 传入参数url为:https://codesign.qq.com/app/design/<projectId>/<pageId>/inspect
65
60
  ```
66
-
package/dist/index.js CHANGED
@@ -2980,7 +2980,7 @@ var require_compile = __commonJS({
2980
2980
  const schOrFunc = root.refs[ref];
2981
2981
  if (schOrFunc)
2982
2982
  return schOrFunc;
2983
- let _sch = resolve6.call(this, root, ref);
2983
+ let _sch = resolve5.call(this, root, ref);
2984
2984
  if (_sch === void 0) {
2985
2985
  const schema = (_a = root.localRefs) === null || _a === void 0 ? void 0 : _a[ref];
2986
2986
  const { schemaId } = this.opts;
@@ -3007,7 +3007,7 @@ var require_compile = __commonJS({
3007
3007
  function sameSchemaEnv(s1, s2) {
3008
3008
  return s1.schema === s2.schema && s1.root === s2.root && s1.baseId === s2.baseId;
3009
3009
  }
3010
- function resolve6(root, ref) {
3010
+ function resolve5(root, ref) {
3011
3011
  let sch;
3012
3012
  while (typeof (sch = this.refs[ref]) == "string")
3013
3013
  ref = sch;
@@ -3582,7 +3582,7 @@ var require_fast_uri = __commonJS({
3582
3582
  }
3583
3583
  return uri;
3584
3584
  }
3585
- function resolve6(baseURI, relativeURI, options) {
3585
+ function resolve5(baseURI, relativeURI, options) {
3586
3586
  const schemelessOptions = options ? Object.assign({ scheme: "null" }, options) : { scheme: "null" };
3587
3587
  const resolved = resolveComponent(parse3(baseURI, schemelessOptions), parse3(relativeURI, schemelessOptions), schemelessOptions, true);
3588
3588
  schemelessOptions.skipEscape = true;
@@ -3809,7 +3809,7 @@ var require_fast_uri = __commonJS({
3809
3809
  var fastUri = {
3810
3810
  SCHEMES,
3811
3811
  normalize,
3812
- resolve: resolve6,
3812
+ resolve: resolve5,
3813
3813
  resolveComponent,
3814
3814
  equal,
3815
3815
  serialize,
@@ -6800,7 +6800,7 @@ var require_dist = __commonJS({
6800
6800
 
6801
6801
  // src/index.ts
6802
6802
  import { createRequire } from "node:module";
6803
- import { dirname as dirname2, join as join4 } from "node:path";
6803
+ import { dirname, join as join4 } from "node:path";
6804
6804
  import { fileURLToPath } from "node:url";
6805
6805
 
6806
6806
  // node_modules/zod/v3/external.js
@@ -18875,7 +18875,7 @@ var Protocol = class {
18875
18875
  return;
18876
18876
  }
18877
18877
  const pollInterval = task2.pollInterval ?? this._options?.defaultTaskPollInterval ?? 1e3;
18878
- await new Promise((resolve6) => setTimeout(resolve6, pollInterval));
18878
+ await new Promise((resolve5) => setTimeout(resolve5, pollInterval));
18879
18879
  options?.signal?.throwIfAborted();
18880
18880
  }
18881
18881
  } catch (error2) {
@@ -18892,7 +18892,7 @@ var Protocol = class {
18892
18892
  */
18893
18893
  request(request, resultSchema, options) {
18894
18894
  const { relatedRequestId, resumptionToken, onresumptiontoken, task, relatedTask } = options ?? {};
18895
- return new Promise((resolve6, reject) => {
18895
+ return new Promise((resolve5, reject) => {
18896
18896
  const earlyReject = (error2) => {
18897
18897
  reject(error2);
18898
18898
  };
@@ -18970,7 +18970,7 @@ var Protocol = class {
18970
18970
  if (!parseResult.success) {
18971
18971
  reject(parseResult.error);
18972
18972
  } else {
18973
- resolve6(parseResult.data);
18973
+ resolve5(parseResult.data);
18974
18974
  }
18975
18975
  } catch (error2) {
18976
18976
  reject(error2);
@@ -19231,12 +19231,12 @@ var Protocol = class {
19231
19231
  }
19232
19232
  } catch {
19233
19233
  }
19234
- return new Promise((resolve6, reject) => {
19234
+ return new Promise((resolve5, reject) => {
19235
19235
  if (signal.aborted) {
19236
19236
  reject(new McpError(ErrorCode.InvalidRequest, "Request cancelled"));
19237
19237
  return;
19238
19238
  }
19239
- const timeoutId = setTimeout(resolve6, interval);
19239
+ const timeoutId = setTimeout(resolve5, interval);
19240
19240
  signal.addEventListener("abort", () => {
19241
19241
  clearTimeout(timeoutId);
19242
19242
  reject(new McpError(ErrorCode.InvalidRequest, "Request cancelled"));
@@ -20336,7 +20336,7 @@ var McpServer = class {
20336
20336
  let task = createTaskResult.task;
20337
20337
  const pollInterval = task.pollInterval ?? 5e3;
20338
20338
  while (task.status !== "completed" && task.status !== "failed" && task.status !== "cancelled") {
20339
- await new Promise((resolve6) => setTimeout(resolve6, pollInterval));
20339
+ await new Promise((resolve5) => setTimeout(resolve5, pollInterval));
20340
20340
  const updatedTask = await extra.taskStore.getTask(taskId);
20341
20341
  if (!updatedTask) {
20342
20342
  throw new McpError(ErrorCode.InternalError, `Task ${taskId} not found during polling`);
@@ -20979,12 +20979,12 @@ var StdioServerTransport = class {
20979
20979
  this.onclose?.();
20980
20980
  }
20981
20981
  send(message) {
20982
- return new Promise((resolve6) => {
20982
+ return new Promise((resolve5) => {
20983
20983
  const json = serializeMessage(message);
20984
20984
  if (this._stdout.write(json)) {
20985
- resolve6();
20985
+ resolve5();
20986
20986
  } else {
20987
- this._stdout.once("drain", resolve6);
20987
+ this._stdout.once("drain", resolve5);
20988
20988
  }
20989
20989
  });
20990
20990
  }
@@ -21008,8 +21008,7 @@ import { resolve as resolve4 } from "node:path";
21008
21008
  import { join } from "node:path";
21009
21009
  var ENV_EDGE_USER_DATA = "Z_MCP_CODESIGN_EDGE_USER_DATA";
21010
21010
  var DEFAULT_CODESIGN_LOGIN_URL = "https://codesign.qq.com/app/login";
21011
- var SCREEN_INSPECT_SELECTOR = ".screen-inspect:visible";
21012
- var DEFAULT_MCP_EDGE_PROFILE = join(process.cwd(), ".z-mcp-codesign-edge");
21011
+ var DEFAULT_MCP_EDGE_PROFILE = join(process.cwd(), "_z-mcp-edge");
21013
21012
 
21014
21013
  // src/lib/login-window.ts
21015
21014
  import { resolve as resolve2 } from "node:path";
@@ -21205,9 +21204,8 @@ var toolError = (text) => ({
21205
21204
  // src/tools/open-login.ts
21206
21205
  var codesignLoginInput = external_exports.object({
21207
21206
  loginUrl: external_exports.string().url().optional().describe(`\u53EF\u9009\uFF1B\u9ED8\u8BA4 ${DEFAULT_CODESIGN_LOGIN_URL}`),
21208
- userDataDir: external_exports.string().optional().describe("\u6D4F\u89C8\u5668\u7528\u6237\u6570\u636E\u6839\u76EE\u5F55\uFF1B\u672A\u4F20\u65F6\u540C\u6293\u56FE\u5DE5\u5177\uFF08\u73AF\u5883\u53D8\u91CF\u6216\u4E34\u65F6\u76EE\u5F55\uFF09\u3002"),
21209
21207
  profileDirectory: external_exports.string().optional().describe(
21210
- "User Data \u4E0B Edge profile \u76EE\u5F55\u540D\uFF08\u5982 Default\uFF09\uFF1B\u4E0E\u6293\u56FE\u5DE5\u5177 codesign_screen_inspect \u4F7F\u7528\u540C\u4E00\u76EE\u5F55\u65F6\u987B\u4F20\u76F8\u540C\u503C\u3002"
21208
+ "User Data \u4E0B Edge profile \u76EE\u5F55\u540D\uFF08\u5982 Default\uFF09\uFF1B\u4E0E\u6293\u56FE\u5DE5\u5177\u4F7F\u7528\u540C\u4E00\u76EE\u5F55\u65F6\u987B\u4F20\u76F8\u540C\u503C\u3002"
21211
21209
  )
21212
21210
  });
21213
21211
  function registerOpenLoginTool(mcpServer2, deps2) {
@@ -21226,7 +21224,7 @@ function registerOpenLoginTool(mcpServer2, deps2) {
21226
21224
  return toolError("loginUrl \u4E0D\u662F\u5408\u6CD5 URL\u3002");
21227
21225
  }
21228
21226
  }
21229
- const persistentDir = resolveEdgeUserDataDir(input.userDataDir);
21227
+ const persistentDir = resolveEdgeUserDataDir(void 0);
21230
21228
  const ok = await openOrNavigateCodesignLogin(
21231
21229
  deps2,
21232
21230
  loginUrl,
@@ -21243,77 +21241,7 @@ function registerOpenLoginTool(mcpServer2, deps2) {
21243
21241
  {
21244
21242
  loginUrl,
21245
21243
  userDataDir: resolve4(persistentDir),
21246
- hint: "\u5728\u5F39\u51FA\u7684\u6D4F\u89C8\u5668\u4E2D\u5B8C\u6210\u767B\u5F55\u540E\u5173\u95ED\u7A97\u53E3\uFF0C\u518D\u8C03\u7528 codesign_screen_inspect\u3002"
21247
- },
21248
- null,
21249
- 2
21250
- )
21251
- );
21252
- }
21253
- );
21254
- }
21255
-
21256
- // src/tools/save-image.ts
21257
- import { resolve as resolve5, dirname, extname } from "node:path";
21258
- import { mkdir as mkdir2, writeFile } from "node:fs/promises";
21259
- var saveImageInput = external_exports.object({
21260
- imageBase64: external_exports.string().min(1).describe(
21261
- "\u56FE\u7247 base64\uFF1B\u652F\u6301\u7EAF base64\uFF0C\u6216 data URL\uFF08\u5982 data:image/png;base64,AAAA...\uFF09\u3002"
21262
- ),
21263
- filePath: external_exports.string().min(1).describe("\u4FDD\u5B58\u8DEF\u5F84\uFF08\u76F8\u5BF9\u6216\u7EDD\u5BF9\uFF09\uFF0C\u5982 ./out/shot.png \u6216 D:/tmp/shot.png"),
21264
- mimeType: external_exports.string().optional().describe("\u53EF\u9009\uFF1B\u7528\u4E8E\u63A8\u65AD\u6269\u5C55\u540D\uFF08\u5F53 filePath \u65E0\u6269\u5C55\u540D\u65F6\uFF09\u3002\u5982 image/png")
21265
- });
21266
- function extFromMime(mimeType) {
21267
- const m = (mimeType || "").trim().toLowerCase();
21268
- if (!m) return null;
21269
- if (m === "image/png") return ".png";
21270
- if (m === "image/jpeg" || m === "image/jpg") return ".jpg";
21271
- if (m === "image/webp") return ".webp";
21272
- if (m === "image/gif") return ".gif";
21273
- return null;
21274
- }
21275
- function parseBase64(input) {
21276
- const s = input.trim();
21277
- const m = s.match(/^data:([^;]+);base64,(.*)$/i);
21278
- if (m) {
21279
- const mimeType = m[1]?.trim();
21280
- const base642 = (m[2] || "").trim();
21281
- if (!base642) return null;
21282
- return { mimeType, base64: base642 };
21283
- }
21284
- return { base64: s };
21285
- }
21286
- function registerSaveImageTool(mcpServer2) {
21287
- mcpServer2.registerTool(
21288
- "codesign_save_image",
21289
- {
21290
- description: "\u5C06 base64 \u56FE\u7247\u4FDD\u5B58\u4E3A\u672C\u5730\u6587\u4EF6\u8DEF\u5F84\u3002",
21291
- inputSchema: saveImageInput
21292
- },
21293
- async (input) => {
21294
- const parsed = parseBase64(input.imageBase64);
21295
- if (!parsed) return toolError("imageBase64 \u4E3A\u7A7A\u6216\u4E0D\u662F\u5408\u6CD5\u7684 base64/dataURL\u3002");
21296
- let filePath = input.filePath.trim();
21297
- if (!filePath) return toolError("filePath \u4E0D\u80FD\u4E3A\u7A7A\u3002");
21298
- const inferredExt = extFromMime(parsed.mimeType) || extFromMime(input.mimeType);
21299
- if (!extname(filePath) && inferredExt) {
21300
- filePath += inferredExt;
21301
- }
21302
- const absPath = resolve5(filePath);
21303
- await mkdir2(dirname(absPath), { recursive: true });
21304
- let buf;
21305
- try {
21306
- buf = Buffer.from(parsed.base64, "base64");
21307
- } catch {
21308
- return toolError("base64 \u89E3\u7801\u5931\u8D25\uFF08\u8BF7\u786E\u8BA4\u4E0D\u662F\u88AB\u622A\u65AD/\u4E0D\u662F base64\uFF09\u3002");
21309
- }
21310
- if (buf.length === 0) return toolError("base64 \u89E3\u7801\u540E\u4E3A\u7A7A\uFF08\u8BF7\u786E\u8BA4\u8F93\u5165\u6B63\u786E\uFF09\u3002");
21311
- await writeFile(absPath, buf);
21312
- return toolResult(
21313
- JSON.stringify(
21314
- {
21315
- savedPath: absPath,
21316
- bytes: buf.length
21244
+ hint: "\u5728\u5F39\u51FA\u7684\u6D4F\u89C8\u5668\u4E2D\u5B8C\u6210\u767B\u5F55\u540E\u5173\u95ED\u7A97\u53E3\uFF0C\u518D\u8C03\u7528\u6293\u56FE/\u5217\u8868/\u4E0B\u8F7D\u7B49\u5DE5\u5177\u3002"
21317
21245
  },
21318
21246
  null,
21319
21247
  2
@@ -21337,9 +21265,9 @@ async function withCodesignPage(deps2, input, fn, opts = {}) {
21337
21265
  ...opts.defaults ?? {}
21338
21266
  };
21339
21267
  const url = input.url;
21340
- const headless = input.headless ?? merged.headless;
21268
+ const headless = opts.allowHeaded ? merged.headless : true;
21341
21269
  const timeoutMs = input.timeoutMs ?? merged.timeoutMs;
21342
- const persistentDir = resolveEdgeUserDataDir(input.userDataDir);
21270
+ const persistentDir = resolveEdgeUserDataDir(void 0);
21343
21271
  const context = await chromium2.launchPersistentContext(
21344
21272
  persistentDir,
21345
21273
  persistentBrowserContextOptions({
@@ -21378,49 +21306,62 @@ async function withCodesignPage(deps2, input, fn, opts = {}) {
21378
21306
  }
21379
21307
  }
21380
21308
 
21381
- // src/lib/screen-inspect-png.ts
21382
- async function screenshotLargestScreenInspectPng(page) {
21383
- const handles = await page.$$(SCREEN_INSPECT_SELECTOR);
21384
- let best = null;
21385
- for (const h of handles) {
21386
- const box = await h.boundingBox();
21387
- if (!box || box.width < 1 || box.height < 1) continue;
21388
- const area = box.width * box.height;
21389
- const bestArea = best ? best.box.width * best.box.height : 0;
21390
- if (!best || area > bestArea) best = { handle: h, box };
21391
- }
21392
- if (!best) return null;
21393
- const buffer = Buffer.from(await best.handle.screenshot({ type: "png" }));
21394
- return { buffer, boundingBox: best.box };
21395
- }
21396
-
21397
- // src/tools/screen-inspect.ts
21398
- var screenInspectInput = external_exports.object({
21399
- url: external_exports.string().url().describe("CoDesign \u9875\u9762\u5730\u5740"),
21400
- headless: external_exports.boolean().optional().describe("\u65E0\u5934\u6A21\u5F0F\uFF0C\u9ED8\u8BA4 true"),
21401
- timeoutMs: external_exports.number().int().positive().optional().describe("\u6574\u4F53\u8D85\u65F6\u6BEB\u79D2\u6570\uFF0C\u9ED8\u8BA4 60000"),
21402
- userDataDir: external_exports.string().optional().describe(
21403
- "\u6D4F\u89C8\u5668\u7528\u6237\u6570\u636E\u6839\u76EE\u5F55\uFF08\u987B\u5148\u9000\u51FA\u5360\u7528\u8BE5\u76EE\u5F55\u7684\u6D4F\u89C8\u5668\uFF09\u3002\u672A\u4F20\u65F6\u4F7F\u7528\u73AF\u5883\u53D8\u91CF Z_MCP_CODESIGN_EDGE_USER_DATA \u6216\u7CFB\u7EDF\u4E34\u65F6\u76EE\u5F55\u4E0B\u9ED8\u8BA4\u914D\u7F6E\u3002"
21309
+ // src/tools/screen-list.ts
21310
+ var screenListInput = external_exports.object({
21311
+ url: external_exports.string().url().describe(
21312
+ "CoDesign \u8BBE\u8BA1\u9875\u9762\u5730\u5740\uFF0C\u5982 https://codesign.qq.com/app/design/<projectId>/<pageId>/inspect"
21404
21313
  ),
21314
+ timeoutMs: external_exports.number().int().positive().optional().describe("\u6574\u4F53\u8D85\u65F6\u6BEB\u79D2\u6570\uFF0C\u9ED8\u8BA4 60000"),
21405
21315
  profileDirectory: external_exports.string().optional().describe("User Data \u4E0B\u914D\u7F6E\u540D\uFF0C\u5982 Default\u3002")
21406
21316
  });
21407
- async function runScreenInspect(deps2, input) {
21317
+ async function runScreenList(deps2, input) {
21408
21318
  return withCodesignPage(
21409
21319
  deps2,
21410
21320
  input,
21411
21321
  async (page) => {
21412
- const shot = await screenshotLargestScreenInspectPng(page);
21413
- if (!shot) {
21414
- return toolError("\u672A\u627E\u5230\u53EF\u89C1\u7684 .screen-inspect \u533A\u57DF\u3002");
21322
+ const pages = await page.evaluate(() => {
21323
+ const items = document.querySelectorAll(".screen-list__item");
21324
+ const result = [];
21325
+ for (const item of items) {
21326
+ const id = item.getAttribute("id") || "";
21327
+ const match = id.match(/^screen-(\d+)$/);
21328
+ if (!match) continue;
21329
+ const pageId = match[1];
21330
+ let name = "";
21331
+ const nameEl = item.querySelector(
21332
+ "[class*='name'], [class*='title'], [class*='label']"
21333
+ );
21334
+ if (nameEl) {
21335
+ name = nameEl.textContent?.trim() || "";
21336
+ }
21337
+ if (!name) {
21338
+ name = item.textContent?.trim().replace(/\s+/g, " ") || "";
21339
+ }
21340
+ result.push({
21341
+ name,
21342
+ pageId
21343
+ });
21344
+ }
21345
+ return result;
21346
+ });
21347
+ if (pages.length === 0) {
21348
+ return toolError("\u672A\u627E\u5230\u5DE6\u4FA7\u83DC\u5355\u4E2D\u7684\u9875\u9762\u5217\u8868\u3002");
21415
21349
  }
21350
+ const currentUrl = new URL(input.url);
21351
+ const pathParts = currentUrl.pathname.split("/");
21352
+ const projectId = pathParts.find((p) => /^\d{10,}$/.test(p)) || "";
21353
+ const currentPageId = pathParts.find((p) => /^\d+$/.test(p)) || "";
21354
+ const resultData = pages.map((p) => ({
21355
+ name: p.name,
21356
+ pageId: p.pageId,
21357
+ url: projectId ? `https://codesign.qq.com/app/design/${projectId}/${p.pageId}/inspect` : currentPageId ? input.url.replace(`/${currentPageId}/`, `/${p.pageId}/`) : input.url
21358
+ }));
21416
21359
  return toolResult(
21417
21360
  JSON.stringify(
21418
21361
  {
21419
- url: input.url,
21420
- title: await page.title(),
21421
- mimeType: "image/png",
21422
- imageBase64: shot.buffer.toString("base64"),
21423
- boundingBox: shot.boundingBox
21362
+ projectId,
21363
+ totalCount: resultData.length,
21364
+ pages: resultData
21424
21365
  },
21425
21366
  null,
21426
21367
  2
@@ -21428,24 +21369,24 @@ async function runScreenInspect(deps2, input) {
21428
21369
  );
21429
21370
  },
21430
21371
  {
21431
- waitForSelector: { selector: ".screen-inspect", state: "visible" },
21372
+ waitForSelector: { selector: ".screen-list__item", state: "attached" },
21432
21373
  defaults: { afterLoadWaitMs: 250 }
21433
21374
  }
21434
21375
  );
21435
21376
  }
21436
- function registerScreenInspectTool(mcpServer2, deps2) {
21377
+ function registerScreenListTool(mcpServer2, deps2) {
21437
21378
  mcpServer2.registerTool(
21438
- "codesign_screen_inspect",
21379
+ "codesign_screen_list",
21439
21380
  {
21440
- description: "\u6253\u5F00 CoDesign \u9875\u9762\uFF0C\u4EC5\u622A\u53D6\u9762\u79EF\u6700\u5927\u7684\u53EF\u89C1 .screen-inspect \u533A\u57DF\uFF0C\u4EE5 PNG \u7684 base64 \u8FD4\u56DE\u3002",
21441
- inputSchema: screenInspectInput
21381
+ description: "\u6839\u636ECoDesign\u9875\u9762\u5730\u5740\u83B7\u53D6\u9879\u76EE\u6240\u6709\u9875\u9762\u5217\u8868\uFF0C\u8FD4\u56DE\u9879\u76EEID\u3001\u603B\u9875\u9762\u6570\u3001\u9875\u9762\u5217\u8868\u3002",
21382
+ inputSchema: screenListInput
21442
21383
  },
21443
- (input) => runScreenInspect(deps2, input)
21384
+ (input) => runScreenList(deps2, input)
21444
21385
  );
21445
21386
  }
21446
21387
 
21447
- // src/tools/screen-inspect-save.ts
21448
- import { mkdir as mkdir3, writeFile as writeFile2 } from "node:fs/promises";
21388
+ // src/tools/screen-viewer-download-save.ts
21389
+ import { mkdir as mkdir2 } from "node:fs/promises";
21449
21390
  import path from "node:path";
21450
21391
  function safeSegment(s) {
21451
21392
  return s.replace(/[^a-zA-Z0-9._-]+/g, "-").replace(/-+/g, "-").replace(/^-|-$/g, "");
@@ -21465,124 +21406,78 @@ function deriveCodesignIds(url) {
21465
21406
  return {};
21466
21407
  }
21467
21408
  }
21468
- var screenInspectSaveInput = external_exports.object({
21409
+ var screenViewerDownloadSaveInput = external_exports.object({
21469
21410
  url: external_exports.string().url().describe("CoDesign \u9875\u9762\u5730\u5740"),
21470
- headless: external_exports.boolean().optional().describe("\u65E0\u5934\u6A21\u5F0F\uFF0C\u9ED8\u8BA4 true"),
21471
21411
  timeoutMs: external_exports.number().int().positive().optional().describe("\u6574\u4F53\u8D85\u65F6\u6BEB\u79D2\u6570\uFF0C\u9ED8\u8BA4 60000"),
21472
- userDataDir: external_exports.string().optional().describe(
21473
- "\u6D4F\u89C8\u5668\u7528\u6237\u6570\u636E\u6839\u76EE\u5F55\uFF08\u987B\u5148\u9000\u51FA\u5360\u7528\u8BE5\u76EE\u5F55\u7684\u6D4F\u89C8\u5668\uFF09\u3002\u672A\u4F20\u65F6\u4F7F\u7528\u73AF\u5883\u53D8\u91CF Z_MCP_CODESIGN_EDGE_USER_DATA \u6216\u7CFB\u7EDF\u4E34\u65F6\u76EE\u5F55\u4E0B\u9ED8\u8BA4\u914D\u7F6E\u3002"
21474
- ),
21475
21412
  profileDirectory: external_exports.string().optional().describe("User Data \u4E0B\u914D\u7F6E\u540D\uFF0C\u5982 Default\u3002")
21476
21413
  });
21477
- async function runScreenInspectSave(deps2, input) {
21414
+ async function clickFirstVisibleText(page, candidates) {
21415
+ for (const text of candidates) {
21416
+ const loc = page.getByText(text, { exact: true }).first();
21417
+ try {
21418
+ if (await loc.isVisible()) {
21419
+ await loc.click();
21420
+ return true;
21421
+ }
21422
+ } catch {
21423
+ }
21424
+ }
21425
+ return false;
21426
+ }
21427
+ async function runScreenViewerDownloadSave(deps2, input) {
21478
21428
  return withCodesignPage(
21479
21429
  deps2,
21480
21430
  input,
21481
21431
  async (page) => {
21482
- const shot = await screenshotLargestScreenInspectPng(page);
21483
- if (!shot) {
21484
- return toolError("\u672A\u627E\u5230\u53EF\u89C1\u7684 .screen-inspect \u533A\u57DF\u3002");
21432
+ const action = page.locator(".screen-viewer__action").first();
21433
+ try {
21434
+ await action.waitFor({ state: "visible", timeout: 5e3 });
21435
+ } catch {
21436
+ return toolError(
21437
+ "\u672A\u627E\u5230\u53EF\u89C1\u7684 .screen-viewer__action\uFF08\u53EF\u80FD\u672A\u8FDB\u5165\u53EF\u4E0B\u8F7D\u7684\u753B\u677F\u89C6\u56FE\uFF09\u3002"
21438
+ );
21439
+ }
21440
+ const downloadPromise = page.waitForEvent("download", {
21441
+ timeout: Math.max(1e4, Math.min(45e3, input.timeoutMs ?? 6e4))
21442
+ });
21443
+ await action.click();
21444
+ const clicked = await clickFirstVisibleText(page, [
21445
+ "\u4E0B\u8F7D\u56FE\u7247",
21446
+ "\u4E0B\u8F7D\u539F\u56FE",
21447
+ "\u4E0B\u8F7D"
21448
+ ]);
21449
+ if (!clicked) {
21450
+ return toolError(
21451
+ "\u672A\u627E\u5230\u201C\u4E0B\u8F7D\u56FE\u7247\u201D\u6309\u94AE/\u83DC\u5355\u9879\uFF08\u53EF\u80FD\u9875\u9762\u7ED3\u6784\u53D8\u66F4\u6216\u672A\u767B\u5F55\uFF09\u3002"
21452
+ );
21485
21453
  }
21486
- const outDir = path.resolve(process.cwd(), ".codesign_screen_imgs");
21454
+ let download;
21455
+ try {
21456
+ download = await downloadPromise;
21457
+ } catch {
21458
+ return toolError("\u672A\u6355\u83B7\u5230\u4E0B\u8F7D\u4E8B\u4EF6\uFF08\u70B9\u51FB\u540E\u672A\u89E6\u53D1\u4E0B\u8F7D\uFF09\u3002");
21459
+ }
21460
+ const suggested = download.suggestedFilename() || "codesign_download.png";
21461
+ const ext = path.extname(suggested) || ".png";
21462
+ const outDir = path.resolve(process.cwd(), "_z-mcp-imgs");
21487
21463
  const { projectId, pageId } = deriveCodesignIds(input.url);
21488
21464
  const filenameParts = [
21489
21465
  "codesign",
21466
+ "viewer_download",
21490
21467
  projectId ? `p${safeSegment(projectId)}` : void 0,
21491
21468
  pageId ? `s${safeSegment(pageId)}` : void 0,
21492
21469
  timestampCompact(/* @__PURE__ */ new Date())
21493
21470
  ].filter(Boolean);
21494
- const finalPath = path.join(outDir, `${filenameParts.join("_")}.png`);
21495
- await mkdir3(path.dirname(finalPath), { recursive: true });
21496
- await writeFile2(finalPath, shot.buffer);
21471
+ const finalPath = path.join(outDir, `${filenameParts.join("_")}${ext}`);
21472
+ await mkdir2(path.dirname(finalPath), { recursive: true });
21473
+ await download.saveAs(finalPath);
21497
21474
  return toolResult(
21498
21475
  JSON.stringify(
21499
21476
  {
21500
21477
  url: input.url,
21501
21478
  title: await page.title(),
21502
- mimeType: "image/png",
21503
- savedPath: finalPath,
21504
- boundingBox: shot.boundingBox
21505
- },
21506
- null,
21507
- 2
21508
- )
21509
- );
21510
- },
21511
- {
21512
- waitForSelector: { selector: ".screen-inspect", state: "visible" },
21513
- defaults: { afterLoadWaitMs: 250 }
21514
- }
21515
- );
21516
- }
21517
- function registerScreenInspectSaveTool(mcpServer2, deps2) {
21518
- mcpServer2.registerTool(
21519
- "codesign_screen_inspect_save",
21520
- {
21521
- description: "\u6253\u5F00 CoDesign \u9875\u9762\uFF0C\u4EC5\u622A\u53D6\u9762\u79EF\u6700\u5927\u7684\u53EF\u89C1 .screen-inspect \u533A\u57DF\uFF0C\u4FDD\u5B58\u4E3A\u672C\u5730 PNG \u6587\u4EF6\u5E76\u8FD4\u56DE\u8DEF\u5F84\uFF08\u9ED8\u8BA4\u4FDD\u5B58\u5230\u9879\u76EE\u6839\u76EE\u5F55 .codesign_screen_imgs\uFF09\u3002",
21522
- inputSchema: screenInspectSaveInput
21523
- },
21524
- (input) => runScreenInspectSave(deps2, input)
21525
- );
21526
- }
21527
-
21528
- // src/tools/screen-list.ts
21529
- var screenListInput = external_exports.object({
21530
- url: external_exports.string().url().describe(
21531
- "CoDesign \u8BBE\u8BA1\u9875\u9762\u5730\u5740\uFF0C\u5982 https://codesign.qq.com/app/design/<projectId>/<pageId>/inspect"
21532
- ),
21533
- headless: external_exports.boolean().optional().describe("\u65E0\u5934\u6A21\u5F0F\uFF0C\u9ED8\u8BA4 true"),
21534
- timeoutMs: external_exports.number().int().positive().optional().describe("\u6574\u4F53\u8D85\u65F6\u6BEB\u79D2\u6570\uFF0C\u9ED8\u8BA4 60000"),
21535
- userDataDir: external_exports.string().optional().describe("\u6D4F\u89C8\u5668\u7528\u6237\u6570\u636E\u6839\u76EE\u5F55\uFF1B\u672A\u4F20\u65F6\u540C\u6293\u56FE\u5DE5\u5177\uFF08\u73AF\u5883\u53D8\u91CF\u6216\u4E34\u65F6\u76EE\u5F55\uFF09\u3002"),
21536
- profileDirectory: external_exports.string().optional().describe("User Data \u4E0B\u914D\u7F6E\u540D\uFF0C\u5982 Default\u3002")
21537
- });
21538
- async function runScreenList(deps2, input) {
21539
- return withCodesignPage(
21540
- deps2,
21541
- input,
21542
- async (page) => {
21543
- const pages = await page.evaluate(() => {
21544
- const items = document.querySelectorAll(".screen-list__item");
21545
- const result = [];
21546
- for (const item of items) {
21547
- const id = item.getAttribute("id") || "";
21548
- const match = id.match(/^screen-(\d+)$/);
21549
- if (!match) continue;
21550
- const pageId = match[1];
21551
- let name = "";
21552
- const nameEl = item.querySelector(
21553
- "[class*='name'], [class*='title'], [class*='label']"
21554
- );
21555
- if (nameEl) {
21556
- name = nameEl.textContent?.trim() || "";
21557
- }
21558
- if (!name) {
21559
- name = item.textContent?.trim().replace(/\s+/g, " ") || "";
21560
- }
21561
- result.push({
21562
- name,
21563
- pageId
21564
- });
21565
- }
21566
- return result;
21567
- });
21568
- if (pages.length === 0) {
21569
- return toolError("\u672A\u627E\u5230\u5DE6\u4FA7\u83DC\u5355\u4E2D\u7684\u9875\u9762\u5217\u8868\u3002");
21570
- }
21571
- const currentUrl = new URL(input.url);
21572
- const pathParts = currentUrl.pathname.split("/");
21573
- const projectId = pathParts.find((p) => /^\d{10,}$/.test(p)) || "";
21574
- const currentPageId = pathParts.find((p) => /^\d+$/.test(p)) || "";
21575
- const resultData = pages.map((p) => ({
21576
- name: p.name,
21577
- pageId: p.pageId,
21578
- url: projectId ? `https://codesign.qq.com/app/design/${projectId}/${p.pageId}/inspect` : currentPageId ? input.url.replace(`/${currentPageId}/`, `/${p.pageId}/`) : input.url
21579
- }));
21580
- return toolResult(
21581
- JSON.stringify(
21582
- {
21583
- projectId,
21584
- totalCount: resultData.length,
21585
- pages: resultData
21479
+ suggestedFilename: suggested,
21480
+ savedPath: finalPath
21586
21481
  },
21587
21482
  null,
21588
21483
  2
@@ -21590,35 +21485,33 @@ async function runScreenList(deps2, input) {
21590
21485
  );
21591
21486
  },
21592
21487
  {
21593
- waitForSelector: { selector: ".screen-list__item", state: "attached" },
21594
- defaults: { afterLoadWaitMs: 250 }
21488
+ waitForSelector: { selector: ".screen-viewer__action", state: "visible" },
21489
+ defaults: { afterLoadWaitMs: 600, networkIdleMaxWaitMs: 2e3 }
21595
21490
  }
21596
21491
  );
21597
21492
  }
21598
- function registerScreenListTool(mcpServer2, deps2) {
21493
+ function registerScreenViewerDownloadSaveTool(mcpServer2, deps2) {
21599
21494
  mcpServer2.registerTool(
21600
- "codesign_screen_list",
21495
+ "codesign_screen_viewer_download_save",
21601
21496
  {
21602
- description: "\u6839\u636ECoDesign\u9875\u9762\u5730\u5740\u83B7\u53D6\u9879\u76EE\u6240\u6709\u9875\u9762\u5217\u8868\uFF0C\u8FD4\u56DE\u9879\u76EEID\u3001\u603B\u9875\u9762\u6570\u3001\u9875\u9762\u5217\u8868\u3002",
21603
- inputSchema: screenListInput
21497
+ description: "\u6253\u5F00 CoDesign \u9875\u9762\uFF0C\u70B9\u51FB\u201C\u4E0B\u8F7D\u56FE\u7247\u201D\uFF0C\u6355\u83B7\u6D4F\u89C8\u5668\u4E0B\u8F7D\u5E76\u4FDD\u5B58\u5230\u9879\u76EE\u6839\u76EE\u5F55 _z-mcp-imgs/\u3002\u4E0B\u8F7DCoDesign\u9875\u9762\u56FE\u7247\u5230\u672C\u5730",
21498
+ inputSchema: screenViewerDownloadSaveInput
21604
21499
  },
21605
- (input) => runScreenList(deps2, input)
21500
+ (input) => runScreenViewerDownloadSave(deps2, input)
21606
21501
  );
21607
21502
  }
21608
21503
 
21609
21504
  // src/tools/register-all.ts
21610
21505
  function registerAllCodesignTools(mcpServer2, deps2) {
21611
- registerScreenInspectTool(mcpServer2, deps2);
21612
- registerScreenInspectSaveTool(mcpServer2, deps2);
21506
+ registerScreenViewerDownloadSaveTool(mcpServer2, deps2);
21613
21507
  registerScreenListTool(mcpServer2, deps2);
21614
21508
  registerOpenLoginTool(mcpServer2, deps2);
21615
- registerSaveImageTool(mcpServer2);
21616
21509
  }
21617
21510
 
21618
21511
  // src/index.ts
21619
21512
  var SERVER_NAME = "z-mcp-codesign";
21620
21513
  var _require = createRequire(import.meta.url);
21621
- var _dirname = dirname2(fileURLToPath(import.meta.url));
21514
+ var _dirname = dirname(fileURLToPath(import.meta.url));
21622
21515
  var SERVER_VERSION = _require(join4(_dirname, "..", "package.json")).version ?? "1.0.0";
21623
21516
  var USAGE = "npx -y z-mcp-codesign --stdio";
21624
21517
  var args = process.argv.slice(2);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "z-mcp-codesign",
3
- "version": "0.1.2",
3
+ "version": "0.1.4",
4
4
  "type": "module",
5
5
  "description": "An MCP server to capture images from Tencent CoDesign pages via Edge",
6
6
  "main": "dist/index.js",