@neuralnomads/codenomad-dev 0.20.1-dev-20261019-e56b286a → 0.20.1-dev-20261019-e32d9331
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/dist/automation/browser-page.js +97 -0
- package/dist/automation/browser-page.js.map +1 -0
- package/dist/automation/browser-page.test.js +119 -0
- package/dist/automation/browser-page.test.js.map +1 -0
- package/dist/automation/page/actions.js +107 -83
- package/dist/automation/page/actions.js.map +1 -1
- package/dist/automation/page/input.js +130 -0
- package/dist/automation/page/input.js.map +1 -0
- package/dist/automation/page/input.test.js +71 -0
- package/dist/automation/page/input.test.js.map +1 -0
- package/dist/automation/page/keys.js +99 -0
- package/dist/automation/page/keys.js.map +1 -0
- package/dist/automation/page/keys.test.js +32 -0
- package/dist/automation/page/keys.test.js.map +1 -0
- package/dist/automation/page/parse.js +79 -0
- package/dist/automation/page/parse.js.map +1 -0
- package/dist/automation/page/protocol.js +143 -0
- package/dist/automation/page/protocol.js.map +1 -0
- package/dist/developer-cdp.js +7 -10
- package/dist/developer-cdp.js.map +1 -1
- package/dist/developer-cdp.test.js +49 -15
- package/dist/developer-cdp.test.js.map +1 -1
- package/dist/opencode/automation/desktop-plugin.js +10 -6
- package/dist/opencode/automation/desktop-plugin.js.map +1 -1
- package/dist/opencode/automation-plugin.js +33 -41
- package/dist/opencode/automation-plugin.js.map +1 -1
- package/dist/opencode/automation-plugin.test.js +42 -1
- package/dist/opencode/automation-plugin.test.js.map +1 -1
- package/dist/plugins/automation/plugin.mjs +230 -40
- package/dist/plugins/missions/plugin.mjs +21 -0
- package/dist/server/routes/automation-plugin.js +7 -5
- package/dist/server/routes/automation-plugin.js.map +1 -1
- package/dist/server/routes/automation-plugin.test.js +17 -4
- package/dist/server/routes/automation-plugin.test.js.map +1 -1
- package/package.json +1 -1
- package/public/assets/{App-z4rDGruy.js → App-DZMdkLQJ.js} +29 -29
- package/public/assets/App-DZMdkLQJ.js.map +1 -0
- package/public/assets/{FilesPanel-BR_6pmtI.js → FilesPanel-CgFgL4q7.js} +3 -3
- package/public/assets/FilesPanel-CgFgL4q7.js.map +1 -0
- package/public/assets/{StatusTab-7nJ4mv4I.js → StatusTab-CaQz8RwN.js} +2 -2
- package/public/assets/{StatusTab-7nJ4mv4I.js.map → StatusTab-CaQz8RwN.js.map} +1 -1
- package/public/assets/{client-state-CdvbHQef.js → client-state-DikXRKzG.js} +2 -2
- package/public/assets/{client-state-CdvbHQef.js.map → client-state-DikXRKzG.js.map} +1 -1
- package/public/assets/{client-state-Dy45bMLT.js → client-state-kpF1cbUu.js} +2 -2
- package/public/assets/{client-state-Dy45bMLT.js.map → client-state-kpF1cbUu.js.map} +1 -1
- package/public/assets/client-state-owner-commands-BMJJQYT-.js +2 -0
- package/public/assets/{client-state-owner-commands-CNRMpxmP.js.map → client-state-owner-commands-BMJJQYT-.js.map} +1 -1
- package/public/assets/{diff-viewer-DidBGGSg.js → diff-viewer-iFIylM56.js} +2 -2
- package/public/assets/{diff-viewer-DidBGGSg.js.map → diff-viewer-iFIylM56.js.map} +1 -1
- package/public/assets/{main-2ULMhXOZ.js → main-B0L3f7sD.js} +52 -52
- package/public/assets/main-B0L3f7sD.js.map +1 -0
- package/public/assets/{markdown-B2vmXRgM.js → markdown-Bh1QlJgA.js} +2 -2
- package/public/assets/{markdown-B2vmXRgM.js.map → markdown-Bh1QlJgA.js.map} +1 -1
- package/public/assets/{opener-startup-state-settings-card-Bj8IDBCX.js → opener-startup-state-settings-card-mzh22bq8.js} +2 -2
- package/public/assets/{opener-startup-state-settings-card-Bj8IDBCX.js.map → opener-startup-state-settings-card-mzh22bq8.js.map} +1 -1
- package/public/assets/{startup-state-settings-card-DAkduiFk.js → startup-state-settings-card-B1UMzsQK.js} +2 -2
- package/public/assets/{startup-state-settings-card-DAkduiFk.js.map → startup-state-settings-card-B1UMzsQK.js.map} +1 -1
- package/public/assets/{startup-state-settings-view-CQ6mkdWM.js → startup-state-settings-view-nZNbhUhQ.js} +2 -2
- package/public/assets/{startup-state-settings-view-CQ6mkdWM.js.map → startup-state-settings-view-nZNbhUhQ.js.map} +1 -1
- package/public/assets/{terminal-square-DeHY-xG8.js → terminal-square-CdJ4Gt6Y.js} +2 -2
- package/public/assets/{terminal-square-DeHY-xG8.js.map → terminal-square-CdJ4Gt6Y.js.map} +1 -1
- package/public/assets/{tool-call-DPmRZgHK.js → tool-call-C9ux0znU.js} +4 -4
- package/public/assets/{tool-call-DPmRZgHK.js.map → tool-call-C9ux0znU.js.map} +1 -1
- package/public/assets/{unified-picker-Dpt39UfN.js → unified-picker-DOP29DIt.js} +2 -2
- package/public/assets/{unified-picker-Dpt39UfN.js.map → unified-picker-DOP29DIt.js.map} +1 -1
- package/public/index.html +1 -1
- package/public/sw.js +1 -1
- package/public/sw.js.map +1 -1
- package/public/assets/App-z4rDGruy.js.map +0 -1
- package/public/assets/FilesPanel-BR_6pmtI.js.map +0 -1
- package/public/assets/client-state-owner-commands-CNRMpxmP.js +0 -2
- package/public/assets/main-2ULMhXOZ.js.map +0 -1
|
@@ -0,0 +1,143 @@
|
|
|
1
|
+
/** Protocol primitives shared by the page engine's actions. */
|
|
2
|
+
/** An element handle passed to a page function, as opposed to a plain value. */
|
|
3
|
+
export class Handle {
|
|
4
|
+
constructor(objectId) {
|
|
5
|
+
this.objectId = objectId;
|
|
6
|
+
}
|
|
7
|
+
}
|
|
8
|
+
export const OBJECT_GROUP = "codenomad-automation";
|
|
9
|
+
export const VALUE_LIMIT = 200;
|
|
10
|
+
export function gone(ref) {
|
|
11
|
+
return new Error(`${ref} is no longer in the page; inspect again`);
|
|
12
|
+
}
|
|
13
|
+
export async function resolveElement(session, ref, backendNodeId) {
|
|
14
|
+
let result;
|
|
15
|
+
try {
|
|
16
|
+
result = await session.channel.send("DOM.resolveNode", { backendNodeId, objectGroup: OBJECT_GROUP });
|
|
17
|
+
}
|
|
18
|
+
catch {
|
|
19
|
+
throw gone(ref);
|
|
20
|
+
}
|
|
21
|
+
await session.checkpoint();
|
|
22
|
+
const objectId = result.object?.objectId;
|
|
23
|
+
if (typeof objectId !== "string")
|
|
24
|
+
throw gone(ref);
|
|
25
|
+
return objectId;
|
|
26
|
+
}
|
|
27
|
+
/** Runs a fixed page function on an element; inputs travel as protocol arguments, never as source.
|
|
28
|
+
* `byValue: false` returns another element handle. */
|
|
29
|
+
export async function callOn(session, ref, objectId, body, args, byValue = true) {
|
|
30
|
+
const result = await session.channel.send("Runtime.callFunctionOn", {
|
|
31
|
+
objectId,
|
|
32
|
+
functionDeclaration: pageFunction(body, session.markAutomation),
|
|
33
|
+
arguments: args.map(value => value instanceof Handle ? { objectId: value.objectId } : { value }),
|
|
34
|
+
returnByValue: byValue,
|
|
35
|
+
objectGroup: OBJECT_GROUP,
|
|
36
|
+
});
|
|
37
|
+
await session.checkpoint();
|
|
38
|
+
const failure = result.exceptionDetails;
|
|
39
|
+
if (failure)
|
|
40
|
+
throw pageError(ref, String(failure.exception?.description ?? failure.text ?? ""));
|
|
41
|
+
const value = result.result;
|
|
42
|
+
if (!byValue) {
|
|
43
|
+
if (typeof value?.objectId !== "string")
|
|
44
|
+
throw gone(ref);
|
|
45
|
+
return value.objectId;
|
|
46
|
+
}
|
|
47
|
+
return value?.value;
|
|
48
|
+
}
|
|
49
|
+
export async function releaseObjects(session) {
|
|
50
|
+
await session.channel.send("Runtime.releaseObjectGroup", { objectGroup: OBJECT_GROUP }).catch(() => undefined);
|
|
51
|
+
}
|
|
52
|
+
function pageFunction(body, mark) {
|
|
53
|
+
const element = `const element = this.nodeType === Node.ELEMENT_NODE ? this : this.parentElement;
|
|
54
|
+
if (!(element instanceof Element) || !element.isConnected) throw new Error("codenomad:gone");`;
|
|
55
|
+
return mark
|
|
56
|
+
? `function (...args) { ${element} globalThis.__codenomadAutomationDepth = (globalThis.__codenomadAutomationDepth || 0) + 1;
|
|
57
|
+
try { return (() => { ${body} })(); } finally { globalThis.__codenomadAutomationDepth--; } }`
|
|
58
|
+
: `function (...args) { ${element} ${body} }`;
|
|
59
|
+
}
|
|
60
|
+
function pageError(ref, description) {
|
|
61
|
+
const [code, detail = ""] = /codenomad:([a-z-]+)(?::(.*))?/.exec(description.split("\n", 1)[0])?.slice(1) ?? [];
|
|
62
|
+
const quoted = JSON.stringify(detail.slice(0, VALUE_LIMIT));
|
|
63
|
+
switch (code) {
|
|
64
|
+
case "gone": return gone(ref);
|
|
65
|
+
case "not-select": return new Error(`${ref} is not a native select; click it to open its menu, then click the option`);
|
|
66
|
+
case "is-select": return new Error(`${ref} is a native select; use select with its option labels`);
|
|
67
|
+
case "no-option": return new Error(`No option ${quoted} in ${ref}; inspect it to see its options`);
|
|
68
|
+
case "option-disabled": return new Error(`Option ${quoted} of ${ref} is disabled`);
|
|
69
|
+
case "single": return new Error(`${ref} takes exactly one value`);
|
|
70
|
+
case "disabled": return new Error(`${ref} is disabled`);
|
|
71
|
+
case "readonly": return new Error(`${ref} is read-only`);
|
|
72
|
+
case "not-editable": return new Error(`${ref} is not a text field`);
|
|
73
|
+
case "not-text": return new Error(`${ref} is not a text field; use click`);
|
|
74
|
+
case "unfocusable": return new Error(`${ref} cannot take focus (hidden, disabled or behind a dialog); reveal it, then inspect again`);
|
|
75
|
+
case "invalid-value": return new Error(`${quoted} is not a valid value for ${ref}`);
|
|
76
|
+
default: return new Error(`The page rejected the action on ${ref}: ${description.split("\n", 1)[0].slice(0, VALUE_LIMIT)}`);
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
/** Marks trusted input (DevTools mouse/keyboard events) in the CodeNomad window. A lease, not a
|
|
80
|
+
* counter: if the clearing call is lost with the connection, the mark still expires. */
|
|
81
|
+
export async function markedInput(session, run) {
|
|
82
|
+
const held = new Map();
|
|
83
|
+
let point = { x: 0, y: 0 };
|
|
84
|
+
let attempted = false;
|
|
85
|
+
const dispatch = async (method, params) => {
|
|
86
|
+
let timestamp;
|
|
87
|
+
if (session.markAutomation) {
|
|
88
|
+
const result = await session.channel.send("Runtime.evaluate", { expression: MARK_INPUT, returnByValue: true });
|
|
89
|
+
const stamp = result.result?.value;
|
|
90
|
+
if (typeof stamp !== "number" || !Number.isFinite(stamp) || stamp <= 0)
|
|
91
|
+
throw new Error("Cannot establish automation input provenance");
|
|
92
|
+
timestamp = stamp;
|
|
93
|
+
}
|
|
94
|
+
const mouse = method === "Input.dispatchMouseEvent";
|
|
95
|
+
if (mouse && typeof params.x === "number" && typeof params.y === "number")
|
|
96
|
+
point = { x: params.x, y: params.y };
|
|
97
|
+
const id = mouse ? `mouse:${params.button}` : `key:${params.code}`;
|
|
98
|
+
const down = mouse ? params.type === "mousePressed" : params.type === "keyDown" || params.type === "rawKeyDown";
|
|
99
|
+
const up = mouse ? params.type === "mouseReleased" : params.type === "keyUp";
|
|
100
|
+
if (down)
|
|
101
|
+
held.set(id, { method, params: { ...params, type: mouse ? "mouseReleased" : "keyUp", buttons: 0, modifiers: 0,
|
|
102
|
+
...(timestamp === undefined ? {} : { timestamp }) } });
|
|
103
|
+
attempted = true;
|
|
104
|
+
await session.channel.send(method, { ...params,
|
|
105
|
+
...(timestamp === undefined || !["Input.dispatchMouseEvent", "Input.dispatchKeyEvent"].includes(method) ? {} : { timestamp }) });
|
|
106
|
+
if (up)
|
|
107
|
+
held.delete(id);
|
|
108
|
+
await session.checkpoint();
|
|
109
|
+
};
|
|
110
|
+
try {
|
|
111
|
+
return await run(dispatch);
|
|
112
|
+
}
|
|
113
|
+
catch (error) {
|
|
114
|
+
if (attempted && error instanceof Error && /timed out|timeout/i.test(error.message)) {
|
|
115
|
+
throw new Error("Input timed out: its outcome is unknown. The page was busy and the input may still be delivered; inspect before retrying.");
|
|
116
|
+
}
|
|
117
|
+
throw error;
|
|
118
|
+
}
|
|
119
|
+
finally {
|
|
120
|
+
// Failure may be after admission but before acknowledgement/checkpoint. Release
|
|
121
|
+
// anything potentially held, without a failed checkpoint suppressing cleanup.
|
|
122
|
+
for (const release of [...held.values()].reverse()) {
|
|
123
|
+
try {
|
|
124
|
+
// A frozen page can time out this write too. The release retains the
|
|
125
|
+
// original press's timestamp, so provenance never depends on the timeout.
|
|
126
|
+
if (session.markAutomation)
|
|
127
|
+
await session.channel.send("Runtime.evaluate", { expression: MARK_INPUT, returnByValue: true }).catch(() => undefined);
|
|
128
|
+
const { text: _text, unmodifiedText: _unmodified, ...params } = release.params;
|
|
129
|
+
await session.channel.send(release.method, { ...params, ...(release.method === "Input.dispatchMouseEvent" ? point : {}) });
|
|
130
|
+
}
|
|
131
|
+
catch { /* Best effort, exact original channel only; never retry the action. */ }
|
|
132
|
+
}
|
|
133
|
+
if (session.markAutomation)
|
|
134
|
+
await session.channel.send("Runtime.evaluate", { expression: UNMARK_INPUT, returnByValue: true }).catch(() => undefined);
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
export const INPUT_MARK_LEASE_MS = 15_000;
|
|
138
|
+
// CDP timestamps use epoch seconds; DOM event.timeStamp uses the page's monotonic
|
|
139
|
+
// origin. Reading both in the page keeps delayed input within its original interval.
|
|
140
|
+
const MARK_INPUT = `globalThis.__codenomadAutomationInputUntil = performance.now() + ${INPUT_MARK_LEASE_MS}; Date.now() / 1000`;
|
|
141
|
+
// Keep the interval's end for events queued before a stalled renderer recovered.
|
|
142
|
+
const UNMARK_INPUT = "globalThis.__codenomadAutomationInputUntil = Math.min(globalThis.__codenomadAutomationInputUntil || 0, performance.now()); true";
|
|
143
|
+
//# sourceMappingURL=protocol.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"protocol.js","sourceRoot":"","sources":["../../../src/automation/page/protocol.ts"],"names":[],"mappings":"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","sourcesContent":["/** Protocol primitives shared by the page engine's actions. */\n\n/** One page's protocol connection, whatever carries it (DevTools socket or a native pipe). */\nexport interface PageChannel {\n send(method: string, params?: Record<string, unknown>): Promise<Record<string, unknown>>\n}\n\nexport interface PageSession {\n channel: PageChannel\n /** Throws once the page changed since the operation began; runs between protocol steps. */\n checkpoint(): Promise<void>\n /** The trusted CodeNomad window: page-side automation and trusted input are marked so they\n * never count as a human answer (`form-answer-interaction.ts`). Never set this in a user's web page. */\n markAutomation: boolean\n}\n\n/** An element handle passed to a page function, as opposed to a plain value. */\nexport class Handle {\n constructor(readonly objectId: string) {}\n}\n\nexport const OBJECT_GROUP = \"codenomad-automation\"\nexport const VALUE_LIMIT = 200\n\nexport function gone(ref: string): Error {\n return new Error(`${ref} is no longer in the page; inspect again`)\n}\n\nexport async function resolveElement(session: PageSession, ref: string, backendNodeId: number): Promise<string> {\n let result: Record<string, unknown>\n try {\n result = await session.channel.send(\"DOM.resolveNode\", { backendNodeId, objectGroup: OBJECT_GROUP })\n } catch {\n throw gone(ref)\n }\n await session.checkpoint()\n const objectId = (result.object as { objectId?: unknown } | undefined)?.objectId\n if (typeof objectId !== \"string\") throw gone(ref)\n return objectId\n}\n\n/** Runs a fixed page function on an element; inputs travel as protocol arguments, never as source.\n * `byValue: false` returns another element handle. */\nexport async function callOn(session: PageSession, ref: string, objectId: string, body: string, args: unknown[], byValue = true): Promise<unknown> {\n const result = await session.channel.send(\"Runtime.callFunctionOn\", {\n objectId,\n functionDeclaration: pageFunction(body, session.markAutomation),\n arguments: args.map(value => value instanceof Handle ? { objectId: value.objectId } : { value }),\n returnByValue: byValue,\n objectGroup: OBJECT_GROUP,\n })\n await session.checkpoint()\n const failure = result.exceptionDetails as { exception?: { description?: unknown }; text?: unknown } | undefined\n if (failure) throw pageError(ref, String(failure.exception?.description ?? failure.text ?? \"\"))\n const value = result.result as { value?: unknown; objectId?: unknown } | undefined\n if (!byValue) {\n if (typeof value?.objectId !== \"string\") throw gone(ref)\n return value.objectId\n }\n return value?.value\n}\n\nexport async function releaseObjects(session: PageSession): Promise<void> {\n await session.channel.send(\"Runtime.releaseObjectGroup\", { objectGroup: OBJECT_GROUP }).catch(() => undefined)\n}\n\nfunction pageFunction(body: string, mark: boolean): string {\n const element = `const element = this.nodeType === Node.ELEMENT_NODE ? this : this.parentElement;\n if (!(element instanceof Element) || !element.isConnected) throw new Error(\"codenomad:gone\");`\n return mark\n ? `function (...args) { ${element} globalThis.__codenomadAutomationDepth = (globalThis.__codenomadAutomationDepth || 0) + 1;\n try { return (() => { ${body} })(); } finally { globalThis.__codenomadAutomationDepth--; } }`\n : `function (...args) { ${element} ${body} }`\n}\n\nfunction pageError(ref: string, description: string): Error {\n const [code, detail = \"\"] = /codenomad:([a-z-]+)(?::(.*))?/.exec(description.split(\"\\n\", 1)[0])?.slice(1) ?? []\n const quoted = JSON.stringify(detail.slice(0, VALUE_LIMIT))\n switch (code) {\n case \"gone\": return gone(ref)\n case \"not-select\": return new Error(`${ref} is not a native select; click it to open its menu, then click the option`)\n case \"is-select\": return new Error(`${ref} is a native select; use select with its option labels`)\n case \"no-option\": return new Error(`No option ${quoted} in ${ref}; inspect it to see its options`)\n case \"option-disabled\": return new Error(`Option ${quoted} of ${ref} is disabled`)\n case \"single\": return new Error(`${ref} takes exactly one value`)\n case \"disabled\": return new Error(`${ref} is disabled`)\n case \"readonly\": return new Error(`${ref} is read-only`)\n case \"not-editable\": return new Error(`${ref} is not a text field`)\n case \"not-text\": return new Error(`${ref} is not a text field; use click`)\n case \"unfocusable\": return new Error(`${ref} cannot take focus (hidden, disabled or behind a dialog); reveal it, then inspect again`)\n case \"invalid-value\": return new Error(`${quoted} is not a valid value for ${ref}`)\n default: return new Error(`The page rejected the action on ${ref}: ${description.split(\"\\n\", 1)[0].slice(0, VALUE_LIMIT)}`)\n }\n}\n\n/** Marks trusted input (DevTools mouse/keyboard events) in the CodeNomad window. A lease, not a\n * counter: if the clearing call is lost with the connection, the mark still expires. */\nexport async function markedInput<T>(session: PageSession, run: (dispatch: (method: string, params: Record<string, unknown>) => Promise<void>) => Promise<T>): Promise<T> {\n const held = new Map<string, { method: string; params: Record<string, unknown> }>()\n let point = { x: 0, y: 0 }\n let attempted = false\n const dispatch = async (method: string, params: Record<string, unknown>) => {\n let timestamp: number | undefined\n if (session.markAutomation) {\n const result = await session.channel.send(\"Runtime.evaluate\", { expression: MARK_INPUT, returnByValue: true })\n const stamp = (result.result as { value?: unknown } | undefined)?.value\n if (typeof stamp !== \"number\" || !Number.isFinite(stamp) || stamp <= 0) throw new Error(\"Cannot establish automation input provenance\")\n timestamp = stamp\n }\n const mouse = method === \"Input.dispatchMouseEvent\"\n if (mouse && typeof params.x === \"number\" && typeof params.y === \"number\") point = { x: params.x, y: params.y }\n const id = mouse ? `mouse:${params.button}` : `key:${params.code}`\n const down = mouse ? params.type === \"mousePressed\" : params.type === \"keyDown\" || params.type === \"rawKeyDown\"\n const up = mouse ? params.type === \"mouseReleased\" : params.type === \"keyUp\"\n if (down) held.set(id, { method, params: { ...params, type: mouse ? \"mouseReleased\" : \"keyUp\", buttons: 0, modifiers: 0,\n ...(timestamp === undefined ? {} : { timestamp }) } })\n attempted = true\n await session.channel.send(method, { ...params,\n ...(timestamp === undefined || ![\"Input.dispatchMouseEvent\", \"Input.dispatchKeyEvent\"].includes(method) ? {} : { timestamp }) })\n if (up) held.delete(id)\n await session.checkpoint()\n }\n try {\n return await run(dispatch)\n } catch (error) {\n if (attempted && error instanceof Error && /timed out|timeout/i.test(error.message)) {\n throw new Error(\"Input timed out: its outcome is unknown. The page was busy and the input may still be delivered; inspect before retrying.\")\n }\n throw error\n } finally {\n // Failure may be after admission but before acknowledgement/checkpoint. Release\n // anything potentially held, without a failed checkpoint suppressing cleanup.\n for (const release of [...held.values()].reverse()) {\n try {\n // A frozen page can time out this write too. The release retains the\n // original press's timestamp, so provenance never depends on the timeout.\n if (session.markAutomation) await session.channel.send(\"Runtime.evaluate\", { expression: MARK_INPUT, returnByValue: true }).catch(() => undefined)\n const { text: _text, unmodifiedText: _unmodified, ...params } = release.params\n await session.channel.send(release.method, { ...params, ...(release.method === \"Input.dispatchMouseEvent\" ? point : {}) })\n } catch { /* Best effort, exact original channel only; never retry the action. */ }\n }\n if (session.markAutomation) await session.channel.send(\"Runtime.evaluate\", { expression: UNMARK_INPUT, returnByValue: true }).catch(() => undefined)\n }\n}\n\nexport const INPUT_MARK_LEASE_MS = 15_000\n// CDP timestamps use epoch seconds; DOM event.timeStamp uses the page's monotonic\n// origin. Reading both in the page keeps delayed input within its original interval.\nconst MARK_INPUT = `globalThis.__codenomadAutomationInputUntil = performance.now() + ${INPUT_MARK_LEASE_MS}; Date.now() / 1000`\n// Keep the interval's end for events queued before a stalled renderer recovered.\nconst UNMARK_INPUT = \"globalThis.__codenomadAutomationInputUntil = Math.min(globalThis.__codenomadAutomationInputUntil || 0, performance.now()); true\"\n"]}
|
package/dist/developer-cdp.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { WebSocket } from "undici";
|
|
2
|
-
import { performAction, snapshotPage } from "./automation/page/actions";
|
|
2
|
+
import { bindRefs, performAction, snapshotPage } from "./automation/page/actions";
|
|
3
3
|
import { RefTable } from "./automation/page/refs";
|
|
4
4
|
import { SnapshotScopeError } from "./automation/page/snapshot";
|
|
5
5
|
const DEFAULT_TIMEOUT_MS = 5_000;
|
|
@@ -56,15 +56,12 @@ export class DeveloperCdp {
|
|
|
56
56
|
};
|
|
57
57
|
});
|
|
58
58
|
}
|
|
59
|
-
async act(action) {
|
|
60
|
-
return this.exclusive(
|
|
61
|
-
const state = await this.ensure(
|
|
62
|
-
const epoch = state.navigationEpoch;
|
|
63
|
-
const
|
|
64
|
-
|
|
65
|
-
return performAction(session, action.kind === "click" ? { kind: "click", ref: action.ref, backendNodeId }
|
|
66
|
-
: action.kind === "type" ? { kind: "type", ref: action.ref, backendNodeId, text: action.text, clear: action.clear }
|
|
67
|
-
: { kind: "select", ref: action.ref, backendNodeId, values: action.values });
|
|
59
|
+
async act(identity, action) {
|
|
60
|
+
return this.exclusive(identity.runId, async () => {
|
|
61
|
+
const state = await this.ensure(identity);
|
|
62
|
+
const epoch = state.navigationEpoch, generation = this.generation(state);
|
|
63
|
+
const page = bindRefs(action, ref => this.refs.resolve(ref, generation));
|
|
64
|
+
return performAction(this.session(state, epoch, "Page changed during action; inspect again"), page);
|
|
68
65
|
});
|
|
69
66
|
}
|
|
70
67
|
/** Refs belong to one target and navigation epoch. */
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"developer-cdp.js","sourceRoot":"","sources":["../src/developer-cdp.ts"],"names":[],"mappings":"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","sourcesContent":["import { WebSocket } from \"undici\"\nimport { performAction, snapshotPage, type PageActionResult, type PageSession } from \"./automation/page/actions\"\nimport { RefTable } from \"./automation/page/refs\"\nimport { SnapshotScopeError } from \"./automation/page/snapshot\"\n\nconst DEFAULT_TIMEOUT_MS = 5_000\nconst DEFAULT_MAX_SCREENSHOT_BYTES = 5 * 1024 * 1024\nconst DIAGNOSTIC_LIMIT = 1_000\nconst DIAGNOSTIC_TEXT_LIMIT = 512\nconst DISCOVERY_TEXT_LIMIT = 256 * 1024\nconst DISCOVERY_TARGET_LIMIT = 64\nconst DISCOVERY_PROBE_CONCURRENCY = 4\nconst CONTEXT_EXPRESSION = `(() => {\n const root = document.querySelector('[data-tab-visible=\"true\"][data-instance-id]');\n const pane = root?.querySelector('.session-cache-pane[data-session-active=\"true\"][data-session-id]');\n return {\n windowId: window.__CODENOMAD_WINDOW_ID__ ?? null,\n instanceId: root?.getAttribute('data-instance-id') ?? null,\n sessionId: pane?.getAttribute('data-session-id') ?? null,\n };\n})()`\n\nexport interface DeveloperCdpSelection {\n endpoint: string\n runId: string\n windowId: string\n sessionId?: string\n}\n\nexport interface DeveloperCdpIdentity extends DeveloperCdpSelection {\n instanceId?: string\n}\n\nexport interface DeveloperCdpContext {\n windowId: string\n instanceId: string | null\n sessionId: string | null\n}\n\nexport interface DeveloperCdpDiagnostic {\n level: \"log\" | \"warning\" | \"error\"\n text: string\n}\n\nexport interface DeveloperCdpInspection {\n target: { id: string; title: string; url: string }\n context: DeveloperCdpContext\n /** Indented accessibility outline with refs (`automation/page/snapshot.ts`). */\n snapshot: string\n /** Lines that hide part of the page; inspect their ref to expand them. */\n collapsed: number\n diagnostics: DeveloperCdpDiagnostic[]\n}\n\n/** A full inspection, or one limited to a ref's subtree and/or matching text. */\nexport interface DeveloperCdpInspectOptions {\n ref?: string\n query?: string\n}\n\nexport type DeveloperCdpAction =\n | (DeveloperCdpIdentity & { kind: \"click\"; ref: string })\n | (DeveloperCdpIdentity & { kind: \"type\"; ref: string; text: string; clear: boolean })\n | (DeveloperCdpIdentity & { kind: \"select\"; ref: string; values: string[] })\n\nexport interface DeveloperCdpScreenshot {\n mediaType: \"image/png\"\n data: string\n bytes: number\n}\n\ninterface CdpSocket {\n onopen: ((event: unknown) => void) | null\n onmessage: ((event: { data: unknown }) => void) | null\n onclose: ((event: unknown) => void) | null\n onerror: ((event: unknown) => void) | null\n send(data: string): void\n close(): void\n}\n\ninterface DeveloperCdpDependencies {\n fetch: typeof globalThis.fetch\n connect: (url: string) => CdpSocket\n timeoutMs: number\n maxScreenshotBytes: number\n}\n\ninterface CdpTarget {\n id: string\n title: string\n type: string\n url: string\n webSocketDebuggerUrl: string\n}\n\ninterface CdpResponse {\n id?: number\n method?: string\n params?: Record<string, unknown>\n result?: Record<string, unknown>\n error?: { code?: number; message?: string }\n}\n\ninterface PendingCommand {\n resolve: (value: Record<string, unknown>) => void\n reject: (error: Error) => void\n timer: NodeJS.Timeout\n}\n\ninterface RunState {\n endpoint: string\n windowId: string\n sessionId?: string\n instanceId?: string\n context?: DeveloperCdpContext\n target?: CdpTarget\n socket?: CdpSocket\n open?: Promise<void>\n /** Distinguishes this run's refs from an earlier state's on the same target. */\n stamp: number\n nextCommandId: number\n navigationEpoch: number\n pending: Map<number, PendingCommand>\n diagnostics: DeveloperCdpDiagnostic[]\n}\n\nexport class DeveloperCdp {\n private readonly dependencies: DeveloperCdpDependencies\n private readonly runs = new Map<string, RunState>()\n private readonly operations = new Map<string, Promise<void>>()\n /** Shared by runs so a ref name is never reused for another element. */\n private readonly refs = new RefTable()\n private stamps = 0\n\n constructor(dependencies: Partial<DeveloperCdpDependencies> = {}) {\n this.dependencies = {\n fetch: globalThis.fetch,\n connect: (url) => new WebSocket(url) as unknown as CdpSocket,\n timeoutMs: DEFAULT_TIMEOUT_MS,\n maxScreenshotBytes: DEFAULT_MAX_SCREENSHOT_BYTES,\n ...dependencies,\n }\n }\n\n async inspect(identity: DeveloperCdpIdentity, options: DeveloperCdpInspectOptions = {}): Promise<DeveloperCdpInspection> {\n return this.exclusive(identity.runId, async () => {\n const state = await this.ensure(identity)\n const generation = this.generation(state)\n const root = options.ref === undefined ? undefined : this.refs.resolve(options.ref, generation)\n let snapshot\n try {\n snapshot = await snapshotPage(this.session(state, state.navigationEpoch, \"Page changed during inspection; inspect again\"),\n () => this.refs.issuer(generation), { rootBackendNodeId: root, query: options.query })\n } catch (error) {\n if (error instanceof SnapshotScopeError) throw new Error(`${options.ref} is no longer in the page; inspect again`)\n throw error\n }\n return {\n target: { id: state.target!.id, title: state.target!.title, url: state.target!.url },\n context: state.context!,\n snapshot: snapshot.text,\n collapsed: snapshot.collapsed,\n diagnostics: state.diagnostics.splice(0),\n }\n })\n }\n\n async act(action: DeveloperCdpAction): Promise<PageActionResult> {\n return this.exclusive(action.runId, async () => {\n const state = await this.ensure(action)\n const epoch = state.navigationEpoch\n const backendNodeId = this.refs.resolve(action.ref, this.generation(state))\n const session = this.session(state, epoch, \"Page changed during action; inspect again\")\n return performAction(session, action.kind === \"click\" ? { kind: \"click\", ref: action.ref, backendNodeId }\n : action.kind === \"type\" ? { kind: \"type\", ref: action.ref, backendNodeId, text: action.text, clear: action.clear }\n : { kind: \"select\", ref: action.ref, backendNodeId, values: action.values })\n })\n }\n\n /** Refs belong to one target and navigation epoch. */\n private generation(state: RunState): string {\n return `${state.stamp}\\0${state.target!.id}\\0${state.navigationEpoch}`\n }\n\n private session(state: RunState, epoch: number, message: string): PageSession {\n return {\n channel: { send: (method, params) => this.command(state, method, params) },\n checkpoint: () => this.assertContextCurrent(state, epoch, message),\n markAutomation: true,\n }\n }\n\n async screenshot(identity: DeveloperCdpIdentity): Promise<DeveloperCdpScreenshot> {\n return this.exclusive(identity.runId, async () => {\n const state = await this.ensure(identity)\n const epoch = state.navigationEpoch\n const result = await this.command(state, \"Page.captureScreenshot\", {\n format: \"png\",\n fromSurface: true,\n captureBeyondViewport: false,\n })\n await this.assertContextCurrent(state, epoch, \"Page changed during screenshot; capture again\")\n if (typeof result.data !== \"string\") throw new Error(\"Chrome returned an invalid screenshot\")\n const bytes = Buffer.from(result.data, \"base64\").byteLength\n if (bytes > this.dependencies.maxScreenshotBytes) {\n throw new Error(`Screenshot exceeds ${this.dependencies.maxScreenshotBytes} byte limit`)\n }\n return { mediaType: \"image/png\", data: result.data, bytes }\n })\n }\n\n close(runId?: string): void {\n for (const [id, state] of this.runs) {\n if (runId && id !== runId) continue\n this.disconnect(state, new Error(\"Developer CDP controller closed\"))\n this.runs.delete(id)\n }\n }\n\n async context(selection: DeveloperCdpSelection): Promise<DeveloperCdpContext> {\n return this.exclusive(selection.runId, async () => (await this.ensure(selection)).context!)\n }\n\n\n private async ensure(identity: DeveloperCdpSelection | DeveloperCdpIdentity): Promise<RunState> {\n if (!identity.runId.trim()) throw new Error(\"runId is required\")\n const endpoint = discoveryUrl(identity.endpoint)\n for (const [runId, previous] of this.runs) {\n if (runId === identity.runId) continue\n this.disconnect(previous, new Error(\"Developer run was replaced\"))\n this.runs.delete(runId)\n }\n let state = this.runs.get(identity.runId)\n if (!state || state.endpoint !== endpoint || state.windowId !== identity.windowId || state.sessionId !== identity.sessionId\n || (\"instanceId\" in identity && state.instanceId !== identity.instanceId)) {\n if (state) this.disconnect(state, new Error(\"CDP endpoint changed\"))\n state = this.newState(endpoint, identity.windowId, identity.sessionId, \"instanceId\" in identity ? identity.instanceId : undefined)\n this.runs.set(identity.runId, state)\n }\n return this.ensureTarget(state)\n }\n\n private newState(endpoint: string, windowId: string, sessionId?: string, instanceId?: string): RunState {\n return {\n endpoint,\n windowId,\n sessionId,\n instanceId,\n stamp: ++this.stamps,\n nextCommandId: 0,\n navigationEpoch: 0,\n pending: new Map(),\n diagnostics: [],\n }\n }\n\n private async ensureTarget(state: RunState): Promise<RunState> {\n const response = await this.dependencies.fetch(state.endpoint, {\n signal: AbortSignal.timeout(this.dependencies.timeoutMs),\n })\n if (!response.ok) throw new Error(`Chrome target discovery failed (HTTP ${response.status})`)\n const contentLength = Number(response.headers.get(\"content-length\"))\n if (Number.isFinite(contentLength) && contentLength > DISCOVERY_TEXT_LIMIT) {\n throw new Error(\"Chrome target discovery response is too large\")\n }\n const text = await response.text()\n if (Buffer.byteLength(text) > DISCOVERY_TEXT_LIMIT) throw new Error(\"Chrome target discovery response is too large\")\n let candidates: unknown\n try { candidates = JSON.parse(text) } catch { throw new Error(\"Chrome target discovery returned invalid JSON\") }\n if (!Array.isArray(candidates)) throw new Error(\"Chrome target discovery returned invalid JSON\")\n const targets = candidates.slice(0, DISCOVERY_TARGET_LIMIT).filter(isTarget).filter((target) => target.type === \"page\")\n let target = state.target && targets.find((candidate) => candidate.id === state.target!.id)\n if (!target) {\n this.disconnect(state, new Error(\"Chrome target was replaced\"))\n target = await this.discoverTarget(state, targets)\n }\n\n const replaced = state.target?.id !== target.id || state.target.webSocketDebuggerUrl !== target.webSocketDebuggerUrl\n if (replaced) this.disconnect(state, new Error(\"Chrome target was replaced\"))\n state.target = target\n if (!state.socket) await this.connect(state, target)\n else await state.open\n const context = await this.readContext(state)\n if (!this.matchesContext(state, context)) {\n this.invalidateRefs(state)\n state.instanceId = undefined\n this.disconnect(state, new Error(\"CodeNomad window or active session changed; inspect again\"))\n throw new Error(\"CodeNomad window or active session changed; inspect again\")\n }\n state.context = context\n return state\n }\n\n private async discoverTarget(state: RunState, targets: CdpTarget[]): Promise<CdpTarget> {\n const matches: CdpTarget[] = []\n for (let index = 0; index < targets.length && matches.length < 2; index += DISCOVERY_PROBE_CONCURRENCY) {\n const batch = await Promise.all(targets.slice(index, index + DISCOVERY_PROBE_CONCURRENCY).map(async (target) => {\n const probe = this.newState(state.endpoint, state.windowId, state.sessionId, state.instanceId)\n probe.target = target\n try {\n await this.connect(probe, target)\n return this.matchesContext(state, await this.readContext(probe)) ? target : undefined\n } catch {\n // Targets can disappear while Chromium's target list is being inspected.\n return undefined\n } finally {\n this.disconnect(probe, new Error(\"CDP target probe completed\"))\n }\n }))\n matches.push(...batch.filter((target): target is CdpTarget => Boolean(target)))\n }\n if (matches.length === 0) throw new Error(\"No CodeNomad page matches the selected window and OpenCode session\")\n if (matches.length > 1) throw new Error(\"Multiple CodeNomad pages match the selected window and OpenCode session\")\n return matches[0]\n }\n\n private async readContext(state: RunState): Promise<DeveloperCdpContext> {\n const response = await this.command(state, \"Runtime.evaluate\", {\n expression: CONTEXT_EXPRESSION,\n returnByValue: true,\n })\n const value = (response.result as { value?: unknown } | undefined)?.value as Partial<DeveloperCdpContext> | undefined\n const optionalId = (id: unknown) => id === null || (typeof id === \"string\" && id.length <= 256)\n if (!value || typeof value.windowId !== \"string\" || value.windowId.length > 256\n || !optionalId(value.instanceId) || !optionalId(value.sessionId)) {\n throw new Error(\"CodeNomad page has no valid window context\")\n }\n return { windowId: value.windowId, instanceId: value.instanceId!, sessionId: value.sessionId! }\n }\n\n private matchesContext(state: RunState, context: DeveloperCdpContext): boolean {\n return context.windowId === state.windowId && (state.sessionId === undefined || context.sessionId === state.sessionId)\n && (state.instanceId === undefined || context.instanceId === state.instanceId)\n }\n\n private async connect(state: RunState, target: CdpTarget): Promise<void> {\n const socket = this.dependencies.connect(target.webSocketDebuggerUrl)\n state.socket = socket\n state.open = new Promise<void>((resolve, reject) => {\n const fail = (error: Error) => {\n clearTimeout(timer)\n if (state.socket === socket) {\n this.disconnect(state, error)\n } else socket.close()\n reject(error)\n }\n const timer = setTimeout(() => fail(new Error(\"CDP WebSocket connection timed out\")), this.dependencies.timeoutMs)\n socket.onopen = () => { clearTimeout(timer); resolve() }\n socket.onerror = () => fail(new Error(\"CDP WebSocket connection failed\"))\n })\n socket.onmessage = (event) => this.onMessage(state, socket, event.data)\n socket.onclose = () => {\n if (state.socket !== socket) return\n state.socket = undefined\n state.open = undefined\n this.invalidateRefs(state)\n this.rejectPending(state, new Error(\"CDP WebSocket closed\"))\n }\n await state.open\n await this.command(state, \"Page.enable\")\n await this.command(state, \"Runtime.enable\")\n await this.command(state, \"Accessibility.enable\")\n }\n\n private command(\n state: RunState,\n method: string,\n params?: Record<string, unknown>,\n ): Promise<Record<string, unknown>> {\n if (!state.socket) return Promise.reject(new Error(\"CDP WebSocket is not connected\"))\n const id = ++state.nextCommandId\n return new Promise((resolve, reject) => {\n const timer = setTimeout(() => {\n state.pending.delete(id)\n reject(new Error(`CDP command ${method} timed out`))\n }, this.dependencies.timeoutMs)\n state.pending.set(id, { resolve, reject, timer })\n state.socket!.send(JSON.stringify({ id, method, ...(params ? { params } : {}) }))\n })\n }\n\n private onMessage(state: RunState, socket: CdpSocket, data: unknown): void {\n if (state.socket !== socket) return\n let message: CdpResponse\n try {\n const text = typeof data === \"string\" ? data : Buffer.from(data as ArrayBuffer).toString(\"utf8\")\n message = JSON.parse(text) as CdpResponse\n } catch {\n return\n }\n if (typeof message.id === \"number\") {\n const pending = state.pending.get(message.id)\n if (!pending) return\n clearTimeout(pending.timer)\n state.pending.delete(message.id)\n if (message.error) pending.reject(new Error(message.error.message ?? `CDP error ${message.error.code ?? \"unknown\"}`))\n else pending.resolve(message.result ?? {})\n return\n }\n if (message.method === \"Page.frameNavigated\") {\n this.invalidateRefs(state)\n return\n }\n const diagnostic = readDiagnostic(message)\n if (diagnostic) {\n state.diagnostics.push(diagnostic)\n if (state.diagnostics.length > DIAGNOSTIC_LIMIT) state.diagnostics.splice(0, state.diagnostics.length - DIAGNOSTIC_LIMIT)\n }\n }\n\n private async assertContextCurrent(state: RunState, epoch: number, message: string): Promise<void> {\n if (state.navigationEpoch !== epoch || !state.socket) {\n throw new Error(message)\n }\n const context = await this.readContext(state)\n if (!this.matchesContext(state, context)) {\n this.invalidateRefs(state)\n state.context = context\n throw new Error(message)\n }\n if (state.navigationEpoch !== epoch || !state.socket) {\n throw new Error(message)\n }\n state.context = context\n }\n\n private exclusive<T>(runId: string, operation: () => Promise<T>): Promise<T> {\n const previous = this.operations.get(runId) ?? Promise.resolve()\n const queued = previous.catch(() => undefined).then(operation)\n const tail = queued.then(() => undefined, () => undefined)\n this.operations.set(runId, tail)\n return queued.finally(() => {\n if (this.operations.get(runId) === tail) this.operations.delete(runId)\n })\n }\n\n /** A new epoch makes every ref issued before it stale. */\n private invalidateRefs(state: RunState): void {\n state.navigationEpoch++\n }\n\n private disconnect(state: RunState, error: Error): void {\n const socket = state.socket\n state.socket = undefined\n state.open = undefined\n this.invalidateRefs(state)\n this.rejectPending(state, error)\n socket?.close()\n }\n\n private rejectPending(state: RunState, error: Error): void {\n for (const pending of state.pending.values()) {\n clearTimeout(pending.timer)\n pending.reject(error)\n }\n state.pending.clear()\n }\n}\n\nfunction discoveryUrl(endpoint: string): string {\n let url: URL\n try {\n url = new URL(endpoint)\n } catch {\n throw new Error(\"CDP endpoint must be an HTTP or HTTPS URL\")\n }\n if (url.protocol !== \"http:\" && url.protocol !== \"https:\") {\n throw new Error(\"CDP endpoint must be an HTTP or HTTPS URL\")\n }\n if (url.hostname !== \"127.0.0.1\") throw new Error(\"CDP endpoint must use the IPv4 loopback address\")\n if (url.username || url.password) throw new Error(\"CDP endpoint must not contain credentials\")\n return new URL(\"/json/list\", url).href\n}\n\nfunction isTarget(value: unknown): value is CdpTarget {\n const target = value as Partial<CdpTarget> | null\n return !!target\n && typeof target.id === \"string\"\n && typeof target.title === \"string\"\n && typeof target.type === \"string\"\n && typeof target.url === \"string\"\n && typeof target.webSocketDebuggerUrl === \"string\"\n && isLoopbackWebSocketUrl(target.webSocketDebuggerUrl)\n}\n\nfunction isLoopbackWebSocketUrl(value: string): boolean {\n try {\n const url = new URL(value)\n return (url.protocol === \"ws:\" || url.protocol === \"wss:\")\n && url.hostname === \"127.0.0.1\"\n && !url.username\n && !url.password\n } catch {\n return false\n }\n}\n\nfunction readDiagnostic(message: CdpResponse): DeveloperCdpDiagnostic | undefined {\n if (message.method === \"Runtime.consoleAPICalled\") {\n const params = message.params as { type?: unknown; args?: Array<{ value?: unknown; description?: unknown }> } | undefined\n const text = params?.args?.map((arg) => String(arg.value ?? arg.description ?? \"\")).join(\" \") ?? \"\"\n const level = params?.type === \"error\" || params?.type === \"assert\" ? \"error\"\n : params?.type === \"warning\" ? \"warning\" : \"log\"\n return { level, text: boundedText(text) }\n }\n if (message.method === \"Runtime.exceptionThrown\") {\n const params = message.params as { exceptionDetails?: { text?: unknown; exception?: { description?: unknown } } } | undefined\n const detail = params?.exceptionDetails\n return { level: \"error\", text: boundedText(String(detail?.exception?.description ?? detail?.text ?? \"Runtime exception\")) }\n }\n return undefined\n}\n\nfunction boundedText(text: string): string {\n return text.length > DIAGNOSTIC_TEXT_LIMIT ? `${text.slice(0, DIAGNOSTIC_TEXT_LIMIT - 3)}...` : text\n}\n"]}
|
|
1
|
+
{"version":3,"file":"developer-cdp.js","sourceRoot":"","sources":["../src/developer-cdp.ts"],"names":[],"mappings":"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","sourcesContent":["import { WebSocket } from \"undici\"\nimport { bindRefs, performAction, snapshotPage, type PageActionResult, type PageSession } from \"./automation/page/actions\"\nimport type { RefAction } from \"./automation/page/parse\"\nimport { RefTable } from \"./automation/page/refs\"\nimport { SnapshotScopeError } from \"./automation/page/snapshot\"\n\nconst DEFAULT_TIMEOUT_MS = 5_000\nconst DEFAULT_MAX_SCREENSHOT_BYTES = 5 * 1024 * 1024\nconst DIAGNOSTIC_LIMIT = 1_000\nconst DIAGNOSTIC_TEXT_LIMIT = 512\nconst DISCOVERY_TEXT_LIMIT = 256 * 1024\nconst DISCOVERY_TARGET_LIMIT = 64\nconst DISCOVERY_PROBE_CONCURRENCY = 4\nconst CONTEXT_EXPRESSION = `(() => {\n const root = document.querySelector('[data-tab-visible=\"true\"][data-instance-id]');\n const pane = root?.querySelector('.session-cache-pane[data-session-active=\"true\"][data-session-id]');\n return {\n windowId: window.__CODENOMAD_WINDOW_ID__ ?? null,\n instanceId: root?.getAttribute('data-instance-id') ?? null,\n sessionId: pane?.getAttribute('data-session-id') ?? null,\n };\n})()`\n\nexport interface DeveloperCdpSelection {\n endpoint: string\n runId: string\n windowId: string\n sessionId?: string\n}\n\nexport interface DeveloperCdpIdentity extends DeveloperCdpSelection {\n instanceId?: string\n}\n\nexport interface DeveloperCdpContext {\n windowId: string\n instanceId: string | null\n sessionId: string | null\n}\n\nexport interface DeveloperCdpDiagnostic {\n level: \"log\" | \"warning\" | \"error\"\n text: string\n}\n\nexport interface DeveloperCdpInspection {\n target: { id: string; title: string; url: string }\n context: DeveloperCdpContext\n /** Indented accessibility outline with refs (`automation/page/snapshot.ts`). */\n snapshot: string\n /** Lines that hide part of the page; inspect their ref to expand them. */\n collapsed: number\n diagnostics: DeveloperCdpDiagnostic[]\n}\n\n/** A full inspection, or one limited to a ref's subtree and/or matching text. */\nexport interface DeveloperCdpInspectOptions {\n ref?: string\n query?: string\n}\n\nexport interface DeveloperCdpScreenshot {\n mediaType: \"image/png\"\n data: string\n bytes: number\n}\n\ninterface CdpSocket {\n onopen: ((event: unknown) => void) | null\n onmessage: ((event: { data: unknown }) => void) | null\n onclose: ((event: unknown) => void) | null\n onerror: ((event: unknown) => void) | null\n send(data: string): void\n close(): void\n}\n\ninterface DeveloperCdpDependencies {\n fetch: typeof globalThis.fetch\n connect: (url: string) => CdpSocket\n timeoutMs: number\n maxScreenshotBytes: number\n}\n\ninterface CdpTarget {\n id: string\n title: string\n type: string\n url: string\n webSocketDebuggerUrl: string\n}\n\ninterface CdpResponse {\n id?: number\n method?: string\n params?: Record<string, unknown>\n result?: Record<string, unknown>\n error?: { code?: number; message?: string }\n}\n\ninterface PendingCommand {\n resolve: (value: Record<string, unknown>) => void\n reject: (error: Error) => void\n timer: NodeJS.Timeout\n}\n\ninterface RunState {\n endpoint: string\n windowId: string\n sessionId?: string\n instanceId?: string\n context?: DeveloperCdpContext\n target?: CdpTarget\n socket?: CdpSocket\n open?: Promise<void>\n /** Distinguishes this run's refs from an earlier state's on the same target. */\n stamp: number\n nextCommandId: number\n navigationEpoch: number\n pending: Map<number, PendingCommand>\n diagnostics: DeveloperCdpDiagnostic[]\n}\n\nexport class DeveloperCdp {\n private readonly dependencies: DeveloperCdpDependencies\n private readonly runs = new Map<string, RunState>()\n private readonly operations = new Map<string, Promise<void>>()\n /** Shared by runs so a ref name is never reused for another element. */\n private readonly refs = new RefTable()\n private stamps = 0\n\n constructor(dependencies: Partial<DeveloperCdpDependencies> = {}) {\n this.dependencies = {\n fetch: globalThis.fetch,\n connect: (url) => new WebSocket(url) as unknown as CdpSocket,\n timeoutMs: DEFAULT_TIMEOUT_MS,\n maxScreenshotBytes: DEFAULT_MAX_SCREENSHOT_BYTES,\n ...dependencies,\n }\n }\n\n async inspect(identity: DeveloperCdpIdentity, options: DeveloperCdpInspectOptions = {}): Promise<DeveloperCdpInspection> {\n return this.exclusive(identity.runId, async () => {\n const state = await this.ensure(identity)\n const generation = this.generation(state)\n const root = options.ref === undefined ? undefined : this.refs.resolve(options.ref, generation)\n let snapshot\n try {\n snapshot = await snapshotPage(this.session(state, state.navigationEpoch, \"Page changed during inspection; inspect again\"),\n () => this.refs.issuer(generation), { rootBackendNodeId: root, query: options.query })\n } catch (error) {\n if (error instanceof SnapshotScopeError) throw new Error(`${options.ref} is no longer in the page; inspect again`)\n throw error\n }\n return {\n target: { id: state.target!.id, title: state.target!.title, url: state.target!.url },\n context: state.context!,\n snapshot: snapshot.text,\n collapsed: snapshot.collapsed,\n diagnostics: state.diagnostics.splice(0),\n }\n })\n }\n\n async act(identity: DeveloperCdpIdentity, action: RefAction): Promise<PageActionResult> {\n return this.exclusive(identity.runId, async () => {\n const state = await this.ensure(identity)\n const epoch = state.navigationEpoch, generation = this.generation(state)\n const page = bindRefs(action, ref => this.refs.resolve(ref, generation))\n return performAction(this.session(state, epoch, \"Page changed during action; inspect again\"), page)\n })\n }\n\n /** Refs belong to one target and navigation epoch. */\n private generation(state: RunState): string {\n return `${state.stamp}\\0${state.target!.id}\\0${state.navigationEpoch}`\n }\n\n private session(state: RunState, epoch: number, message: string): PageSession {\n return {\n channel: { send: (method, params) => this.command(state, method, params) },\n checkpoint: () => this.assertContextCurrent(state, epoch, message),\n markAutomation: true,\n }\n }\n\n async screenshot(identity: DeveloperCdpIdentity): Promise<DeveloperCdpScreenshot> {\n return this.exclusive(identity.runId, async () => {\n const state = await this.ensure(identity)\n const epoch = state.navigationEpoch\n const result = await this.command(state, \"Page.captureScreenshot\", {\n format: \"png\",\n fromSurface: true,\n captureBeyondViewport: false,\n })\n await this.assertContextCurrent(state, epoch, \"Page changed during screenshot; capture again\")\n if (typeof result.data !== \"string\") throw new Error(\"Chrome returned an invalid screenshot\")\n const bytes = Buffer.from(result.data, \"base64\").byteLength\n if (bytes > this.dependencies.maxScreenshotBytes) {\n throw new Error(`Screenshot exceeds ${this.dependencies.maxScreenshotBytes} byte limit`)\n }\n return { mediaType: \"image/png\", data: result.data, bytes }\n })\n }\n\n close(runId?: string): void {\n for (const [id, state] of this.runs) {\n if (runId && id !== runId) continue\n this.disconnect(state, new Error(\"Developer CDP controller closed\"))\n this.runs.delete(id)\n }\n }\n\n async context(selection: DeveloperCdpSelection): Promise<DeveloperCdpContext> {\n return this.exclusive(selection.runId, async () => (await this.ensure(selection)).context!)\n }\n\n\n private async ensure(identity: DeveloperCdpSelection | DeveloperCdpIdentity): Promise<RunState> {\n if (!identity.runId.trim()) throw new Error(\"runId is required\")\n const endpoint = discoveryUrl(identity.endpoint)\n for (const [runId, previous] of this.runs) {\n if (runId === identity.runId) continue\n this.disconnect(previous, new Error(\"Developer run was replaced\"))\n this.runs.delete(runId)\n }\n let state = this.runs.get(identity.runId)\n if (!state || state.endpoint !== endpoint || state.windowId !== identity.windowId || state.sessionId !== identity.sessionId\n || (\"instanceId\" in identity && state.instanceId !== identity.instanceId)) {\n if (state) this.disconnect(state, new Error(\"CDP endpoint changed\"))\n state = this.newState(endpoint, identity.windowId, identity.sessionId, \"instanceId\" in identity ? identity.instanceId : undefined)\n this.runs.set(identity.runId, state)\n }\n return this.ensureTarget(state)\n }\n\n private newState(endpoint: string, windowId: string, sessionId?: string, instanceId?: string): RunState {\n return {\n endpoint,\n windowId,\n sessionId,\n instanceId,\n stamp: ++this.stamps,\n nextCommandId: 0,\n navigationEpoch: 0,\n pending: new Map(),\n diagnostics: [],\n }\n }\n\n private async ensureTarget(state: RunState): Promise<RunState> {\n const response = await this.dependencies.fetch(state.endpoint, {\n signal: AbortSignal.timeout(this.dependencies.timeoutMs),\n })\n if (!response.ok) throw new Error(`Chrome target discovery failed (HTTP ${response.status})`)\n const contentLength = Number(response.headers.get(\"content-length\"))\n if (Number.isFinite(contentLength) && contentLength > DISCOVERY_TEXT_LIMIT) {\n throw new Error(\"Chrome target discovery response is too large\")\n }\n const text = await response.text()\n if (Buffer.byteLength(text) > DISCOVERY_TEXT_LIMIT) throw new Error(\"Chrome target discovery response is too large\")\n let candidates: unknown\n try { candidates = JSON.parse(text) } catch { throw new Error(\"Chrome target discovery returned invalid JSON\") }\n if (!Array.isArray(candidates)) throw new Error(\"Chrome target discovery returned invalid JSON\")\n const targets = candidates.slice(0, DISCOVERY_TARGET_LIMIT).filter(isTarget).filter((target) => target.type === \"page\")\n let target = state.target && targets.find((candidate) => candidate.id === state.target!.id)\n if (!target) {\n this.disconnect(state, new Error(\"Chrome target was replaced\"))\n target = await this.discoverTarget(state, targets)\n }\n\n const replaced = state.target?.id !== target.id || state.target.webSocketDebuggerUrl !== target.webSocketDebuggerUrl\n if (replaced) this.disconnect(state, new Error(\"Chrome target was replaced\"))\n state.target = target\n if (!state.socket) await this.connect(state, target)\n else await state.open\n const context = await this.readContext(state)\n if (!this.matchesContext(state, context)) {\n this.invalidateRefs(state)\n state.instanceId = undefined\n this.disconnect(state, new Error(\"CodeNomad window or active session changed; inspect again\"))\n throw new Error(\"CodeNomad window or active session changed; inspect again\")\n }\n state.context = context\n return state\n }\n\n private async discoverTarget(state: RunState, targets: CdpTarget[]): Promise<CdpTarget> {\n const matches: CdpTarget[] = []\n for (let index = 0; index < targets.length && matches.length < 2; index += DISCOVERY_PROBE_CONCURRENCY) {\n const batch = await Promise.all(targets.slice(index, index + DISCOVERY_PROBE_CONCURRENCY).map(async (target) => {\n const probe = this.newState(state.endpoint, state.windowId, state.sessionId, state.instanceId)\n probe.target = target\n try {\n await this.connect(probe, target)\n return this.matchesContext(state, await this.readContext(probe)) ? target : undefined\n } catch {\n // Targets can disappear while Chromium's target list is being inspected.\n return undefined\n } finally {\n this.disconnect(probe, new Error(\"CDP target probe completed\"))\n }\n }))\n matches.push(...batch.filter((target): target is CdpTarget => Boolean(target)))\n }\n if (matches.length === 0) throw new Error(\"No CodeNomad page matches the selected window and OpenCode session\")\n if (matches.length > 1) throw new Error(\"Multiple CodeNomad pages match the selected window and OpenCode session\")\n return matches[0]\n }\n\n private async readContext(state: RunState): Promise<DeveloperCdpContext> {\n const response = await this.command(state, \"Runtime.evaluate\", {\n expression: CONTEXT_EXPRESSION,\n returnByValue: true,\n })\n const value = (response.result as { value?: unknown } | undefined)?.value as Partial<DeveloperCdpContext> | undefined\n const optionalId = (id: unknown) => id === null || (typeof id === \"string\" && id.length <= 256)\n if (!value || typeof value.windowId !== \"string\" || value.windowId.length > 256\n || !optionalId(value.instanceId) || !optionalId(value.sessionId)) {\n throw new Error(\"CodeNomad page has no valid window context\")\n }\n return { windowId: value.windowId, instanceId: value.instanceId!, sessionId: value.sessionId! }\n }\n\n private matchesContext(state: RunState, context: DeveloperCdpContext): boolean {\n return context.windowId === state.windowId && (state.sessionId === undefined || context.sessionId === state.sessionId)\n && (state.instanceId === undefined || context.instanceId === state.instanceId)\n }\n\n private async connect(state: RunState, target: CdpTarget): Promise<void> {\n const socket = this.dependencies.connect(target.webSocketDebuggerUrl)\n state.socket = socket\n state.open = new Promise<void>((resolve, reject) => {\n const fail = (error: Error) => {\n clearTimeout(timer)\n if (state.socket === socket) {\n this.disconnect(state, error)\n } else socket.close()\n reject(error)\n }\n const timer = setTimeout(() => fail(new Error(\"CDP WebSocket connection timed out\")), this.dependencies.timeoutMs)\n socket.onopen = () => { clearTimeout(timer); resolve() }\n socket.onerror = () => fail(new Error(\"CDP WebSocket connection failed\"))\n })\n socket.onmessage = (event) => this.onMessage(state, socket, event.data)\n socket.onclose = () => {\n if (state.socket !== socket) return\n state.socket = undefined\n state.open = undefined\n this.invalidateRefs(state)\n this.rejectPending(state, new Error(\"CDP WebSocket closed\"))\n }\n await state.open\n await this.command(state, \"Page.enable\")\n await this.command(state, \"Runtime.enable\")\n await this.command(state, \"Accessibility.enable\")\n }\n\n private command(\n state: RunState,\n method: string,\n params?: Record<string, unknown>,\n ): Promise<Record<string, unknown>> {\n if (!state.socket) return Promise.reject(new Error(\"CDP WebSocket is not connected\"))\n const id = ++state.nextCommandId\n return new Promise((resolve, reject) => {\n const timer = setTimeout(() => {\n state.pending.delete(id)\n reject(new Error(`CDP command ${method} timed out`))\n }, this.dependencies.timeoutMs)\n state.pending.set(id, { resolve, reject, timer })\n state.socket!.send(JSON.stringify({ id, method, ...(params ? { params } : {}) }))\n })\n }\n\n private onMessage(state: RunState, socket: CdpSocket, data: unknown): void {\n if (state.socket !== socket) return\n let message: CdpResponse\n try {\n const text = typeof data === \"string\" ? data : Buffer.from(data as ArrayBuffer).toString(\"utf8\")\n message = JSON.parse(text) as CdpResponse\n } catch {\n return\n }\n if (typeof message.id === \"number\") {\n const pending = state.pending.get(message.id)\n if (!pending) return\n clearTimeout(pending.timer)\n state.pending.delete(message.id)\n if (message.error) pending.reject(new Error(message.error.message ?? `CDP error ${message.error.code ?? \"unknown\"}`))\n else pending.resolve(message.result ?? {})\n return\n }\n if (message.method === \"Page.frameNavigated\") {\n this.invalidateRefs(state)\n return\n }\n const diagnostic = readDiagnostic(message)\n if (diagnostic) {\n state.diagnostics.push(diagnostic)\n if (state.diagnostics.length > DIAGNOSTIC_LIMIT) state.diagnostics.splice(0, state.diagnostics.length - DIAGNOSTIC_LIMIT)\n }\n }\n\n private async assertContextCurrent(state: RunState, epoch: number, message: string): Promise<void> {\n if (state.navigationEpoch !== epoch || !state.socket) {\n throw new Error(message)\n }\n const context = await this.readContext(state)\n if (!this.matchesContext(state, context)) {\n this.invalidateRefs(state)\n state.context = context\n throw new Error(message)\n }\n if (state.navigationEpoch !== epoch || !state.socket) {\n throw new Error(message)\n }\n state.context = context\n }\n\n private exclusive<T>(runId: string, operation: () => Promise<T>): Promise<T> {\n const previous = this.operations.get(runId) ?? Promise.resolve()\n const queued = previous.catch(() => undefined).then(operation)\n const tail = queued.then(() => undefined, () => undefined)\n this.operations.set(runId, tail)\n return queued.finally(() => {\n if (this.operations.get(runId) === tail) this.operations.delete(runId)\n })\n }\n\n /** A new epoch makes every ref issued before it stale. */\n private invalidateRefs(state: RunState): void {\n state.navigationEpoch++\n }\n\n private disconnect(state: RunState, error: Error): void {\n const socket = state.socket\n state.socket = undefined\n state.open = undefined\n this.invalidateRefs(state)\n this.rejectPending(state, error)\n socket?.close()\n }\n\n private rejectPending(state: RunState, error: Error): void {\n for (const pending of state.pending.values()) {\n clearTimeout(pending.timer)\n pending.reject(error)\n }\n state.pending.clear()\n }\n}\n\nfunction discoveryUrl(endpoint: string): string {\n let url: URL\n try {\n url = new URL(endpoint)\n } catch {\n throw new Error(\"CDP endpoint must be an HTTP or HTTPS URL\")\n }\n if (url.protocol !== \"http:\" && url.protocol !== \"https:\") {\n throw new Error(\"CDP endpoint must be an HTTP or HTTPS URL\")\n }\n if (url.hostname !== \"127.0.0.1\") throw new Error(\"CDP endpoint must use the IPv4 loopback address\")\n if (url.username || url.password) throw new Error(\"CDP endpoint must not contain credentials\")\n return new URL(\"/json/list\", url).href\n}\n\nfunction isTarget(value: unknown): value is CdpTarget {\n const target = value as Partial<CdpTarget> | null\n return !!target\n && typeof target.id === \"string\"\n && typeof target.title === \"string\"\n && typeof target.type === \"string\"\n && typeof target.url === \"string\"\n && typeof target.webSocketDebuggerUrl === \"string\"\n && isLoopbackWebSocketUrl(target.webSocketDebuggerUrl)\n}\n\nfunction isLoopbackWebSocketUrl(value: string): boolean {\n try {\n const url = new URL(value)\n return (url.protocol === \"ws:\" || url.protocol === \"wss:\")\n && url.hostname === \"127.0.0.1\"\n && !url.username\n && !url.password\n } catch {\n return false\n }\n}\n\nfunction readDiagnostic(message: CdpResponse): DeveloperCdpDiagnostic | undefined {\n if (message.method === \"Runtime.consoleAPICalled\") {\n const params = message.params as { type?: unknown; args?: Array<{ value?: unknown; description?: unknown }> } | undefined\n const text = params?.args?.map((arg) => String(arg.value ?? arg.description ?? \"\")).join(\" \") ?? \"\"\n const level = params?.type === \"error\" || params?.type === \"assert\" ? \"error\"\n : params?.type === \"warning\" ? \"warning\" : \"log\"\n return { level, text: boundedText(text) }\n }\n if (message.method === \"Runtime.exceptionThrown\") {\n const params = message.params as { exceptionDetails?: { text?: unknown; exception?: { description?: unknown } } } | undefined\n const detail = params?.exceptionDetails\n return { level: \"error\", text: boundedText(String(detail?.exception?.description ?? detail?.text ?? \"Runtime exception\")) }\n }\n return undefined\n}\n\nfunction boundedText(text: string): string {\n return text.length > DIAGNOSTIC_TEXT_LIMIT ? `${text.slice(0, DIAGNOSTIC_TEXT_LIMIT - 3)}...` : text\n}\n"]}
|
|
@@ -52,6 +52,9 @@ class FakeChrome {
|
|
|
52
52
|
this.sockets.push(socket);
|
|
53
53
|
return socket;
|
|
54
54
|
};
|
|
55
|
+
/** Visible box of every element, or none; and the element hit at its centre. */
|
|
56
|
+
this.quads = [];
|
|
57
|
+
this.hit = 42;
|
|
55
58
|
}
|
|
56
59
|
receive(socket, command) {
|
|
57
60
|
this.commands.push(command);
|
|
@@ -59,15 +62,24 @@ class FakeChrome {
|
|
|
59
62
|
this.deferred.set(command.method, { socket, command });
|
|
60
63
|
return;
|
|
61
64
|
}
|
|
62
|
-
queueMicrotask(() => socket.respond(command,
|
|
65
|
+
queueMicrotask(() => socket.respond(command, command.method === "Runtime.evaluate" && String(command.params?.expression).includes("__codenomadAutomationInputUntil")
|
|
66
|
+
? { result: { value: 1_700_000_000 } } : this.result(command.method)));
|
|
63
67
|
}
|
|
64
68
|
result(method) {
|
|
65
69
|
if (method === "Accessibility.getFullAXTree")
|
|
66
70
|
return {
|
|
67
71
|
nodes: this.axNodes,
|
|
68
72
|
};
|
|
73
|
+
if (method === "DOM.getContentQuads")
|
|
74
|
+
return { quads: this.quads };
|
|
75
|
+
if (method === "DOM.getNodeForLocation")
|
|
76
|
+
return { backendNodeId: this.hit };
|
|
77
|
+
if (method === "Page.getLayoutMetrics")
|
|
78
|
+
return { cssLayoutViewport: { clientWidth: 800, clientHeight: 600 } };
|
|
69
79
|
if (method === "Runtime.evaluate")
|
|
70
80
|
return { result: { value: this.context } };
|
|
81
|
+
if (method === "Runtime.callFunctionOn")
|
|
82
|
+
return { result: { value: this.functionResult } };
|
|
71
83
|
if (method === "Page.captureScreenshot")
|
|
72
84
|
return { data: this.screenshotData };
|
|
73
85
|
if (method === "DOM.resolveNode")
|
|
@@ -99,7 +111,7 @@ describe("DeveloperCdp", () => {
|
|
|
99
111
|
try {
|
|
100
112
|
const first = await client.inspect(window);
|
|
101
113
|
chrome.context = { ...chrome.context, sessionId: "another-session", instanceId: "another-project" };
|
|
102
|
-
await client.act(
|
|
114
|
+
await client.act(window, { action: "click", ref: refOf(first) });
|
|
103
115
|
assert.equal((await client.inspect(window)).context.sessionId, "another-session");
|
|
104
116
|
chrome.context = { windowId: "window-1", sessionId: null, instanceId: null };
|
|
105
117
|
assert.equal((await client.inspect(window)).context.sessionId, null);
|
|
@@ -158,7 +170,7 @@ describe("DeveloperCdp", () => {
|
|
|
158
170
|
assert.equal(second.target.id, "two");
|
|
159
171
|
assert.equal(oldSocket.closed, true);
|
|
160
172
|
assert.equal(chrome.sockets.length, 4);
|
|
161
|
-
await assert.rejects(client.act(
|
|
173
|
+
await assert.rejects(client.act(identity, { action: "click", ref: oldRef }), /stale ref/);
|
|
162
174
|
});
|
|
163
175
|
it("refreshes target metadata without reconnecting", async () => {
|
|
164
176
|
const chrome = new FakeChrome();
|
|
@@ -170,13 +182,35 @@ describe("DeveloperCdp", () => {
|
|
|
170
182
|
assert.equal(inspection.target.url, "http://app.test/current");
|
|
171
183
|
assert.equal(chrome.sockets.length, 2);
|
|
172
184
|
});
|
|
173
|
-
it("
|
|
185
|
+
it("clicks a visible element with marked real pointer input at its visible centre", async () => {
|
|
174
186
|
const chrome = new FakeChrome();
|
|
187
|
+
chrome.quads = [[100, 40, 300, 40, 300, 80, 100, 80]];
|
|
175
188
|
const client = chrome.client();
|
|
176
189
|
const inspection = await client.inspect(identity);
|
|
177
|
-
|
|
190
|
+
chrome.commands.length = 0;
|
|
191
|
+
await client.act(identity, { action: "click", ref: refOf(inspection) });
|
|
192
|
+
const sequence = chrome.commands.filter(command => command.method === "Input.dispatchMouseEvent"
|
|
193
|
+
|| (command.method === "Runtime.evaluate" && /__codenomadAutomationInputUntil/.test(String(command.params?.expression))))
|
|
194
|
+
.map(command => command.method === "Runtime.evaluate" ? (/Math.min/.test(String(command.params.expression)) ? "unmark" : "mark")
|
|
195
|
+
: `${command.params.type}@${command.params.x},${command.params.y}`);
|
|
196
|
+
assert.deepEqual(sequence, ["mark", "mouseMoved@200,60", "mark", "mousePressed@200,60", "mark", "mouseReleased@200,60", "unmark"], "every trusted event in the CodeNomad window is marked, and the mark is cleared");
|
|
197
|
+
});
|
|
198
|
+
it("refuses a covered element and activates a box-less one programmatically", async () => {
|
|
199
|
+
const chrome = new FakeChrome();
|
|
200
|
+
chrome.quads = [[0, 0, 10, 0, 10, 10, 0, 10]];
|
|
201
|
+
chrome.hit = 7;
|
|
202
|
+
const client = chrome.client();
|
|
203
|
+
const inspection = await client.inspect(identity);
|
|
204
|
+
chrome.commands.length = 0;
|
|
205
|
+
chrome.functionResult = "div \"Overlay\"";
|
|
206
|
+
await assert.rejects(client.act(identity, { action: "click", ref: refOf(inspection) }), /is covered by div "Overlay"; close or move it first/);
|
|
207
|
+
assert.equal(chrome.commands.some(command => command.method === "Input.dispatchMouseEvent"), false);
|
|
208
|
+
chrome.functionResult = undefined;
|
|
209
|
+
chrome.quads = [];
|
|
210
|
+
chrome.commands.length = 0;
|
|
211
|
+
await client.act(identity, { action: "click", ref: refOf(inspection) });
|
|
178
212
|
assert.equal(chrome.commands.some((command) => command.method === "Input.dispatchMouseEvent"), false);
|
|
179
|
-
assert.equal(chrome.commands.
|
|
213
|
+
assert.equal(chrome.commands.filter((command) => command.method === "Runtime.callFunctionOn").at(-1)?.params?.objectId, "node-1");
|
|
180
214
|
});
|
|
181
215
|
it("retries a failed WebSocket connection", async () => {
|
|
182
216
|
const chrome = new FakeChrome();
|
|
@@ -207,14 +241,14 @@ describe("DeveloperCdp", () => {
|
|
|
207
241
|
const client = chrome.client();
|
|
208
242
|
const inspection = await client.inspect(identity);
|
|
209
243
|
chrome.sockets.at(-1).emit("Page.frameNavigated", { frame: { id: "main" } });
|
|
210
|
-
await assert.rejects(client.act(
|
|
244
|
+
await assert.rejects(client.act(identity, { action: "type", ref: refOf(inspection), text: "hello", clear: true }), /stale ref/);
|
|
211
245
|
});
|
|
212
246
|
it("invalidates refs when the target socket reconnects", async () => {
|
|
213
247
|
const chrome = new FakeChrome();
|
|
214
248
|
const client = chrome.client();
|
|
215
249
|
const inspection = await client.inspect(identity);
|
|
216
250
|
chrome.sockets.at(-1).close();
|
|
217
|
-
await assert.rejects(client.act(
|
|
251
|
+
await assert.rejects(client.act(identity, { action: "click", ref: refOf(inspection) }), /stale ref/);
|
|
218
252
|
assert.equal(chrome.sockets.length, 3);
|
|
219
253
|
});
|
|
220
254
|
it("rejects actions after the visible OpenCode session changes", async () => {
|
|
@@ -222,7 +256,7 @@ describe("DeveloperCdp", () => {
|
|
|
222
256
|
const client = chrome.client();
|
|
223
257
|
const inspection = await client.inspect(identity);
|
|
224
258
|
chrome.context = { ...chrome.context, sessionId: "session-2" };
|
|
225
|
-
await assert.rejects(client.act(
|
|
259
|
+
await assert.rejects(client.act(identity, { action: "click", ref: refOf(inspection) }), /active session changed/);
|
|
226
260
|
assert.equal(chrome.commands.some((command) => command.method === "Input.dispatchMouseEvent"), false);
|
|
227
261
|
});
|
|
228
262
|
it("invalidates refs and pending commands when an open socket errors", async () => {
|
|
@@ -232,7 +266,7 @@ describe("DeveloperCdp", () => {
|
|
|
232
266
|
const oldRef = refOf(inspection);
|
|
233
267
|
const socket = chrome.sockets.at(-1);
|
|
234
268
|
socket.onerror?.({});
|
|
235
|
-
await assert.rejects(client.act(
|
|
269
|
+
await assert.rejects(client.act(identity, { action: "click", ref: oldRef }), /stale ref/);
|
|
236
270
|
assert.equal(socket.closed, true);
|
|
237
271
|
});
|
|
238
272
|
it("rejects non-loopback WebSocket targets", async () => {
|
|
@@ -246,7 +280,7 @@ describe("DeveloperCdp", () => {
|
|
|
246
280
|
const client = chrome.client();
|
|
247
281
|
const inspection = await client.inspect(identity);
|
|
248
282
|
chrome.deferred.set("DOM.resolveNode", undefined);
|
|
249
|
-
const action = client.act(
|
|
283
|
+
const action = client.act(identity, { action: "click", ref: refOf(inspection) });
|
|
250
284
|
while (!chrome.deferred.get("DOM.resolveNode"))
|
|
251
285
|
await new Promise((resolve) => setImmediate(resolve));
|
|
252
286
|
const deferred = chrome.deferred.get("DOM.resolveNode");
|
|
@@ -260,7 +294,7 @@ describe("DeveloperCdp", () => {
|
|
|
260
294
|
const client = chrome.client();
|
|
261
295
|
const inspection = await client.inspect(identity);
|
|
262
296
|
chrome.deferred.set("DOM.resolveNode", undefined);
|
|
263
|
-
const action = client.act(
|
|
297
|
+
const action = client.act(identity, { action: "click", ref: refOf(inspection) });
|
|
264
298
|
while (!chrome.deferred.get("DOM.resolveNode"))
|
|
265
299
|
await new Promise((resolve) => setImmediate(resolve));
|
|
266
300
|
chrome.context = { ...chrome.context, instanceId: "workspace-2" };
|
|
@@ -304,7 +338,7 @@ describe("DeveloperCdp", () => {
|
|
|
304
338
|
const save = /button "Save" \[(e\d+)\]/.exec(full.snapshot)[1];
|
|
305
339
|
const scoped = await client.inspect(identity, { ref: dialog });
|
|
306
340
|
assert.equal(scoped.snapshot, `- dialog "Rename" [${dialog}]\n - textbox "Title" [${/textbox "Title" \[(e\d+)\]/.exec(full.snapshot)[1]}] value="Old"`);
|
|
307
|
-
await client.act(
|
|
341
|
+
await client.act(identity, { action: "click", ref: save });
|
|
308
342
|
assert.match((await client.inspect(identity, { query: "save" })).snapshot, new RegExp(`button "Save" \\[${save}\\]`));
|
|
309
343
|
assert.equal(/button "Save" \[(e\d+)\]/.exec((await client.inspect(identity)).snapshot)[1], save);
|
|
310
344
|
// A later inspection of the same page never invalidates an earlier ref; a node that
|
|
@@ -313,9 +347,9 @@ describe("DeveloperCdp", () => {
|
|
|
313
347
|
chrome.axNodes[0] = { ...chrome.axNodes[0], childIds: ["toolbar"] };
|
|
314
348
|
await client.inspect(identity);
|
|
315
349
|
await assert.rejects(client.inspect(identity, { ref: dialog }), /is no longer in the page/);
|
|
316
|
-
await client.act(
|
|
350
|
+
await client.act(identity, { action: "click", ref: dialog });
|
|
317
351
|
chrome.sockets.at(-1).emit("Page.frameNavigated", { frame: { id: "main" } });
|
|
318
|
-
await assert.rejects(client.act(
|
|
352
|
+
await assert.rejects(client.act(identity, { action: "click", ref: save }), /stale ref/);
|
|
319
353
|
});
|
|
320
354
|
it("closes active target connections", async () => {
|
|
321
355
|
const chrome = new FakeChrome();
|