@everydaydevopsio/pilot 0.5.2 → 0.6.1
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/.mcp.json +9 -0
- package/AGENTS.md +9 -0
- package/CHANGELOG.md +64 -0
- package/CLAUDE.md +7 -7
- package/README.md +282 -69
- package/dist/browser/browser-manager.d.ts +60 -0
- package/dist/browser/browser-manager.d.ts.map +1 -0
- package/dist/browser/browser-manager.js +355 -0
- package/dist/browser/browser-manager.js.map +1 -0
- package/dist/browser/chrome-launcher.d.ts +31 -0
- package/dist/browser/chrome-launcher.d.ts.map +1 -0
- package/dist/browser/chrome-launcher.js +225 -0
- package/dist/browser/chrome-launcher.js.map +1 -0
- package/dist/browser/connection.d.ts +23 -0
- package/dist/browser/connection.d.ts.map +1 -0
- package/dist/browser/connection.js +53 -0
- package/dist/browser/connection.js.map +1 -0
- package/dist/browser/events.d.ts +9 -0
- package/dist/browser/events.d.ts.map +1 -0
- package/dist/browser/events.js +99 -0
- package/dist/browser/events.js.map +1 -0
- package/dist/browser/index.d.ts +4 -0
- package/dist/browser/index.d.ts.map +1 -0
- package/dist/browser/index.js +4 -0
- package/dist/browser/index.js.map +1 -0
- package/dist/browser/inspect/element-ref.d.ts +23 -0
- package/dist/browser/inspect/element-ref.d.ts.map +1 -0
- package/dist/browser/inspect/element-ref.js +52 -0
- package/dist/browser/inspect/element-ref.js.map +1 -0
- package/dist/browser/inspect/snapshot.d.ts +11 -0
- package/dist/browser/inspect/snapshot.d.ts.map +1 -0
- package/dist/browser/inspect/snapshot.js +282 -0
- package/dist/browser/inspect/snapshot.js.map +1 -0
- package/dist/browser/inspect/styles.d.ts +45 -0
- package/dist/browser/inspect/styles.d.ts.map +1 -0
- package/dist/browser/inspect/styles.js +190 -0
- package/dist/browser/inspect/styles.js.map +1 -0
- package/dist/browser/inspect/types.d.ts +35 -0
- package/dist/browser/inspect/types.d.ts.map +1 -0
- package/dist/browser/inspect/types.js +2 -0
- package/dist/browser/inspect/types.js.map +1 -0
- package/dist/browser/interaction/dialogs.d.ts +18 -0
- package/dist/browser/interaction/dialogs.d.ts.map +1 -0
- package/dist/browser/interaction/dialogs.js +35 -0
- package/dist/browser/interaction/dialogs.js.map +1 -0
- package/dist/browser/interaction/downloads.d.ts +23 -0
- package/dist/browser/interaction/downloads.d.ts.map +1 -0
- package/dist/browser/interaction/downloads.js +87 -0
- package/dist/browser/interaction/downloads.js.map +1 -0
- package/dist/browser/interaction/events.d.ts +17 -0
- package/dist/browser/interaction/events.d.ts.map +1 -0
- package/dist/browser/interaction/events.js +66 -0
- package/dist/browser/interaction/events.js.map +1 -0
- package/dist/browser/interaction/ref-resolver.d.ts +18 -0
- package/dist/browser/interaction/ref-resolver.d.ts.map +1 -0
- package/dist/browser/interaction/ref-resolver.js +32 -0
- package/dist/browser/interaction/ref-resolver.js.map +1 -0
- package/dist/browser/interaction/uploads.d.ts +4 -0
- package/dist/browser/interaction/uploads.d.ts.map +1 -0
- package/dist/browser/interaction/uploads.js +43 -0
- package/dist/browser/interaction/uploads.js.map +1 -0
- package/dist/browser/network/network-buffer.d.ts +19 -0
- package/dist/browser/network/network-buffer.d.ts.map +1 -0
- package/dist/browser/network/network-buffer.js +118 -0
- package/dist/browser/network/network-buffer.js.map +1 -0
- package/dist/browser/network/network-monitor.d.ts +8 -0
- package/dist/browser/network/network-monitor.d.ts.map +1 -0
- package/dist/browser/network/network-monitor.js +85 -0
- package/dist/browser/network/network-monitor.js.map +1 -0
- package/dist/browser/network/redaction.d.ts +3 -0
- package/dist/browser/network/redaction.d.ts.map +1 -0
- package/dist/browser/network/redaction.js +36 -0
- package/dist/browser/network/redaction.js.map +1 -0
- package/dist/browser/network/types.d.ts +45 -0
- package/dist/browser/network/types.d.ts.map +1 -0
- package/dist/browser/network/types.js +2 -0
- package/dist/browser/network/types.js.map +1 -0
- package/dist/browser/performance/analyze.d.ts +5 -0
- package/dist/browser/performance/analyze.d.ts.map +1 -0
- package/dist/browser/performance/analyze.js +230 -0
- package/dist/browser/performance/analyze.js.map +1 -0
- package/dist/browser/performance/trace.d.ts +10 -0
- package/dist/browser/performance/trace.d.ts.map +1 -0
- package/dist/browser/performance/trace.js +38 -0
- package/dist/browser/performance/trace.js.map +1 -0
- package/dist/browser/performance/types.d.ts +31 -0
- package/dist/browser/performance/types.d.ts.map +1 -0
- package/dist/browser/performance/types.js +2 -0
- package/dist/browser/performance/types.js.map +1 -0
- package/dist/browser/preflight.d.ts +20 -0
- package/dist/browser/preflight.d.ts.map +1 -0
- package/dist/browser/preflight.js +105 -0
- package/dist/browser/preflight.js.map +1 -0
- package/dist/browser/security/origins.d.ts +18 -0
- package/dist/browser/security/origins.d.ts.map +1 -0
- package/dist/browser/security/origins.js +84 -0
- package/dist/browser/security/origins.js.map +1 -0
- package/dist/browser/tabs.d.ts +15 -0
- package/dist/browser/tabs.d.ts.map +1 -0
- package/dist/browser/tabs.js +71 -0
- package/dist/browser/tabs.js.map +1 -0
- package/dist/browser/types.d.ts +32 -0
- package/dist/browser/types.d.ts.map +1 -0
- package/dist/browser/types.js +2 -0
- package/dist/browser/types.js.map +1 -0
- package/dist/browser.d.ts +2 -99
- package/dist/browser.d.ts.map +1 -1
- package/dist/browser.js +3 -653
- package/dist/browser.js.map +1 -1
- package/dist/cli/init.d.ts +3 -0
- package/dist/cli/init.d.ts.map +1 -1
- package/dist/cli/init.js +33 -48
- package/dist/cli/init.js.map +1 -1
- package/dist/cli/linux-check.d.ts +2 -0
- package/dist/cli/linux-check.d.ts.map +1 -0
- package/dist/cli/linux-check.js +20 -0
- package/dist/cli/linux-check.js.map +1 -0
- package/dist/cli/skill-template.d.ts +1 -1
- package/dist/cli/skill-template.d.ts.map +1 -1
- package/dist/cli/skill-template.js +3 -113
- package/dist/cli/skill-template.js.map +1 -1
- package/dist/commands/check.d.ts +17 -0
- package/dist/commands/check.d.ts.map +1 -0
- package/dist/commands/check.js +38 -0
- package/dist/commands/check.js.map +1 -0
- package/dist/commands/click.d.ts +3 -1
- package/dist/commands/click.d.ts.map +1 -1
- package/dist/commands/click.js +16 -4
- package/dist/commands/click.js.map +1 -1
- package/dist/commands/connect.d.ts +14 -0
- package/dist/commands/connect.d.ts.map +1 -0
- package/dist/commands/connect.js +19 -0
- package/dist/commands/connect.js.map +1 -0
- package/dist/commands/dialog.d.ts +19 -0
- package/dist/commands/dialog.d.ts.map +1 -0
- package/dist/commands/dialog.js +51 -0
- package/dist/commands/dialog.js.map +1 -0
- package/dist/commands/downloads.d.ts +30 -0
- package/dist/commands/downloads.d.ts.map +1 -0
- package/dist/commands/downloads.js +66 -0
- package/dist/commands/downloads.js.map +1 -0
- package/dist/commands/fill.d.ts +16 -0
- package/dist/commands/fill.d.ts.map +1 -0
- package/dist/commands/fill.js +15 -0
- package/dist/commands/fill.js.map +1 -0
- package/dist/commands/find.d.ts +14 -0
- package/dist/commands/find.d.ts.map +1 -0
- package/dist/commands/find.js +26 -0
- package/dist/commands/find.js.map +1 -0
- package/dist/commands/hover.d.ts +13 -0
- package/dist/commands/hover.d.ts.map +1 -0
- package/dist/commands/hover.js +24 -0
- package/dist/commands/hover.js.map +1 -0
- package/dist/commands/navigate.d.ts.map +1 -1
- package/dist/commands/navigate.js +13 -0
- package/dist/commands/navigate.js.map +1 -1
- package/dist/commands/network.d.ts +33 -0
- package/dist/commands/network.d.ts.map +1 -0
- package/dist/commands/network.js +84 -0
- package/dist/commands/network.js.map +1 -0
- package/dist/commands/performance.d.ts +12 -0
- package/dist/commands/performance.d.ts.map +1 -0
- package/dist/commands/performance.js +26 -0
- package/dist/commands/performance.js.map +1 -0
- package/dist/commands/press_key.d.ts +10 -0
- package/dist/commands/press_key.d.ts.map +1 -0
- package/dist/commands/press_key.js +103 -0
- package/dist/commands/press_key.js.map +1 -0
- package/dist/commands/scroll.d.ts +21 -0
- package/dist/commands/scroll.d.ts.map +1 -0
- package/dist/commands/scroll.js +54 -0
- package/dist/commands/scroll.js.map +1 -0
- package/dist/commands/select.d.ts +18 -0
- package/dist/commands/select.d.ts.map +1 -0
- package/dist/commands/select.js +53 -0
- package/dist/commands/select.js.map +1 -0
- package/dist/commands/snapshot.d.ts +9 -0
- package/dist/commands/snapshot.d.ts.map +1 -0
- package/dist/commands/snapshot.js +7 -0
- package/dist/commands/snapshot.js.map +1 -0
- package/dist/commands/styles.d.ts +13 -0
- package/dist/commands/styles.d.ts.map +1 -0
- package/dist/commands/styles.js +7 -0
- package/dist/commands/styles.js.map +1 -0
- package/dist/commands/type.d.ts +3 -1
- package/dist/commands/type.d.ts.map +1 -1
- package/dist/commands/type.js +11 -2
- package/dist/commands/type.js.map +1 -1
- package/dist/commands/upload.d.ts +12 -0
- package/dist/commands/upload.d.ts.map +1 -0
- package/dist/commands/upload.js +13 -0
- package/dist/commands/upload.js.map +1 -0
- package/dist/mcp/console-buffer.d.ts +9 -0
- package/dist/mcp/console-buffer.d.ts.map +1 -1
- package/dist/mcp/console-buffer.js.map +1 -1
- package/dist/mcp/index.js +20 -2
- package/dist/mcp/index.js.map +1 -1
- package/dist/mcp/server.d.ts +10 -0
- package/dist/mcp/server.d.ts.map +1 -1
- package/dist/mcp/server.js +63 -2
- package/dist/mcp/server.js.map +1 -1
- package/dist/mcp/tools/browser.d.ts.map +1 -1
- package/dist/mcp/tools/browser.js +109 -11
- package/dist/mcp/tools/browser.js.map +1 -1
- package/dist/mcp/tools/connect.d.ts +5 -0
- package/dist/mcp/tools/connect.d.ts.map +1 -0
- package/dist/mcp/tools/connect.js +48 -0
- package/dist/mcp/tools/connect.js.map +1 -0
- package/dist/mcp/tools/files.d.ts +4 -0
- package/dist/mcp/tools/files.d.ts.map +1 -0
- package/dist/mcp/tools/files.js +85 -0
- package/dist/mcp/tools/files.js.map +1 -0
- package/dist/mcp/tools/interaction.d.ts +4 -0
- package/dist/mcp/tools/interaction.d.ts.map +1 -0
- package/dist/mcp/tools/interaction.js +176 -0
- package/dist/mcp/tools/interaction.js.map +1 -0
- package/dist/mcp/tools/names.d.ts +1 -1
- package/dist/mcp/tools/names.d.ts.map +1 -1
- package/dist/mcp/tools/names.js +19 -1
- package/dist/mcp/tools/names.js.map +1 -1
- package/dist/mcp/tools/network.d.ts +4 -0
- package/dist/mcp/tools/network.d.ts.map +1 -0
- package/dist/mcp/tools/network.js +68 -0
- package/dist/mcp/tools/network.js.map +1 -0
- package/dist/mcp/tools/performance.d.ts +4 -0
- package/dist/mcp/tools/performance.d.ts.map +1 -0
- package/dist/mcp/tools/performance.js +26 -0
- package/dist/mcp/tools/performance.js.map +1 -0
- package/dist/mcp/tools/runtime.d.ts +4 -0
- package/dist/mcp/tools/runtime.d.ts.map +1 -0
- package/dist/mcp/tools/runtime.js +128 -0
- package/dist/mcp/tools/runtime.js.map +1 -0
- package/dist/mcp/tools/snapshot.d.ts +4 -0
- package/dist/mcp/tools/snapshot.d.ts.map +1 -0
- package/dist/mcp/tools/snapshot.js +42 -0
- package/dist/mcp/tools/snapshot.js.map +1 -0
- package/dist/mcp/tools/styles.d.ts +4 -0
- package/dist/mcp/tools/styles.d.ts.map +1 -0
- package/dist/mcp/tools/styles.js +27 -0
- package/dist/mcp/tools/styles.js.map +1 -0
- package/dist/util/config.d.ts.map +1 -1
- package/dist/util/config.js +3 -2
- package/dist/util/config.js.map +1 -1
- package/package.json +5 -4
- package/skills/pilot/SKILL.md +48 -0
- package/src/browser/browser-manager.ts +465 -0
- package/src/browser/chrome-launcher.ts +282 -0
- package/src/browser/connection.ts +84 -0
- package/src/browser/events.ts +121 -0
- package/src/browser/index.ts +20 -0
- package/src/browser/inspect/element-ref.ts +68 -0
- package/src/browser/inspect/snapshot.ts +348 -0
- package/src/browser/inspect/styles.ts +258 -0
- package/src/browser/inspect/types.ts +37 -0
- package/src/browser/interaction/dialogs.ts +54 -0
- package/src/browser/interaction/downloads.ts +116 -0
- package/src/browser/interaction/events.ts +79 -0
- package/src/browser/interaction/ref-resolver.ts +53 -0
- package/src/browser/interaction/uploads.ts +60 -0
- package/src/browser/network/network-buffer.ts +133 -0
- package/src/browser/network/network-monitor.ts +107 -0
- package/src/browser/network/redaction.ts +42 -0
- package/src/browser/network/types.ts +47 -0
- package/src/browser/performance/analyze.ts +298 -0
- package/src/browser/performance/trace.ts +60 -0
- package/src/browser/performance/types.ts +34 -0
- package/src/browser/preflight.ts +144 -0
- package/src/browser/security/origins.ts +102 -0
- package/src/browser/tabs.ts +110 -0
- package/src/browser/types.ts +32 -0
- package/src/browser.ts +22 -798
- package/src/cli/init.ts +40 -49
- package/src/cli/linux-check.ts +23 -0
- package/src/cli/skill-template.ts +4 -113
- package/src/commands/check.ts +59 -0
- package/src/commands/click.ts +18 -4
- package/src/commands/connect.ts +40 -0
- package/src/commands/dialog.ts +79 -0
- package/src/commands/downloads.ts +111 -0
- package/src/commands/fill.ts +34 -0
- package/src/commands/find.ts +52 -0
- package/src/commands/hover.ts +42 -0
- package/src/commands/navigate.ts +20 -0
- package/src/commands/network.ts +141 -0
- package/src/commands/performance.ts +51 -0
- package/src/commands/press_key.ts +134 -0
- package/src/commands/scroll.ts +79 -0
- package/src/commands/select.ts +78 -0
- package/src/commands/snapshot.ts +18 -0
- package/src/commands/styles.ts +24 -0
- package/src/commands/type.ts +12 -2
- package/src/commands/upload.ts +36 -0
- package/src/mcp/console-buffer.ts +10 -0
- package/src/mcp/index.ts +24 -2
- package/src/mcp/server.ts +74 -2
- package/src/mcp/tools/browser.ts +125 -9
- package/src/mcp/tools/connect.ts +62 -0
- package/src/mcp/tools/files.ts +120 -0
- package/src/mcp/tools/interaction.ts +232 -0
- package/src/mcp/tools/names.ts +19 -1
- package/src/mcp/tools/network.ts +86 -0
- package/src/mcp/tools/performance.ts +40 -0
- package/src/mcp/tools/runtime.ts +164 -0
- package/src/mcp/tools/snapshot.ts +64 -0
- package/src/mcp/tools/styles.ts +41 -0
- package/src/util/config.ts +3 -2
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import type { Client } from 'chrome-remote-interface';
|
|
2
|
+
import type { ElementRefMap } from '../browser/inspect/element-ref.js';
|
|
3
|
+
import { resolveRef } from '../browser/interaction/ref-resolver.js';
|
|
4
|
+
|
|
5
|
+
export interface HoverParams {
|
|
6
|
+
ref?: string;
|
|
7
|
+
x?: number;
|
|
8
|
+
y?: number;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export interface HoverResult {
|
|
12
|
+
x: number;
|
|
13
|
+
y: number;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export async function executeHover(
|
|
17
|
+
client: Client,
|
|
18
|
+
refMap: ElementRefMap,
|
|
19
|
+
params: HoverParams
|
|
20
|
+
): Promise<HoverResult> {
|
|
21
|
+
let x: number;
|
|
22
|
+
let y: number;
|
|
23
|
+
|
|
24
|
+
if (params.ref) {
|
|
25
|
+
const resolved = await resolveRef(client, refMap, params.ref);
|
|
26
|
+
x = resolved.x;
|
|
27
|
+
y = resolved.y;
|
|
28
|
+
} else if (params.x !== undefined && params.y !== undefined) {
|
|
29
|
+
x = params.x;
|
|
30
|
+
y = params.y;
|
|
31
|
+
} else {
|
|
32
|
+
throw new Error('Either ref or x/y coordinates are required');
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
await client.Input.dispatchMouseEvent({
|
|
36
|
+
type: 'mouseMoved',
|
|
37
|
+
x,
|
|
38
|
+
y
|
|
39
|
+
});
|
|
40
|
+
|
|
41
|
+
return { x, y };
|
|
42
|
+
}
|
package/src/commands/navigate.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { z } from 'zod';
|
|
2
2
|
import type { Client } from 'chrome-remote-interface';
|
|
3
3
|
import type { BrowserManager } from '../browser.js';
|
|
4
|
+
import { checkOrigin } from '../browser/security/origins.js';
|
|
4
5
|
|
|
5
6
|
export const NavigateParamsSchema = z.object({
|
|
6
7
|
url: z.string().url(),
|
|
@@ -22,6 +23,14 @@ export async function executeNavigate(
|
|
|
22
23
|
manager: BrowserManager,
|
|
23
24
|
params: NavigateParams
|
|
24
25
|
): Promise<NavigateResult> {
|
|
26
|
+
// Check origin policy before navigation
|
|
27
|
+
const originCheck = checkOrigin(params.url);
|
|
28
|
+
if (!originCheck.allowed) {
|
|
29
|
+
throw new Error(
|
|
30
|
+
originCheck.reason ?? 'Navigation blocked by origin policy'
|
|
31
|
+
);
|
|
32
|
+
}
|
|
33
|
+
|
|
25
34
|
let finalStatus = 200;
|
|
26
35
|
|
|
27
36
|
const navigationPromise = new Promise<{ url: string; status: number }>(
|
|
@@ -97,6 +106,17 @@ export async function executeNavigate(
|
|
|
97
106
|
|
|
98
107
|
try {
|
|
99
108
|
const result = await navigationPromise;
|
|
109
|
+
|
|
110
|
+
// Check final URL after redirects — block if redirected to a blocked origin
|
|
111
|
+
const finalCheck = checkOrigin(result.url);
|
|
112
|
+
if (!finalCheck.allowed) {
|
|
113
|
+
// Navigate away from the blocked origin
|
|
114
|
+
await client.Page.navigate({ url: 'about:blank' });
|
|
115
|
+
throw new Error(
|
|
116
|
+
`Redirect landed on blocked origin: ${finalCheck.reason}`
|
|
117
|
+
);
|
|
118
|
+
}
|
|
119
|
+
|
|
100
120
|
return result;
|
|
101
121
|
} finally {
|
|
102
122
|
// Event listeners are auto-removed when client disconnects;
|
|
@@ -0,0 +1,141 @@
|
|
|
1
|
+
import type { Client } from 'chrome-remote-interface';
|
|
2
|
+
import type { NetworkBuffer } from '../browser/network/network-buffer.js';
|
|
3
|
+
import type {
|
|
4
|
+
NetworkSummary,
|
|
5
|
+
NetworkDetail,
|
|
6
|
+
NetworkFilter
|
|
7
|
+
} from '../browser/network/types.js';
|
|
8
|
+
import { getResponseBody } from '../browser/network/network-monitor.js';
|
|
9
|
+
|
|
10
|
+
export type NetworkAction = 'list' | 'get' | 'clear';
|
|
11
|
+
|
|
12
|
+
export interface NetworkParams {
|
|
13
|
+
action: NetworkAction;
|
|
14
|
+
requestId?: string;
|
|
15
|
+
url?: string;
|
|
16
|
+
method?: string;
|
|
17
|
+
statusMin?: number;
|
|
18
|
+
statusMax?: number;
|
|
19
|
+
resourceType?: string;
|
|
20
|
+
failed?: boolean;
|
|
21
|
+
limit?: number;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
export interface NetworkListResult {
|
|
25
|
+
action: 'list';
|
|
26
|
+
count: number;
|
|
27
|
+
requests: NetworkSummary[];
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
export interface NetworkGetResult {
|
|
31
|
+
action: 'get';
|
|
32
|
+
request: NetworkDetail;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
export interface NetworkClearResult {
|
|
36
|
+
action: 'clear';
|
|
37
|
+
cleared: number;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
export type NetworkResult =
|
|
41
|
+
NetworkListResult | NetworkGetResult | NetworkClearResult;
|
|
42
|
+
|
|
43
|
+
export async function executeNetwork(
|
|
44
|
+
client: Client,
|
|
45
|
+
buffer: NetworkBuffer,
|
|
46
|
+
params: NetworkParams
|
|
47
|
+
): Promise<NetworkResult> {
|
|
48
|
+
switch (params.action) {
|
|
49
|
+
case 'list': {
|
|
50
|
+
const filter: NetworkFilter = {
|
|
51
|
+
url: params.url,
|
|
52
|
+
method: params.method,
|
|
53
|
+
statusMin: params.statusMin,
|
|
54
|
+
statusMax: params.statusMax,
|
|
55
|
+
resourceType: params.resourceType,
|
|
56
|
+
failed: params.failed,
|
|
57
|
+
limit: params.limit
|
|
58
|
+
};
|
|
59
|
+
const requests = buffer.list(filter);
|
|
60
|
+
return { action: 'list', count: requests.length, requests };
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
case 'get': {
|
|
64
|
+
if (!params.requestId) {
|
|
65
|
+
throw new Error('requestId is required for action "get"');
|
|
66
|
+
}
|
|
67
|
+
const record = buffer.getDetail(params.requestId);
|
|
68
|
+
if (!record) {
|
|
69
|
+
throw new Error(
|
|
70
|
+
`No network request found with id: ${params.requestId}`
|
|
71
|
+
);
|
|
72
|
+
}
|
|
73
|
+
// Lazily fetch response body
|
|
74
|
+
const bodyResult = await getResponseBody(client, params.requestId);
|
|
75
|
+
// record headers are already redacted by getDetail()
|
|
76
|
+
const detail: NetworkDetail = {
|
|
77
|
+
...record,
|
|
78
|
+
...(bodyResult && {
|
|
79
|
+
responseBody: bodyResult.body,
|
|
80
|
+
bodyTruncated: bodyResult.truncated
|
|
81
|
+
})
|
|
82
|
+
};
|
|
83
|
+
return { action: 'get', request: detail };
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
case 'clear': {
|
|
87
|
+
const cleared = buffer.clear();
|
|
88
|
+
return { action: 'clear', cleared };
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
export function formatNetworkList(result: NetworkListResult): string {
|
|
94
|
+
if (result.requests.length === 0) {
|
|
95
|
+
return 'No network requests captured.';
|
|
96
|
+
}
|
|
97
|
+
const lines = [`Network requests (${result.count}):\n`];
|
|
98
|
+
for (const req of result.requests) {
|
|
99
|
+
const status = req.failed ? 'FAILED' : String(req.status);
|
|
100
|
+
const dur = req.duration !== undefined ? `${req.duration}ms` : '...';
|
|
101
|
+
const size =
|
|
102
|
+
req.size > 1024 ? `${(req.size / 1024).toFixed(1)}KB` : `${req.size}B`;
|
|
103
|
+
lines.push(
|
|
104
|
+
` [${req.id}] ${req.method} ${status} ${req.url} (${dur}, ${size})`
|
|
105
|
+
);
|
|
106
|
+
}
|
|
107
|
+
return lines.join('\n');
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
export function formatNetworkDetail(result: NetworkGetResult): string {
|
|
111
|
+
const r = result.request;
|
|
112
|
+
const lines = [
|
|
113
|
+
`${r.method} ${r.url}`,
|
|
114
|
+
`Status: ${r.failed ? `FAILED — ${r.errorText}` : r.status}`,
|
|
115
|
+
`Type: ${r.resourceType} (${r.mimeType})`,
|
|
116
|
+
`Duration: ${r.duration !== undefined ? `${r.duration}ms` : 'unknown'}`,
|
|
117
|
+
`Size: ${r.encodedDataLength}B`,
|
|
118
|
+
`Cache: ${r.fromCache ? 'yes' : 'no'}`
|
|
119
|
+
];
|
|
120
|
+
|
|
121
|
+
if (Object.keys(r.requestHeaders).length > 0) {
|
|
122
|
+
lines.push('', 'Request Headers:');
|
|
123
|
+
for (const [k, v] of Object.entries(r.requestHeaders)) {
|
|
124
|
+
lines.push(` ${k}: ${v}`);
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
if (Object.keys(r.responseHeaders).length > 0) {
|
|
129
|
+
lines.push('', 'Response Headers:');
|
|
130
|
+
for (const [k, v] of Object.entries(r.responseHeaders)) {
|
|
131
|
+
lines.push(` ${k}: ${v}`);
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
if (r.responseBody !== undefined) {
|
|
136
|
+
const truncNote = r.bodyTruncated ? ' (truncated to 1MB)' : '';
|
|
137
|
+
lines.push('', `Response Body${truncNote}:`, r.responseBody);
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
return lines.join('\n');
|
|
141
|
+
}
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import type { Client } from 'chrome-remote-interface';
|
|
2
|
+
import {
|
|
3
|
+
type TraceState,
|
|
4
|
+
startTrace,
|
|
5
|
+
stopTrace
|
|
6
|
+
} from '../browser/performance/trace.js';
|
|
7
|
+
import {
|
|
8
|
+
analyzePerformance,
|
|
9
|
+
formatPerformanceSummary
|
|
10
|
+
} from '../browser/performance/analyze.js';
|
|
11
|
+
|
|
12
|
+
export type PerformanceAction = 'start' | 'stop' | 'analyze';
|
|
13
|
+
|
|
14
|
+
export interface PerformanceParams {
|
|
15
|
+
action: PerformanceAction;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export interface PerformanceResult {
|
|
19
|
+
action: PerformanceAction;
|
|
20
|
+
text: string;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export async function executePerformance(
|
|
24
|
+
client: Client,
|
|
25
|
+
traceState: TraceState,
|
|
26
|
+
params: PerformanceParams
|
|
27
|
+
): Promise<PerformanceResult> {
|
|
28
|
+
switch (params.action) {
|
|
29
|
+
case 'start': {
|
|
30
|
+
await client.Performance.enable();
|
|
31
|
+
await startTrace(client, traceState);
|
|
32
|
+
return { action: 'start', text: 'Performance tracing started.' };
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
case 'stop': {
|
|
36
|
+
await stopTrace(client, traceState);
|
|
37
|
+
return {
|
|
38
|
+
action: 'stop',
|
|
39
|
+
text: `Tracing stopped. ${traceState.events.length} events captured. Use action="analyze" to view summary.`
|
|
40
|
+
};
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
case 'analyze': {
|
|
44
|
+
const summary = await analyzePerformance(client, traceState.events);
|
|
45
|
+
return {
|
|
46
|
+
action: 'analyze',
|
|
47
|
+
text: formatPerformanceSummary(summary)
|
|
48
|
+
};
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
}
|
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
import type { Client } from 'chrome-remote-interface';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Map of named keys to CDP key event properties.
|
|
5
|
+
*/
|
|
6
|
+
const KEY_MAP: Record<
|
|
7
|
+
string,
|
|
8
|
+
{ key: string; code: string; keyCode?: number; text?: string }
|
|
9
|
+
> = {
|
|
10
|
+
enter: { key: 'Enter', code: 'Enter', keyCode: 13, text: '\r' },
|
|
11
|
+
tab: { key: 'Tab', code: 'Tab', keyCode: 9 },
|
|
12
|
+
escape: { key: 'Escape', code: 'Escape', keyCode: 27 },
|
|
13
|
+
backspace: { key: 'Backspace', code: 'Backspace', keyCode: 8 },
|
|
14
|
+
delete: { key: 'Delete', code: 'Delete', keyCode: 46 },
|
|
15
|
+
space: { key: ' ', code: 'Space', keyCode: 32, text: ' ' },
|
|
16
|
+
arrowup: { key: 'ArrowUp', code: 'ArrowUp', keyCode: 38 },
|
|
17
|
+
arrowdown: { key: 'ArrowDown', code: 'ArrowDown', keyCode: 40 },
|
|
18
|
+
arrowleft: { key: 'ArrowLeft', code: 'ArrowLeft', keyCode: 37 },
|
|
19
|
+
arrowright: { key: 'ArrowRight', code: 'ArrowRight', keyCode: 39 },
|
|
20
|
+
home: { key: 'Home', code: 'Home', keyCode: 36 },
|
|
21
|
+
end: { key: 'End', code: 'End', keyCode: 35 },
|
|
22
|
+
pageup: { key: 'PageUp', code: 'PageUp', keyCode: 33 },
|
|
23
|
+
pagedown: { key: 'PageDown', code: 'PageDown', keyCode: 34 },
|
|
24
|
+
f1: { key: 'F1', code: 'F1', keyCode: 112 },
|
|
25
|
+
f2: { key: 'F2', code: 'F2', keyCode: 113 },
|
|
26
|
+
f3: { key: 'F3', code: 'F3', keyCode: 114 },
|
|
27
|
+
f4: { key: 'F4', code: 'F4', keyCode: 115 },
|
|
28
|
+
f5: { key: 'F5', code: 'F5', keyCode: 116 },
|
|
29
|
+
f6: { key: 'F6', code: 'F6', keyCode: 117 },
|
|
30
|
+
f7: { key: 'F7', code: 'F7', keyCode: 118 },
|
|
31
|
+
f8: { key: 'F8', code: 'F8', keyCode: 119 },
|
|
32
|
+
f9: { key: 'F9', code: 'F9', keyCode: 120 },
|
|
33
|
+
f10: { key: 'F10', code: 'F10', keyCode: 121 },
|
|
34
|
+
f11: { key: 'F11', code: 'F11', keyCode: 122 },
|
|
35
|
+
f12: { key: 'F12', code: 'F12', keyCode: 123 }
|
|
36
|
+
};
|
|
37
|
+
|
|
38
|
+
// CDP modifier bit flags
|
|
39
|
+
const MODIFIER_FLAGS: Record<string, number> = {
|
|
40
|
+
alt: 1,
|
|
41
|
+
control: 2,
|
|
42
|
+
ctrl: 2,
|
|
43
|
+
meta: 4,
|
|
44
|
+
cmd: 4,
|
|
45
|
+
command: 4,
|
|
46
|
+
shift: 8
|
|
47
|
+
};
|
|
48
|
+
|
|
49
|
+
export interface PressKeyParams {
|
|
50
|
+
key: string;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
export interface PressKeyResult {
|
|
54
|
+
key: string;
|
|
55
|
+
modifiers: string[];
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
/**
|
|
59
|
+
* Parse a key string like "Control+a", "Meta+Shift+Enter", or "Tab".
|
|
60
|
+
*/
|
|
61
|
+
function parseKeyCombo(input: string): {
|
|
62
|
+
modifiers: number;
|
|
63
|
+
modifierNames: string[];
|
|
64
|
+
keyInfo: { key: string; code: string; keyCode?: number; text?: string };
|
|
65
|
+
} {
|
|
66
|
+
const parts = input.split('+');
|
|
67
|
+
let modifiers = 0;
|
|
68
|
+
const modifierNames: string[] = [];
|
|
69
|
+
|
|
70
|
+
// Last part is the actual key, everything before is a modifier
|
|
71
|
+
const keyPart = parts.pop()!;
|
|
72
|
+
|
|
73
|
+
for (const mod of parts) {
|
|
74
|
+
const flag = MODIFIER_FLAGS[mod.toLowerCase()];
|
|
75
|
+
if (flag) {
|
|
76
|
+
modifiers |= flag;
|
|
77
|
+
modifierNames.push(mod);
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
// Look up the key in our map
|
|
82
|
+
const mapped = KEY_MAP[keyPart.toLowerCase()];
|
|
83
|
+
if (mapped) {
|
|
84
|
+
return { modifiers, modifierNames, keyInfo: mapped };
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
// Single character key
|
|
88
|
+
if (keyPart.length === 1) {
|
|
89
|
+
const charCode = keyPart.charCodeAt(0);
|
|
90
|
+
return {
|
|
91
|
+
modifiers,
|
|
92
|
+
modifierNames,
|
|
93
|
+
keyInfo: {
|
|
94
|
+
key: keyPart,
|
|
95
|
+
code: `Key${keyPart.toUpperCase()}`,
|
|
96
|
+
keyCode: charCode,
|
|
97
|
+
text: modifiers === 0 ? keyPart : undefined
|
|
98
|
+
}
|
|
99
|
+
};
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
// Unknown named key — pass through
|
|
103
|
+
return {
|
|
104
|
+
modifiers,
|
|
105
|
+
modifierNames,
|
|
106
|
+
keyInfo: { key: keyPart, code: keyPart }
|
|
107
|
+
};
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
export async function executePressKey(
|
|
111
|
+
client: Client,
|
|
112
|
+
params: PressKeyParams
|
|
113
|
+
): Promise<PressKeyResult> {
|
|
114
|
+
const { modifiers, modifierNames, keyInfo } = parseKeyCombo(params.key);
|
|
115
|
+
|
|
116
|
+
await client.Input.dispatchKeyEvent({
|
|
117
|
+
type: 'keyDown',
|
|
118
|
+
key: keyInfo.key,
|
|
119
|
+
code: keyInfo.code,
|
|
120
|
+
windowsVirtualKeyCode: keyInfo.keyCode,
|
|
121
|
+
modifiers,
|
|
122
|
+
...(keyInfo.text && { text: keyInfo.text })
|
|
123
|
+
});
|
|
124
|
+
|
|
125
|
+
await client.Input.dispatchKeyEvent({
|
|
126
|
+
type: 'keyUp',
|
|
127
|
+
key: keyInfo.key,
|
|
128
|
+
code: keyInfo.code,
|
|
129
|
+
windowsVirtualKeyCode: keyInfo.keyCode,
|
|
130
|
+
modifiers
|
|
131
|
+
});
|
|
132
|
+
|
|
133
|
+
return { key: keyInfo.key, modifiers: modifierNames };
|
|
134
|
+
}
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
import type { Client } from 'chrome-remote-interface';
|
|
2
|
+
import type { ElementRefMap } from '../browser/inspect/element-ref.js';
|
|
3
|
+
|
|
4
|
+
export interface ScrollParams {
|
|
5
|
+
ref?: string;
|
|
6
|
+
x?: number;
|
|
7
|
+
y?: number;
|
|
8
|
+
direction?: 'up' | 'down' | 'left' | 'right';
|
|
9
|
+
amount?: number;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export interface ScrollResult {
|
|
13
|
+
scrollX: number;
|
|
14
|
+
scrollY: number;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* Scroll the page or an element.
|
|
19
|
+
* - If ref is given, scrolls the element into view.
|
|
20
|
+
* - If direction/amount is given, scrolls the page.
|
|
21
|
+
* - If x/y is given, scrolls the page to absolute coordinates.
|
|
22
|
+
*/
|
|
23
|
+
export async function executeScroll(
|
|
24
|
+
client: Client,
|
|
25
|
+
refMap: ElementRefMap,
|
|
26
|
+
params: ScrollParams
|
|
27
|
+
): Promise<ScrollResult> {
|
|
28
|
+
if (params.ref) {
|
|
29
|
+
// Scroll element into view
|
|
30
|
+
const backendNodeId = refMap.resolve(params.ref);
|
|
31
|
+
await client.DOM.scrollIntoViewIfNeeded({ backendNodeId });
|
|
32
|
+
|
|
33
|
+
const { result } = await client.Runtime.evaluate({
|
|
34
|
+
expression: `JSON.stringify({ scrollX: window.scrollX, scrollY: window.scrollY })`,
|
|
35
|
+
returnByValue: true
|
|
36
|
+
});
|
|
37
|
+
const pos = JSON.parse(String(result.value));
|
|
38
|
+
return { scrollX: pos.scrollX, scrollY: pos.scrollY };
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
if (params.direction) {
|
|
42
|
+
const amount = params.amount ?? 300;
|
|
43
|
+
const deltaX =
|
|
44
|
+
params.direction === 'left'
|
|
45
|
+
? -amount
|
|
46
|
+
: params.direction === 'right'
|
|
47
|
+
? amount
|
|
48
|
+
: 0;
|
|
49
|
+
const deltaY =
|
|
50
|
+
params.direction === 'up'
|
|
51
|
+
? -amount
|
|
52
|
+
: params.direction === 'down'
|
|
53
|
+
? amount
|
|
54
|
+
: 0;
|
|
55
|
+
|
|
56
|
+
await client.Input.dispatchMouseEvent({
|
|
57
|
+
type: 'mouseWheel',
|
|
58
|
+
x: 0,
|
|
59
|
+
y: 0,
|
|
60
|
+
deltaX,
|
|
61
|
+
deltaY
|
|
62
|
+
});
|
|
63
|
+
|
|
64
|
+
// Small delay for scroll to take effect
|
|
65
|
+
await new Promise((r) => setTimeout(r, 100));
|
|
66
|
+
} else if (params.x !== undefined || params.y !== undefined) {
|
|
67
|
+
await client.Runtime.evaluate({
|
|
68
|
+
expression: `window.scrollTo(${params.x ?? 0}, ${params.y ?? 0})`,
|
|
69
|
+
awaitPromise: false
|
|
70
|
+
});
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
const { result } = await client.Runtime.evaluate({
|
|
74
|
+
expression: `JSON.stringify({ scrollX: window.scrollX, scrollY: window.scrollY })`,
|
|
75
|
+
returnByValue: true
|
|
76
|
+
});
|
|
77
|
+
const pos = JSON.parse(String(result.value));
|
|
78
|
+
return { scrollX: pos.scrollX, scrollY: pos.scrollY };
|
|
79
|
+
}
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
import type { Client } from 'chrome-remote-interface';
|
|
2
|
+
import type { ElementRefMap } from '../browser/inspect/element-ref.js';
|
|
3
|
+
import { resolveRef, focusRef } from '../browser/interaction/ref-resolver.js';
|
|
4
|
+
|
|
5
|
+
export interface SelectParams {
|
|
6
|
+
ref: string;
|
|
7
|
+
value?: string;
|
|
8
|
+
label?: string;
|
|
9
|
+
index?: number;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export interface SelectResult {
|
|
13
|
+
ref: string;
|
|
14
|
+
selectedValue: string;
|
|
15
|
+
selectedLabel: string;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* Select an option in a <select> element by value, label, or index.
|
|
20
|
+
*/
|
|
21
|
+
export async function executeSelect(
|
|
22
|
+
client: Client,
|
|
23
|
+
refMap: ElementRefMap,
|
|
24
|
+
params: SelectParams
|
|
25
|
+
): Promise<SelectResult> {
|
|
26
|
+
const resolved = await resolveRef(client, refMap, params.ref);
|
|
27
|
+
|
|
28
|
+
await focusRef(client, resolved.backendNodeId);
|
|
29
|
+
|
|
30
|
+
const { result, exceptionDetails } = await client.Runtime.callFunctionOn({
|
|
31
|
+
objectId: resolved.objectId,
|
|
32
|
+
functionDeclaration: `function(byValue, byLabel, byIndex) {
|
|
33
|
+
if (this.tagName.toLowerCase() !== 'select') {
|
|
34
|
+
throw new Error('Element is not a <select>');
|
|
35
|
+
}
|
|
36
|
+
var found = false;
|
|
37
|
+
for (var i = 0; i < this.options.length; i++) {
|
|
38
|
+
var opt = this.options[i];
|
|
39
|
+
if (byValue !== null && opt.value === byValue) {
|
|
40
|
+
this.selectedIndex = i; found = true; break;
|
|
41
|
+
}
|
|
42
|
+
if (byLabel !== null && opt.textContent.trim() === byLabel) {
|
|
43
|
+
this.selectedIndex = i; found = true; break;
|
|
44
|
+
}
|
|
45
|
+
if (byIndex !== null && i === byIndex) {
|
|
46
|
+
this.selectedIndex = i; found = true; break;
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
if (!found) throw new Error('Option not found');
|
|
50
|
+
this.dispatchEvent(new Event('input', { bubbles: true }));
|
|
51
|
+
this.dispatchEvent(new Event('change', { bubbles: true }));
|
|
52
|
+
var sel = this.options[this.selectedIndex];
|
|
53
|
+
return { value: sel.value, label: sel.textContent.trim() };
|
|
54
|
+
}`,
|
|
55
|
+
arguments: [
|
|
56
|
+
{ value: params.value ?? null },
|
|
57
|
+
{ value: params.label ?? null },
|
|
58
|
+
{ value: params.index ?? null }
|
|
59
|
+
],
|
|
60
|
+
returnByValue: true,
|
|
61
|
+
awaitPromise: false
|
|
62
|
+
});
|
|
63
|
+
|
|
64
|
+
if (exceptionDetails) {
|
|
65
|
+
throw new Error(
|
|
66
|
+
exceptionDetails.exception?.description ??
|
|
67
|
+
exceptionDetails.text ??
|
|
68
|
+
'Failed to select option'
|
|
69
|
+
);
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
const selected = result.value as { value: string; label: string };
|
|
73
|
+
return {
|
|
74
|
+
ref: params.ref,
|
|
75
|
+
selectedValue: selected.value,
|
|
76
|
+
selectedLabel: selected.label
|
|
77
|
+
};
|
|
78
|
+
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import type { Client } from 'chrome-remote-interface';
|
|
2
|
+
import type { ElementRefMap } from '../browser/inspect/element-ref.js';
|
|
3
|
+
import { buildSnapshot, formatSnapshot } from '../browser/inspect/snapshot.js';
|
|
4
|
+
import type { SnapshotResult } from '../browser/inspect/types.js';
|
|
5
|
+
|
|
6
|
+
export interface SnapshotCommandResult {
|
|
7
|
+
text: string;
|
|
8
|
+
snapshot: SnapshotResult;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export async function executeSnapshot(
|
|
12
|
+
client: Client,
|
|
13
|
+
refMap: ElementRefMap
|
|
14
|
+
): Promise<SnapshotCommandResult> {
|
|
15
|
+
const snapshot = await buildSnapshot(client, refMap);
|
|
16
|
+
const text = formatSnapshot(snapshot);
|
|
17
|
+
return { text, snapshot };
|
|
18
|
+
}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import type { Client } from 'chrome-remote-interface';
|
|
2
|
+
import type { ElementRefMap } from '../browser/inspect/element-ref.js';
|
|
3
|
+
import { getStyles, formatStyles } from '../browser/inspect/styles.js';
|
|
4
|
+
import type { StylesResult } from '../browser/inspect/styles.js';
|
|
5
|
+
|
|
6
|
+
export interface StylesParams {
|
|
7
|
+
ref: string;
|
|
8
|
+
properties?: string[];
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export interface StylesCommandResult {
|
|
12
|
+
text: string;
|
|
13
|
+
styles: StylesResult;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export async function executeStyles(
|
|
17
|
+
client: Client,
|
|
18
|
+
refMap: ElementRefMap,
|
|
19
|
+
params: StylesParams
|
|
20
|
+
): Promise<StylesCommandResult> {
|
|
21
|
+
const styles = await getStyles(client, refMap, params.ref, params.properties);
|
|
22
|
+
const text = formatStyles(styles);
|
|
23
|
+
return { text, styles };
|
|
24
|
+
}
|
package/src/commands/type.ts
CHANGED
|
@@ -1,8 +1,11 @@
|
|
|
1
1
|
import { z } from 'zod';
|
|
2
2
|
import type { Client } from 'chrome-remote-interface';
|
|
3
|
+
import type { ElementRefMap } from '../browser/inspect/element-ref.js';
|
|
4
|
+
import { resolveRef, focusRef } from '../browser/interaction/ref-resolver.js';
|
|
3
5
|
|
|
4
6
|
export const TypeParamsSchema = z.object({
|
|
5
7
|
text: z.string(),
|
|
8
|
+
ref: z.string().optional(),
|
|
6
9
|
selector: z.string().optional(),
|
|
7
10
|
clearFirst: z.boolean().default(false),
|
|
8
11
|
delayMs: z.number().int().min(0).default(0)
|
|
@@ -30,9 +33,16 @@ async function focusSelector(client: Client, selector: string): Promise<void> {
|
|
|
30
33
|
|
|
31
34
|
export async function executeType(
|
|
32
35
|
client: Client,
|
|
33
|
-
params: TypeParams
|
|
36
|
+
params: TypeParams,
|
|
37
|
+
refMap?: ElementRefMap
|
|
34
38
|
): Promise<TypeResult> {
|
|
35
|
-
if (params.
|
|
39
|
+
if (params.ref) {
|
|
40
|
+
if (!refMap) {
|
|
41
|
+
throw new Error('Element ref map is required when using ref parameter');
|
|
42
|
+
}
|
|
43
|
+
const resolved = await resolveRef(client, refMap, params.ref);
|
|
44
|
+
await focusRef(client, resolved.backendNodeId);
|
|
45
|
+
} else if (params.selector) {
|
|
36
46
|
await focusSelector(client, params.selector);
|
|
37
47
|
}
|
|
38
48
|
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import type { Client } from 'chrome-remote-interface';
|
|
2
|
+
import type { ElementRefMap } from '../browser/inspect/element-ref.js';
|
|
3
|
+
import {
|
|
4
|
+
validateUploadFiles,
|
|
5
|
+
uploadFiles
|
|
6
|
+
} from '../browser/interaction/uploads.js';
|
|
7
|
+
|
|
8
|
+
export interface UploadParams {
|
|
9
|
+
ref: string;
|
|
10
|
+
files: string[];
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export interface UploadResult {
|
|
14
|
+
ref: string;
|
|
15
|
+
files: string[];
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export async function executeUpload(
|
|
19
|
+
client: Client,
|
|
20
|
+
refMap: ElementRefMap,
|
|
21
|
+
params: UploadParams
|
|
22
|
+
): Promise<UploadResult> {
|
|
23
|
+
if (params.files.length === 0) {
|
|
24
|
+
throw new Error('At least one file is required');
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
const backendNodeId = refMap.resolve(params.ref);
|
|
28
|
+
|
|
29
|
+
// Validate files before uploading
|
|
30
|
+
validateUploadFiles(params.files);
|
|
31
|
+
|
|
32
|
+
// Upload to the file input element
|
|
33
|
+
await uploadFiles(client, backendNodeId, params.files);
|
|
34
|
+
|
|
35
|
+
return { ref: params.ref, files: params.files };
|
|
36
|
+
}
|