dsh-eda-mcp 0.7.3 → 0.7.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.
- package/CHANGELOG.md +23 -0
- package/docs/schematic-workflow.md +1 -0
- package/dsh-eda-bridge-extension-0.4.2.zip +0 -0
- package/lib/bridge.mjs +7 -2
- package/lib/index.js +1 -0
- package/package.json +1 -1
- package/src/bridge/index.ts +17 -2
- package/src/skills-content.ts +1 -0
- package/test/skills.test.ts +1 -0
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,28 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 0.7.4 — 2026-10-20
|
|
4
|
+
|
|
5
|
+
发布后补:v0.7.3 已上 npm,但它的两个流水线问题和两处诚实性问题是在 tag 之后才修的,这一版把它们发出去。
|
|
6
|
+
|
|
7
|
+
### 诚实性修复(宿主侧,线协议不变 = 0.4.2,**无需重导扩展**)
|
|
8
|
+
|
|
9
|
+
- **`opened` 必须表示"目标文档真的被激活了"**:导入新扩展会重启 EDA,应用回到 Start Page(`tab_page1`),旧实现靠"标签数 > 1"猜,于是又谎报 `opened: true`。现在要求 `activeDocumentUuid === documentUuid`,新增 `activated` 字段,提示里直接写"先 `eda_open_project` 再开文档"。
|
|
10
|
+
- **空 BOM 不再装成功**:PCB 活动时 `getBomFile` 返回 null,原来给 `{bom:"", encoding:"unknown"}`,看着像"BOM 是空的"。现在直接报错并给出修复动作(切到原理图页)。
|
|
11
|
+
|
|
12
|
+
### CI 两处真 bug(这就是 v0.7.3 一度没上 npm 的原因)
|
|
13
|
+
|
|
14
|
+
- **`publish.yml` 的守卫误杀自己的文档字符串**:守卫用 `Select-String -Path lib/*.js -Pattern 'node_modules/'`,而它命中我写进技能第 12 章的正确文案(`node_modules/dsh-eda-mcp/dsh-eda-bridge-extension-<版本>.zip`),于是 `exit 1`,`Publish` 步骤从未执行。已抽成 `scripts/guard-bundle-paths.mjs`(`pnpm guard:bundle`),只认**绝对路径形态**的构建机泄露(`D:/…/node_modules`、`/home/`、`/Users/`、`/root/`、`node_modules/.pnpm`),两个 workflow 共用;并做了负向验证(注入泄露 → exit 1 并报出文件与行号)。
|
|
15
|
+
- **`check.yml` 上传已被删除的别名 zip**:0.7.0 起构建只产出 `dsh-eda-bridge-extension-<版本>.zip`,而 workflow 仍指向 `dsh-eda-bridge-extension.zip` 且 `if-no-files-found: error`,导致 check 一直红。改为版本号通配,并把 `guard:bundle` 加进 check。
|
|
16
|
+
- **防复发**:`test/seams.test.ts` 新增 6 条断言 —— check.yml 必须用版本号通配、两个 workflow 都必须调用共享守卫、不得内联重写守卫、`guard:bundle` 必须在 `pnpm check` 链里。
|
|
17
|
+
|
|
18
|
+
### 仓库卫生
|
|
19
|
+
|
|
20
|
+
- `.tmp-*` 加入 `.gitignore`,并删掉之前误提交的临时文件(提交信息临时文件曾随 `git add -A` 进库)。
|
|
21
|
+
|
|
22
|
+
### 门禁
|
|
23
|
+
|
|
24
|
+
`pnpm check` = typecheck → 6 套单测(含 seams 新增断言)→ `contract-check` → `build` → **`guard:bundle`**;本次发布前全部复跑通过。
|
|
25
|
+
|
|
3
26
|
## 0.7.3 — 2026-10-20
|
|
4
27
|
|
|
5
28
|
本版把原理图与 PCB 两侧都跑了一遍真机,修掉 5 个真问题,并把「三层改动的生效范围」写进随包文档,避免新会话无从下手。
|
|
@@ -110,6 +110,7 @@
|
|
|
110
110
|
| connectedClients=0 / 报未连接 | EDA 没开、扩展没导入或没点「连接」、端口被占 | 先 eda_status 看 hint;必要时 eda_launch 起应用并等扩展连上 |
|
|
111
111
|
| extensionVersions 与 bridgeVersion 不一致 | 装的是旧扩展 | 重新导入对应版本 zip(文件名带版本号)并重启应用 |
|
|
112
112
|
| API method not allowed | 该 API 不在扩展白名单 | 调 eda_list_api_methods 看清单;优先用包装好的工具 |
|
|
113
|
+
| open_document 返回 opened:false / activated:false | 应用停在 Start Page(tab_page1),该构建无法从 HOME 程序化打开文档 | 先 eda_open_project 打开工程,再 eda_open_document;用 activeDocumentUuid 确认,别假设已激活 |
|
|
113
114
|
| 调用 60s 超时 | 页面切走留下僵尸 socket,或单次改太多 | 只读调用会自动换页面重试一次;把批量拆到 ≤20/批 |
|
|
114
115
|
| 读图返回空 / null | 活动文档不是原理图页 | eda_get_active_document → eda_open_document 切页 |
|
|
115
116
|
| createNetLabel 无响应 | 3.2.186 该 API 不 resolve | 自动降级文本标签 + stub 导线;批量用 eda_place_net_labels |
|
|
Binary file
|
package/lib/bridge.mjs
CHANGED
|
@@ -2086,13 +2086,15 @@ async function executeTool(name, rawArgs, timeoutMs) {
|
|
|
2086
2086
|
activeUuid = String(pickValue(info, "uuid", "Uuid") ?? "");
|
|
2087
2087
|
} catch {
|
|
2088
2088
|
}
|
|
2089
|
-
const
|
|
2089
|
+
const activeIsTarget = activeUuid === documentUuid;
|
|
2090
|
+
const tabOpened = activeIsTarget || tabIds.some((tab) => tab.startsWith(documentUuid + "@"));
|
|
2090
2091
|
return {
|
|
2091
2092
|
...raw !== null && typeof raw === "object" ? raw : { result: raw ?? null },
|
|
2092
2093
|
opened: tabOpened,
|
|
2094
|
+
activated: activeIsTarget,
|
|
2093
2095
|
activeDocumentUuid: activeUuid || null,
|
|
2094
2096
|
tabIds,
|
|
2095
|
-
hint: tabOpened ? null : "The app accepted openDocument but
|
|
2097
|
+
hint: tabOpened ? null : "The app accepted openDocument but the document is not active (activeDocumentUuid=" + (activeUuid || "unknown") + ", tabs: " + (tabIds.join(", ") || "none") + "). After an app restart the editor sits on the Start Page: open the project first (eda_open_project), then open the document. This build cannot open a document from the HOME screen programmatically, so never assume a page is active without checking activeDocumentUuid."
|
|
2096
2098
|
};
|
|
2097
2099
|
}
|
|
2098
2100
|
case "eda_set_no_connect": {
|
|
@@ -2342,6 +2344,9 @@ async function executeTool(name, rawArgs, timeoutMs) {
|
|
|
2342
2344
|
const payload = await callExtension("sch_ManufactureData.getBomFile", [void 0, format], toolTimeout(args, timeoutMs));
|
|
2343
2345
|
if (format === "xlsx") return payload;
|
|
2344
2346
|
const raw = typeof payload === "string" ? payload : payload !== null && typeof payload === "object" && typeof payload.text === "string" ? payload.text : "";
|
|
2347
|
+
if (raw === "") {
|
|
2348
|
+
throw new Error("eda_export_bom: no BOM data (the schematic page must be the active document; call eda_get_active_document, then eda_open_document on the schematic page and retry)");
|
|
2349
|
+
}
|
|
2345
2350
|
const encoding = payload !== null && typeof payload === "object" ? payload.encoding : void 0;
|
|
2346
2351
|
return { format, bom: decodeTextPayload(raw), encoding: encoding ?? "unknown" };
|
|
2347
2352
|
}
|
package/lib/index.js
CHANGED
|
@@ -138,6 +138,7 @@ var SKILL_PLAYBOOK_LINES = [
|
|
|
138
138
|
"| connectedClients=0 / \u62A5\u672A\u8FDE\u63A5 | EDA \u6CA1\u5F00\u3001\u6269\u5C55\u6CA1\u5BFC\u5165\u6216\u6CA1\u70B9\u300C\u8FDE\u63A5\u300D\u3001\u7AEF\u53E3\u88AB\u5360 | \u5148 eda_status \u770B hint\uFF1B\u5FC5\u8981\u65F6 eda_launch \u8D77\u5E94\u7528\u5E76\u7B49\u6269\u5C55\u8FDE\u4E0A |",
|
|
139
139
|
"| extensionVersions \u4E0E bridgeVersion \u4E0D\u4E00\u81F4 | \u88C5\u7684\u662F\u65E7\u6269\u5C55 | \u91CD\u65B0\u5BFC\u5165\u5BF9\u5E94\u7248\u672C zip\uFF08\u6587\u4EF6\u540D\u5E26\u7248\u672C\u53F7\uFF09\u5E76\u91CD\u542F\u5E94\u7528 |",
|
|
140
140
|
"| API method not allowed | \u8BE5 API \u4E0D\u5728\u6269\u5C55\u767D\u540D\u5355 | \u8C03 eda_list_api_methods \u770B\u6E05\u5355\uFF1B\u4F18\u5148\u7528\u5305\u88C5\u597D\u7684\u5DE5\u5177 |",
|
|
141
|
+
"| open_document \u8FD4\u56DE opened:false / activated:false | \u5E94\u7528\u505C\u5728 Start Page\uFF08tab_page1\uFF09\uFF0C\u8BE5\u6784\u5EFA\u65E0\u6CD5\u4ECE HOME \u7A0B\u5E8F\u5316\u6253\u5F00\u6587\u6863 | \u5148 eda_open_project \u6253\u5F00\u5DE5\u7A0B\uFF0C\u518D eda_open_document\uFF1B\u7528 activeDocumentUuid \u786E\u8BA4\uFF0C\u522B\u5047\u8BBE\u5DF2\u6FC0\u6D3B |",
|
|
141
142
|
"| \u8C03\u7528 60s \u8D85\u65F6 | \u9875\u9762\u5207\u8D70\u7559\u4E0B\u50F5\u5C38 socket\uFF0C\u6216\u5355\u6B21\u6539\u592A\u591A | \u53EA\u8BFB\u8C03\u7528\u4F1A\u81EA\u52A8\u6362\u9875\u9762\u91CD\u8BD5\u4E00\u6B21\uFF1B\u628A\u6279\u91CF\u62C6\u5230 \u226420/\u6279 |",
|
|
142
143
|
"| \u8BFB\u56FE\u8FD4\u56DE\u7A7A / null | \u6D3B\u52A8\u6587\u6863\u4E0D\u662F\u539F\u7406\u56FE\u9875 | eda_get_active_document \u2192 eda_open_document \u5207\u9875 |",
|
|
143
144
|
"| createNetLabel \u65E0\u54CD\u5E94 | 3.2.186 \u8BE5 API \u4E0D resolve | \u81EA\u52A8\u964D\u7EA7\u6587\u672C\u6807\u7B7E + stub \u5BFC\u7EBF\uFF1B\u6279\u91CF\u7528 eda_place_net_labels |",
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "dsh-eda-mcp",
|
|
3
|
-
"version": "0.7.
|
|
3
|
+
"version": "0.7.4",
|
|
4
4
|
"description": "DeepSeek Harness (DSH) plugin — connects DSH to JLCEDA Pro (EasyEDA Pro) via a local MCP bridge and draws schematics through the JLCEDA extension API.",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"author": {
|
package/src/bridge/index.ts
CHANGED
|
@@ -1903,15 +1903,23 @@ async function executeTool(name, rawArgs, timeoutMs) {
|
|
|
1903
1903
|
const info = asRecord(await callExtension('dmt_SelectControl.getCurrentDocumentInfo', [], Math.min(timeoutMs, 4000)))
|
|
1904
1904
|
activeUuid = String(pickValue(info, 'uuid', 'Uuid') ?? '')
|
|
1905
1905
|
} catch { /* ignore */ }
|
|
1906
|
-
|
|
1906
|
+
// "opened" must mean "this document is now the active one": right after an app
|
|
1907
|
+
// restart the call answers success while the Start Page (tab_page1) stays active,
|
|
1908
|
+
// and a length-based guess reported opened:true for it.
|
|
1909
|
+
const activeIsTarget = activeUuid === documentUuid
|
|
1910
|
+
const tabOpened = activeIsTarget || tabIds.some(tab => tab.startsWith(documentUuid + '@'))
|
|
1907
1911
|
return {
|
|
1908
1912
|
...(raw !== null && typeof raw === 'object' ? raw : { result: raw ?? null }),
|
|
1909
1913
|
opened: tabOpened,
|
|
1914
|
+
activated: activeIsTarget,
|
|
1910
1915
|
activeDocumentUuid: activeUuid || null,
|
|
1911
1916
|
tabIds,
|
|
1912
1917
|
hint: tabOpened
|
|
1913
1918
|
? null
|
|
1914
|
-
: 'The app accepted openDocument but
|
|
1919
|
+
: 'The app accepted openDocument but the document is not active (activeDocumentUuid=' +
|
|
1920
|
+
(activeUuid || 'unknown') + ', tabs: ' + (tabIds.join(', ') || 'none') +
|
|
1921
|
+
'). After an app restart the editor sits on the Start Page: open the project first'
|
|
1922
|
+
+ ' (eda_open_project), then open the document. This build cannot open a document from the HOME screen programmatically, so never assume a page is active without checking activeDocumentUuid.',
|
|
1915
1923
|
}
|
|
1916
1924
|
}
|
|
1917
1925
|
|
|
@@ -2134,6 +2142,13 @@ async function executeTool(name, rawArgs, timeoutMs) {
|
|
|
2134
2142
|
const raw = typeof payload === 'string'
|
|
2135
2143
|
? payload
|
|
2136
2144
|
: (payload !== null && typeof payload === 'object' && typeof payload.text === 'string' ? payload.text : '')
|
|
2145
|
+
// Measured: with the PCB active the export answers null and this used to return
|
|
2146
|
+
// { bom: '', encoding: 'unknown' }, which reads like an empty BOM instead of the
|
|
2147
|
+
// real cause. Say it plainly, like eda_export_netlist does.
|
|
2148
|
+
if (raw === '') {
|
|
2149
|
+
throw new Error('eda_export_bom: no BOM data (the schematic page must be the active document; ' +
|
|
2150
|
+
'call eda_get_active_document, then eda_open_document on the schematic page and retry)')
|
|
2151
|
+
}
|
|
2137
2152
|
// The extension (0.4.1+) decodes text payloads with the encoding named by their BOM;
|
|
2138
2153
|
// decodeTextPayload stays as a fallback for older extensions.
|
|
2139
2154
|
const encoding = payload !== null && typeof payload === 'object' ? payload.encoding : undefined
|
package/src/skills-content.ts
CHANGED
|
@@ -117,6 +117,7 @@ export const SKILL_PLAYBOOK_LINES: readonly string[] = [
|
|
|
117
117
|
"| connectedClients=0 / 报未连接 | EDA 没开、扩展没导入或没点「连接」、端口被占 | 先 eda_status 看 hint;必要时 eda_launch 起应用并等扩展连上 |",
|
|
118
118
|
"| extensionVersions 与 bridgeVersion 不一致 | 装的是旧扩展 | 重新导入对应版本 zip(文件名带版本号)并重启应用 |",
|
|
119
119
|
"| API method not allowed | 该 API 不在扩展白名单 | 调 eda_list_api_methods 看清单;优先用包装好的工具 |",
|
|
120
|
+
"| open_document 返回 opened:false / activated:false | 应用停在 Start Page(tab_page1),该构建无法从 HOME 程序化打开文档 | 先 eda_open_project 打开工程,再 eda_open_document;用 activeDocumentUuid 确认,别假设已激活 |",
|
|
120
121
|
"| 调用 60s 超时 | 页面切走留下僵尸 socket,或单次改太多 | 只读调用会自动换页面重试一次;把批量拆到 ≤20/批 |",
|
|
121
122
|
"| 读图返回空 / null | 活动文档不是原理图页 | eda_get_active_document → eda_open_document 切页 |",
|
|
122
123
|
"| createNetLabel 无响应 | 3.2.186 该 API 不 resolve | 自动降级文本标签 + stub 导线;批量用 eda_place_net_labels |",
|