dsh-eda-mcp 0.6.2 → 0.7.1
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/CHANGELOG.md +64 -1
- package/CONTRIBUTING.md +1 -1
- package/LICENSE +55 -55
- package/README.md +27 -4
- package/README.zh.md +26 -5
- package/docs/release.md +1 -1
- package/docs/tools.md +30 -0
- package/dsh-eda-bridge-extension-0.4.0.zip +0 -0
- package/jlc-extension/api-bridge.js +66 -12
- package/jlc-extension/extension.json +1 -1
- package/lib/bridge.mjs +680 -16
- package/lib/client.js +34 -1
- package/lib/index.js +24 -5
- package/package.json +5 -4
- package/src/bridge/eda-launch.ts +267 -0
- package/src/bridge/index.ts +582 -18
- package/src/client/EdaMcpSettingsCard.tsx +38 -1
- package/src/client/locales.ts +4 -0
- package/src/index.ts +29 -2
- package/src/routes.ts +3 -1
- package/test/eda-launch.test.ts +114 -0
- package/test/seams.test.ts +14 -0
- package/dsh-eda-bridge-extension.zip +0 -0
package/CHANGELOG.md
CHANGED
|
@@ -1,8 +1,71 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 0.7.1 — 2026-10-20
|
|
4
|
+
|
|
5
|
+
构建产物修正:**扩展 zip 只保留带版本号的那一个**。
|
|
6
|
+
|
|
7
|
+
- 0.7.0 里为了不打破旧引用,同时产出了 `dsh-eda-bridge-extension-0.4.0.zip` 和同内容的 `dsh-eda-bridge-extension.zip`(SHA256 完全一致)。两个同内容文件摆在一起只会让人分不清哪份是当前版本,属于多余的冗余,删掉。
|
|
8
|
+
- 现在只生成 `dsh-eda-bridge-extension-<线协议版本>.zip`。所有引用改为**按版本解析**:
|
|
9
|
+
- `src/index.ts`:从 `jlc-extension/extension.json` 读版本号拼出文件名(找不到时回退旧的固定名,兼容旧布局);
|
|
10
|
+
- `src/routes.ts`:下载响应头用实际文件名的 `basename`,浏览器另存下来自带版本号;
|
|
11
|
+
- 设置卡片的下载链接去掉固定的 `download` 属性,改由响应头决定文件名;
|
|
12
|
+
- 桥接里所有提示语改成 `dsh-eda-bridge-extension-' + VERSION + '.zip`;
|
|
13
|
+
- README(中/英)、CONTRIBUTING、`docs/release.md`、扩展头部注释同步。
|
|
14
|
+
- `package.json files` 去掉固定名条目,只保留 `dsh-eda-bridge-extension-*.zip`。
|
|
15
|
+
|
|
16
|
+
线协议仍是 **0.4.0**,扩展内容未变——已导入 0.4.0 的机器**无需重新导入**。
|
|
17
|
+
|
|
18
|
+
## 0.7.0 — 2026-10-20
|
|
19
|
+
|
|
20
|
+
主题:把「操作嘉立创EDA」从**画图**扩展到**应用与工程**层面,并把这一路真机联调暴露的 bug 全部修掉。
|
|
21
|
+
|
|
22
|
+
> **线协议 0.3.5 → 0.4.0**:扩展白名单有变动,**必须重新导入** `dsh-eda-bridge-extension-0.4.0.zip`,否则新工具会返回 `API method not allowed`。用 `eda_status` 的 `extensionVersions` 核对。
|
|
23
|
+
|
|
24
|
+
### 新增工具(83 → 97)
|
|
25
|
+
|
|
26
|
+
- **应用控制**:`eda_launch`(启动 EDA,可选带工程,并等扩展连上;Windows 上把启动交给 `explorer.exe`,见下)、`eda_close`(先优雅后强制,返回关闭前后进程数)。
|
|
27
|
+
- **工程**:`eda_list_projects`(无过滤时跨本地目录聚合)、`eda_get_project`、`eda_open_project`(uuid 或**中文工程名**)、`eda_create_project`、`eda_delete_project`、`eda_modify_project`(改名/描述)、`eda_copy_project`、`eda_import_project`(**导入即建工程**,需目标团队)、`eda_export_project`(给 `outPath` 直接落盘)、`eda_close_document`。
|
|
28
|
+
- **工作区**:`eda_get_workspace`(当前工作区 + 团队)、`eda_list_teams`、`eda_list_folders`。
|
|
29
|
+
- **设置卡片**新增「嘉立创EDA 可执行文件(可选)」输入框(`edaExecutable`,留空自动探测)。
|
|
30
|
+
|
|
31
|
+
### 真机验证过的能力(EDA 3.2.186、已登录、半离线)
|
|
32
|
+
|
|
33
|
+
| 能力 | 结果 |
|
|
34
|
+
|---|---|
|
|
35
|
+
| 启动 / 关闭 EDA | ✅ `eda_launch` 走 explorer 交接,5 个进程 + 窗口,扩展 9 s 内连上;`eda_close` 5 → 0 进程(强制路径) |
|
|
36
|
+
| 列出全部本地工程 | ✅ 跨目录聚合,7 → 8 个 |
|
|
37
|
+
| 按中文工程名打开工程 | ✅ 两次成功 |
|
|
38
|
+
| 打开原理图页并读取 | ✅ 页 `P1`、器件 `DC1`、板卡 `波形变换电路` |
|
|
39
|
+
| 导出工程文件 | ✅ 1 056 450 字节 / 1.9 s |
|
|
40
|
+
| **导入工程(= 变相新建)** | ✅ 新建出 `DSH-IMPORT-TEST`(4.7 s) |
|
|
41
|
+
| `createProject` / `deleteProject` | ❌ 该构建无副作用(详见已知限制) |
|
|
42
|
+
|
|
43
|
+
### 修掉的真 bug(全部由真机暴露,不是推测)
|
|
44
|
+
|
|
45
|
+
1. **扩展硬编码版本常量**:`api-bridge.js` 里手写的 `var VERSION = '0.3.5'` 不随清单更新,导致「清单显示 0.3.6、运行时永远报 0.3.5」。改为构建时从 `extension.json` 注入 `__EXTENSION_VERSION__`,并有 3 条单测禁止再出现硬编码。
|
|
46
|
+
2. **僵尸客户端吞调用**:`firstReadyClient()` 取第一个 `ready` socket,页面跳转留下的半开连接照样 `ready`,调用进黑洞直到 60 s 超时(就是那次 `MCP error -32001`)。改为「每条数据刷新 lastSeenAt + 优先最活跃 + 静默 >60 s 只作兜底 + 只读方法超时才换客户端重试一次」。
|
|
47
|
+
3. **连接状态会撒谎**:`connectedClients`、`waitForExtensionConnection`、`/healthz` 全用原始 `ready` → 改用活跃客户端。
|
|
48
|
+
4. **`optional is not defined`**:`optional()` 只定义在 `dsh.internal.pcbCreateRegion` 内部,导出/导入助手看不见 → 提升为共享函数并清掉 `optional2/optional3`。
|
|
49
|
+
5. **团队/工程/文件夹被摘要成空对象**:`summarizePrimitive` 字段名不匹配 → 这些结构改为原样返回。
|
|
50
|
+
6. **按名字找不到工程**:`getAllProjectsUuid()` 在半离线模式恒为空(本地目录才是 "team")→ 无过滤时遍历团队聚合。
|
|
51
|
+
7. **`reg query` 不带 `/s` 只返回默认值**,列不出 `OpenWithProgids`;拼键时少了分隔符;probe 缓存把「列全部值」和「默认值」当同一键。
|
|
52
|
+
8. **GBK 解码**:`reg.exe` 按控制台代码页输出、progid 名是中文,UTF-8 解码出替换字符就拼不回注册表键 → 新增 `decodeRegistryOutput()`。
|
|
53
|
+
9. **直接 spawn 起不来 GUI**:`lceda-pro.exe` 直接 spawn(含 ShellExecute)以退出码 0 立刻退出,只有交给 `explorer.exe` 才行 → `eda_launch` 在 Windows 固定走 explorer + 临时 `.cmd`。
|
|
54
|
+
10. **`eda_close` 在非 Windows 把进程数当 -1**(等于谎报已关闭)→ 改用 `pgrep -f` 真实计数。
|
|
55
|
+
|
|
56
|
+
### 可执行契约检查(新增)
|
|
57
|
+
|
|
58
|
+
`scripts/contract-check.mjs`(接入 `pnpm check` 与 CI)机械校验四类**已真实发生过**的错配:工具声明 ↔ handler(自动解析 11 个别名)、桥接调用的每个 API ↔ 扩展白名单(`ALLOWED_API` + `FILE_METHODS`,160 条)、`dsh.internal.*` ↔ 扩展实现、`objectSchema` 的 required ↔ properties(94+ 个 schema)。
|
|
59
|
+
|
|
60
|
+
### 构建与发布
|
|
61
|
+
|
|
62
|
+
- zip 包名带版本号:`dsh-eda-bridge-extension-0.4.0.zip`,同时保留 `dsh-eda-bridge-extension.zip` 别名(内容一致;分发放带版本号的那个)。
|
|
63
|
+
- 工具总数 **97**,单测 **110** 条全通过,`docs/tools.md` 自动重生成(0 未分组)。
|
|
64
|
+
|
|
65
|
+
|
|
3
66
|
## 0.6.2
|
|
4
67
|
|
|
5
|
-
>
|
|
68
|
+
> **已发布到 npm**(`dsh-eda-mcp@0.6.2`,首个版本)。下面 0.6.1 的内容从未发布过,同批一起带上。
|
|
6
69
|
|
|
7
70
|
### 设置页:勾选框改为官方开关组件
|
|
8
71
|
|
package/CONTRIBUTING.md
CHANGED
|
@@ -33,7 +33,7 @@ packaged with PowerShell `Compress-Archive`.
|
|
|
33
33
|
pnpm install
|
|
34
34
|
pnpm typecheck # tsc --noEmit
|
|
35
35
|
pnpm test # unit tests + the seam compatibility test
|
|
36
|
-
pnpm build # lib/*.js, lib/bridge.mjs, lib/client.js, dsh-eda-bridge-extension
|
|
36
|
+
pnpm build # lib/*.js, lib/bridge.mjs, lib/client.js, dsh-eda-bridge-extension-<version>.zip
|
|
37
37
|
pnpm check # all three, in that order
|
|
38
38
|
pnpm docs # regenerate docs/tools.md from the built bridge
|
|
39
39
|
pnpm seam-check # assert the plugin's DSH contracts against another version line
|
package/LICENSE
CHANGED
|
@@ -1,55 +1,55 @@
|
|
|
1
|
-
Apache License
|
|
2
|
-
|
|
3
|
-
Version 2.0, January 2004
|
|
4
|
-
|
|
5
|
-
http://www.apache.org/licenses/
|
|
6
|
-
|
|
7
|
-
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
|
8
|
-
|
|
9
|
-
1. Definitions.
|
|
10
|
-
|
|
11
|
-
"License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document.
|
|
12
|
-
|
|
13
|
-
"Licensor" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License.
|
|
14
|
-
|
|
15
|
-
"Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity.
|
|
16
|
-
|
|
17
|
-
"You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this License.
|
|
18
|
-
|
|
19
|
-
"Source" form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files.
|
|
20
|
-
|
|
21
|
-
"Object" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types.
|
|
22
|
-
|
|
23
|
-
"Work" shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below).
|
|
24
|
-
|
|
25
|
-
"Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof.
|
|
26
|
-
|
|
27
|
-
"Contribution" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as "Not a Contribution."
|
|
28
|
-
|
|
29
|
-
"Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work.
|
|
30
|
-
|
|
31
|
-
2. Grant of Copyright License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form.
|
|
32
|
-
|
|
33
|
-
3. Grant of Patent License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed.
|
|
34
|
-
|
|
35
|
-
4. Redistribution. You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions:
|
|
36
|
-
|
|
37
|
-
You must give any other recipients of the Work or Derivative Works a copy of this License; and
|
|
38
|
-
|
|
39
|
-
You must cause any modified files to carry prominent notices stating that You changed the files; and
|
|
40
|
-
|
|
41
|
-
You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and
|
|
42
|
-
|
|
43
|
-
If the Work includes a "NOTICE" text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License. You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License.
|
|
44
|
-
|
|
45
|
-
5. Submission of Contributions. Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions.
|
|
46
|
-
|
|
47
|
-
6. Trademarks. This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file.
|
|
48
|
-
|
|
49
|
-
7. Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License.
|
|
50
|
-
|
|
51
|
-
8. Limitation of Liability. In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages.
|
|
52
|
-
|
|
53
|
-
9. Accepting Warranty or Additional Liability. While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability.
|
|
54
|
-
|
|
55
|
-
END OF TERMS AND CONDITIONS
|
|
1
|
+
Apache License
|
|
2
|
+
|
|
3
|
+
Version 2.0, January 2004
|
|
4
|
+
|
|
5
|
+
http://www.apache.org/licenses/
|
|
6
|
+
|
|
7
|
+
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
|
8
|
+
|
|
9
|
+
1. Definitions.
|
|
10
|
+
|
|
11
|
+
"License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document.
|
|
12
|
+
|
|
13
|
+
"Licensor" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License.
|
|
14
|
+
|
|
15
|
+
"Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity.
|
|
16
|
+
|
|
17
|
+
"You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this License.
|
|
18
|
+
|
|
19
|
+
"Source" form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files.
|
|
20
|
+
|
|
21
|
+
"Object" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types.
|
|
22
|
+
|
|
23
|
+
"Work" shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below).
|
|
24
|
+
|
|
25
|
+
"Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof.
|
|
26
|
+
|
|
27
|
+
"Contribution" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as "Not a Contribution."
|
|
28
|
+
|
|
29
|
+
"Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work.
|
|
30
|
+
|
|
31
|
+
2. Grant of Copyright License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form.
|
|
32
|
+
|
|
33
|
+
3. Grant of Patent License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed.
|
|
34
|
+
|
|
35
|
+
4. Redistribution. You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions:
|
|
36
|
+
|
|
37
|
+
You must give any other recipients of the Work or Derivative Works a copy of this License; and
|
|
38
|
+
|
|
39
|
+
You must cause any modified files to carry prominent notices stating that You changed the files; and
|
|
40
|
+
|
|
41
|
+
You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and
|
|
42
|
+
|
|
43
|
+
If the Work includes a "NOTICE" text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License. You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License.
|
|
44
|
+
|
|
45
|
+
5. Submission of Contributions. Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions.
|
|
46
|
+
|
|
47
|
+
6. Trademarks. This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file.
|
|
48
|
+
|
|
49
|
+
7. Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License.
|
|
50
|
+
|
|
51
|
+
8. Limitation of Liability. In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages.
|
|
52
|
+
|
|
53
|
+
9. Accepting Warranty or Additional Liability. While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability.
|
|
54
|
+
|
|
55
|
+
END OF TERMS AND CONDITIONS
|
package/README.md
CHANGED
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
[](https://github.com/MCviseron/dsh-eda-mcp/actions/workflows/check.yml)
|
|
4
4
|
[](LICENSE)
|
|
5
|
+
[](https://www.npmjs.com/package/dsh-eda-mcp)
|
|
5
6
|
[](package.json)
|
|
6
7
|
|
|
7
8
|
DeepSeek Harness (DSH) plugin that connects DSH to **嘉立创EDA专业版 / EasyEDA Pro** and lets the agent draw schematics through MCP tools.
|
|
@@ -15,7 +16,7 @@ DSH agent
|
|
|
15
16
|
└─ mcp__jlceda__eda_* tools
|
|
16
17
|
└─ @deepseek-ai/dsh-mcp-client (stdio)
|
|
17
18
|
└─ lib/bridge.mjs (local MCP + WebSocket server, 127.0.0.1:39009)
|
|
18
|
-
└─ JLCEDA Pro extension dsh-eda-bridge-extension
|
|
19
|
+
└─ JLCEDA Pro extension dsh-eda-bridge-extension-<version>.zip
|
|
19
20
|
└─ official EasyEDA Pro extension API (eda.sch_*)
|
|
20
21
|
```
|
|
21
22
|
|
|
@@ -27,6 +28,19 @@ The DSH plugin starts a zero-dependency bridge child process. The bridge speaks
|
|
|
27
28
|
>
|
|
28
29
|
> Verified generations: `0.1.5-rc.2` (this repo's dev set) and `0.2.0-rc.2` / `0.2.1-rc.2` (desktop runtime and npm `next`). Re-check any new line with `pnpm seam-check next` — it installs that generation's seam packages into a throwaway directory and asserts every contract this plugin probes: mcp-client `inject: ['tools']` and its config keys, the settings faces, `systemPrompt.section({ name, order, text })`, `webServer.register({ kind: 'exact' })`, and the `window.__ModuleLoader__.load({ id, factory })` wrapper.
|
|
29
30
|
|
|
31
|
+
### 0. From npm (recommended)
|
|
32
|
+
|
|
33
|
+
```sh
|
|
34
|
+
cd "$DSH_HOME/profiles/<profile>"
|
|
35
|
+
pnpm add dsh-eda-mcp # or use the plugin manager in the Web GUI
|
|
36
|
+
# restart DSH
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
The published package already contains `lib/` and `dsh-eda-bridge-extension-<version>.zip`, so no build
|
|
40
|
+
toolchain is needed. The extension still has to be imported into JLCEDA Pro by hand — the zip
|
|
41
|
+
sits at `node_modules/dsh-eda-mcp/dsh-eda-bridge-extension-<version>.zip` (or download it from the
|
|
42
|
+
settings card).
|
|
43
|
+
|
|
30
44
|
### 1. Install into the DSH web profile
|
|
31
45
|
|
|
32
46
|
```sh
|
|
@@ -57,10 +71,10 @@ The plugin now resolves a real Node executable (`src/node-runtime.ts`), in order
|
|
|
57
71
|
|
|
58
72
|
### 2. Import the JLCEDA Pro bridge extension
|
|
59
73
|
|
|
60
|
-
Build produces `dsh-eda-bridge-extension
|
|
74
|
+
Build produces `dsh-eda-bridge-extension-<version>.zip` only — a second copy under the stable name only made it unclear which build you were looking at. In JLCEDA Pro:
|
|
61
75
|
|
|
62
76
|
1. Open **扩展 → 导入** (Extensions → Import).
|
|
63
|
-
2. Select `dsh-eda-bridge-extension
|
|
77
|
+
2. Select `dsh-eda-bridge-extension-<version>.zip`.
|
|
64
78
|
3. In the extension list find **DSH EDA MCP Bridge**.
|
|
65
79
|
4. Enable it and, importantly, enable **允许外部交互 / Allow external interactions**.
|
|
66
80
|
|
|
@@ -93,6 +107,10 @@ after routing.
|
|
|
93
107
|
| Tool | Purpose |
|
|
94
108
|
|---|---|
|
|
95
109
|
| `eda_status` | Bridge status and connected JLCEDA clients |
|
|
110
|
+
| `eda_launch` | Start JLCEDA Pro (optionally opening a `.eprj2` project) and wait until the extension connects |
|
|
111
|
+
| `eda_list_projects` / `eda_get_project` | List workspace projects (uuid + name) / read the current project |
|
|
112
|
+
| `eda_open_project` | Open a project by uuid, or by friendly name |
|
|
113
|
+
| `eda_create_project` | Create a project and return its uuid (optionally open it) |
|
|
96
114
|
| `eda_search_components` | Search JLCEDA library devices |
|
|
97
115
|
| `eda_place_component` | Place a component/symbol |
|
|
98
116
|
| `eda_place_net_flag` | Place VCC/GND/Power net flag |
|
|
@@ -170,7 +188,7 @@ Build outputs:
|
|
|
170
188
|
- `lib/index.js` — DSH host plugin.
|
|
171
189
|
- `lib/client.js` — DSH Web GUI settings card.
|
|
172
190
|
- `lib/bridge.mjs` — standalone MCP/WebSocket bridge child.
|
|
173
|
-
- `dsh-eda-bridge-extension
|
|
191
|
+
- `dsh-eda-bridge-extension-<version>.zip` — JLCEDA Pro extension (the version is the wire contract).
|
|
174
192
|
|
|
175
193
|
`lib/` and the zip are build outputs and are git-ignored; the host loads the packaged `lib/index.js`, so a change under `src/` needs `pnpm build` plus a Host restart (a plugin reload does not re-import a cached ESM module).
|
|
176
194
|
|
|
@@ -193,6 +211,11 @@ See [CONTRIBUTING.md](CONTRIBUTING.md) for the repository layout, the release/ta
|
|
|
193
211
|
- The JLCEDA extension socket is registered through `sys_WebSocket`, which has neither a close callback nor auto-reconnect; versions before 0.3.3 therefore had to be reconnected by hand after the DSH bridge child restarted. 0.3.3 adds a 10 s heartbeat that re-registers the socket when `pong` stops arriving.
|
|
194
212
|
- Screenshot/canvas image return is not yet wired into MCP image blocks.
|
|
195
213
|
- Changing the bridge WebSocket port requires editing both the bridge env and `jlc-extension/api-bridge.js`, then rebuilding/reimporting the extension zip.
|
|
214
|
+
- `dmt_Project.createProject` / `deleteProject` do nothing on this EDA build (3.2.186, signed in but semi-offline): the former returns no uuid for a directory team, a canonical team uuid, or either with/without a folderUuid, and the latter always returns `false` (verified with the project open and after switching away). **Create projects with `eda_import_project` instead** (importing a project file does create one — verified), and delete them in the app.
|
|
215
|
+
- In semi-offline mode EDA models **each local project directory as a "team"**: the `uuid` reported by `eda_list_teams` is the directory path, while a project record's `teamUuid` is a real uuid. `eda_list_projects` without a filter walks every team, so the first call is slower than one with `teamUuid`.
|
|
216
|
+
- Spawning `lceda-pro.exe` directly (even through ShellExecute) makes it exit with code 0 immediately; only handing the launch to the desktop shell (`explorer.exe`) starts it, so on Windows `eda_launch` always goes through explorer plus a temporary `.cmd`.
|
|
217
|
+
- A project path on the command line is **ignored by the app** (the active document stays HOME), so `eda_launch({ project })` only forwards the argument; the result carries `projectOpenedByArgument: false` and a `projectNote`. Use `eda_open_project` to actually open one.
|
|
218
|
+
- The extension and bridge wire versions must move **together** (currently 0.4.0): a changed allow-list requires re-importing the zip, otherwise new tools answer `API method not allowed`. `eda_status` reports every connected page's version in `extensionVersions`.
|
|
196
219
|
|
|
197
220
|
## Repository
|
|
198
221
|
|
package/README.zh.md
CHANGED
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
[](https://github.com/MCviseron/dsh-eda-mcp/actions/workflows/check.yml)
|
|
4
4
|
[](LICENSE)
|
|
5
|
+
[](https://www.npmjs.com/package/dsh-eda-mcp)
|
|
5
6
|
[](package.json)
|
|
6
7
|
|
|
7
8
|
把 DeepSeek Harness (DSH) 连接到 **嘉立创EDA专业版 / EasyEDA Pro**,让 Agent 通过 MCP 工具画原理图。
|
|
@@ -15,7 +16,7 @@ DSH Agent
|
|
|
15
16
|
└─ mcp__jlceda__eda_* 工具
|
|
16
17
|
└─ @deepseek-ai/dsh-mcp-client(stdio)
|
|
17
18
|
└─ lib/bridge.mjs(本地 MCP + WebSocket 服务,127.0.0.1:39009)
|
|
18
|
-
└─ 嘉立创EDA专业版扩展 dsh-eda-bridge-extension
|
|
19
|
+
└─ 嘉立创EDA专业版扩展 dsh-eda-bridge-extension-<版本号>.zip
|
|
19
20
|
└─ 官方扩展 API(eda.sch_*)
|
|
20
21
|
```
|
|
21
22
|
|
|
@@ -38,6 +39,17 @@ DSH 插件启动一个零第三方依赖的桥接子进程。桥接进程通过
|
|
|
38
39
|
>
|
|
39
40
|
> 已验证的代际:`0.1.5-rc.2`(本仓库开发用)与 `0.2.0-rc.2` / `0.2.1-rc.2`(桌面端运行时与 npm `next`)。出现新版本线时跑 `pnpm seam-check next` —— 它会把该代际的接缝包装到临时目录,逐条断言本插件依赖的契约:mcp-client 的 `inject: ['tools']` 与配置键、设置接缝、`systemPrompt.section({ name, order, text })`、`webServer.register({ kind: 'exact' })`、以及 `window.__ModuleLoader__.load({ id, factory })` 包装格式。
|
|
40
41
|
|
|
42
|
+
### 0. 从 npm 安装(推荐)
|
|
43
|
+
|
|
44
|
+
```sh
|
|
45
|
+
cd "$DSH_HOME/profiles/<profile>"
|
|
46
|
+
pnpm add dsh-eda-mcp # 或用 Web GUI 的插件管理器安装
|
|
47
|
+
# 重启 DSH
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
发布包里已包含 `lib/` 与 `dsh-eda-bridge-extension-<线协议版本>.zip`,**不需要构建工具链**。扩展仍需手动导入
|
|
51
|
+
嘉立创EDA:zip 位于 `node_modules/dsh-eda-mcp/dsh-eda-bridge-extension-<线协议版本>.zip`(也可从设置卡片下载,下载到的文件名自带版本号)。
|
|
52
|
+
|
|
41
53
|
### 1. 安装到 DSH Web profile
|
|
42
54
|
|
|
43
55
|
```sh
|
|
@@ -53,10 +65,10 @@ cd dsh-eda-mcp && pnpm install && dsh plugin --profile web add "link:$PWD"
|
|
|
53
65
|
|
|
54
66
|
### 2. 导入嘉立创EDA桥接扩展
|
|
55
67
|
|
|
56
|
-
|
|
68
|
+
构建只生成 `dsh-eda-bridge-extension-<版本号>.zip`(不再保留同名别名——两个同内容文件只会让人分不清是哪一版)。在嘉立创EDA专业版中:
|
|
57
69
|
|
|
58
70
|
1. 打开 **扩展 → 导入**。
|
|
59
|
-
2. 选择 `dsh-eda-bridge-extension
|
|
71
|
+
2. 选择 `dsh-eda-bridge-extension-<版本号>.zip`。
|
|
60
72
|
3. 在扩展列表中找到 **DSH EDA MCP Bridge**。
|
|
61
73
|
4. 启用它,并务必开启 **允许外部交互**。
|
|
62
74
|
|
|
@@ -149,6 +161,10 @@ DSH 桥接服务开着但 EDA 没有连上时,有三种入口:
|
|
|
149
161
|
| 工具 | 用途 |
|
|
150
162
|
|---|---|
|
|
151
163
|
| `eda_status` | 桥接状态、端口、已连接 EDA 客户端数 |
|
|
164
|
+
| `eda_launch` | 启动嘉立创EDA(命令行工程参数被应用忽略,见 docs/tools.md)并等待扩展连上 |
|
|
165
|
+
| `eda_list_projects` / `eda_get_project` | 列出工作区工程(uuid + 名称)/ 读取当前工程 |
|
|
166
|
+
| `eda_open_project` | 按 uuid 或工程名打开工程 |
|
|
167
|
+
| `eda_create_project` | 新建工程并返回 uuid(可选立即打开) |
|
|
152
168
|
| `eda_get_page_info` | 当前页、A4 图框、标题栏、安全区 |
|
|
153
169
|
| `eda_get_schematic_pages` | 原理图图页列表 / 按 uuid 查询 |
|
|
154
170
|
| `eda_search_components` | 搜索嘉立创库器件 |
|
|
@@ -266,7 +282,7 @@ JLCEDA 工程文件里真实的板框记录是:
|
|
|
266
282
|
|
|
267
283
|
## 完整工具参考
|
|
268
284
|
|
|
269
|
-
`docs/tools.md` 由构建产物(bridge 的 MCP `tools/list`)自动生成(`pnpm docs`),共 **
|
|
285
|
+
`docs/tools.md` 由构建产物(bridge 的 MCP `tools/list`)自动生成(`pnpm docs`),共 **97 个工具**,是唯一权威清单:名称、描述、参数与必填项都在里面。下面只写流程与坑。
|
|
270
286
|
|
|
271
287
|
## 设计规范
|
|
272
288
|
|
|
@@ -338,7 +354,7 @@ pnpm check # 以上三步
|
|
|
338
354
|
- `lib/index.js` — DSH Host 插件。
|
|
339
355
|
- `lib/client.js` — DSH Web GUI 设置卡片。
|
|
340
356
|
- `lib/bridge.mjs` — 独立 MCP/WebSocket 桥接子进程。
|
|
341
|
-
- `dsh-eda-bridge-extension
|
|
357
|
+
- `dsh-eda-bridge-extension-<版本号>.zip` — 嘉立创EDA专业版扩展(版本号 = 线协议版本)。
|
|
342
358
|
|
|
343
359
|
`lib/` 与 zip 都是构建产物(已 gitignore);宿主加载的是打包后的 `lib/index.js`,所以改 `src/` 后必须 `pnpm build` **并重启 Host**(插件重载不会重新 import 已缓存的 ESM 模块)。仓库结构、发布打 tag 流程、bridge/扩展版本规则见 [CONTRIBUTING.md](CONTRIBUTING.md);**发布到 npm** 的完整流程(首次手动发布,之后由 `v*` tag 经 trusted publishing/OIDC 自动发布)见 [docs/release.md](docs/release.md)。
|
|
344
360
|
|
|
@@ -357,6 +373,11 @@ pnpm check # 以上三步
|
|
|
357
373
|
- 修改桥接端口需要同时改桥接环境变量和 `jlc-extension/api-bridge.js`,再重新构建并重新导入扩展。
|
|
358
374
|
- 元件包围盒检查依赖 `sch_Primitive.getPrimitivesBBox`;个别图元若没有 BBox,会跳过超界检查。
|
|
359
375
|
- 扩展 `activationEvents` 已包含 onStartupFinished / onProjectOpened / onEditorSchematic / onEditorPcb;如果导入旧版扩展后切到 PCB 掉线,请重新导入新版 zip。
|
|
376
|
+
- `dmt_Project.createProject` / `deleteProject` 在本机 EDA 3.2.186(登录但半离线)上**无副作用**:前者返回空 uuid(目录路径 / 真 team uuid / 带不带 folderUuid 四种组合都试过),后者始终返回 `false`(工程打开时与切走后各试一次)。**新建工程请改用 `eda_import_project`(导入一个工程文件即建出工程,已实测)**,删除请在应用内完成。
|
|
377
|
+
- 半离线模式下 EDA 把**每个本地工程目录当成一个 "team"**:`eda_list_teams` 返回的 `uuid` 就是目录路径,而工程记录里的 `teamUuid` 是另一种真 uuid。`eda_list_projects` 无过滤时会遍历所有 team 聚合,因此第一次调用会比带 `teamUuid` 时慢一些。
|
|
378
|
+
- `lceda-pro.exe` 直接 `spawn`(含 ShellExecute)会**立刻以退出码 0 退出**:只有交给桌面外壳(`explorer.exe`)才能起来,所以 `eda_launch` 在 Windows 上固定走 explorer + 临时 `.cmd`。
|
|
379
|
+
- 命令行带工程路径启动**被应用忽略**(活动文档仍是 HOME),所以 `eda_launch({ project })` 只保证"把参数传下去",返回值里 `projectOpenedByArgument: false` 与 `projectNote` 会明确说明;要打开工程请用 `eda_open_project`。
|
|
380
|
+
- 扩展与桥接的**线协议版本必须成对升级**(当前 0.4.0):白名单变了就要重新导入 zip,否则新工具会报 `API method not allowed`;`eda_status` 的 `extensionVersions` 会指出每个页面连上来的版本。
|
|
360
381
|
|
|
361
382
|
## 仓库结构
|
|
362
383
|
|
package/docs/release.md
CHANGED
|
@@ -30,7 +30,7 @@ npm pack --dry-run # 看 tarball 内容
|
|
|
30
30
|
```
|
|
31
31
|
|
|
32
32
|
`npm pack --dry-run` 里必须能同时看到 **`lib/index.js`、`lib/client.js`、`lib/bridge.mjs`、
|
|
33
|
-
`dsh-eda-bridge-extension
|
|
33
|
+
`dsh-eda-bridge-extension-<线协议版本>.zip`、`cordis.patch.yml`、`docs/`**。`lib/` 与 zip 是构建产物(被 gitignore),
|
|
34
34
|
由 `prepare` 脚本在打包时生成 —— 所以**打包机必须是 Windows**(zip 用 PowerShell `Compress-Archive`)。
|
|
35
35
|
|
|
36
36
|
## 2. 首次手动发布(必须手动,顺序不能颠倒)
|
package/docs/tools.md
CHANGED
|
@@ -7,6 +7,36 @@ authoritative list of what the agent can call. Coordinates: schematic/symbol = *
|
|
|
7
7
|
Design limits enforced by these tools (track widths, via sizes, net naming) are documented in
|
|
8
8
|
[design-rules.md](design-rules.md).
|
|
9
9
|
|
|
10
|
+
## Project
|
|
11
|
+
|
|
12
|
+
| Tool | Description | Parameters |
|
|
13
|
+
|---|---|---|
|
|
14
|
+
| `eda_list_projects` | List project UUIDs in the workspace (dmt_Project.getAllProjectsUuid). resolveNames=true (default) also reads getProjectInfo per project so callers can match by friendly name. | `teamUuid?: string, folderUuid?: string, workspaceUuid?: string, resolveNames?: boolean, limit?: integer` |
|
|
15
|
+
| `eda_get_project` | Read the current project (dmt_Project.getCurrentProjectInfo): uuid, friendly name, folder/team and other brief fields. | `` |
|
|
16
|
+
| `eda_open_project` | Open a project by uuid, or by friendly name when only a name is known (the name is resolved through getAllProjectsUuid + getProjectInfo, exact match first, then case-insensitive substring). | `uuid?: string, name?: string` |
|
|
17
|
+
| `eda_create_project` | Create a project (dmt_Project.createProject) and return its uuid; open=true (default false) also opens it. friendlyName is required; name and folder/team default to the app defaults. | `friendlyName: string, name?: string, teamUuid?: string, folderUuid?: string, description?: string, open?: boolean` |
|
|
18
|
+
| `eda_delete_project` | Delete a project (dmt_Project.deleteProject) by uuid or friendly name. | `uuid?: string, name?: string` |
|
|
19
|
+
| `eda_modify_project` | Rename a project or change its description (dmt_Project.modifyProjectFriendlyName / modifyProjectDescription), addressed by uuid or current friendly name. | `uuid?: string, name?: string, friendlyName?: string, description?: string` |
|
|
20
|
+
| `eda_copy_project` | Copy a project (dmt_Project.copyProject), optionally into another team/folder and with a new friendly name; returns the new uuid. | `sourceUuid?: string, sourceName?: string, targetTeamUuid?: string, targetFolderUuid?: string, newFriendlyName?: string, newName?: string` |
|
|
21
|
+
| `eda_close_document` | Close one editor tab (dmt_EditorControl.closeDocument). | `tabId: string` |
|
|
22
|
+
| `eda_export_project` | Export a project file (sys_FileManager.getProjectFile / getProjectFileByProjectUuid). With outPath the bridge writes the bytes to disk and returns the path; without it the base64 payload is returned only when inline=true. | `uuid?: string, fileName?: string, fileType?: string, outPath?: string, inline?: boolean` |
|
|
23
|
+
| `eda_import_project` | Import a local project file (.eprj2/.epro/...) into a team (sys_FileManager.importProjectByProjectFile with a saveTo target). teamUuid is required in local mode: without a target team the app imports nothing. Returns whether the import actually landed. | `path: string, teamUuid?: string, folderUuid?: string, friendlyName?: string, fileType?: string, open?: boolean` |
|
|
24
|
+
|
|
25
|
+
## Workspace
|
|
26
|
+
|
|
27
|
+
| Tool | Description | Parameters |
|
|
28
|
+
|---|---|---|
|
|
29
|
+
| `eda_get_workspace` | Read the current workspace and team (dmt_Workspace.getCurrentWorkspaceInfo / dmt_Team.getCurrentTeamInfo). Needed before creating a project, which requires a team (and usually a folder) uuid. | `` |
|
|
30
|
+
| `eda_list_teams` | List teams (dmt_Team.getAllTeamsInfo), optionally including the teams this account is involved in. | `involved?: boolean` |
|
|
31
|
+
| `eda_list_folders` | List folder uuids of one team (dmt_Folder.getAllFoldersUuid), optionally resolved through getFolderInfo. | `teamUuid?: string, resolveNames?: boolean` |
|
|
32
|
+
|
|
33
|
+
## App control
|
|
34
|
+
|
|
35
|
+
| Tool | Description | Parameters |
|
|
36
|
+
|---|---|---|
|
|
37
|
+
| `eda_close` | Close JLCEDA Pro gracefully (taskkill /T on Windows, SIGTERM elsewhere), then force-kill if it is still running after waitMs (default 8000). Returns the remaining process count. | `force?: boolean, waitMs?: integer` |
|
|
38
|
+
| `eda_launch` | Launch JLCEDA Pro (EasyEDA Pro), optionally opening a .eprj2/.eprj project, then wait until the bridge extension connects. The executable is resolved from DSH_EDA_EXECUTABLE/JLCEDA_EXECUTABLE, the registry handler the installer registered, conventional install directories, then PATH - so it works on any machine without configuration. | `project?: string, executable?: string, waitForExtension?: boolean, timeoutMs?: integer` |
|
|
39
|
+
|
|
10
40
|
## Schematic: query
|
|
11
41
|
|
|
12
42
|
| Tool | Description | Parameters |
|
|
Binary file
|
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
/* eslint-disable */
|
|
2
2
|
/**
|
|
3
|
-
* dsh-eda-mcp — JLCEDA Pro bridge extension (
|
|
3
|
+
* dsh-eda-mcp — JLCEDA Pro bridge extension (version injected from extension.json).
|
|
4
4
|
*
|
|
5
5
|
* Connects to the local dsh-eda-mcp bridge process over WebSocket and executes
|
|
6
6
|
* a strict allow-list of official EasyEDA Pro extension APIs. Import through:
|
|
7
|
-
* Extensions → Import → select dsh-eda-bridge-extension
|
|
7
|
+
* Extensions → Import → select dsh-eda-bridge-extension-<version>.zip
|
|
8
8
|
* then enable "Allow interactive with external" for this extension.
|
|
9
9
|
*/
|
|
10
10
|
var DSH_EDA_MCP = {
|
|
11
|
-
version: '
|
|
11
|
+
version: '__EXTENSION_VERSION__',
|
|
12
12
|
connect: null,
|
|
13
13
|
disconnect: null,
|
|
14
14
|
status: null,
|
|
@@ -37,7 +37,9 @@ function dshEdaMcpStatus() {
|
|
|
37
37
|
(function () {
|
|
38
38
|
'use strict';
|
|
39
39
|
|
|
40
|
-
|
|
40
|
+
// Injected from extension.json at build time: a hand-maintained constant here
|
|
41
|
+
// went stale once and made the bridge report a version mismatch forever.
|
|
42
|
+
var VERSION = '__EXTENSION_VERSION__';
|
|
41
43
|
var WS_ID = 'dsh-eda-mcp-bridge';
|
|
42
44
|
var WS_PORT = 39009;
|
|
43
45
|
var WS_URL = 'ws://127.0.0.1:' + WS_PORT + '/ws';
|
|
@@ -133,12 +135,29 @@ function dshEdaMcpStatus() {
|
|
|
133
135
|
'dmt_Schematic.getSchematicInfo',
|
|
134
136
|
'dmt_Schematic.getSchematicPageInfo',
|
|
135
137
|
'dmt_Schematic.getAllSchematicPagesInfo',
|
|
138
|
+
'dmt_Team.getAllTeamsInfo',
|
|
139
|
+
'dmt_Team.getAllInvolvedTeamInfo',
|
|
140
|
+
'dmt_Team.getCurrentTeamInfo',
|
|
141
|
+
'dmt_Folder.getAllFoldersUuid',
|
|
142
|
+
'dmt_Folder.getFolderInfo',
|
|
143
|
+
'dmt_Workspace.getCurrentWorkspaceInfo',
|
|
144
|
+
'dmt_Project.getAllProjectsUuid',
|
|
145
|
+
'dmt_Project.getProjectInfo',
|
|
146
|
+
'dmt_Project.getCurrentProjectInfo',
|
|
147
|
+
'dmt_Project.openProject',
|
|
148
|
+
'dmt_Project.createProject',
|
|
149
|
+
'dmt_Project.deleteProject',
|
|
150
|
+
'dmt_Project.modifyProjectFriendlyName',
|
|
151
|
+
'dmt_Project.modifyProjectDescription',
|
|
152
|
+
'dmt_Project.copyProject',
|
|
153
|
+
'dmt_Project.moveProjectToFolder',
|
|
136
154
|
'dmt_Board.createBoard',
|
|
137
155
|
'dmt_Board.getAllBoardsInfo',
|
|
138
156
|
'dmt_Board.getCurrentBoardInfo',
|
|
139
157
|
'dmt_EditorControl.getSplitScreenTree',
|
|
140
158
|
'dmt_EditorControl.getTabsBySplitScreenId',
|
|
141
159
|
'dmt_EditorControl.openDocument',
|
|
160
|
+
'dmt_EditorControl.closeDocument',
|
|
142
161
|
'dmt_EditorControl.activateDocument',
|
|
143
162
|
'dmt_EditorControl.zoomToRegion',
|
|
144
163
|
'dmt_EditorControl.zoomToAllPrimitives',
|
|
@@ -286,6 +305,11 @@ function dshEdaMcpStatus() {
|
|
|
286
305
|
return btoa(parts.join(''));
|
|
287
306
|
}
|
|
288
307
|
|
|
308
|
+
// JSON turns undefined array slots into null; the official API expects
|
|
309
|
+
// undefined (omitted) for optional parameters. One shared helper: the earlier
|
|
310
|
+
// per-helper copies were invisible to the project file helpers.
|
|
311
|
+
function optional(value) { return value === null ? undefined : value; }
|
|
312
|
+
|
|
289
313
|
async function blobToDataUrl(blob) {
|
|
290
314
|
var buffer = await blob.arrayBuffer();
|
|
291
315
|
var bytes = new Uint8Array(buffer);
|
|
@@ -313,6 +337,41 @@ function dshEdaMcpStatus() {
|
|
|
313
337
|
if (typeof message.method !== 'string') {
|
|
314
338
|
throw new Error('API method not allowed: ' + String(message.method));
|
|
315
339
|
}
|
|
340
|
+
// Internal helper: export a project as a File and hand it back as base64
|
|
341
|
+
// (the File object cannot cross the WebSocket boundary).
|
|
342
|
+
if (message.method === 'dsh.internal.exportProjectFile') {
|
|
343
|
+
var expArgs = message.args || [];
|
|
344
|
+
if (typeof eda === 'undefined' || !eda.sys_FileManager) {
|
|
345
|
+
throw new Error('dsh.internal.exportProjectFile: sys_FileManager is not available');
|
|
346
|
+
}
|
|
347
|
+
var expUuid = expArgs[0] == null ? undefined : String(expArgs[0]);
|
|
348
|
+
var expFile = expUuid === undefined
|
|
349
|
+
? await eda.sys_FileManager.getProjectFile(optional(expArgs[1]), optional(expArgs[2]), optional(expArgs[3]))
|
|
350
|
+
: await eda.sys_FileManager.getProjectFileByProjectUuid(expUuid, optional(expArgs[1]), optional(expArgs[2]), optional(expArgs[3]));
|
|
351
|
+
if (!expFile) throw new Error('dsh.internal.exportProjectFile: the app returned no file (project not open, or the export was cancelled)');
|
|
352
|
+
var expBuffer = new Uint8Array(await expFile.arrayBuffer());
|
|
353
|
+
var expBinary = '';
|
|
354
|
+
for (var expIndex = 0; expIndex < expBuffer.length; expIndex += 1) expBinary += String.fromCharCode(expBuffer[expIndex]);
|
|
355
|
+
return { name: expFile.name || String(expArgs[1] || 'project.epro'), size: expBuffer.length, base64: btoa(expBinary) };
|
|
356
|
+
}
|
|
357
|
+
// Internal helper: import a project from base64 (local .eprj2/.epro handed over
|
|
358
|
+
// by the bridge) by rebuilding the File the official API expects.
|
|
359
|
+
if (message.method === 'dsh.internal.importProjectFile') {
|
|
360
|
+
var impArgs = message.args || [];
|
|
361
|
+
if (typeof eda === 'undefined' || !eda.sys_FileManager) {
|
|
362
|
+
throw new Error('dsh.internal.importProjectFile: sys_FileManager is not available');
|
|
363
|
+
}
|
|
364
|
+
var impBinary = atob(String(impArgs[0] || ''));
|
|
365
|
+
var impBytes = new Uint8Array(impBinary.length);
|
|
366
|
+
for (var impIndex = 0; impIndex < impBinary.length; impIndex += 1) impBytes[impIndex] = impBinary.charCodeAt(impIndex);
|
|
367
|
+
var impFile = new File([impBytes], String(impArgs[1] || 'import.eprj2'), { type: 'application/octet-stream' });
|
|
368
|
+
// saveTo (4th argument) is what gives the import a target team; without it the
|
|
369
|
+
// app answers undefined and imports nothing.
|
|
370
|
+
var impResult = await eda.sys_FileManager.importProjectByProjectFile(
|
|
371
|
+
impFile, optional(impArgs[2]), undefined, optional(impArgs[3])
|
|
372
|
+
);
|
|
373
|
+
return { result: impResult === undefined ? null : toJsonable(impResult) };
|
|
374
|
+
}
|
|
316
375
|
// Internal helper: build an IPCB_Polygon from a raw source array and create
|
|
317
376
|
// a PCB region (used for the board outline).
|
|
318
377
|
if (message.method === 'dsh.internal.pcbCreateRegion') {
|
|
@@ -322,9 +381,6 @@ function dshEdaMcpStatus() {
|
|
|
322
381
|
}
|
|
323
382
|
var regionPolygon = eda.pcb_MathPolygon.createPolygon(regionArgs[1]);
|
|
324
383
|
if (!regionPolygon) throw new Error('dsh.internal.pcbCreateRegion: invalid polygon source');
|
|
325
|
-
// JSON turns undefined array slots into null; the official API expects
|
|
326
|
-
// undefined (omitted) for optional parameters.
|
|
327
|
-
function optional(value) { return value === null ? undefined : value; }
|
|
328
384
|
var region = await eda.pcb_PrimitiveRegion.create(
|
|
329
385
|
optional(regionArgs[0]), regionPolygon, optional(regionArgs[2]), optional(regionArgs[3]),
|
|
330
386
|
optional(regionArgs[4]), optional(regionArgs[5])
|
|
@@ -341,12 +397,11 @@ function dshEdaMcpStatus() {
|
|
|
341
397
|
if (typeof eda === 'undefined' || !eda.pcb_MathPolygon || !eda.pcb_PrimitivePolyline) {
|
|
342
398
|
throw new Error('dsh.internal.polylineCreate: PCB polyline APIs are not available');
|
|
343
399
|
}
|
|
344
|
-
function optional2(value) { return value === null ? undefined : value; }
|
|
345
400
|
var source = polyArgs[2];
|
|
346
401
|
var polygon = eda.pcb_MathPolygon.createPolygon(source);
|
|
347
402
|
if (!polygon) throw new Error('dsh.internal.polylineCreate: invalid polygon source ' + JSON.stringify(source));
|
|
348
403
|
var polyline = await eda.pcb_PrimitivePolyline.create(
|
|
349
|
-
|
|
404
|
+
optional(polyArgs[0]), optional(polyArgs[1]), polygon, optional(polyArgs[3]), optional(polyArgs[4])
|
|
350
405
|
);
|
|
351
406
|
if (!polyline) {
|
|
352
407
|
throw new Error('dsh.internal.polylineCreate: pcb_PrimitivePolyline.create returned undefined (net/layer/polygon rejected)');
|
|
@@ -361,12 +416,11 @@ function dshEdaMcpStatus() {
|
|
|
361
416
|
if (typeof eda === 'undefined' || !eda.pcb_MathPolygon || !eda.pcb_PrimitivePour) {
|
|
362
417
|
throw new Error('dsh.internal.pourCreate: PCB pour APIs are not available');
|
|
363
418
|
}
|
|
364
|
-
function optional3(value) { return value === null ? undefined : value; }
|
|
365
419
|
var pourPolygon = eda.pcb_MathPolygon.createPolygon(pourArgs[2]);
|
|
366
420
|
if (!pourPolygon) throw new Error('dsh.internal.pourCreate: invalid polygon source ' + JSON.stringify(pourArgs[2]));
|
|
367
421
|
var pour = await eda.pcb_PrimitivePour.create(
|
|
368
|
-
|
|
369
|
-
|
|
422
|
+
optional(pourArgs[0]), optional(pourArgs[1]), pourPolygon, optional(pourArgs[3]), optional(pourArgs[4]),
|
|
423
|
+
optional(pourArgs[5]), optional(pourArgs[6]), optional(pourArgs[7]), optional(pourArgs[8])
|
|
370
424
|
);
|
|
371
425
|
if (!pour) {
|
|
372
426
|
throw new Error('dsh.internal.pourCreate: pcb_PrimitivePour.create returned undefined (net/layer/polygon rejected; copper layers only)');
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
"uuid": "d5e0a1b2c3f4456789abcdef01234567",
|
|
3
3
|
"name": "dsh-eda-bridge",
|
|
4
4
|
"displayName": "DSH EDA MCP Bridge",
|
|
5
|
-
"version": "0.
|
|
5
|
+
"version": "0.4.0",
|
|
6
6
|
"description": "Local WebSocket bridge used by the dsh-eda-mcp plugin to let DeepSeek Harness draw schematics and drive PCB operations in JLCEDA Pro through the official extension API.",
|
|
7
7
|
"entry": "./api-bridge.js",
|
|
8
8
|
"publisher": "dsh-eda-mcp",
|