# EasyEDA Action Reference

Run `easyeda actions` for the authoritative machine-readable list.

## Playbook 回放(`easyeda apply`)— 批量步骤的首选载体

> **多步批量操作(>~5 步)不要再写 shell/python 胶水脚本**——写成 playbook JSON,
> `easyeda apply` 按步执行,自带变量捕获、门禁、journal 断点续跑。
> 完整格式与错误处理语义见 `docs/design-apply-playbook.md`(单一真源)。

```bash
easyeda apply steps.json                    # 顺序执行(meta.project 定目标工程)
easyeda apply steps.json --dry-run          # 预检 + 打印计划,不执行
easyeda apply steps.json --project demo2    # CLI flag > 文件(同一份打到另一工程)
easyeda apply steps.json --var LIB=<uuid>   # 变量复写(参数化)
easyeda apply steps.json --resume           # 按 journal 跳过已完成步骤(恢复 captured 变量)
easyeda apply steps.json --from 12 --to 30  # 区间执行
easyeda apply steps.json --yes              # 放行确认门控(delete/clear/rip-up/import 类)
```

要点(实现与设计一致,已单测+真机验证):
- 每步 `action:`(typed action)或 `run:`(Cobra 子命令,如 `pcb auto-place`)二选一;
  `notify:` 弹 toast。`payload/flags/args` 内 `${VAR}` 替换。
- `capture: {"U1": "$.primitiveId"}` 把结果存变量给后步用(id 跨会话会变,这是复现的命门)。
- `assert: {"$.score": ">=95", "$.overlaps": "==0"}` = 门禁,不过即停(`onFail: stop` 默认)。
- **错误纪律**:失败即终止;只读步骤自动重试 2 次;**变更类步骤超时不自动重试**
  (mutation 可能已生效——先读回校验再 `--resume`);变更步骤可带 `verify:` 读回块自证。
- journal 头带 playbook sha,文件改动会拒绝 `--resume`(改用 `--from`)。
- **dry-run 纯计算铁律(ADR-0004)**:所有 `--dry-run` **机械保证零 mutation** ——
  dry-run 模式下派发层直接拒绝任何 Mutates 动作,预览绝不落件;可以放心把
  dry-run 当纯只读预演用。

**录制导出**:`easyeda audit export --playbook --day 2026-07-03 --since 15:17 --until 15:19
-o replay.json` 把真实会话(审计日志)提取成 playbook——只留变更步骤、自动挤压 autosave
风暴、**自动接线 capture/${var}**(后步引用前步 result.primitiveId 时);引用「窗外出生」
裸 id 的步骤会标 `raw-id` 警告(只能对同一板态回放,先 review)。⚠️ 提取物可能含
rip-up/clear 等破坏性步骤——整册回放前先 `--dry-run` 看计划,或用 `--from/--to` 只放安全区间
(已实证:esp32 移件段 18 步区间回放,幂等,lint 保持 100)。

## Navigation

- `system.health` — daemon + connector 可用性，已连接窗口列表
- `project.current` — 当前工程 uuid / name / teamUuid
- `document.current` — 当前激活文档 uuid / tabId / documentType
- `document.open` — 按 UUID 打开任意文档（原理图或 PCB）
- `schematic.pages.list` — 工程内全部原理图及页面
- `schematic.page.open` — 切换到指定原理图页（兼容旧用法）

## Sheet / 图页管理 + 明细表（title block）

均映射 `eda.dmt_Schematic.*`。**注意：EasyEDA Pro 无设置纸张尺寸(A4/A3)的公开 API**；可编辑的「图纸」属性就是明细表(title block)。CLI：`easyeda sch …`。

- `schematic.titleblock.get` — 读当前（或指定 `pageUuid`）图页的明细表：`showTitleBlock` + 各字段 `titleBlockData`。**改前先 get 拿到字段 key** → `easyeda sch titleblock-get`
- `schematic.titleblock.modify` — 调整明细表：显隐 + 字段值（只传要改的项）→ `easyeda sch titleblock --show` / `--data '{"Title":{"value":"电源模块"}}'`。⚠**当前禁用 `--data` 写入(2026-08-17 定案)**:写路径会损毁 sheet 图元的符号引用(component 变成名字塞 uuid 位、libraryUuid 丢),控制台报「元件 $NI… 器件/符号属性有误」,save 落盘后**下次重启平台校验拒载 = 图框丢失**。修复=`sch prim-delete --allow-sheet` 删损坏实例 + `sch place` 重放 `Drawing-Symbol_A4`(lib 0819f05c4eef4c71ace90d822a990e87 / device bc676184ec9748d7b372ad543982403a,@(0,0))。连接器写路径修好前图签留白,`gate --strict` 的 missing-titleblock 如实报告。
  - **平台会对写不进去的字段返回成功**（官方 remarks 原文：「无法识别的明细项将被忽略」且「仍将返回 `true`」，与「删除 API 撒谎」同族）。handler 因此**改前快照 → 写 → 回读逐项比对**，产出 `applied`/`alreadySet`/`notApplied`/`unknownKeys`；全部落空即 ERROR，部分落空回 `partial:true` + warnings，CLI 非零退出（#151 三态约定）。
  - **`unknownKeys` = 这些根本不是本页的明细项**，修法是换 key 而不是重试 —— 先 `sch titleblock-get` 看可用 key。**明细表改不了纸张尺寸**：曾有 20 次调用拿 `Size`/`Width`/`Height`/`Page Size` 当纸张属性写，全部失败（audit 实测该 action 一度 32 次调用 0 次成功）。
  - **只能改当前聚焦页** —— 官方签名无 `pageUuid` 参数（`titleblock.get` 反而支持，两者不对称）。改之前先确认聚焦页就是目标页。
- `schematic.page.create` — 新建图页（`schematicUuid`）→ `easyeda sch page-new --schematic <uuid>`
- `schematic.page.rename` — 重命名图页 → `easyeda sch page-rename --page <uuid> --name ...`
- `schematic.page.delete` — 删除图页（**需确认**，无 undo）→ `easyeda sch page-delete --page <uuid>`
- `schematic.rename` — 重命名整张原理图文档（非单页；可能联动复用模块符号 + PCB）→ `easyeda sch rename --schematic <uuid> --name ...`

## View（画布视图快捷键，原理图 + PCB 通用）

作用于当前聚焦的画布，等价于编辑器工具栏/快捷键。CLI：`easyeda view …`。

- `view.fit` — 适应全部（`K` 快捷键）；缩放至显示全部图元 → `easyeda view fit`
- `view.fit_selection` — 适应选中；先 `schematic.select` 再缩放至选中图元 → `easyeda view fit-selection`
- `view.zoom` — 缩放到坐标/比例（x/y/scale，scale 为百分比，省略则保持当前值）→ `easyeda view zoom --scale 200`
- `view.region` — 缩放到矩形区域（left/right/top/bottom，单位：原理图 0.01inch、PCB mil）→ `easyeda view region --left 0 --right 1000 --top 1000 --bottom 0`

## Inspect Schematic

- `schematic.components.list` — 当前页（或全页）所有元件，可含 pins
- `schematic.select` — 按 primitiveId 选中图元
- `schematic.export.image` — **看局部原理图的首选**（#166）。把当前页、或**只把指定图元**渲染成 **SVG / PNG / PDF**：`easyeda sch export-image --ids id1,id2 --out block.svg`（省略 `--ids` 导整页；`--format` 默认 svg；`--scope selection|page|project`；`--page` 定位页）。给了 `--ids` 就自动选中并**把画布裁到选区**（实测 3 个器件 → 283×155，整页是 1191×846）。**比 `view region` + `snapshot` 可靠**：不走视口，所以不受「后台标签页不重绘 → 截回上一帧整页」的影响；SVG 还是矢量，放大看密集接线不糊。⚠️ 底层 `getExportDocumentFile` 的 `object` 字面量**官方类型定义是错的**，传错不报错、直接让 promise 永远悬着（编辑器卡在 1% 进度条）——真值已固化在 handler 里，**不要照 .d.ts "修正"**；handler 另有 30s 超时兜底。
- ~~`schematic.snapshot`~~ — **已移除(2026-08-12)**:视口截图易 stale,原理图出图统一走 `sch export-image`(整页或 `--ids` 选区,渲染文档数据、无视口依赖)。**落盘绝对路径**在 `result.artifactPath`(所有产 artifact 的动作统一如此,含 pcb snapshot/BOM/网表导出),stderr 另有 `📎 artifact saved: <path>` 一行 —— 读图直接用这个路径

## Mutate Schematic

- `schematic.component.place` — 从库放置元件（libraryUuid + uuid + x/y）。放置后自动把 `supplierId` 回填为 device 的**真立创 C 号**（平台 create 默认填成 `<MPN>.1` 的 subPartName，会让官方「器件标准化」面板全标红、BOM Supplier Part 不可下单，#157）；device 无 C 号（外采占位件）不动。⚠️ **超时不等于没落地(假失败定律)**:`connector did not respond` 时器件通常**已经建在画布上**,只是回执丢了 —— 直接重发会造重复件。`sch block-apply` 已自带**收编**(放置前快照 id → 失败后 settle 回读 → 认「不在快照里 + componentType==part + 落在下发坐标 ±5」的那个件,进 `rollback.adoptedPrimitiveIds` 并照常删除;绝不凭空造 id,也永不碰页面上原有的同型同坐标器件)。**「认不出」分两种,别混**(2026-08-20 修):回读被证明新鲜 → `adopt ✓ …确实没有落地`,可以直接重跑;回读**没被证明新鲜** = 不可信 → `adopt ? …无法判断` + PARTIAL STATE,**此时绝不能读成「没落地」**。旧版少了这道新鲜度门,真机上把「C8 明明就在 (440,535)」报成了「确实没有落地,页面上没有残件」。**新鲜度分两档**(连接器 FIFO 上线后):**算术档(强证据)**——连接器每条响应带 `seq`/`seqAbandoned`,回读期间 `seqAbandoned` 没变 = FIFO 保证这次回读的 handler 在那次 place 的 handler settle 之后才开跑,**连探针都不需要**(第一件就超时也能出结论);`seqAbandoned` 变了 = 有 handler 被放弃且仍在跑、效果可能稍后才落地 → 一律 uncertain。**探针档(弱证据)**——连接器比 CLI 旧、响应不带这些字段时退回旧启发式(本命令此前已落地的器件必须一个不缺地出现在回读里),报文会标 `证据档:弱(探针启发式)` 并给升级步骤;**绝不因为缺字段就默认新鲜**。⚠️ `seq` 证明的是 **handler 边界**的先后,**不是**「文档已提交」——「确实没有落地」的准确含义永远是「在可证的最新一刻那里没有新器件」。**手工调 place 时同样办法**:先 `sch list` 对比坐标确认落没落地,再决定重发还是删除,别盲重试。收到 `ACTION_ABANDONED` = 这次写没有已知完成时刻,盲重发会造重复件;收到 `QUEUE_OVERFLOW` = 该动作**根本没执行**,消化积压后重发是安全的。
- `schematic.text.list` — **只读**枚举当前激活页全部文本图元（id/content/x/y/rotation/fontSize/color），配 `sch prim-delete --ids` 清理孤儿 zone-draw 标签，免走 `debug exec` 逃生舱（#156）。页懒加载定律：只见激活页，多页工程用 `--page` 逐页扫。CLI：`easyeda sch text-list [--page P2]`
- `sch note`（CLI 内部 exec_js，同 zone-draw 惯例，无新 action）— **放电路说明文本**（分页分区+电路说明三件套的第三件，布局默认必做）：`--text`（`\n` 换行）`--x/--y`（y-UP）`--font-size`（默认 10）`--color`（默认 #5A5A5A）。创建后回读 primitiveId 验证 + 显式 save；枚举 `sch text-list`、清理 `sch prim-delete`。CLI：`easyeda sch note --text "LDO: 5V→3V3 1A\n输入/输出各 100nF" --x 700 --y 300 --doc P2`
- `schematic.component.resolve_lcsc` — **确定性「已放置器件→真 C 号」解析（#158）**。精确匹配链（实例 C 号 → MPN 严格相等 → 工程库名），**匹配的封装必须与实例一致**（唯一命中但封装不同 = 封装变体不符，照样进 unresolved）——绝不模糊兜底取 r[0]（真机事故：裸 search 把 U.FL 天线座解析成 C1017 磁珠）。默认 dry-run 报告；`--apply` 把解析出的 C 号写回 supplierId 非 C 号形状的实例（整板 supplierId 修复一条命令）。unresolved 附候选列表供人工确认。同 MPN+封装缓存；只扫激活页，多页 `--page` 逐页。CLI：`easyeda sch resolve-lcsc [--apply] [--page P2] [--id <pid>]`
- `pcb.component.attrs_backfill` — **PCB 器件属性回填（器件标准化 PCB 侧）**。平台 sch→PCB 导入把 otherProperty 建成**键在值空**（Value/耐压/精度/Datasheet 全 ""），且原理图实例属性值 save/reload 后同样为空（不可作源）——唯一稳定源是 **device 库记录**：按实例 C 号 `getByLcscIds` 解析，只填 PCB 侧空值键（手改值优先，`--overwrite` 强制），全程 PCB 前台。无 C 号器件跳过并报告。`pcb import-changes` 成功后**自动跑**（`--no-sync-attrs` 关）。⚠️ **平台投影键绝不参与 merge**（`Designator`/`Unique ID`/`Name`/`Add into BOM`/`Manufacturer*`/`Supplier*`——它们存在顶层图元状态；库记录的 `Designator:"C?"` 占位键灌进实例会被平台同步成图元位号,一板位号全灭 = 166/166 U? 事故真因,2026-08-09 根治）。CLI：`easyeda pcb sync-attrs [--overwrite]`
- `pcb sync-designators`（`pcb.components.list` + `pcb.component.modify` 编排,无新 action）— **修占位位号**（`U?`/`C?`）：按 `uniqueId`（平台首次导入铸造、跨文档同一命名空间）从原理图回填。只动占位符（手设真实位号绝不覆盖）；每笔回读验证；修完立落 `pcb.save` 检查点；原理图侧同为占位符的件归类「先标注原理图」。`--dry-run`/`--json`（Failed>0 非零退出）。`import-changes` 后自动**殿后**跑（在 attrs 之后,`--no-sync-designators` 关）。CLI：`easyeda pcb sync-designators`
- `schematic.component.modify` — 修改位置、位号、BOM 属性等
- `schematic.component.delete` — 删除元件（需确认）。**级联清理独占桩线/flag**（ADR-0004 Decision 5：只挂在被删件引脚上的桩线树+旗随件删净，**共享树只断不删**；回读证实，结果带 `cascaded` 字段列明细；payload `cascade:false` 退回旧行为）
- `schematic.wire.create` — 创建导线折线
- `schematic.netflag.create` — 创建电源/地/网络端口/短路 flag
- `schematic.power.connect_pin` — 复合操作：从 pin 拉导线 + 在末端放 flag（防止 flag-on-pin DRC fatal）
- `schematic.pin.disconnect` — `connect_pin` 的逆操作：把某 pin 的 stub 导线**连同**末端 netflag/netport 一并删除，避免只删 flag 留下孤儿 stub（EasyEDA 会给残留 wire 分配 `$3N…` 自动网名，`sch check` 现已能识别报 WARN）。按 `--pin U1:5`、`pinX`/`pinY` 坐标(`sch autoconnect --replace` 换网时用)或 `--flag-id`/`--wire-id` 定位。**合并树感知(0.15.1/#137)**：定位到的线可能是 EasyEDA 合并后的长树——flag 搜索覆盖**全折线（顶点+中段）**一并删除失宿 flag，且返回 `alsoDisconnectedPins[]` 列出因删线被连带断开的其它 pin——**该字段非空必须逐个重连**（`sch autoconnect`），否则邻居 pin 静默悬空。**删后回读验证（0.26.x）**：三条定位路径都收齐该脚**全部**触脚桩线（同脚多桩不再漏删），删除后回读画布——平台对并入共享树的 wire 会静默 no-op 仍返 true，此时按部分应用约定（#151）返回 `partial:true` + `survivedIds`/`notApplied` 并在 stderr 打警告，`deletedWires`/`deletedFlags` 只计证实消失的 id。**不再需要删后自己回读确认假成功——看 partial 字段/警告即可**；`partial` 非空说明 pin 可能仍连着，按 `survivedIds` 处理残留。CLI：`easyeda sch disconnect --pin U1:5`
- `schematic.pin.set_no_connect` — 给引脚打/清「非连接标识」(NC, X 标记)，告诉 DRC 该脚是故意悬空。按 `--designator` + `--pin`（可多个）定位；`--clear` 清除。底层必须走器件实例 `component.getAllPins()`，每脚 `setState_NoConnected(...)` 后 `await pin.done()` 才真正落到画布，再用新实例回读验证。CLI：`easyeda sch no-connect --designator U1 --pin 23,24`
- `schematic.rebind.footprint` — 换封装（五步绑定法）。`modify` 改不了已放置件的封装引用，故走 `lib_Device.modify → delete → create → 恢复位号/坐标/属性`。先以 LCSC C 号→MPN→工程库名解析已放置件的**真 32 位 device uuid**（`getState_Component().uuid` 是 16 位符号实例 id，库 API 一律拒收）。**系统库 device 记录不可写**（`lib_Device.modify` 恒 false）——此时自动走**个人库克隆回退**：复制/复用同名副本 → 副本绑新封装 → 重放置，结果 `mode='cloned-to-personal-library'` + `clonedDevice`；克隆触发的「符号/封装另存为」冲突弹框由 MutationObserver 自动点「确认」（后台 tab 定时器被 Chrome 节流，轮询点击不可用）。按封装名精确匹配（同名多命中或未命中会报错，可用 `--footprint-uuid` 直连）。**重建会换新 primitiveId，导线可能需重连——务必跑 `sch drc`/`sch check` 复核连通性。** CLI：`easyeda sch rebind-footprint --id <primitiveId> --footprint <name>`
- `schematic.rebind.symbol` — 换符号，机制同上（五步绑定法 + 同款克隆回退/弹框自动确认）。CLI：`easyeda sch rebind-symbol --id <primitiveId> --symbol <name>`
- `schematic.component.replace` — **整器件替换（换型号）**，官方「器件标准化」面板「使用推荐器件」的 API 等价物（该面板本身零 API）。官方无 rebind-device 原语（`modify` 改不了 device 绑定），走 delete → 同位姿 create 新 device → 恢复位号 + uniqueId（保留 uniqueId 使 sch→PCB `import-changes` 走 UPDATE 而非删加）。**器件身份字段（name/制造商/供应商/LCSC）故意不带过去**——跟新 device 走；`--keep-properties` 才连带旧自定义属性。目标三选一：`--lcsc <C号>`（须唯一解析）/ `--device-uuid <u> --device-lib <l>`（确定性）/ `--query <名称>`（须唯一命中）。delete 后失败自动回滚重建原器件（含完整身份）。返回 **pinDiff**（同位姿下按 pinNumber 对比 removed/added/moved）——非空说明旧导线对不上新引脚，**必须重接线再跑 `sch drc`/`sch check`**。CLI：`easyeda sch replace --id <primitiveId> --lcsc C14663`
- `schematic.save` — 保存原理图。既定流程中的阶段过门后必须显式保存并核对
  `saved:true`；只有用户要求逐步确认时才在保存前停下

## Library

- `schematic.library.search` — 自由文本搜索立创/EasyEDA 器件库，返回 libraryUuid + uuid。当 `query` 为纯 LCSC C 号（`^C\d+$`）时自动切换为精确模式，仅保留 `lcsc`/`supplierId` 严格相等的条目；无精确命中则报错。传 `allowFuzzy`（CLI `--allow-fuzzy`）可保留原模糊排序结果

## Verify & Export

- `schematic.drc.check` — 调官方 `eda.sch_Drc.check` 作为 SDK DRC 门。当前 EasyEDA build 可能只返回 boolean/聚合结果,即使 `includeVerboseError=true` 也不保证有逐条 UI warning；CLI: `easyeda sch drc [--json]`。**不要单靠它宣称“官方 UI DRC 干净”**。
- `schematic.check` — 我们的逐条重建检查:从 primitives + 官方 `sch_ManufactureData.getNetlistFile()` 交叉校验，覆盖悬空脚、导线交叉/穿脚、网络名不一致、零长/悬挂线及 duplicate/titleblock/marker overlap。`--json` 是 `{id,type,version,ok,result}` 信封，findings 位于 `result.findings`。CLI: `easyeda sch check [--json] [--strict]`。
- `schematic.bridgeCheck` — 线树粒度检查 `wire-bridge`、orphan stub/flag，补 `sch check` 逐 wire 视角的盲区。CLI: `easyeda sch bridge-check [--json]`。
- `schematic.read` — 一次读取 components、pin→net、nets、floating pins 与 check；新设计对照 spec，既有原理图重构前后对照黄金 pin→net/NC 集合。components 每条含 **`primitiveId`（改动句柄：select/modify/delete/replace/rebind 都吃它，16 位 hex）**；⚠️ `uniqueId`（`gge…`）是 sch↔PCB 关联键**不是** primitiveId，喂给按 id 的 mutation 必 notFound（真机事故：read 曾漏输出 primitiveId，agent 抓了 uniqueId 全部落空）。CLI: `easyeda sch read [--page <page>]`。
- `schematic.export.netlist` — 导出网表为 artifact。底层必须走官方推荐的 `eda.sch_ManufactureData.getNetlistFile(fileName, netlistType)` 并读取返回的 `File`;不要使用已废弃的 `eda.sch_Netlist.getNetlist()`。官方文档标注 `getNetlist()` obsolete 且建议替代为 `getNetlistFile()`,并且 upstream issue [easyeda/pro-api-sdk#30](https://github.com/easyeda/pro-api-sdk/issues/30) 已复现它在含悬空引脚的原理图上可能无限卡死。CLI: `easyeda sch netlist`
- `schematic.export.bom` — 导出 BOM（csv 或 xlsx）为 artifact。CLI `easyeda bom export --type csv` **默认在导出后就地补全 LCSC C 号**（按 Manufacturer Part 关联 `standard-parts.json`，把 `Supplier Part` 从 `<MPN>.1` 改写为可下单的 C 号）；`--enrich=false` 关闭，xlsx 不补全（二进制）。补全是 best-effort（缺 python3/脚本只告警、导出仍成功）。脚本自动解析顺序：`--script` > `$EASYEDA_SKILLS_DIR/easyeda-agent/scripts/bom-enrich.py` > 二进制/工作目录向上找 `skills/` > PATH；安装版二进制在 `/usr/local/bin` 时设 `EASYEDA_SKILLS_DIR` 最稳。

## PCB 基础上下文（非穷举）

- `pcb.documents.list` — 工程内所有 PCB 文档（uuid + name）
- `pcb.components.list` — PCB 上的封装/器件（可含 pads）
- `pcb.layers.list` — PCB 层列表 + 当前层 + 铜层数（会先激活 PCB tab 保证 `currentLayer` 可读回；无当前层时附带 `visibleLayers` 作为显示状态证据）→ `easyeda pcb layers`
- `pcb.layers.set_current` — 切换当前编辑层（`--layer` 接受 id|层名|top|bottom|inner1）→ `easyeda pcb layer-set --layer bottom`
- `pcb.layers.visibility` — 显示/隐藏/聚焦层做视觉 QA：`--preset top-only|bottom-only|copper-only|silk-only`，或 `--show/--hide`（可加 `--exclusive` 只留所选）→ `easyeda pcb layer-visibility --preset bottom-only`
- `pcb.view.side` — 切到顶面/底面视图（选该面铜层为当前层 + 聚焦该面铜+丝印），随后 `pcb snapshot` 即反映该面。注意：EasyEDA 无原生画布翻面 API，这是「层聚焦」近似而非物理翻板 → `easyeda pcb view-side --side bottom`
- `pcb.nets.list` — PCB 全部网络

## Board（板子/组合 — 原理图↔PCB 绑定）

一个 **Board = 1 张原理图 + 1 块 PCB**，原理图与 PCB 就是通过它「组合」在一起（`import_changes` 也沿此链接同步）。Board 以**名称**标识。CLI：`easyeda board …`。

- `board.list` / `board.current` — 列出全部组合（名称 + 原理图 + PCB）/ 当前组合
- `board.create` — 把原理图和/或 PCB 绑成新组合（`--schematic` / `--pcb`）；游离 PCB 在 `import_changes` 前的修复手段
- `board.rename` — 重命名组合（`--name` → `--new`）
- `board.copy` — 复制组合（连同原理图 + PCB）
- `board.delete` — 删除组合（**需确认**，无 undo）

## Confirmation Required

- `schematic.component.delete`
- `schematic.page.delete`（删除图页，无 undo）
- `board.delete`（删除组合/板子，无 undo）
- 生成的多步 mutation 计划
- `debug.exec_js`（任何情况）

