A Cocos Creator 3.5–3.8 editor extension that opens a small localhost HTTP endpoint, so an external tool can drive the editor you already have open.
Zero dependencies. 127.0.0.1 only. Token-authenticated. ~220 lines.
Two things the editor owns that no headless process can reach:
- the asset database — whose file watcher holds its queue until the editor
window takes focus, so a file your tool added or deleted stays invisible until
a human clicks.
refresh-assetdrains it on demand. - a warm builder —
CocosCreator --buildboots a second headless instance for minutes and races the open editor overlibrary/. Asking the open editor takes ~15s.
The bridge forwards to the Editor API and nothing else. It understands nothing about your project — no scanning, no parsing, no opinion.
It is also editor-main-process only: no scene script, no cc.*, nothing that
touches the running engine. That is what keeps one extension working across
3.5–3.8, since the engine-facing surface is where the version differences live.
Every project on this machine — symlinks this checkout into the editor's extension dirs, so edits here take effect on the next restart:
bash install-global.shOne project — a self-contained copy into <project>/extensions/cocos-bridge:
./install.sh /path/to/CocosProjectEither way, then restart Cocos Creator (Extension Manager → reload is enough for an updated extension, not a newly installed one). Don't use both for the same project — they collide by name.
Confirm it is up:
curl -s -X POST http://127.0.0.1:3789/ready -H 'content-type: application/json' -d '{}'
# → {"ready":true,"version":"3.8.6","project":"/path/to/CocosProject","token":"…"}A cocos-bridge.json is the switch: its mere presence starts the bridge
when the editor loads. Two locations, most specific last — the installers
write the one that matches how you installed:
| File | Scope |
|---|---|
~/.CocosCreator/cocos-bridge.json |
every project this editor opens (install-global.sh) |
<project>/cocos-bridge.json |
that project only (install.sh) |
{ "autoStart": false, "port": 3789 }autoStart is read from the most specific file that states it, so a project can
opt out of a global install — and the global file can disable it everywhere.
With no file anywhere, the bridge stays fully manual: Bridge ▸ start / stop.
It binds 127.0.0.1 from 3789 upward, taking the next free port up to
3838, so several editor windows each get their own. /ready returns the open
project, which is how a client picks the right one when several are up.
All routes are POST with a JSON body.
| Route | Body | Does |
|---|---|---|
/ready |
{} |
{ ready, version, project, token } — the only unauthenticated route |
/reimport |
{url} |
asset-db reimport-asset. Refuses a url the DB never imported ({ok:false}) rather than reporting a no-op as success |
/refresh |
{url} |
asset-db refresh-asset — a file or directory. Sees an externally added/deleted file, works unfocused, and is what triggers a script compile |
/uuid |
{url} |
{ uuid } (null if unknown) |
/build |
{platform, wait?, debug?, options?} |
queue a build in this editor; wait:true → { ok, code } |
/build-tasks |
{} |
the builder's task queue (read-only): id / state / progress |
/ready hands out a per-session token; every other route requires it as
X-Bridge-Token. /ready is safe to leave open because a cross-origin page
can POST here but the browser blocks it from reading the response — so it can
never learn the token, and a blind CSRF POST is rejected. That matters most for
/build, which a page could otherwise use to make the editor rebuild on a loop.
The token rotates on every start; re-read it after a restart.
Which of the two import routes you want:
/refreshfor anything added, deleted, or changed on disk. This is the common case, and the only one that works with the editor unfocused./reimportonly to re-run an import the database already has — e.g. to force the editor to re-read a file it already knows, and to find out whether it still imports cleanly.
The implementation carries comments on why two things are built the way they are
(add-task vs command-build, and the port-scan bind) — each cost a debugging
cycle and is worth reading before changing them.
The bridge forwards; it does not interpret. No project scanning, no dependency graph, no log parsing, no editing, and nothing that reaches into the running engine. A client that wants those does them itself — the project files are on disk, and the bridge's job is only the part that can't be done from outside.