EasyEDA Pro を外部から自動操作する — 拡張機能を書かずに CDP で叩く
GUI しか入口がない設計ツールを、どう自動化するか
基板設計を繰り返していると、同じ操作を何度もやることになります。 製造データを出す、部品表を吐く、回路図の変更を基板に反映する。 どれも決まった手順なのに、毎回マウスで同じ場所をクリックしています。
EasyEDA Pro には拡張機能の仕組みがあり、eda.* という API が公開されています。
ドキュメント上はこれで自動化できます。
しかし拡張機能として書くと、面倒がついて回ります。
- 拡張をパッケージしてインストールする必要がある
- 変更のたびに入れ直す
- 外部のスクリプトやCIから呼び出せない。あくまでアプリ内の話
やりたいのは「アプリの外から1コマンドで叩く」ことでした。
結論から言うと、拡張機能を1つも書かずに、同じ API をすべて外部から呼べます。
この記事が向いていない人
- 公式にサポートされた方法だけを使いたい方 — これは内部実装に依存した手段です。 アップデートで動かなくなる可能性があります
- 業務の本番フローに組み込みたい方 — 上と同じ理由で、壊れたときに自分で直せる前提が要ります
- EasyEDA(標準版・Web版)を使っている方 — この記事はデスクトップ版の Pro の話です
仕組み:Electron のレンダラに拡張APIが露出している
EasyEDA Pro のデスクトップ版は Electron アプリです。 つまり中身は Chromium で、**Chrome DevTools Protocol(CDP)**が使えます。
ここからが本題です。
エディタのレンダラプロセスには window._EXTAPI_ROOT_ というオブジェクトがあります。
そしてこれが、拡張機能の中で eda として参照するオブジェクトそのものです。
拡張機能の中: eda.pcb_ManufactureData.getGerberFile(...)
レンダラの中: window._EXTAPI_ROOT_.pcb_ManufactureData.getGerberFile(...)
同じものです。
つまり CDP でレンダラに接続して JavaScript を評価できれば、 拡張をインストールすることも、署名を通すことも不要で、API 全体に手が届きます。
筆者の環境では 94 名前空間 / 703 メソッドが見えています。
手順
1. CDP を有効にして起動する
easyeda-pro.exe --remote-debugging-port=9222
素で起動したプロセスには後から CDP を生やせません。 すでに起動している場合は、一度終了してから起動し直す必要があります。 未保存の作業があると失われるので、ここは自動で kill せず、 手動で保存・終了してもらう運用にしています。
2. レンダラのターゲットを探す
http://127.0.0.1:9222/json
これでターゲットの一覧が JSON で返ります。
複数のウィンドウやワーカーが並ぶので、エディタのレンダラを選ぶ必要があります。
webSocketDebuggerUrl が接続先です。
3. WebSocket で繋いで評価する
Runtime.evaluate に式を渡します。非同期 API が多いので
awaitPromise: true を付けるのがポイントです。
{
"id": 1,
"method": "Runtime.evaluate",
"params": {
"expression": "window._EXTAPI_ROOT_.sys_Environment.getEditorCurrentVersion()",
"awaitPromise": true,
"returnByValue": true
}
}
これだけです。あとは呼びたいメソッドを差し替えるだけになります。
筆者は PowerShell のラッパーを書いて、こう叩けるようにしています。
.\eda-cdp.ps1 -Expression 'await eda.sys_Environment.getEditorCurrentVersion()'
.\eda-cdp.ps1 -File .\scripts\pcb-summary.js
Claude Code から自動化する場合、この1行が実行単位になります。 API の名前さえ分かれば、あとは自然言語で指示して実行させられます。
API の名前をどう調べるか
推測は当たりません。引数の順序も enum の値も、想像とは違います。
幸い、クライアントに api-types.d.ts が同梱されています。
型定義とコメント(中国語)が入っているので、これを検索するのが確実です。
クラス名から名前空間への変換は、先頭だけ小文字という規則です。
PCB_Document → eda.pcb_Document
SYS_Message → eda.sys_Message
ハマった点
ここからが本題かもしれません。素直に動かない API がそれなりにあります。
ヘッドレスでない API がある
pcb_Document.importChanges()(回路図→基板の反映)は、
確認ダイアログを開くだけで true を返します。
「Apply Changes」を押すまで、実際には何も変わりません。
返り値が true なので、成功したと勘違いします。
対処は単純で、ダイアログのボタンを自分で押す関数を用意しました。
await eda.pcb_Document.importChanges();
await clickDialogButton('apply changes');
アプリ自身のボタンを押すのは安全ですが、DOM 要素を消してはいけません。
トースト通知を消そうとして要素を削除したら、UI 全体が消えました
(レンダラをリロードして復帰しました)。
showToastMessage の表示時間に 0(無期限)を渡すと閉じる API が無いので、
必ず有限値を渡す、というのも同じ話です。
ファイルを外に出せない
sys_FileSystem.saveFile() はネイティブの保存ダイアログが出ます。自動化になりません。
saveFileToFileSystem() は拡張の権限が無いため Failed to fetch で落ちます。
そこで、生成したデータを base64 にして CDP の戻り値として吸い出し、
呼び出し側で書き出す方式にしました。ガーバー、BOM、ピック&プレース、
DXF、PDF、IPC-2581、ODB++、3D — 製造データ系は
pcb_ManufactureData に揃っているので、これで全部持ち出せます。
壊れている API
呼んでも意図通りに動かないものがあります。
| 呼び出し | 挙動 |
|---|---|
dmt_Project.createProject() | 必須の path を送らないため常に undefined |
dmt_Project.deleteProject() | @internal で無効 |
dmt_Project.getAllProjectsUuid() | ローカル工程では常に [] |
sys_FileSystem.getProjectsPaths() | 応答しない(await するとハングする) |
sch_Netlist.getNetlist() | 同上。sch_ManufactureData.getNetlistFile() を使う |
sys_FileSystem.saveFileToFileSystem() | 拡張権限が無く Failed to fetch |
工程(プロジェクト)の作成・削除は拡張 API ではできません。 内部の HTTP API を直接叩く別スクリプトで回避しています。
なお、開いたままの工程を削除するとフロントエンドの状態が壊れます。
以降 openProject() が黙って false を返し続けるようになるので、
削除処理の後は自動でリロードするようにしました。
危険な API に注意
これが一番重要です。
pcb_ManufactureData.place*Order()
sch_ManufactureData.place*Order()
これらは 実際に JLCPCB へ発注します。
API 一覧を眺めながら「何が呼べるか試す」という進め方をしていると、 うっかり呼ぶ可能性があります。名前で判断せず、必ず事前に確認してください。
同様に、openProject() と closeDocument() は
未保存の変更を無確認で破棄します。
先に sch_Document.save() / pcb_Document.save() を呼ぶ必要がありますが、
save はディスク上を上書きするので、これも安易に呼べません。
自動化スクリプトを書くときは、破壊的な操作の前に必ず人間の確認を挟む設計にしています。
この方法の位置づけ
利点は明確です。
- 拡張のインストールも署名も不要
- 変更のたびに入れ直す必要がない。スクリプトを書き換えて再実行するだけ
- 外部プロセスから呼べる。シェル、CI、AI エージェント、どこからでも
- 拡張機能で使える API がすべてそのまま使える
欠点も明確です。
- 内部実装への依存。
_EXTAPI_ROOT_という名前が変われば動かなくなります - 公式にサポートされた方法ではありません
- CDP ポートを開ける必要がある(ローカル限定にすべきです)
個人の作業を自動化する用途には十分に実用的で、 実際に基板の製造データ出力まで一通り自動化できています。 業務の本番フローに載せるなら、壊れたときに自分で直せることが前提になります。
まとめ
- EasyEDA Pro は Electron 製。CDP でレンダラに繋げる
- レンダラの
window._EXTAPI_ROOT_が拡張 API そのもの。拡張の作成もインストールも不要 - API 名は推測せず、同梱の
api-types.d.tsを検索する。クラス名は先頭だけ小文字にすると名前空間になる - ダイアログを開くだけの API がある。 返り値が
trueでも完了していない - ファイル出力はネイティブダイアログを避け、戻り値で吸い出す
place*Order()は本当に発注する。 試しに呼んではいけない
GUI しか入口がないと思っていたツールでも、 Electron 製なら中身に手が届くことがあります。 同じ発想は他の Electron アプリにも使えるはずです。
実装の詳細(依存なしの Node.js コード、ターゲットの特定方法、ヘルパーの実装)は Zenn に書きました → EasyEDA Pro を拡張機能なしで外部から自動操作する