@petukhovart/agent-view 0.16.1 → 0.17.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/.claude-plugin/plugin.json +1 -1
- package/README.md +5 -3
- package/agents/act-decider.md +40 -0
- package/dist/cdp/transport.d.ts +2 -0
- package/dist/cdp/transport.d.ts.map +1 -1
- package/dist/cdp/transport.js +168 -9
- package/dist/cdp/transport.js.map +1 -1
- package/dist/cdp/types.d.ts +44 -1
- package/dist/cdp/types.d.ts.map +1 -1
- package/dist/cdp/types.js.map +1 -1
- package/dist/cli/client.d.ts.map +1 -1
- package/dist/cli/client.js +1 -3
- package/dist/cli/client.js.map +1 -1
- package/dist/cli/commands/act.d.ts +3 -0
- package/dist/cli/commands/act.d.ts.map +1 -0
- package/dist/cli/commands/act.js +19 -0
- package/dist/cli/commands/act.js.map +1 -0
- package/dist/cli/commands/click.d.ts +2 -0
- package/dist/cli/commands/click.d.ts.map +1 -1
- package/dist/cli/commands/click.js +9 -4
- package/dist/cli/commands/click.js.map +1 -1
- package/dist/cli/commands/dom.d.ts.map +1 -1
- package/dist/cli/commands/dom.js +1 -0
- package/dist/cli/commands/dom.js.map +1 -1
- package/dist/cli/commands/fill.d.ts +2 -0
- package/dist/cli/commands/fill.d.ts.map +1 -1
- package/dist/cli/commands/fill.js +11 -4
- package/dist/cli/commands/fill.js.map +1 -1
- package/dist/cli/commands/screenshot.d.ts +2 -0
- package/dist/cli/commands/screenshot.d.ts.map +1 -1
- package/dist/cli/commands/screenshot.js +10 -1
- package/dist/cli/commands/screenshot.js.map +1 -1
- package/dist/cli/commands/wait.d.ts +3 -1
- package/dist/cli/commands/wait.d.ts.map +1 -1
- package/dist/cli/commands/wait.js +6 -2
- package/dist/cli/commands/wait.js.map +1 -1
- package/dist/cli/commands/watch.js +1 -1
- package/dist/cli/commands/watch.js.map +1 -1
- package/dist/cli/index.js +46 -4
- package/dist/cli/index.js.map +1 -1
- package/dist/config/manager.d.ts.map +1 -1
- package/dist/config/manager.js +4 -0
- package/dist/config/manager.js.map +1 -1
- package/dist/config/types.d.ts +5 -0
- package/dist/config/types.d.ts.map +1 -1
- package/dist/inspectors/controls/index.d.ts +39 -0
- package/dist/inspectors/controls/index.d.ts.map +1 -0
- package/dist/inspectors/controls/index.js +140 -0
- package/dist/inspectors/controls/index.js.map +1 -0
- package/dist/inspectors/dom/index.d.ts +2 -0
- package/dist/inspectors/dom/index.d.ts.map +1 -1
- package/dist/inspectors/dom/index.js +21 -14
- package/dist/inspectors/dom/index.js.map +1 -1
- package/dist/server/act-replay.d.ts +23 -0
- package/dist/server/act-replay.d.ts.map +1 -0
- package/dist/server/act-replay.js +143 -0
- package/dist/server/act-replay.js.map +1 -0
- package/dist/server/act-script.d.ts +46 -0
- package/dist/server/act-script.d.ts.map +1 -0
- package/dist/server/act-script.js +46 -0
- package/dist/server/act-script.js.map +1 -0
- package/dist/server/act-session.d.ts +23 -0
- package/dist/server/act-session.d.ts.map +1 -0
- package/dist/server/act-session.js +304 -0
- package/dist/server/act-session.js.map +1 -0
- package/dist/server/locator.d.ts +19 -0
- package/dist/server/locator.d.ts.map +1 -0
- package/dist/server/locator.js +33 -0
- package/dist/server/locator.js.map +1 -0
- package/dist/server/port.d.ts +7 -0
- package/dist/server/port.d.ts.map +1 -0
- package/dist/server/port.js +19 -0
- package/dist/server/port.js.map +1 -0
- package/dist/server/server.d.ts +1 -0
- package/dist/server/server.d.ts.map +1 -1
- package/dist/server/server.js +76 -18
- package/dist/server/server.js.map +1 -1
- package/dist/types.d.ts +2 -0
- package/dist/types.d.ts.map +1 -1
- package/package.json +2 -1
- package/skills/verify/SKILL.md +12 -20
- package/skills/verify/references/commands.md +118 -4
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import type { PageSession } from '../cdp/types.js';
|
|
2
|
+
export declare function testIdAttributes(configured: string | undefined): readonly string[];
|
|
3
|
+
export type Locator = {
|
|
4
|
+
css: string;
|
|
5
|
+
label: string;
|
|
6
|
+
};
|
|
7
|
+
export declare function locatorFromArgs(args: Record<string, unknown>): Locator | undefined;
|
|
8
|
+
export declare function testIdLocator(testid: string, testIdAttribute: string | undefined): Locator;
|
|
9
|
+
/**
|
|
10
|
+
* The first visible element a locator names. The label says when it was one of
|
|
11
|
+
* several, since acting on the first of many usually means the locator is too broad.
|
|
12
|
+
*/
|
|
13
|
+
export declare function findByLocator(conn: PageSession, locator: Locator): Promise<{
|
|
14
|
+
backendDOMNodeId: number;
|
|
15
|
+
label: string;
|
|
16
|
+
} | {
|
|
17
|
+
error: string;
|
|
18
|
+
}>;
|
|
19
|
+
//# sourceMappingURL=locator.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"locator.d.ts","sourceRoot":"","sources":["../../src/server/locator.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,iBAAiB,CAAA;AASlD,wBAAgB,gBAAgB,CAAC,UAAU,EAAE,MAAM,GAAG,SAAS,GAAG,SAAS,MAAM,EAAE,CAGlF;AAED,MAAM,MAAM,OAAO,GAAG;IAAE,GAAG,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,CAAA;AAEpD,wBAAgB,eAAe,CAAC,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,OAAO,GAAG,SAAS,CAIlF;AAED,wBAAgB,aAAa,CAAC,MAAM,EAAE,MAAM,EAAE,eAAe,EAAE,MAAM,GAAG,SAAS,GAAG,OAAO,CAG1F;AAED;;;GAGG;AACH,wBAAsB,aAAa,CACjC,IAAI,EAAE,WAAW,EACjB,OAAO,EAAE,OAAO,GACf,OAAO,CAAC;IAAE,gBAAgB,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,GAAG;IAAE,KAAK,EAAE,MAAM,CAAA;CAAE,CAAC,CAK1E"}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Attributes tried when `testIdAttribute` is not configured, in order. Projects
|
|
3
|
+
* rarely set more than one, so accepting all of them spares every project the
|
|
4
|
+
* config line.
|
|
5
|
+
*/
|
|
6
|
+
const DEFAULT_TEST_ID_ATTRIBUTES = ['data-testid', 'data-test-id', 'data-test', 'data-qa', 'data-cy'];
|
|
7
|
+
export function testIdAttributes(configured) {
|
|
8
|
+
// The DOM reports attribute names lowercased; a camel-cased config would match nothing.
|
|
9
|
+
return configured ? [configured.toLowerCase()] : DEFAULT_TEST_ID_ATTRIBUTES;
|
|
10
|
+
}
|
|
11
|
+
export function locatorFromArgs(args) {
|
|
12
|
+
const { testid, selector, testIdAttribute } = args;
|
|
13
|
+
if (typeof testid === 'string')
|
|
14
|
+
return testIdLocator(testid, typeof testIdAttribute === 'string' ? testIdAttribute : undefined);
|
|
15
|
+
return typeof selector === 'string' ? { css: selector, label: `selector "${selector}"` } : undefined;
|
|
16
|
+
}
|
|
17
|
+
export function testIdLocator(testid, testIdAttribute) {
|
|
18
|
+
const value = `"${testid.replace(/["\\]/g, '\\$&')}"`;
|
|
19
|
+
return { css: testIdAttributes(testIdAttribute).map(attr => `[${attr}=${value}]`).join(','), label: `testid "${testid}"` };
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* The first visible element a locator names. The label says when it was one of
|
|
23
|
+
* several, since acting on the first of many usually means the locator is too broad.
|
|
24
|
+
*/
|
|
25
|
+
export async function findByLocator(conn, locator) {
|
|
26
|
+
const { backendDOMNodeId, count } = await conn.queryVisible(locator.css);
|
|
27
|
+
if (count === 0)
|
|
28
|
+
return { error: `No element matches ${locator.label}` };
|
|
29
|
+
if (backendDOMNodeId === null)
|
|
30
|
+
return { error: `None of ${count} element(s) matching ${locator.label} is visible` };
|
|
31
|
+
return { backendDOMNodeId, label: count > 1 ? `${locator.label} (first visible of ${count})` : locator.label };
|
|
32
|
+
}
|
|
33
|
+
//# sourceMappingURL=locator.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"locator.js","sourceRoot":"","sources":["../../src/server/locator.ts"],"names":[],"mappings":"AAEA;;;;GAIG;AACH,MAAM,0BAA0B,GAAG,CAAC,aAAa,EAAE,cAAc,EAAE,WAAW,EAAE,SAAS,EAAE,SAAS,CAAU,CAAA;AAE9G,MAAM,UAAU,gBAAgB,CAAC,UAA8B;IAC7D,wFAAwF;IACxF,OAAO,UAAU,CAAC,CAAC,CAAC,CAAC,UAAU,CAAC,WAAW,EAAE,CAAC,CAAC,CAAC,CAAC,0BAA0B,CAAA;AAC7E,CAAC;AAID,MAAM,UAAU,eAAe,CAAC,IAA6B;IAC3D,MAAM,EAAE,MAAM,EAAE,QAAQ,EAAE,eAAe,EAAE,GAAG,IAAI,CAAA;IAClD,IAAI,OAAO,MAAM,KAAK,QAAQ;QAAE,OAAO,aAAa,CAAC,MAAM,EAAE,OAAO,eAAe,KAAK,QAAQ,CAAC,CAAC,CAAC,eAAe,CAAC,CAAC,CAAC,SAAS,CAAC,CAAA;IAC/H,OAAO,OAAO,QAAQ,KAAK,QAAQ,CAAC,CAAC,CAAC,EAAE,GAAG,EAAE,QAAQ,EAAE,KAAK,EAAE,aAAa,QAAQ,GAAG,EAAE,CAAC,CAAC,CAAC,SAAS,CAAA;AACtG,CAAC;AAED,MAAM,UAAU,aAAa,CAAC,MAAc,EAAE,eAAmC;IAC/E,MAAM,KAAK,GAAG,IAAI,MAAM,CAAC,OAAO,CAAC,QAAQ,EAAE,MAAM,CAAC,GAAG,CAAA;IACrD,OAAO,EAAE,GAAG,EAAE,gBAAgB,CAAC,eAAe,CAAC,CAAC,GAAG,CAAC,IAAI,CAAC,EAAE,CAAC,IAAI,IAAI,IAAI,KAAK,GAAG,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,EAAE,KAAK,EAAE,WAAW,MAAM,GAAG,EAAE,CAAA;AAC5H,CAAC;AAED;;;GAGG;AACH,MAAM,CAAC,KAAK,UAAU,aAAa,CACjC,IAAiB,EACjB,OAAgB;IAEhB,MAAM,EAAE,gBAAgB,EAAE,KAAK,EAAE,GAAG,MAAM,IAAI,CAAC,YAAY,CAAC,OAAO,CAAC,GAAG,CAAC,CAAA;IACxE,IAAI,KAAK,KAAK,CAAC;QAAE,OAAO,EAAE,KAAK,EAAE,sBAAsB,OAAO,CAAC,KAAK,EAAE,EAAE,CAAA;IACxE,IAAI,gBAAgB,KAAK,IAAI;QAAE,OAAO,EAAE,KAAK,EAAE,WAAW,KAAK,wBAAwB,OAAO,CAAC,KAAK,aAAa,EAAE,CAAA;IACnH,OAAO,EAAE,gBAAgB,EAAE,KAAK,EAAE,KAAK,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,OAAO,CAAC,KAAK,sBAAsB,KAAK,GAAG,CAAC,CAAC,CAAC,OAAO,CAAC,KAAK,EAAE,CAAA;AAChH,CAAC"}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
/** `AGENT_VIEW_SERVER_PORT` runs a dev build next to the installed server. The spawned server inherits it. */
|
|
2
|
+
export declare const SERVER_PORT: number;
|
|
3
|
+
/** Home of the token files and saved act scripts. */
|
|
4
|
+
export declare const AGENT_VIEW_DIR: string;
|
|
5
|
+
/** Per-port token, so a dev server never overwrites the live server's token. */
|
|
6
|
+
export declare const TOKEN_PATH: string;
|
|
7
|
+
//# sourceMappingURL=port.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"port.d.ts","sourceRoot":"","sources":["../../src/server/port.ts"],"names":[],"mappings":"AAKA,8GAA8G;AAC9G,eAAO,MAAM,WAAW,QAAiD,CAAA;AAEzE,qDAAqD;AACrD,eAAO,MAAM,cAAc,QAAiC,CAAA;AAE5D,gFAAgF;AAChF,eAAO,MAAM,UAAU,QAA+F,CAAA"}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { homedir } from 'node:os';
|
|
2
|
+
import { join } from 'node:path';
|
|
3
|
+
const DEFAULT_SERVER_PORT = 47922;
|
|
4
|
+
/** `AGENT_VIEW_SERVER_PORT` runs a dev build next to the installed server. The spawned server inherits it. */
|
|
5
|
+
export const SERVER_PORT = serverPort(process.env.AGENT_VIEW_SERVER_PORT);
|
|
6
|
+
/** Home of the token files and saved act scripts. */
|
|
7
|
+
export const AGENT_VIEW_DIR = join(homedir(), '.agent-view');
|
|
8
|
+
/** Per-port token, so a dev server never overwrites the live server's token. */
|
|
9
|
+
export const TOKEN_PATH = join(AGENT_VIEW_DIR, SERVER_PORT === DEFAULT_SERVER_PORT ? 'token' : `token-${SERVER_PORT}`);
|
|
10
|
+
function serverPort(raw) {
|
|
11
|
+
if (raw === undefined || raw === '')
|
|
12
|
+
return DEFAULT_SERVER_PORT;
|
|
13
|
+
const port = Number(raw);
|
|
14
|
+
// A typo must not fall back to the default: that is the live server the variable exists to avoid.
|
|
15
|
+
if (!Number.isInteger(port) || port < 1 || port > 65_535)
|
|
16
|
+
throw new Error(`AGENT_VIEW_SERVER_PORT=${raw} is not a port`);
|
|
17
|
+
return port;
|
|
18
|
+
}
|
|
19
|
+
//# sourceMappingURL=port.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"port.js","sourceRoot":"","sources":["../../src/server/port.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,SAAS,CAAA;AACjC,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAA;AAEhC,MAAM,mBAAmB,GAAG,KAAK,CAAA;AAEjC,8GAA8G;AAC9G,MAAM,CAAC,MAAM,WAAW,GAAG,UAAU,CAAC,OAAO,CAAC,GAAG,CAAC,sBAAsB,CAAC,CAAA;AAEzE,qDAAqD;AACrD,MAAM,CAAC,MAAM,cAAc,GAAG,IAAI,CAAC,OAAO,EAAE,EAAE,aAAa,CAAC,CAAA;AAE5D,gFAAgF;AAChF,MAAM,CAAC,MAAM,UAAU,GAAG,IAAI,CAAC,cAAc,EAAE,WAAW,KAAK,mBAAmB,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,SAAS,WAAW,EAAE,CAAC,CAAA;AAEtH,SAAS,UAAU,CAAC,GAAuB;IACzC,IAAI,GAAG,KAAK,SAAS,IAAI,GAAG,KAAK,EAAE;QAAE,OAAO,mBAAmB,CAAA;IAC/D,MAAM,IAAI,GAAG,MAAM,CAAC,GAAG,CAAC,CAAA;IACxB,kGAAkG;IAClG,IAAI,CAAC,MAAM,CAAC,SAAS,CAAC,IAAI,CAAC,IAAI,IAAI,GAAG,CAAC,IAAI,IAAI,GAAG,MAAM;QAAE,MAAM,IAAI,KAAK,CAAC,0BAA0B,GAAG,gBAAgB,CAAC,CAAA;IACxH,OAAO,IAAI,CAAA;AACb,CAAC"}
|
package/dist/server/server.d.ts
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"server.d.ts","sourceRoot":"","sources":["../../src/server/server.ts"],"names":[],"mappings":"AAcA,OAAO,EAKL,KAAK,WAAW,EAEhB,KAAK,UAAU,EAKhB,MAAM,iBAAiB,CAAA;AAgExB,+FAA+F;AAC/F,wBAAgB,iBAAiB,CAAC,OAAO,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,MAAM,GAAG,IAAI,CAK/F;AA6BD,KAAK,YAAY,GACb;IAAE,IAAI,EAAE,QAAQ,CAAC;IAAC,IAAI,EAAE,MAAM,CAAC;IAAC,IAAI,CAAC,EAAE,MAAM,CAAA;CAAE,GAC/C;IAAE,IAAI,EAAE,WAAW,CAAC;IAAC,GAAG,EAAE,MAAM,CAAA;CAAE,CAAA;AAEtC,wBAAgB,YAAY,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,EAAE,QAAQ,EAAE,MAAM,GAAG,SAAS,GAAG,MAAM,GAAG,SAAS,CAIzG;AAED,wBAAsB,mBAAmB,CAAC,IAAI,EAAE,WAAW,EAAE,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CA6B5F;AAED,wBAAgB,WAAW,CAAC,MAAM,EAAE,MAAM,GAAG,YAAY,CAaxD;AAED;;;;;GAKG;AACH,wBAAgB,iBAAiB,CAAC,GAAG,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,GAAG;IAAE,IAAI,EAAE,MAAM,CAAA;CAAE,GAAG;IAAE,KAAK,EAAE,MAAM,CAAA;CAAE,CAUhG;
|
|
1
|
+
{"version":3,"file":"server.d.ts","sourceRoot":"","sources":["../../src/server/server.ts"],"names":[],"mappings":"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"}
|
package/dist/server/server.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { createServer } from 'node:net';
|
|
2
2
|
import { writeFile, unlink } from 'node:fs/promises';
|
|
3
3
|
import { randomBytes } from 'node:crypto';
|
|
4
|
-
import {
|
|
4
|
+
import { tmpdir } from 'node:os';
|
|
5
5
|
import { join, resolve } from 'node:path';
|
|
6
6
|
import { existsSync, mkdirSync, statSync, writeFileSync } from 'node:fs';
|
|
7
7
|
import { getAdapter } from '../adapters/registry.js';
|
|
@@ -28,8 +28,10 @@ import { AxTreeCache } from '../cdp/ax-cache.js';
|
|
|
28
28
|
import { WatchSession } from './watch-session.js';
|
|
29
29
|
import { StopReason, WATCH_MIN_INTERVAL_MS } from '../inspectors/watch/index.js';
|
|
30
30
|
import { buildMatcher } from './pattern.js';
|
|
31
|
+
import { findByLocator, locatorFromArgs, testIdAttributes } from './locator.js';
|
|
32
|
+
import { SERVER_PORT, AGENT_VIEW_DIR, TOKEN_PATH } from './port.js';
|
|
33
|
+
import { runAct } from './act-session.js';
|
|
31
34
|
import { DEFAULT_TAIL_LINES, LogRecorder, filterLogLines, localStamp, parseSinceToken, readFeedLines, resolveLogFile, } from './log-recorder.js';
|
|
32
|
-
const SERVER_PORT = 47922;
|
|
33
35
|
/**
|
|
34
36
|
* Every non-streaming command answers within this budget (plus its own `--timeout`,
|
|
35
37
|
* for the commands that poll). A CDP call that never returns used to leave the CLI
|
|
@@ -43,8 +45,6 @@ const VALID_ENGINES = new Set(Object.values(WebGLEngine));
|
|
|
43
45
|
const IDLE_TIMEOUT_MS = 5 * 60 * 1000;
|
|
44
46
|
const DELIMITER = '\n';
|
|
45
47
|
const MAX_BUFFER_SIZE = 1_048_576; // 1 MB
|
|
46
|
-
const TOKEN_DIR = join(homedir(), '.agent-view');
|
|
47
|
-
const TOKEN_PATH = join(TOKEN_DIR, 'token');
|
|
48
48
|
const EVAL_OUTPUT_CAP = 64 * 1024;
|
|
49
49
|
const DEFAULT_CONSOLE_TARGETS = [TargetType.Page, TargetType.SharedWorker, TargetType.ServiceWorker];
|
|
50
50
|
const DEFAULT_NETWORK_BUFFER = 200;
|
|
@@ -194,13 +194,14 @@ export class AgentViewServer {
|
|
|
194
194
|
coverage: (req) => this.handleCoverage(req),
|
|
195
195
|
listeners: (req) => this.handleListeners(req),
|
|
196
196
|
heap: (req) => this.handleHeap(req),
|
|
197
|
+
act: (req) => this.handleAct(req),
|
|
197
198
|
stop: () => this.handleStop(),
|
|
198
199
|
};
|
|
199
200
|
streamingCommands = new Set(['watch']);
|
|
200
201
|
validCommands = new Set([...Object.keys(this.handlers), ...this.streamingCommands]);
|
|
201
202
|
async start() {
|
|
202
203
|
installCDPErrorGuard();
|
|
203
|
-
mkdirSync(
|
|
204
|
+
mkdirSync(AGENT_VIEW_DIR, { recursive: true });
|
|
204
205
|
this.token = randomBytes(32).toString('hex');
|
|
205
206
|
// Publish the token only after winning the port. A server that loses the bind race
|
|
206
207
|
// must not overwrite the live server's token — that leaves every CLI call
|
|
@@ -225,6 +226,7 @@ export class AgentViewServer {
|
|
|
225
226
|
networkRefs: new Map(),
|
|
226
227
|
networkNextRef: 1,
|
|
227
228
|
logRecorder: null,
|
|
229
|
+
act: null,
|
|
228
230
|
};
|
|
229
231
|
this.portStates.set(port, state);
|
|
230
232
|
}
|
|
@@ -509,6 +511,7 @@ export class AgentViewServer {
|
|
|
509
511
|
startRef: this.refStore.getNextRef(),
|
|
510
512
|
compact,
|
|
511
513
|
maxLines: argNum(req.args, 'maxLines'),
|
|
514
|
+
testIds: await conn.getTestIds(testIdAttributes(argStr(req.args, 'testIdAttribute'))),
|
|
512
515
|
});
|
|
513
516
|
this.refStore.store(refs, req.port, targetId, nextRef);
|
|
514
517
|
if (isDiff) {
|
|
@@ -634,6 +637,15 @@ export class AgentViewServer {
|
|
|
634
637
|
this.axTreeCache.invalidate(cacheKey);
|
|
635
638
|
return { ok: true, data: `${verb} at (${x}, ${y})` };
|
|
636
639
|
}
|
|
640
|
+
const locator = locatorFromArgs(req.args);
|
|
641
|
+
if (locator) {
|
|
642
|
+
const found = await findByLocator(conn, locator);
|
|
643
|
+
if ('error' in found)
|
|
644
|
+
return { ok: false, error: found.error };
|
|
645
|
+
await conn.clickByNodeId(found.backendDOMNodeId, clickOpts);
|
|
646
|
+
this.axTreeCache.invalidate(cacheKey);
|
|
647
|
+
return { ok: true, data: `${verb} ${found.label}` };
|
|
648
|
+
}
|
|
637
649
|
const clickFilter = argStr(req.args, 'filter');
|
|
638
650
|
if (clickFilter) {
|
|
639
651
|
const filter = clickFilter;
|
|
@@ -648,7 +660,7 @@ export class AgentViewServer {
|
|
|
648
660
|
}
|
|
649
661
|
const ref = argNum(req.args, 'ref');
|
|
650
662
|
if (ref === undefined) {
|
|
651
|
-
return { ok: false, error: 'click requires --ref, --filter, or --pos' };
|
|
663
|
+
return { ok: false, error: 'click requires --ref, --filter, --testid, --selector, or --pos' };
|
|
652
664
|
}
|
|
653
665
|
const entry = this.refStore.get(ref);
|
|
654
666
|
if (!entry) {
|
|
@@ -658,6 +670,19 @@ export class AgentViewServer {
|
|
|
658
670
|
this.axTreeCache.invalidate(cacheKey);
|
|
659
671
|
return { ok: true, data: `${verb} ref ${ref}` };
|
|
660
672
|
}
|
|
673
|
+
async handleAct(req) {
|
|
674
|
+
const { targetId } = await this.resolveWindow(req);
|
|
675
|
+
const cacheKey = `${req.port}:${targetId}`;
|
|
676
|
+
return runAct(this.stateFor(req.port), req.args, {
|
|
677
|
+
conn: await this.getPageSession(req, targetId),
|
|
678
|
+
invalidateAxCache: () => this.axTreeCache.invalidate(cacheKey),
|
|
679
|
+
// A login that reloads the window kills the cached socket before its disconnect event lands.
|
|
680
|
+
reconnect: async () => {
|
|
681
|
+
this.dropSessionsForPort(req.port);
|
|
682
|
+
return this.getPageSession(req, (await this.resolveWindow(req)).targetId);
|
|
683
|
+
},
|
|
684
|
+
});
|
|
685
|
+
}
|
|
661
686
|
async handleDrag(req) {
|
|
662
687
|
const { targetId } = await this.resolveWindow(req);
|
|
663
688
|
const conn = await this.getPageSession(req, targetId);
|
|
@@ -712,6 +737,15 @@ export class AgentViewServer {
|
|
|
712
737
|
if (value === undefined) {
|
|
713
738
|
return { ok: false, error: 'fill requires --value' };
|
|
714
739
|
}
|
|
740
|
+
const locator = locatorFromArgs(req.args);
|
|
741
|
+
if (locator) {
|
|
742
|
+
const found = await findByLocator(conn, locator);
|
|
743
|
+
if ('error' in found)
|
|
744
|
+
return { ok: false, error: found.error };
|
|
745
|
+
await conn.fillByNodeId(found.backendDOMNodeId, value);
|
|
746
|
+
this.axTreeCache.invalidate(cacheKey);
|
|
747
|
+
return { ok: true, data: `Filled ${found.label} with "${value}"` };
|
|
748
|
+
}
|
|
715
749
|
const fillFilter = argStr(req.args, 'filter');
|
|
716
750
|
if (fillFilter) {
|
|
717
751
|
const filter = fillFilter;
|
|
@@ -726,7 +760,7 @@ export class AgentViewServer {
|
|
|
726
760
|
}
|
|
727
761
|
const fillRef = argNum(req.args, 'ref');
|
|
728
762
|
if (fillRef === undefined) {
|
|
729
|
-
return { ok: false, error: 'fill requires --ref or --
|
|
763
|
+
return { ok: false, error: 'fill requires --ref, --filter, --testid, or --selector' };
|
|
730
764
|
}
|
|
731
765
|
const entry = this.refStore.get(fillRef);
|
|
732
766
|
if (!entry) {
|
|
@@ -1035,14 +1069,25 @@ export class AgentViewServer {
|
|
|
1035
1069
|
}
|
|
1036
1070
|
async handleWait(req) {
|
|
1037
1071
|
const filter = argStr(req.args, 'filter');
|
|
1038
|
-
|
|
1039
|
-
|
|
1072
|
+
const locator = locatorFromArgs(req.args);
|
|
1073
|
+
if (!filter && !locator) {
|
|
1074
|
+
return { ok: false, error: 'wait requires --filter, --testid, or --selector' };
|
|
1040
1075
|
}
|
|
1041
1076
|
const timeout = argNum(req.args, 'timeout') ?? 10;
|
|
1042
1077
|
const interval = 500;
|
|
1043
1078
|
const maxAttempts = Math.ceil((timeout * 1000) / interval);
|
|
1044
1079
|
const { targetId } = await this.resolveWindow(req);
|
|
1045
1080
|
const conn = await this.getPageSession(req, targetId);
|
|
1081
|
+
if (locator) {
|
|
1082
|
+
let found = await findByLocator(conn, locator);
|
|
1083
|
+
for (let i = 1; i < maxAttempts && 'error' in found; i++) {
|
|
1084
|
+
await new Promise(resolve => setTimeout(resolve, interval));
|
|
1085
|
+
found = await findByLocator(conn, locator);
|
|
1086
|
+
}
|
|
1087
|
+
return 'error' in found
|
|
1088
|
+
? { ok: false, error: `Timeout after ${timeout}s: ${found.error}` }
|
|
1089
|
+
: { ok: true, data: `Found ${found.label}` };
|
|
1090
|
+
}
|
|
1046
1091
|
for (let i = 0; i < maxAttempts; i++) {
|
|
1047
1092
|
const nodes = await conn.getAccessibilityTree();
|
|
1048
1093
|
const { refs, text, nextRef } = formatAccessibilityTree(nodes, {
|
|
@@ -1069,20 +1114,33 @@ export class AgentViewServer {
|
|
|
1069
1114
|
const conn = await this.getPageSession(req, targetId);
|
|
1070
1115
|
const scale = argNum(req.args, 'scale');
|
|
1071
1116
|
const cropFilter = argStr(req.args, 'crop');
|
|
1117
|
+
const locator = locatorFromArgs(req.args);
|
|
1072
1118
|
let warning;
|
|
1073
1119
|
let clip;
|
|
1074
|
-
|
|
1120
|
+
let cropNode;
|
|
1121
|
+
if (locator) {
|
|
1122
|
+
// Unlike a text filter, an exact locator that misses is a wrong locator: a
|
|
1123
|
+
// full-window fallback would spend ~19k vision tokens on the wrong question.
|
|
1124
|
+
const found = await findByLocator(conn, locator);
|
|
1125
|
+
if ('error' in found)
|
|
1126
|
+
return { ok: false, error: found.error };
|
|
1127
|
+
cropNode = found.backendDOMNodeId;
|
|
1128
|
+
}
|
|
1129
|
+
else if (cropFilter !== undefined) {
|
|
1075
1130
|
const found = await this.findByFilter(conn, cropFilter, req, targetId);
|
|
1076
|
-
if (
|
|
1077
|
-
|
|
1131
|
+
if (found) {
|
|
1132
|
+
cropNode = found.backendDOMNodeId;
|
|
1078
1133
|
}
|
|
1079
1134
|
else {
|
|
1080
|
-
|
|
1081
|
-
|
|
1082
|
-
|
|
1083
|
-
|
|
1084
|
-
|
|
1085
|
-
|
|
1135
|
+
warning = `crop filter '${cropFilter}' matched nothing — capturing full window`;
|
|
1136
|
+
}
|
|
1137
|
+
}
|
|
1138
|
+
if (cropNode !== undefined) {
|
|
1139
|
+
const cropUp = argNum(req.args, 'cropUp') ?? 0;
|
|
1140
|
+
clip = await conn.getBoxRect(cropNode, { scrollIntoView: true, ancestorLevels: cropUp });
|
|
1141
|
+
if (clip.height < TEXT_LINE_HEIGHT_PX && cropUp === 0) {
|
|
1142
|
+
warning = `crop matched a text-sized box (${Math.round(clip.width)}×${Math.round(clip.height)}) — `
|
|
1143
|
+
+ `pass --crop-up 1 (or 2) to capture the surrounding container instead`;
|
|
1086
1144
|
}
|
|
1087
1145
|
}
|
|
1088
1146
|
const filepath = await this.captureScreenshotToFile(conn, { scale, clip });
|