cocos-web-inspector-mcp 2.0.1 → 2.1.0

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/README.md CHANGED
@@ -12,7 +12,7 @@ The current MVP supports:
12
12
  - Scene, node, component, and public-property inspection
13
13
  - Temporary DOM-only node highlighting
14
14
 
15
- By default it does not edit Cocos game state, launch browsers, expose browser network/storage data, or support Firefox/WebKit. Runtime mutations require the explicit `--allow-runtime-mutation` startup flag and affect the attached web build only.
15
+ By default it does not edit Cocos game state, launch browsers, expose browser console/network/storage data, or support Firefox/WebKit. Runtime mutations require the explicit `--allow-runtime-mutation` startup flag and affect the attached web build only. Console, network, and storage tools require the separate `--allow-browser-data` flag.
16
16
 
17
17
  ## Requirements
18
18
 
@@ -113,9 +113,11 @@ The endpoint may instead be provided through the MCP process environment:
113
113
  }
114
114
  ```
115
115
 
116
+ Add `--allow-runtime-mutation` for debugger tools, `--allow-browser-data` for console, network, and storage tools, and `--allow-method-call` for `cocos_call_method`; each flag registers only its own tools.
117
+
116
118
  ## Tools
117
119
 
118
- All tool input objects are strict. Unknown fields are rejected. Inspection tools are annotated as read-only, idempotent, non-destructive, and closed-world. The highlight tool is annotated as a non-destructive, non-idempotent, closed-world temporary mutation because repeated calls reset its removal timer. Runtime debugger tools are non-read-only and closed-world; `cocos_click_node` is additionally destructive and open-world because game click handlers can reach real servers or make irreversible changes, and it and `cocos_step_frame` are non-idempotent.
120
+ All tool input objects are strict. Unknown fields are rejected. Inspection tools are annotated as read-only, idempotent, non-destructive, and closed-world. The highlight tool is annotated as a non-destructive, non-idempotent, closed-world temporary mutation because repeated calls reset its removal timer. Runtime debugger tools are non-read-only and closed-world; `cocos_click_node`, `cocos_drag_node`, and `cocos_type_text` are additionally destructive and open-world because game input handlers can reach real servers or make irreversible changes, and they, `cocos_step_frame`, and `cocos_analyze_batches` are non-idempotent.
119
121
 
120
122
  | Tool | Purpose | Inputs |
121
123
  | --- | --- | --- |
@@ -125,10 +127,14 @@ All tool input objects are strict. Unknown fields are rejected. Inspection tools
125
127
  | `cocos_set_node_active` | Set one node's active state. Registered only with `--allow-runtime-mutation`; returns before/after state. | `pageUrl?`; `uuid`; `active` boolean |
126
128
  | `cocos_set_transform` | Update supplied position, rotation, and/or scale fields for one node. | `pageUrl?`; `uuid`; `position?`, `rotation?`, `scale?` finite vectors |
127
129
  | `cocos_set_property` | Update one bounded public component data property. | `pageUrl?`; node `uuid`; `componentUuid`; `key`; primitive/vector/size/color `value` matching current shape |
128
- | `cocos_click_node` | Dispatch a real mouse click at the visible center of one UI node so Button/touch handlers run. Registered only with `--allow-runtime-mutation`. | `pageUrl?`; `uuid` |
130
+ | `cocos_click_node` | Dispatch a real click at the visible center of one UI node so Button/touch handlers run (a tap under mobile emulation). Registered only with `--allow-runtime-mutation`. | `pageUrl?`; `uuid` |
131
+ | `cocos_drag_node` | Drag from the visible center of one UI node with real pointer input (touch under mobile emulation), to scroll a ScrollView, flip a PageView, or move a Slider. | `pageUrl?`; `uuid`; `dx`, `dy` CSS pixels `-4000..4000`, not both zero; `steps?` `1..60`, default `10`; `durationMs?` `0..5000`, default `300` |
132
+ | `cocos_type_text` | Tap one EditBox and type with real keyboard input, replacing its content, so `text-changed` and editing events fire. Returns the resulting text, or `redacted` for password boxes. | `pageUrl?`; `uuid`; `text` up to 2,000 chars; `submit?` presses Enter |
133
+ | `cocos_analyze_batches` | Capture the 2D draw batches of the next rendered frame: the node that starts each batch and why the previous one broke. | `pageUrl?`; `limit?` `1..500`, default `100`; `tintMs?` `100..30000` overlays each batch in its own color |
129
134
  | `cocos_pause` | Pause the Cocos director when its public API supports it. | `pageUrl?` |
130
135
  | `cocos_resume` | Resume the Cocos director when its public API supports it. | `pageUrl?` |
131
136
  | `cocos_step_frame` | Advance a paused game by 1–60 fixed-delta frames through `cc.game.step`, then stay paused. Fails unless paused first. | `pageUrl?`, `frames?` |
137
+ | `cocos_set_time_scale` | Speed up or slow down the whole game by scaling each frame's delta time; `1` restores it, no `scale` reads it. Registered only with `--allow-runtime-mutation`. | `pageUrl?`; `scale?` `(0, 100]` |
132
138
  | `cocos_show_stats` | Show or hide the engine's FPS, draw-call, and triangle overlay through the public `profiler` API. | `pageUrl?`; `visible` boolean |
133
139
  | `cocos_emulate_device` | Emulate a mobile device like the Chrome device toolbar: viewport, DPR, touch (mouse input arrives as touch), user agent and `navigator.platform`, and orientation; optionally slow the CPU or network. Settings merge across calls. | `pageUrl?`; `preset?` (`iphone-se`, `iphone-14`, `iphone-14-pro-max`, `pixel-7`, `galaxy-s20`, `ipad-mini`) or `width`+`height` `200..4000` with `deviceScaleFactor?` `1..4` and `mobile?`; `orientation?`; `cpuSlowdown?` `1..20`; `network?` (`online`, `offline`, `slow-3g`, `fast-3g`, `fast-4g`); `reload?`; or `reset: true` |
134
140
  | `cocos_scene_tree` | Return a bounded scene tree with node and component summaries. | `pageUrl?`; `maxDepth?` integer `0..20`, default `6`; `maxNodes?` integer `1..5000`, default `500` |
@@ -140,10 +146,27 @@ All tool input objects are strict. Unknown fields are rejected. Inspection tools
140
146
  | `cocos_capture_node` | Return an in-memory viewport-clipped image for one visible UI node: PNG, falling back to JPEG when PNG exceeds the response limit; no file is written. | `pageUrl?`; `uuid` |
141
147
  | `cocos_get_properties` | Serialize public properties for a node or one component selected by type or UUID. | `pageUrl?`; `uuid`; `componentType?` or `componentUuid?`; `maxDepth?` integer `0..6`, default `3`; `0` returns top-level primitives |
142
148
  | `cocos_wait_for_property` | Poll one top-level property until it strictly equals a primitive value or the timeout passes. | `pageUrl?`; `uuid`; `componentType?` or `componentUuid?`; `key`; `equals`; `timeoutMs?` `100..30000`, default `5000`; `intervalMs?` `50..5000`, default `200` |
149
+ | `cocos_explain_click` | Explain whether a tap reaches a node, or what a tap at a viewport point would hit, by replaying the engine's touch dispatch order without dispatching anything: the claiming node, the hit stack, and reasons. | `pageUrl?`; `uuid` or `path`, and/or `x`+`y` viewport CSS pixels |
150
+ | `cocos_listener_report` | Report timers, update callbacks, tweens, and director/game/view listeners that outlive their owner: on destroyed or detached nodes and components, and unowned callbacks (arrow functions, `bind`) grouped by event and name. | `pageUrl?`; `limit?` `1..500`, default `100` |
151
+ | `cocos_dynamic_atlas` | Report the dynamic atlas: config, each page with its packed textures (position, size, a node that draws each), fill ratio and GPU bytes, and why visible sprites stayed out. | `pageUrl?`; `limit?` `1..500`, default `100` |
152
+ | `cocos_asset_report` | List assets in the asset cache with type, `refCount`, bundle, texture GPU bytes, and a `status`: `used` (a live renderer references it), `dependency` (reached from one, the scene, or a persist-root node), `builtin`, or `unused`. Unused assets sort first. | `pageUrl?`; `type?` asset class such as `Texture2D`; `unusedOnly?`; `limit?` `1..500`, default `100` |
153
+ | `cocos_call_method` | Call one public method of a node or component and return its bounded result or thrown error. Runs game code. Registered only with `--allow-method-call`. | `pageUrl?`; node `uuid`; `componentUuid?` (omit to call on the node); `method` identifier; `args?` up to 20 JSON values, where `{"$node": uuid}`, `{"$component": uuid}`, and `{"$asset": uuid}` pass live objects; `awaitMs?` `0..30000` waits for a returned promise; `maxDepth?` `0..6`, default `2` |
154
+ | `cocos_console_messages` | Recent console messages and uncaught page errors since the server attached, newest last, with secrets masked. Registered only with `--allow-browser-data`. | `pageUrl?`; `types?` (`log`, `debug`, `info`, `error`, `warning`, `assert`, `trace`, `pageerror`); `textContains?`; `limit?` `1..200`, default `50` |
155
+ | `cocos_network_requests` | Recent requests since the server attached: id, method, masked URL, resource type, status, failure, duration. | `pageUrl?`; `urlContains?`; `resourceType?`; `failedOnly?` (network failures and HTTP 4xx/5xx); `limit?` `1..200`, default `50` |
156
+ | `cocos_network_request` | One request by id: masked headers, status, timing, and with `includeBody` the request and text response bodies, masked by key, up to 20 KB. | `pageUrl?`; `id`; `includeBody?` |
157
+ | `cocos_storage` | `localStorage` or `sessionStorage` entries with secret-like keys and JSON fields masked, or cookie names and attributes without values. | `pageUrl?`; `area` (`local`, `session`, `cookies`); `keyContains?`; `limit?` `1..500`, default `100` |
143
158
  | `cocos_get_selection` | Return the node the user last Alt+clicked on the game canvas: node summary and path, viewport box, and the hit stack under the point (topmost first). The first call installs the picker. | `pageUrl?`; `disable?` removes the picker, overlay, and selection |
144
159
  | `cocos_highlight_node` | Draw a temporary pointer-transparent overlay around a UI node. | `pageUrl?`; `uuid`; `durationMs?` integer `100..10000`, default `2000` |
145
160
 
146
- `cocos_runtime_diagnostics` does not enable profiler/statistics systems. Render metrics are read from the values `Root` and the GFX device already update every frame (the same sources as `root.fps` and `device.numDrawCalls`), whether or not the profiler is shown; draw calls include the profiler overlay when it is visible. Generic invalid-reference checks still return `UNSUPPORTED_PUBLIC_API`. `cocos_get_selection` lets the user point at a node instead of describing it: it installs a capture-phase listener that consumes only Alt+clicks on the game canvas, so the game never sees them, and draws a labelled pointer-transparent overlay. It picks the topmost active node whose box contains the point and that draws or takes input (a 2D renderer such as Sprite or Label, or a Button, Toggle, EditBox, or Slider). Inactive nodes, layout-only containers, and nodes at zero opacity (including through a parent) are skipped, so invisible full-screen blockers do not swallow picks. The selection is reported `stale` once that node leaves the scene, and the picker disappears on reload. `cocos_highlight_node` temporarily mutates the page DOM only. It does not mutate the Cocos node/component graph or game state. `cocos_capture_node` clips only to the visible browser viewport, never falls back to full-page capture, bounds captures by the visible viewport and encoded response size rather than a fixed pixel cap, returns PNG (or JPEG fallback) base64 in-memory, downscales down to 0.25× (reported as `scale`) when JPEG quality steps are not enough, and rejects responses still oversized after that. Mutation results provide `before` values for manual inverse calls, but restoration cannot undo lifecycle callbacks or other runtime side effects. `cocos_step_frame` uses the public `cc.game.step` (fixed `game.frameTime` delta); because `director.tick` skips logic while the director is paused, it resumes the director only for the synchronous step call and pauses it again. `cocos_show_stats` is an explicit configuration change; while the overlay is visible, draw-call metrics include it. `cocos_emulate_device` holds a CDP session per page: emulation ends on `reset`, when the server disconnects, or when the server process exits, and `reset` also clears viewport overrides set by other CDP clients on that page. Cocos reads the user agent and touch support at startup, so pass `reload: true` for the game to see a new device class. Network throttling only delays traffic; it never reads requests or responses.
161
+ `cocos_runtime_diagnostics` does not enable profiler/statistics systems. Render metrics are read from the values `Root` and the GFX device already update every frame (the same sources as `root.fps` and `device.numDrawCalls`), whether or not the profiler is shown; draw calls include the profiler overlay when it is visible. Generic invalid-reference checks still return `UNSUPPORTED_PUBLIC_API`. `cocos_get_selection` lets the user point at a node instead of describing it: it installs a capture-phase listener that consumes only Alt+clicks on the game canvas, so the game never sees them, and draws a labelled pointer-transparent overlay. It picks the topmost active node whose box contains the point and that draws or takes input (a 2D renderer such as Sprite or Label, or a Button, Toggle, EditBox, or Slider). Inactive nodes, layout-only containers, and nodes at zero opacity (including through a parent) are skipped, so invisible full-screen blockers do not swallow picks. The selection is reported `stale` once that node leaves the scene, and the picker disappears on reload. `cocos_highlight_node` temporarily mutates the page DOM only. It does not mutate the Cocos node/component graph or game state. `cocos_capture_node` clips only to the visible browser viewport, never falls back to full-page capture, bounds captures by the visible viewport and encoded response size rather than a fixed pixel cap, returns PNG (or JPEG fallback) base64 in-memory, downscales down to 0.25× (reported as `scale`) when JPEG quality steps are not enough, and rejects responses still oversized after that. Mutation results provide `before` values for manual inverse calls, but restoration cannot undo lifecycle callbacks or other runtime side effects. `cocos_step_frame` uses the public `cc.game.step` (fixed `game.frameTime` delta); because `director.tick` skips logic while the director is paused, it resumes the director only for the synchronous step call and pauses it again. `cocos_show_stats` is an explicit configuration change; while the overlay is visible, draw-call metrics include it. `cocos_emulate_device` holds a CDP session per page: emulation ends on `reset`, when the server disconnects, or when the server process exits, and `reset` also clears viewport overrides set by other CDP clients on that page. Cocos reads the user agent and touch support at startup, so pass `reload: true` for the game to see a new device class. The tool returns after the canvas has resized to the new viewport, so a following click or capture sees the new layout. Network throttling only delays traffic; it never reads requests or responses.
162
+
163
+ `cocos_type_text` exists because `EditBox.string` is an accessor that `cocos_set_property` refuses, and assigning it would skip the `text-changed` and `editing-did-ended` events that login forms listen for. It taps the EditBox, waits for the DOM input the engine opens, selects its content, and inserts the text through the browser keyboard. `submit` presses Enter: single-line boxes fire `editing-return` and close; multi-line boxes (the default `InputMode.ANY`) insert a newline instead. Password boxes (`InputFlag.PASSWORD`) report `redacted`, and `cocos_get_properties` omits their text. `cocos_drag_node` and `cocos_click_node` send mouse events normally and CDP touch events under mobile emulation, where Chrome would otherwise convert and never acknowledge the mouse input; Cocos only listens for touch once it detected a touch device at startup, so emulate with `reload: true` first.
164
+
165
+ `cocos_analyze_batches` explains draw calls. The 2D batcher merges consecutive renderers that share texture, material, stencil state, and layer into one draw call; anything else in between splits them. Batches are rebuilt every frame and keep no node reference, so the tool wraps the batcher's `commitComp`, `commitModel`, `commitMiddleware`, and `commitIA` for one rendered frame (from `EVENT_BEFORE_DRAW` to `EVENT_AFTER_DRAW`), then restores them. Each batch reports the node and component that started it and a reason: `TEXTURE`, `MATERIAL`, `STENCIL`, `LAYER`, `MASK` (a Mask enters a stencil level), `MODEL` (Graphics and UIMeshRenderer draw on their own), `MIDDLEWARE` (Spine or DragonBones that cannot merge), `CUSTOM_IA`, `BUFFER` (the vertex buffer filled up or the render data changed), `FIRST`, `STATE_RESET`, or `AFTER_MODEL`. With `tintMs`, a pointer-transparent DOM overlay outlines every node of each batch in that batch's color, labels the first node with its index and reason, and removes itself after `tintMs`; each batch reports its `color` and the result reports how many nodes were `tinted` (up to 1,000; nodes without a UI transform or off screen are skipped). The game must be running; a paused director renders no frames. Reasons are read from private `Batcher2D` fields verified on Creator 3.7.4 through 3.8.8, and `drawCalls` from the GFX device also includes non-2D passes and the stats overlay.
166
+
167
+ ### Node targets
168
+
169
+ Every tool that acts on one node takes either `uuid` or `path`, never both. A path is the absolute scene path that `cocos_find_node` and `cocos_scene_tree` report, such as `/LoginScene/Canvas/panLogin/btnLogin`. Paths survive scene reloads, while node UUIDs created at runtime change on each load, so agents can act in one call instead of looking the UUID up first. Sibling nodes may share a name, so a path that matches more than one node fails with `AMBIGUOUS_NODE` and lists the candidate UUIDs; no tool acts on an ambiguous path. `cocos_call_method` arguments accept `{"$path": path}` the same way.
147
170
 
148
171
  ### Page selection
149
172
 
@@ -188,13 +211,18 @@ CDP endpoints may use HTTP, HTTPS, WS, or WSS only on these loopback hosts:
188
211
 
189
212
  CDP endpoint credentials, query strings, and fragments are rejected. Page targets must use HTTP or HTTPS on loopback.
190
213
 
191
- The server intentionally exposes no tools for:
214
+ The server never exposes arbitrary JavaScript evaluation or cookie values. `cocos_call_method` (only with `--allow-method-call`) runs existing public methods of game objects; it cannot run new code, refuses `_`-prefixed, secret-like, `constructor`, and `destroy` members, and returns results through the property serializer, so getters are not invoked and secret-like keys are dropped. A called method still runs with the game's full privileges.
215
+
216
+ Console messages, network requests, and storage are available only with `--allow-browser-data`, as `cocos_console_messages`, `cocos_network_requests`, `cocos_network_request`, and `cocos_storage`. Without the flag those tools are not registered. With it, redaction is best effort and runs before data leaves the server:
217
+
218
+ - Authorization, cookie, set-cookie, API-key, and CSRF headers are replaced with `[redacted]`.
219
+ - URL credentials are dropped and secret-like query parameters masked.
220
+ - JSON and form bodies, and JSON storage values, are masked by key name with the same secret-like key list as property serialization.
221
+ - Free text (console messages, plain-text bodies) is masked for JWTs, `Bearer`/`Basic` tokens, and `key=value` or `"key": value` pairs with secret-like keys.
222
+ - Cookie values are never returned; names and attributes are.
223
+ - Bodies are text only and capped at 20 KB; binary responses report their content type.
192
224
 
193
- - Arbitrary JavaScript evaluation
194
- - Cookies or storage state
195
- - Request/response bodies or headers
196
- - Authorization headers
197
- - Network or console logs
225
+ Secrets in free-form text that match none of these patterns can still be returned. Use the flag only against a disposable profile and test accounts, and treat everything these tools return as untrusted page output.
198
226
 
199
227
  Inspection still executes fixed bridge code inside the attached page. Treat all page content as untrusted data, including text that resembles instructions or prompt injection.
200
228
 
@@ -221,6 +249,20 @@ npm run check
221
249
 
222
250
  The test suite covers URL policy, CDP connection reuse and recovery, scene traversal, property redaction and cycle handling, output bounds, Cocos version rejection, strict tool schemas, exact tool annotations, real page selection, inspector and debugger tools, visual bounds/capture, snapshots, diagnostics, and browser reconnection. CI installs the matching Playwright Chromium revision, runs the full check on Ubuntu and Windows with Node.js 20, 22, and 24, and rejects high-severity production dependency advisories.
223
251
 
252
+ ### Tap and callback debugging
253
+
254
+ `cocos_explain_click` answers "why does tapping this do nothing". It rebuilds the order the engine's pointer dispatcher uses (higher camera priority first, then nodes drawn later before what they cover), runs the engine's own `UITransform.hitTest` (which also applies Masks) on every node listening for touch, and reports the first that would claim the tap. Reasons: `INACTIVE`, `ZERO_SIZE`, `OUTSIDE_VIEWPORT`, `BUTTON_NOT_INTERACTABLE`, `BUTTON_DISABLED`, `NO_TOUCH_LISTENER`, `BLOCKED_BY_BLOCK_INPUT_EVENTS`, `COVERED_BY_OTHER_NODE`, and `MASKED_OR_OUTSIDE_HIT_AREA`. A tap claimed by a descendant still counts, because touch events bubble up to the target. Nothing is dispatched.
255
+
256
+ `cocos_listener_report` complements `cocos_asset_report`. The engine purges callbacks bound to a destroyed Cocos object on its own (listeners on the next emit, tweens on the next frame, component timers on destroy), so those rarely leak. What it can never purge is a callback whose target is not a Cocos object: an arrow function or `bind(this)` registered on `director`, `game`, `view`, or `systemEvent` each time a popup opens keeps the popup's closure alive and runs again. The report groups such unowned callbacks by kind, event, and function name with a count; compare two calls around opening and closing a popup, and a growing count is the leak. Callbacks on destroyed objects are listed as `destroyed`, and those on nodes outside the scene as `detached`, which pooled nodes legitimately are.
257
+
258
+ `cocos_set_time_scale` multiplies the delta time `director.tick` passes to components, the scheduler, tweens, animation, physics, and rendering, so the whole game runs faster or slower; frame stepping with `cocos_step_frame` still uses the fixed frame time. Reload or `scale: 1` restores normal speed.
259
+
260
+ ### Dynamic atlas and asset leaks
261
+
262
+ `cocos_dynamic_atlas` answers why sprites do or do not batch through the dynamic atlas. Each page lists its packed textures with their position, size, and one node that draws each, its `fill` (packed texture area over page area) and `shelfFill` (the height the shelf packer has used), and its GPU bytes. Runtime textures, such as Label text rendered in bitmap cache mode, have no asset uuid and report `runtime: true`. For every visible sprite outside the atlas it gives the reason, checked in the engine's own order: `DISABLED`, `NOT_TEXTURE2D`, `COMPRESSED`, `TOO_LARGE` (over `maxFrameSize`), `NOT_PACKABLE`, `FILTER` (not linear, or mipmapped), `ATLAS_FULL`, or `NOT_YET_RENDERED`. Pages are never compacted: removed textures leave holes until the atlas resets on scene change, so a high `shelfFill` with a low `fill` means wasted space.
263
+
264
+ `cocos_asset_report` is for leak hunting. One call is a snapshot, so compare two: take one, open and close the popup or scene under test, take another, and look for assets whose status is `unused` or whose `refCount` grew. An `unused` asset is still cached although no active renderer, scene dependency, or persist-root node reaches it; it usually means a missing `decRef` or `releaseAsset`. Asset names come from `_name` and are empty in release builds, so identify assets by `uuid`, `type`, and `bundle`. `gpuMemory` comes from the GFX device and covers every texture and buffer, including atlases and render targets the asset cache does not list. Both tools are read-only: they read backing fields such as `_ref`, `_atlases`, and `Cache._map`, call no getters except the side-effect-free `isCompressed`, and verified on Creator 3.7.4 through 3.8.8.
265
+
224
266
  ## Troubleshooting
225
267
 
226
268
  - `CDP_UNAVAILABLE`: start Chromium with loopback remote debugging; rerun `npm run install:chromium` for development tests. A `404` usually means Chrome's built-in remote debugging toggle (`chrome://inspect/#remote-debugging`) holds the port; turn it off or use another port, and check listeners with `lsof -nP -iTCP:9222 -sTCP:LISTEN`. When `127.0.0.1` returns 404 or refuses the connection, the server retries `[::1]` on the same port.
@@ -230,6 +272,15 @@ The test suite covers URL policy, CDP connection reuse and recovery, scene trave
230
272
  - Bounds/capture unavailable: select a visible UI node with `UITransform`. `INACTIVE` means the node or an ancestor is inactive. Capture is viewport-only, bounded, and never falls back to a full-page screenshot; `RESPONSE_LIMIT` means even a 0.25× JPEG exceeded the response budget, so capture a smaller node.
231
273
  - `cocos_snapshot_subtree` returns `RESPONSE_LIMIT` with a partial tree: snapshot a deeper node, or lower `maxDepth`.
232
274
  - `cocos_step_frame` returns `INVALID_MUTATION`: call `cocos_pause` first.
275
+ - `cocos_explain_click` says `NO_TOUCH_LISTENER` for a node that reacts in the game: the game may listen on an ancestor or through a global `input.on`; check the ancestors in `hitStack` or explain the tap by `x`/`y`.
276
+ - `cocos_call_method` returns `pending: true`: the method returned a promise; pass `awaitMs` to wait for it.
277
+ - `cocos_console_messages` or `cocos_network_requests` misses startup output: they list only what happened after the server attached; reload the page and call again.
278
+ - `cocos_network_request` returns `REQUEST_NOT_FOUND`: Playwright drops old requests and bodies to bound memory; list requests again and inspect recent ones promptly.
279
+ - `cocos_asset_report` lists an asset as `unused` right after a scene change: the previous scene's assets stay cached until `autoReleaseAssets` or the game's own release runs; take the comparison snapshot a moment later.
280
+ - `cocos_dynamic_atlas` reports `NOT_YET_RENDERED`: the sprite packs on its first render; check again after a frame.
281
+ - `cocos_analyze_batches` returns `INVALID_MUTATION` with "no frame rendered": call `cocos_resume`; batches are captured from a live frame.
282
+ - `cocos_type_text` returns `NOT_FOCUSED`: the EditBox did not open its input, usually because another node covers it or it is disabled.
283
+ - `cocos_drag_node` moves nothing under mobile emulation: call `cocos_emulate_device` with `reload: true` so Cocos starts listening for touch.
233
284
  - `doctor` reports HTTP 404 on the port: Chrome's built-in remote debugging holds it; see `CDP_UNAVAILABLE` above.
234
285
  - Game still behaves like desktop after `cocos_emulate_device`: call it again with `reload: true`; Cocos detects mobile and touch at startup.
235
286
  - Render metrics `fps` reads 0: the engine publishes FPS once per elapsed second, so read again after the scene has run for a second.
@@ -67,6 +67,10 @@ export type BridgeRequest = {
67
67
  } | {
68
68
  action: 'showStats';
69
69
  visible: boolean;
70
+ } | {
71
+ action: 'analyzeBatches';
72
+ limit?: number | undefined;
73
+ tintMs?: number | undefined;
70
74
  } | {
71
75
  action: 'selection';
72
76
  disable?: boolean | undefined;
@@ -74,12 +78,40 @@ export type BridgeRequest = {
74
78
  action: 'pickAt';
75
79
  x: number;
76
80
  y: number;
81
+ } | {
82
+ action: 'dynamicAtlas';
83
+ limit?: number | undefined;
84
+ } | {
85
+ action: 'explainClick';
86
+ uuid?: string | undefined;
87
+ x?: number | undefined;
88
+ y?: number | undefined;
89
+ } | {
90
+ action: 'listenerReport';
91
+ limit?: number | undefined;
92
+ } | {
93
+ action: 'timeScale';
94
+ scale?: number | undefined;
95
+ } | {
96
+ action: 'callMethod';
97
+ uuid: string;
98
+ componentUuid?: string | undefined;
99
+ method: string;
100
+ args: MethodArgument[];
101
+ awaitMs?: number | undefined;
102
+ maxDepth?: number | undefined;
103
+ } | {
104
+ action: 'assetReport';
105
+ type?: string | undefined;
106
+ unusedOnly?: boolean | undefined;
107
+ limit?: number | undefined;
77
108
  };
78
109
  type Vector3 = {
79
110
  x: number;
80
111
  y: number;
81
112
  z: number;
82
113
  };
114
+ export type MethodArgument = unknown;
83
115
  type Quaternion = {
84
116
  x: number;
85
117
  y: number;
@@ -103,6 +135,8 @@ type PropertyValue = boolean | number | string | {
103
135
  export declare function runBridge(page: Page, request: BridgeRequest): Promise<unknown>;
104
136
  export declare function captureNode(page: Page, uuid: string): Promise<unknown>;
105
137
  export declare function clickNode(page: Page, uuid: string): Promise<unknown>;
138
+ export declare function dragNode(page: Page, uuid: string, dx: number, dy: number, steps?: number, durationMs?: number): Promise<unknown>;
139
+ export declare function typeText(page: Page, uuid: string, text: string, submit: boolean): Promise<unknown>;
106
140
  export declare const devicePresets: {
107
141
  readonly 'iphone-se': {
108
142
  readonly width: 375;