@olimsaidov/icdp 0.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/LICENSE +21 -0
- package/README.md +104 -0
- package/dist/frame/ax-tree.mjs +2186 -0
- package/dist/frame/index.d.mts +17 -0
- package/dist/frame/index.mjs +782 -0
- package/dist/host/index.d.mts +99 -0
- package/dist/host/index.mjs +328 -0
- package/dist/protocol.d.mts +102 -0
- package/dist/protocol.mjs +16 -0
- package/dist/relay/core.d.mts +54 -0
- package/dist/relay/core.mjs +411 -0
- package/dist/relay/node.d.mts +24 -0
- package/dist/relay/node.mjs +99 -0
- package/package.json +77 -0
- package/src/frame/ax-tree.ts +2393 -0
- package/src/frame/index.ts +1048 -0
- package/src/host/index.ts +422 -0
- package/src/protocol.ts +125 -0
- package/src/relay/core.ts +499 -0
- package/src/relay/node.ts +135 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Olim Saidov
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
# icdp
|
|
2
|
+
|
|
3
|
+
Chrome DevTools Protocol over an iframe boundary. External CDP tools (agent-browser, chrome-remote-interface, Playwright best-effort) drive and inspect an app embedded in an iframe — including a **cross-origin** iframe — without a real browser debugging session.
|
|
4
|
+
|
|
5
|
+
```
|
|
6
|
+
agent-browser / CDP client
|
|
7
|
+
│ WebSocket (standard CDP, flat sessions)
|
|
8
|
+
▼
|
|
9
|
+
Relay ← @olimsaidov/icdp/relay (+ /relay/node) server
|
|
10
|
+
│ WebSocket (bridge protocol)
|
|
11
|
+
▼
|
|
12
|
+
Host ← @olimsaidov/icdp/host parent window
|
|
13
|
+
│ MessagePort (per iframe)
|
|
14
|
+
▼
|
|
15
|
+
Frame Agent ← @olimsaidov/icdp/frame inside the iframe'd app
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
See `CONTEXT.md` for the project language (Frame Agent, Host, Relay, Client, Target, Pairing) and `docs/adr/` for architectural decisions.
|
|
19
|
+
|
|
20
|
+
## The three pieces
|
|
21
|
+
|
|
22
|
+
### Frame Agent — in the embedded app
|
|
23
|
+
|
|
24
|
+
The app under automation includes the agent itself (cooperative embedding — the Host never injects):
|
|
25
|
+
|
|
26
|
+
```ts
|
|
27
|
+
import { startFrameAgent } from "@olimsaidov/icdp/frame";
|
|
28
|
+
|
|
29
|
+
startFrameAgent({ allowedParents: ["https://shell.example.com"] });
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
The agent announces itself to the parent on boot and stays dormant unless the parent's origin is allowlisted. `allowedParents: "*"` hands DOM read/write/eval to **any** embedder — only for sandboxed or throwaway pages.
|
|
33
|
+
|
|
34
|
+
### Host — in the parent window
|
|
35
|
+
|
|
36
|
+
```ts
|
|
37
|
+
import { IcdpHost } from "@olimsaidov/icdp/host";
|
|
38
|
+
|
|
39
|
+
const host = new IcdpHost();
|
|
40
|
+
host.pair(iframeElement, { targetId: "preview", origins: ["https://app.example.com"] });
|
|
41
|
+
|
|
42
|
+
// Local consumption — no server needed (e.g. a console panel):
|
|
43
|
+
const session = host.attach("preview");
|
|
44
|
+
session.onEvent((method, params) => {
|
|
45
|
+
/* Runtime.consoleAPICalled, ... */
|
|
46
|
+
});
|
|
47
|
+
await session.send("Runtime.enable");
|
|
48
|
+
|
|
49
|
+
// Forward everything to a Relay so external tools can connect:
|
|
50
|
+
const disconnect = host.connectRelay({ url: "ws://localhost:9222/icdp/host" });
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
Target identity belongs to the Pairing: reloads and navigations keep the same `targetId` (Clients see `Page.frameNavigated`); commands in flight when a document dies fail fast with `-32000`. The Relay uplink is just another consumer of the same hub — events broadcast to all attached sessions, domain enables are ref-counted.
|
|
54
|
+
|
|
55
|
+
### Relay — the server
|
|
56
|
+
|
|
57
|
+
```ts
|
|
58
|
+
import { serveRelay } from "@olimsaidov/icdp/relay/node";
|
|
59
|
+
|
|
60
|
+
const relay = await serveRelay({ port: 9222 });
|
|
61
|
+
console.log(relay.browserWsUrl); // ws://127.0.0.1:9222/devtools/browser ← CDP clients
|
|
62
|
+
console.log(relay.hostWsUrl); // ws://127.0.0.1:9222/icdp/host ← Host uplink
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
The Node adapter is built on `node:http` + `ws`. The runtime-agnostic core (`@olimsaidov/icdp/relay` → `RelayCore`) takes plain `{ send, close }` sockets, so other runtimes only need a thin adapter. HTTP discovery: `/json/version`, `/json/list`, `/icdp/status`.
|
|
66
|
+
|
|
67
|
+
One Host per Relay, new-wins: a newly connecting Host replaces a stale one, with `targetDestroyed`/`targetCreated` churn surfaced to attached Clients.
|
|
68
|
+
|
|
69
|
+
## Protocol shape
|
|
70
|
+
|
|
71
|
+
- **Flat sessions only.** Clients connect to the single browser-level endpoint and use `Target.getTargets` / `Target.attachToTarget` (or `Target.setAutoAttach`) + `sessionId` routing. There are no per-target WebSocket URLs. Session-scoped `Target.*`/`Browser.*` housekeeping (e.g. agent-browser's session-scoped `Target.setAutoAttach`) is answered by the Relay; the Frame Agent never sees it.
|
|
72
|
+
- **Compatibility bar: agent-browser.** The supported command surface is the prior art's support matrix (AX-tree snapshots, semantic locators, click/fill/type, eval, waits, console, SPA history). Screenshots, PDF, file uploads, drag-and-drop, dialogs, and real network interception are intentionally out — page JavaScript cannot provide them. Raw Playwright over `connectOverCDP` is best-effort, not promised.
|
|
73
|
+
|
|
74
|
+
## Driving an icdp target with agent-browser
|
|
75
|
+
|
|
76
|
+
Use the per-command `--cdp <relay port>` flag, and issue one `wait` first to sync agent-browser's page model from the live target:
|
|
77
|
+
|
|
78
|
+
```sh
|
|
79
|
+
agent-browser --cdp 9222 wait --text "My App" # first command: syncs the model
|
|
80
|
+
agent-browser --cdp 9222 snapshot -i
|
|
81
|
+
agent-browser --cdp 9222 find role button click --name "Save"
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
Do not use `agent-browser connect <ws-url>`: as of agent-browser 0.27.x the session-bound connect no longer routes follow-up commands over the connection (this reproduces against real Chrome too, both browser and page endpoints — it is not an icdp limitation).
|
|
85
|
+
|
|
86
|
+
## Playground
|
|
87
|
+
|
|
88
|
+
`npm run playground` starts a runnable demo of the full topology — see [playground/README.md](./playground/README.md).
|
|
89
|
+
|
|
90
|
+
## Development
|
|
91
|
+
|
|
92
|
+
Node ≥22, VoidZero tooling (Vitest, oxlint, oxfmt, tsdown/Rolldown):
|
|
93
|
+
|
|
94
|
+
```sh
|
|
95
|
+
npm install
|
|
96
|
+
npm test # unit + in-process integration (vitest)
|
|
97
|
+
npm run test:e2e # conformance suite (needs agent-browser CLI + Chrome, ~90s)
|
|
98
|
+
npm run test:all # everything
|
|
99
|
+
npm run check # tsc + oxlint + oxfmt --check
|
|
100
|
+
npm run fmt # oxfmt
|
|
101
|
+
npm run build # tsdown -> dist/
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
The e2e conformance suite (ported from the prior art) drives the full chain with a real browser: agent-browser opens a shell page whose Host pairs with a **cross-origin** iframe running the Frame Agent, uplinked to a real Relay; a second agent-browser session then exercises snapshots, semantic locators, keyboard/mouse, navigation, and graceful failures through `--cdp`. Set `ICDP_DEBUG=1` to log all relay traffic.
|