@isoldex/sentinel 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +15 -0
- package/README.md +122 -0
- package/dist/api/act.d.ts +22 -0
- package/dist/api/act.d.ts.map +1 -0
- package/dist/api/act.js +116 -0
- package/dist/api/act.js.map +1 -0
- package/dist/api/extract.d.ts +13 -0
- package/dist/api/extract.d.ts.map +1 -0
- package/dist/api/extract.js +52 -0
- package/dist/api/extract.js.map +1 -0
- package/dist/api/observe.d.ts +17 -0
- package/dist/api/observe.d.ts.map +1 -0
- package/dist/api/observe.js +56 -0
- package/dist/api/observe.js.map +1 -0
- package/dist/complex-test.d.ts +2 -0
- package/dist/complex-test.d.ts.map +1 -0
- package/dist/complex-test.js +58 -0
- package/dist/complex-test.js.map +1 -0
- package/dist/core/driver.d.ts +23 -0
- package/dist/core/driver.d.ts.map +1 -0
- package/dist/core/driver.js +61 -0
- package/dist/core/driver.js.map +1 -0
- package/dist/core/state-parser.d.ts +38 -0
- package/dist/core/state-parser.d.ts.map +1 -0
- package/dist/core/state-parser.js +103 -0
- package/dist/core/state-parser.js.map +1 -0
- package/dist/debug-aom.d.ts +2 -0
- package/dist/debug-aom.d.ts.map +1 -0
- package/dist/debug-aom.js +22 -0
- package/dist/debug-aom.js.map +1 -0
- package/dist/demo.d.ts +2 -0
- package/dist/demo.d.ts.map +1 -0
- package/dist/demo.js +41 -0
- package/dist/demo.js.map +1 -0
- package/dist/index.d.ts +85 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +143 -0
- package/dist/index.js.map +1 -0
- package/dist/list-all.d.ts +2 -0
- package/dist/list-all.d.ts.map +1 -0
- package/dist/list-all.js +25 -0
- package/dist/list-all.js.map +1 -0
- package/dist/list-models.d.ts +2 -0
- package/dist/list-models.d.ts.map +1 -0
- package/dist/list-models.js +11 -0
- package/dist/list-models.js.map +1 -0
- package/dist/reliability/verifier.d.ts +18 -0
- package/dist/reliability/verifier.d.ts.map +1 -0
- package/dist/reliability/verifier.js +64 -0
- package/dist/reliability/verifier.js.map +1 -0
- package/dist/test-versions.d.ts +2 -0
- package/dist/test-versions.d.ts.map +1 -0
- package/dist/test-versions.js +33 -0
- package/dist/test-versions.js.map +1 -0
- package/dist/utils/gemini.d.ts +12 -0
- package/dist/utils/gemini.d.ts.map +1 -0
- package/dist/utils/gemini.js +103 -0
- package/dist/utils/gemini.js.map +1 -0
- package/dist/whatsapp-test.d.ts +2 -0
- package/dist/whatsapp-test.d.ts.map +1 -0
- package/dist/whatsapp-test.js +56 -0
- package/dist/whatsapp-test.js.map +1 -0
- package/package.json +53 -0
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import type { CDPSession, Page } from 'playwright';
|
|
2
|
+
export interface UIElement {
|
|
3
|
+
id: number;
|
|
4
|
+
role: string;
|
|
5
|
+
name: string;
|
|
6
|
+
description?: string;
|
|
7
|
+
boundingClientRect: {
|
|
8
|
+
x: number;
|
|
9
|
+
y: number;
|
|
10
|
+
width: number;
|
|
11
|
+
height: number;
|
|
12
|
+
};
|
|
13
|
+
attributes?: Record<string, string>;
|
|
14
|
+
state?: {
|
|
15
|
+
disabled?: boolean;
|
|
16
|
+
hidden?: boolean;
|
|
17
|
+
focused?: boolean;
|
|
18
|
+
checked?: boolean | 'mixed';
|
|
19
|
+
};
|
|
20
|
+
}
|
|
21
|
+
export interface SimplifiedState {
|
|
22
|
+
url: string;
|
|
23
|
+
title: string;
|
|
24
|
+
elements: UIElement[];
|
|
25
|
+
}
|
|
26
|
+
export declare class StateParser {
|
|
27
|
+
private page;
|
|
28
|
+
private cdp;
|
|
29
|
+
private elementCounter;
|
|
30
|
+
private cachedState;
|
|
31
|
+
private cacheTimestamp;
|
|
32
|
+
constructor(page: Page, cdp: CDPSession);
|
|
33
|
+
invalidateCache(): void;
|
|
34
|
+
parse(): Promise<SimplifiedState>;
|
|
35
|
+
private isInteractive;
|
|
36
|
+
private nodeToUIElement;
|
|
37
|
+
}
|
|
38
|
+
//# sourceMappingURL=state-parser.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"state-parser.d.ts","sourceRoot":"","sources":["../../src/core/state-parser.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,IAAI,EAAE,MAAM,YAAY,CAAC;AAEnD,MAAM,WAAW,SAAS;IACxB,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,kBAAkB,EAAE;QAClB,CAAC,EAAE,MAAM,CAAC;QACV,CAAC,EAAE,MAAM,CAAC;QACV,KAAK,EAAE,MAAM,CAAC;QACd,MAAM,EAAE,MAAM,CAAC;KAChB,CAAC;IACF,UAAU,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACpC,KAAK,CAAC,EAAE;QACN,QAAQ,CAAC,EAAE,OAAO,CAAC;QACnB,MAAM,CAAC,EAAE,OAAO,CAAC;QACjB,OAAO,CAAC,EAAE,OAAO,CAAC;QAClB,OAAO,CAAC,EAAE,OAAO,GAAG,OAAO,CAAC;KAC7B,CAAC;CACH;AAED,MAAM,WAAW,eAAe;IAC9B,GAAG,EAAE,MAAM,CAAC;IACZ,KAAK,EAAE,MAAM,CAAC;IACd,QAAQ,EAAE,SAAS,EAAE,CAAC;CACvB;AAID,qBAAa,WAAW;IAKV,OAAO,CAAC,IAAI;IAAQ,OAAO,CAAC,GAAG;IAJ3C,OAAO,CAAC,cAAc,CAAK;IAC3B,OAAO,CAAC,WAAW,CAAgC;IACnD,OAAO,CAAC,cAAc,CAAK;gBAEP,IAAI,EAAE,IAAI,EAAU,GAAG,EAAE,UAAU;IAEvD,eAAe;IAKT,KAAK,IAAI,OAAO,CAAC,eAAe,CAAC;IAwDvC,OAAO,CAAC,aAAa;IAerB,OAAO,CAAC,eAAe;CA8BxB"}
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
const STATE_CACHE_TTL_MS = 500;
|
|
2
|
+
export class StateParser {
|
|
3
|
+
page;
|
|
4
|
+
cdp;
|
|
5
|
+
elementCounter = 0;
|
|
6
|
+
cachedState = null;
|
|
7
|
+
cacheTimestamp = 0;
|
|
8
|
+
constructor(page, cdp) {
|
|
9
|
+
this.page = page;
|
|
10
|
+
this.cdp = cdp;
|
|
11
|
+
}
|
|
12
|
+
invalidateCache() {
|
|
13
|
+
this.cachedState = null;
|
|
14
|
+
this.cacheTimestamp = 0;
|
|
15
|
+
}
|
|
16
|
+
async parse() {
|
|
17
|
+
const now = Date.now();
|
|
18
|
+
if (this.cachedState && (now - this.cacheTimestamp) < STATE_CACHE_TTL_MS) {
|
|
19
|
+
return this.cachedState;
|
|
20
|
+
}
|
|
21
|
+
this.elementCounter = 0;
|
|
22
|
+
const { nodes } = await this.cdp.send('Accessibility.getFullAXTree');
|
|
23
|
+
// Step 1: Filter interactive nodes synchronously (no I/O)
|
|
24
|
+
const interactiveNodes = nodes.filter((node) => this.isInteractive(node) && node.backendDOMNodeId);
|
|
25
|
+
// Step 2: Fire ALL getBoxModel requests in parallel
|
|
26
|
+
const boxModelResults = await Promise.allSettled(interactiveNodes.map((node) => this.cdp.send('DOM.getBoxModel', { backendNodeId: node.backendDOMNodeId })));
|
|
27
|
+
// Step 3: Build UIElement list from results
|
|
28
|
+
const uiElements = [];
|
|
29
|
+
for (let i = 0; i < interactiveNodes.length; i++) {
|
|
30
|
+
const node = interactiveNodes[i];
|
|
31
|
+
const result = boxModelResults[i];
|
|
32
|
+
if (!result || result.status === 'rejected')
|
|
33
|
+
continue;
|
|
34
|
+
const fulfilled = result;
|
|
35
|
+
const { model } = fulfilled.value;
|
|
36
|
+
if (!model?.content || model.content.length < 8)
|
|
37
|
+
continue;
|
|
38
|
+
const element = this.nodeToUIElement(node);
|
|
39
|
+
if (!element)
|
|
40
|
+
continue;
|
|
41
|
+
const x = model.content[0];
|
|
42
|
+
const y = model.content[1];
|
|
43
|
+
const width = model.content[2] - model.content[0];
|
|
44
|
+
const height = model.content[7] - model.content[1];
|
|
45
|
+
element.boundingClientRect = { x, y, width, height };
|
|
46
|
+
uiElements.push(element);
|
|
47
|
+
}
|
|
48
|
+
const state = {
|
|
49
|
+
url: this.page.url(),
|
|
50
|
+
title: await this.page.title(),
|
|
51
|
+
elements: uiElements,
|
|
52
|
+
};
|
|
53
|
+
this.cachedState = state;
|
|
54
|
+
this.cacheTimestamp = Date.now();
|
|
55
|
+
return state;
|
|
56
|
+
}
|
|
57
|
+
isInteractive(node) {
|
|
58
|
+
const interactiveRoles = [
|
|
59
|
+
// Standard form controls
|
|
60
|
+
'button', 'link', 'textbox', 'checkbox', 'combobox',
|
|
61
|
+
'listbox', 'menuitem', 'radio', 'searchbox', 'slider',
|
|
62
|
+
'spinbutton', 'switch', 'tab', 'treeitem',
|
|
63
|
+
// List-based UI (e.g. WhatsApp chat list items, dropdowns)
|
|
64
|
+
'listitem', 'option', 'row', 'gridcell',
|
|
65
|
+
// Custom interactive containers often used in SPAs
|
|
66
|
+
'menuitemcheckbox', 'menuitemradio', 'columnheader',
|
|
67
|
+
];
|
|
68
|
+
return interactiveRoles.includes(node.role?.value) && !node.ignored;
|
|
69
|
+
}
|
|
70
|
+
// Made synchronous – no async needed, no await inside
|
|
71
|
+
nodeToUIElement(node) {
|
|
72
|
+
const role = node.role?.value || 'unknown';
|
|
73
|
+
const name = node.name?.value || '';
|
|
74
|
+
const description = node.description?.value || '';
|
|
75
|
+
// Use description as fallback name (common in SPA list items)
|
|
76
|
+
const effectiveName = name || description;
|
|
77
|
+
// Skip completely nameless elements unless they are a textbox (always useful)
|
|
78
|
+
if (!effectiveName && role !== 'textbox')
|
|
79
|
+
return null;
|
|
80
|
+
const state = {};
|
|
81
|
+
if (node.properties) {
|
|
82
|
+
for (const prop of node.properties) {
|
|
83
|
+
if (prop.name === 'disabled')
|
|
84
|
+
state.disabled = prop.value.value;
|
|
85
|
+
if (prop.name === 'hidden')
|
|
86
|
+
state.hidden = prop.value.value;
|
|
87
|
+
if (prop.name === 'focused')
|
|
88
|
+
state.focused = prop.value.value;
|
|
89
|
+
if (prop.name === 'checked')
|
|
90
|
+
state.checked = prop.value.value;
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
return {
|
|
94
|
+
id: this.elementCounter++,
|
|
95
|
+
role,
|
|
96
|
+
name: effectiveName,
|
|
97
|
+
description,
|
|
98
|
+
boundingClientRect: { x: 0, y: 0, width: 0, height: 0 },
|
|
99
|
+
state,
|
|
100
|
+
};
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
//# sourceMappingURL=state-parser.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"state-parser.js","sourceRoot":"","sources":["../../src/core/state-parser.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"debug-aom.d.ts","sourceRoot":"","sources":["../src/debug-aom.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { SentinelDriver } from './core/driver.js';
|
|
2
|
+
import { StateParser } from './core/state-parser.js';
|
|
3
|
+
import dotenv from 'dotenv';
|
|
4
|
+
import fs from 'fs';
|
|
5
|
+
dotenv.config();
|
|
6
|
+
async function debugAOM() {
|
|
7
|
+
const driver = new SentinelDriver({ headless: false });
|
|
8
|
+
await driver.initialize();
|
|
9
|
+
const page = driver.getPage();
|
|
10
|
+
await page.goto("https://web.whatsapp.com");
|
|
11
|
+
console.log("WAITING FOR MANUAL LOGIN...");
|
|
12
|
+
await page.waitForSelector('#pane-side', { timeout: 120000 });
|
|
13
|
+
console.log("Logged in. Capturing AOM...");
|
|
14
|
+
const cdp = driver.getCDPSession();
|
|
15
|
+
const parser = new StateParser(page, cdp);
|
|
16
|
+
const state = await parser.parse();
|
|
17
|
+
fs.writeFileSync('whatsapp-aom-debug.json', JSON.stringify(state, null, 2));
|
|
18
|
+
console.log("AOM saved to whatsapp-aom-debug.json");
|
|
19
|
+
await driver.close();
|
|
20
|
+
}
|
|
21
|
+
debugAOM();
|
|
22
|
+
//# sourceMappingURL=debug-aom.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"debug-aom.js","sourceRoot":"","sources":["../src/debug-aom.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAE,MAAM,kBAAkB,CAAC;AAClD,OAAO,EAAE,WAAW,EAAE,MAAM,wBAAwB,CAAC;AACrD,OAAO,MAAM,MAAM,QAAQ,CAAC;AAC5B,OAAO,EAAE,MAAM,IAAI,CAAC;AAEpB,MAAM,CAAC,MAAM,EAAE,CAAC;AAEhB,KAAK,UAAU,QAAQ;IACrB,MAAM,MAAM,GAAG,IAAI,cAAc,CAAC,EAAE,QAAQ,EAAE,KAAK,EAAE,CAAC,CAAC;IACvD,MAAM,MAAM,CAAC,UAAU,EAAE,CAAC;IAC1B,MAAM,IAAI,GAAG,MAAM,CAAC,OAAO,EAAG,CAAC;IAE/B,MAAM,IAAI,CAAC,IAAI,CAAC,0BAA0B,CAAC,CAAC;IAC5C,OAAO,CAAC,GAAG,CAAC,6BAA6B,CAAC,CAAC;IAC3C,MAAM,IAAI,CAAC,eAAe,CAAC,YAAY,EAAE,EAAE,OAAO,EAAE,MAAM,EAAE,CAAC,CAAC;IAE9D,OAAO,CAAC,GAAG,CAAC,6BAA6B,CAAC,CAAC;IAC3C,MAAM,GAAG,GAAG,MAAM,CAAC,aAAa,EAAG,CAAC;IACpC,MAAM,MAAM,GAAG,IAAI,WAAW,CAAC,IAAI,EAAE,GAAG,CAAC,CAAC;IAC1C,MAAM,KAAK,GAAG,MAAM,MAAM,CAAC,KAAK,EAAE,CAAC;IAEnC,EAAE,CAAC,aAAa,CAAC,yBAAyB,EAAE,IAAI,CAAC,SAAS,CAAC,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC,CAAC;IAC5E,OAAO,CAAC,GAAG,CAAC,sCAAsC,CAAC,CAAC;IAEpD,MAAM,MAAM,CAAC,KAAK,EAAE,CAAC;AACvB,CAAC;AAED,QAAQ,EAAE,CAAC"}
|
package/dist/demo.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"demo.d.ts","sourceRoot":"","sources":["../src/demo.ts"],"names":[],"mappings":""}
|
package/dist/demo.js
ADDED
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import { Sentinel, z } from './index.js';
|
|
2
|
+
import dotenv from 'dotenv';
|
|
3
|
+
dotenv.config();
|
|
4
|
+
async function main() {
|
|
5
|
+
const sentinel = new Sentinel({
|
|
6
|
+
apiKey: process.env.GEMINI_API_KEY || 'YOUR_API_KEY',
|
|
7
|
+
headless: false,
|
|
8
|
+
verbose: 1,
|
|
9
|
+
enableCaching: true,
|
|
10
|
+
domSettleTimeoutMs: 3000,
|
|
11
|
+
});
|
|
12
|
+
try {
|
|
13
|
+
await sentinel.init();
|
|
14
|
+
// Direct Playwright page access (like Stagehand)
|
|
15
|
+
console.log('Current URL:', sentinel.page.url());
|
|
16
|
+
await sentinel.goto('https://www.google.com');
|
|
17
|
+
// act() with variables support
|
|
18
|
+
await sentinel.act("Click 'Alle akzeptieren' or 'I agree' to cookies if present", { retries: 1 });
|
|
19
|
+
// act() with %variable% interpolation
|
|
20
|
+
const searchTerm = 'Stagehand AI';
|
|
21
|
+
await sentinel.act('Type %term% into the search bar and press enter', {
|
|
22
|
+
variables: { term: searchTerm },
|
|
23
|
+
});
|
|
24
|
+
// extract() with Zod schema
|
|
25
|
+
const results = await sentinel.extract('Extract the first 5 search result titles', z.object({
|
|
26
|
+
titles: z.array(z.string()),
|
|
27
|
+
}));
|
|
28
|
+
console.log('Search results:', results);
|
|
29
|
+
// observe() with optional instruction
|
|
30
|
+
const actions = await sentinel.observe('Find navigation or search elements');
|
|
31
|
+
console.log('Observable actions:', actions);
|
|
32
|
+
}
|
|
33
|
+
catch (error) {
|
|
34
|
+
console.error('Sentinel Error:', error);
|
|
35
|
+
}
|
|
36
|
+
finally {
|
|
37
|
+
await sentinel.close();
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
main();
|
|
41
|
+
//# sourceMappingURL=demo.js.map
|
package/dist/demo.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"demo.js","sourceRoot":"","sources":["../src/demo.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,CAAC,EAAE,MAAM,YAAY,CAAC;AACzC,OAAO,MAAM,MAAM,QAAQ,CAAC;AAC5B,MAAM,CAAC,MAAM,EAAE,CAAC;AAEhB,KAAK,UAAU,IAAI;IACjB,MAAM,QAAQ,GAAG,IAAI,QAAQ,CAAC;QAC5B,MAAM,EAAE,OAAO,CAAC,GAAG,CAAC,cAAc,IAAI,cAAc;QACpD,QAAQ,EAAE,KAAK;QACf,OAAO,EAAE,CAAC;QACV,aAAa,EAAE,IAAI;QACnB,kBAAkB,EAAE,IAAI;KACzB,CAAC,CAAC;IAEH,IAAI,CAAC;QACH,MAAM,QAAQ,CAAC,IAAI,EAAE,CAAC;QAEtB,iDAAiD;QACjD,OAAO,CAAC,GAAG,CAAC,cAAc,EAAE,QAAQ,CAAC,IAAI,CAAC,GAAG,EAAE,CAAC,CAAC;QAEjD,MAAM,QAAQ,CAAC,IAAI,CAAC,wBAAwB,CAAC,CAAC;QAE9C,+BAA+B;QAC/B,MAAM,QAAQ,CAAC,GAAG,CAAC,6DAA6D,EAAE,EAAE,OAAO,EAAE,CAAC,EAAE,CAAC,CAAC;QAElG,sCAAsC;QACtC,MAAM,UAAU,GAAG,cAAc,CAAC;QAClC,MAAM,QAAQ,CAAC,GAAG,CAAC,iDAAiD,EAAE;YACpE,SAAS,EAAE,EAAE,IAAI,EAAE,UAAU,EAAE;SAChC,CAAC,CAAC;QAEH,4BAA4B;QAC5B,MAAM,OAAO,GAAG,MAAM,QAAQ,CAAC,OAAO,CACpC,0CAA0C,EAC1C,CAAC,CAAC,MAAM,CAAC;YACP,MAAM,EAAE,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,MAAM,EAAE,CAAC;SAC5B,CAAC,CACH,CAAC;QACF,OAAO,CAAC,GAAG,CAAC,iBAAiB,EAAE,OAAO,CAAC,CAAC;QAExC,sCAAsC;QACtC,MAAM,OAAO,GAAG,MAAM,QAAQ,CAAC,OAAO,CAAC,oCAAoC,CAAC,CAAC;QAC7E,OAAO,CAAC,GAAG,CAAC,qBAAqB,EAAE,OAAO,CAAC,CAAC;IAE9C,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,OAAO,CAAC,KAAK,CAAC,iBAAiB,EAAE,KAAK,CAAC,CAAC;IAC1C,CAAC;YAAS,CAAC;QACT,MAAM,QAAQ,CAAC,KAAK,EAAE,CAAC;IACzB,CAAC;AACH,CAAC;AAED,IAAI,EAAE,CAAC"}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
import type { Page, BrowserContext } from 'playwright';
|
|
2
|
+
import type { ActOptions, ActionResult } from './api/act.js';
|
|
3
|
+
import type { ObserveResult } from './api/observe.js';
|
|
4
|
+
import type { SchemaInput } from './utils/gemini.js';
|
|
5
|
+
import { z } from 'zod';
|
|
6
|
+
export { z };
|
|
7
|
+
export type { ActOptions, ActionResult, ObserveResult };
|
|
8
|
+
export interface SentinelOptions {
|
|
9
|
+
/** Gemini API key */
|
|
10
|
+
apiKey: string;
|
|
11
|
+
/** Run browser in headless mode (default: false) */
|
|
12
|
+
headless?: boolean;
|
|
13
|
+
/** Viewport size (default: 1280x720) */
|
|
14
|
+
viewport?: {
|
|
15
|
+
width: number;
|
|
16
|
+
height: number;
|
|
17
|
+
};
|
|
18
|
+
/**
|
|
19
|
+
* Verbosity level:
|
|
20
|
+
* 0 = silent, 1 = key actions only (default), 2 = full debug
|
|
21
|
+
*/
|
|
22
|
+
verbose?: 0 | 1 | 2;
|
|
23
|
+
/**
|
|
24
|
+
* Enable state caching between calls (default: true).
|
|
25
|
+
* Set to false to always fetch a fresh AOM state.
|
|
26
|
+
*/
|
|
27
|
+
enableCaching?: boolean;
|
|
28
|
+
/**
|
|
29
|
+
* How long (ms) to wait for the DOM to settle after navigation/actions (default: 3000).
|
|
30
|
+
*/
|
|
31
|
+
domSettleTimeoutMs?: number;
|
|
32
|
+
}
|
|
33
|
+
export declare class Sentinel {
|
|
34
|
+
private driver;
|
|
35
|
+
private stateParser;
|
|
36
|
+
private actionEngine;
|
|
37
|
+
private extractionEngine;
|
|
38
|
+
private observationEngine;
|
|
39
|
+
private verifier;
|
|
40
|
+
private gemini;
|
|
41
|
+
private readonly verbose;
|
|
42
|
+
private readonly enableCaching;
|
|
43
|
+
private readonly domSettleTimeoutMs;
|
|
44
|
+
constructor(options: SentinelOptions);
|
|
45
|
+
/** Direct access to the Playwright Page object */
|
|
46
|
+
get page(): Page;
|
|
47
|
+
/** Direct access to the Playwright BrowserContext object */
|
|
48
|
+
get context(): BrowserContext;
|
|
49
|
+
init(): Promise<void>;
|
|
50
|
+
goto(url: string): Promise<void>;
|
|
51
|
+
close(): Promise<void>;
|
|
52
|
+
/**
|
|
53
|
+
* Perform an action on the page described in natural language.
|
|
54
|
+
*
|
|
55
|
+
* @param instruction Natural-language instruction, e.g. "Click the login button"
|
|
56
|
+
* @param options Optional: variables for interpolation, retry count
|
|
57
|
+
*
|
|
58
|
+
* @example
|
|
59
|
+
* await sentinel.act('Fill %email% into the email field', { variables: { email: 'tom@example.com' } });
|
|
60
|
+
*/
|
|
61
|
+
act(instruction: string, options?: ActOptions & {
|
|
62
|
+
retries?: number;
|
|
63
|
+
}): Promise<ActionResult>;
|
|
64
|
+
/**
|
|
65
|
+
* Extract structured data from the current page.
|
|
66
|
+
*
|
|
67
|
+
* @param instruction What to extract, e.g. "Get all product names and prices"
|
|
68
|
+
* @param schema Zod schema or raw JSON Schema describing the expected output
|
|
69
|
+
*
|
|
70
|
+
* @example
|
|
71
|
+
* const data = await sentinel.extract('Get the page title', z.object({ title: z.string() }));
|
|
72
|
+
*/
|
|
73
|
+
extract<T>(instruction: string, schema: SchemaInput<T>): Promise<T>;
|
|
74
|
+
/**
|
|
75
|
+
* Observe the current page and return a list of possible interactions.
|
|
76
|
+
*
|
|
77
|
+
* @param instruction Optional focus hint, e.g. "Find all navigation links"
|
|
78
|
+
*
|
|
79
|
+
* @example
|
|
80
|
+
* const actions = await sentinel.observe('Find login-related elements');
|
|
81
|
+
*/
|
|
82
|
+
observe(instruction?: string): Promise<ObserveResult[]>;
|
|
83
|
+
private log;
|
|
84
|
+
}
|
|
85
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,IAAI,EAAE,cAAc,EAAE,MAAM,YAAY,CAAC;AAKvD,OAAO,KAAK,EAAE,UAAU,EAAE,YAAY,EAAE,MAAM,cAAc,CAAC;AAG7D,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,kBAAkB,CAAC;AAEtD,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAErD,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAGxB,OAAO,EAAE,CAAC,EAAE,CAAC;AACb,YAAY,EAAE,UAAU,EAAE,YAAY,EAAE,aAAa,EAAE,CAAC;AAExD,MAAM,WAAW,eAAe;IAC9B,qBAAqB;IACrB,MAAM,EAAE,MAAM,CAAC;IACf,oDAAoD;IACpD,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,wCAAwC;IACxC,QAAQ,CAAC,EAAE;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAA;KAAE,CAAC;IAC7C;;;OAGG;IACH,OAAO,CAAC,EAAE,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;IACpB;;;OAGG;IACH,aAAa,CAAC,EAAE,OAAO,CAAC;IACxB;;OAEG;IACH,kBAAkB,CAAC,EAAE,MAAM,CAAC;CAC7B;AAED,qBAAa,QAAQ;IACnB,OAAO,CAAC,MAAM,CAAiB;IAC/B,OAAO,CAAC,WAAW,CAA4B;IAC/C,OAAO,CAAC,YAAY,CAA6B;IACjD,OAAO,CAAC,gBAAgB,CAAiC;IACzD,OAAO,CAAC,iBAAiB,CAAkC;IAC3D,OAAO,CAAC,QAAQ,CAAyB;IACzC,OAAO,CAAC,MAAM,CAAgB;IAE9B,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAY;IACpC,OAAO,CAAC,QAAQ,CAAC,aAAa,CAAU;IACxC,OAAO,CAAC,QAAQ,CAAC,kBAAkB,CAAS;gBAEhC,OAAO,EAAE,eAAe;IAcpC,kDAAkD;IAClD,IAAI,IAAI,IAAI,IAAI,CAEf;IAED,4DAA4D;IAC5D,IAAI,OAAO,IAAI,cAAc,CAE5B;IAIK,IAAI;IAcJ,IAAI,CAAC,GAAG,EAAE,MAAM;IAMhB,KAAK;IAOX;;;;;;;;OAQG;IACG,GAAG,CAAC,WAAW,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,UAAU,GAAG;QAAE,OAAO,CAAC,EAAE,MAAM,CAAA;KAAE,GAAG,OAAO,CAAC,YAAY,CAAC;IAsClG;;;;;;;;OAQG;IACG,OAAO,CAAC,CAAC,EAAE,WAAW,EAAE,MAAM,EAAE,MAAM,EAAE,WAAW,CAAC,CAAC,CAAC,GAAG,OAAO,CAAC,CAAC,CAAC;IAOzE;;;;;;;OAOG;IACG,OAAO,CAAC,WAAW,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,aAAa,EAAE,CAAC;IAS7D,OAAO,CAAC,GAAG;CAKZ"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,143 @@
|
|
|
1
|
+
import { SentinelDriver } from './core/driver.js';
|
|
2
|
+
import { StateParser } from './core/state-parser.js';
|
|
3
|
+
import { ActionEngine } from './api/act.js';
|
|
4
|
+
import { ExtractionEngine } from './api/extract.js';
|
|
5
|
+
import { ObservationEngine } from './api/observe.js';
|
|
6
|
+
import { GeminiService } from './utils/gemini.js';
|
|
7
|
+
import { Verifier } from './reliability/verifier.js';
|
|
8
|
+
import { z } from 'zod';
|
|
9
|
+
// Re-export z and types so users can do: import { Sentinel, z } from './index.js'
|
|
10
|
+
export { z };
|
|
11
|
+
export class Sentinel {
|
|
12
|
+
driver;
|
|
13
|
+
stateParser = null;
|
|
14
|
+
actionEngine = null;
|
|
15
|
+
extractionEngine = null;
|
|
16
|
+
observationEngine = null;
|
|
17
|
+
verifier = null;
|
|
18
|
+
gemini;
|
|
19
|
+
verbose;
|
|
20
|
+
enableCaching;
|
|
21
|
+
domSettleTimeoutMs;
|
|
22
|
+
constructor(options) {
|
|
23
|
+
const driverOptions = {
|
|
24
|
+
headless: options.headless ?? false,
|
|
25
|
+
...(options.viewport ? { viewport: options.viewport } : {}),
|
|
26
|
+
};
|
|
27
|
+
this.driver = new SentinelDriver(driverOptions);
|
|
28
|
+
this.gemini = new GeminiService(options.apiKey);
|
|
29
|
+
this.verbose = options.verbose ?? 1;
|
|
30
|
+
this.enableCaching = options.enableCaching ?? true;
|
|
31
|
+
this.domSettleTimeoutMs = options.domSettleTimeoutMs ?? 3000;
|
|
32
|
+
}
|
|
33
|
+
// ─── Playwright passthrough ───────────────────────────────────────────────
|
|
34
|
+
/** Direct access to the Playwright Page object */
|
|
35
|
+
get page() {
|
|
36
|
+
return this.driver.getPage();
|
|
37
|
+
}
|
|
38
|
+
/** Direct access to the Playwright BrowserContext object */
|
|
39
|
+
get context() {
|
|
40
|
+
return this.driver.getContext();
|
|
41
|
+
}
|
|
42
|
+
// ─── Lifecycle ────────────────────────────────────────────────────────────
|
|
43
|
+
async init() {
|
|
44
|
+
await this.driver.initialize();
|
|
45
|
+
const page = this.driver.getPage();
|
|
46
|
+
const cdp = this.driver.getCDPSession();
|
|
47
|
+
this.stateParser = new StateParser(page, cdp);
|
|
48
|
+
this.actionEngine = new ActionEngine(page, this.stateParser, this.gemini);
|
|
49
|
+
this.extractionEngine = new ExtractionEngine(page, this.stateParser, this.gemini);
|
|
50
|
+
this.observationEngine = new ObservationEngine(page, this.stateParser, this.gemini);
|
|
51
|
+
this.verifier = new Verifier(page, this.stateParser, this.gemini);
|
|
52
|
+
this.log(1, '🚀 Sentinel initialized');
|
|
53
|
+
}
|
|
54
|
+
async goto(url) {
|
|
55
|
+
await this.driver.goto(url);
|
|
56
|
+
this.stateParser?.invalidateCache();
|
|
57
|
+
this.log(1, `🌐 Navigated to ${url}`);
|
|
58
|
+
}
|
|
59
|
+
async close() {
|
|
60
|
+
await this.driver.close();
|
|
61
|
+
this.log(1, '🔒 Sentinel closed');
|
|
62
|
+
}
|
|
63
|
+
// ─── Core API ─────────────────────────────────────────────────────────────
|
|
64
|
+
/**
|
|
65
|
+
* Perform an action on the page described in natural language.
|
|
66
|
+
*
|
|
67
|
+
* @param instruction Natural-language instruction, e.g. "Click the login button"
|
|
68
|
+
* @param options Optional: variables for interpolation, retry count
|
|
69
|
+
*
|
|
70
|
+
* @example
|
|
71
|
+
* await sentinel.act('Fill %email% into the email field', { variables: { email: 'tom@example.com' } });
|
|
72
|
+
*/
|
|
73
|
+
async act(instruction, options) {
|
|
74
|
+
if (!this.actionEngine || !this.stateParser || !this.verifier) {
|
|
75
|
+
throw new Error('Sentinel not initialized. Call init() first.');
|
|
76
|
+
}
|
|
77
|
+
if (!this.enableCaching)
|
|
78
|
+
this.stateParser.invalidateCache();
|
|
79
|
+
const retries = options?.retries ?? 2;
|
|
80
|
+
let currentAttempt = 0;
|
|
81
|
+
while (currentAttempt <= retries) {
|
|
82
|
+
const stateBefore = await this.stateParser.parse();
|
|
83
|
+
const result = await this.actionEngine.act(instruction, options);
|
|
84
|
+
if (!result.success) {
|
|
85
|
+
this.log(1, `⚠️ Action failed: ${result.message}. Attempt ${currentAttempt + 1}/${retries + 1}`);
|
|
86
|
+
currentAttempt++;
|
|
87
|
+
continue;
|
|
88
|
+
}
|
|
89
|
+
const stateAfter = await this.stateParser.parse();
|
|
90
|
+
const verification = await this.verifier.verifyAction(instruction, stateBefore, stateAfter);
|
|
91
|
+
if (verification.success && verification.confidence > 0.7) {
|
|
92
|
+
this.log(1, `✅ "${instruction}" verified (confidence: ${(verification.confidence * 100).toFixed(0)}%)`);
|
|
93
|
+
return { success: true, message: verification.message, ...(result.action ? { action: result.action } : {}) };
|
|
94
|
+
}
|
|
95
|
+
else {
|
|
96
|
+
this.log(1, `⚠️ Verification weak (${(verification.confidence * 100).toFixed(0)}%): ${verification.message}. ` +
|
|
97
|
+
`Retrying... (${currentAttempt + 1}/${retries + 1})`);
|
|
98
|
+
currentAttempt++;
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
return { success: false, message: `Failed to execute "${instruction}" after ${retries + 1} attempts.` };
|
|
102
|
+
}
|
|
103
|
+
/**
|
|
104
|
+
* Extract structured data from the current page.
|
|
105
|
+
*
|
|
106
|
+
* @param instruction What to extract, e.g. "Get all product names and prices"
|
|
107
|
+
* @param schema Zod schema or raw JSON Schema describing the expected output
|
|
108
|
+
*
|
|
109
|
+
* @example
|
|
110
|
+
* const data = await sentinel.extract('Get the page title', z.object({ title: z.string() }));
|
|
111
|
+
*/
|
|
112
|
+
async extract(instruction, schema) {
|
|
113
|
+
if (!this.extractionEngine)
|
|
114
|
+
throw new Error('Sentinel not initialized. Call init() first.');
|
|
115
|
+
if (!this.enableCaching)
|
|
116
|
+
this.stateParser?.invalidateCache();
|
|
117
|
+
this.log(2, `🔍 Extracting: "${instruction}"`);
|
|
118
|
+
return await this.extractionEngine.extract(instruction, schema);
|
|
119
|
+
}
|
|
120
|
+
/**
|
|
121
|
+
* Observe the current page and return a list of possible interactions.
|
|
122
|
+
*
|
|
123
|
+
* @param instruction Optional focus hint, e.g. "Find all navigation links"
|
|
124
|
+
*
|
|
125
|
+
* @example
|
|
126
|
+
* const actions = await sentinel.observe('Find login-related elements');
|
|
127
|
+
*/
|
|
128
|
+
async observe(instruction) {
|
|
129
|
+
if (!this.observationEngine)
|
|
130
|
+
throw new Error('Sentinel not initialized. Call init() first.');
|
|
131
|
+
if (!this.enableCaching)
|
|
132
|
+
this.stateParser?.invalidateCache();
|
|
133
|
+
this.log(2, `👁️ Observing${instruction ? `: "${instruction}"` : ''}`);
|
|
134
|
+
return await this.observationEngine.observe(instruction);
|
|
135
|
+
}
|
|
136
|
+
// ─── Internal helpers ─────────────────────────────────────────────────────
|
|
137
|
+
log(level, message) {
|
|
138
|
+
if (this.verbose >= level) {
|
|
139
|
+
console.log(`[Sentinel] ${message}`);
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"list-all.d.ts","sourceRoot":"","sources":["../src/list-all.ts"],"names":[],"mappings":""}
|
package/dist/list-all.js
ADDED
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import dotenv from 'dotenv';
|
|
2
|
+
dotenv.config();
|
|
3
|
+
async function listAllModels() {
|
|
4
|
+
const key = process.env.GEMINI_API_KEY;
|
|
5
|
+
const versions = ['v1', 'v1beta', 'v1alpha'];
|
|
6
|
+
for (const v of versions) {
|
|
7
|
+
try {
|
|
8
|
+
console.log(`Checking models in ${v}...`);
|
|
9
|
+
const resp = await fetch(`https://generativelanguage.googleapis.com/${v}/models?key=${key}`);
|
|
10
|
+
const data = await resp.json();
|
|
11
|
+
if (data.models) {
|
|
12
|
+
data.models.forEach((m) => {
|
|
13
|
+
if (m.name.includes('flash') || m.name.includes('3')) {
|
|
14
|
+
console.log(`- ${m.name} (${m.displayName})`);
|
|
15
|
+
}
|
|
16
|
+
});
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
catch (e) {
|
|
20
|
+
console.log(`Error in ${v}: ${e.message}`);
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
listAllModels();
|
|
25
|
+
//# sourceMappingURL=list-all.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"list-all.js","sourceRoot":"","sources":["../src/list-all.ts"],"names":[],"mappings":"AAAA,OAAO,MAAM,MAAM,QAAQ,CAAC;AAC5B,MAAM,CAAC,MAAM,EAAE,CAAC;AAEhB,KAAK,UAAU,aAAa;IAC1B,MAAM,GAAG,GAAG,OAAO,CAAC,GAAG,CAAC,cAAc,CAAC;IACvC,MAAM,QAAQ,GAAG,CAAC,IAAI,EAAE,QAAQ,EAAE,SAAS,CAAC,CAAC;IAE7C,KAAK,MAAM,CAAC,IAAI,QAAQ,EAAE,CAAC;QACzB,IAAI,CAAC;YACH,OAAO,CAAC,GAAG,CAAC,sBAAsB,CAAC,KAAK,CAAC,CAAC;YAC1C,MAAM,IAAI,GAAG,MAAM,KAAK,CAAC,6CAA6C,CAAC,eAAe,GAAG,EAAE,CAAC,CAAC;YAC7F,MAAM,IAAI,GAAG,MAAM,IAAI,CAAC,IAAI,EAAE,CAAC;YAC/B,IAAI,IAAI,CAAC,MAAM,EAAE,CAAC;gBAChB,IAAI,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,CAAM,EAAE,EAAE;oBAC7B,IAAI,CAAC,CAAC,IAAI,CAAC,QAAQ,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,EAAE,CAAC;wBACrD,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC,IAAI,KAAK,CAAC,CAAC,WAAW,GAAG,CAAC,CAAC;oBAChD,CAAC;gBACH,CAAC,CAAC,CAAC;YACL,CAAC;QACH,CAAC;QAAC,OAAO,CAAM,EAAE,CAAC;YAChB,OAAO,CAAC,GAAG,CAAC,YAAY,CAAC,KAAK,CAAC,CAAC,OAAO,EAAE,CAAC,CAAC;QAC7C,CAAC;IACH,CAAC;AACH,CAAC;AAED,aAAa,EAAE,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"list-models.d.ts","sourceRoot":"","sources":["../src/list-models.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { GoogleGenerativeAI } from "@google/generative-ai";
|
|
2
|
+
import dotenv from 'dotenv';
|
|
3
|
+
dotenv.config();
|
|
4
|
+
async function listModels() {
|
|
5
|
+
const genAI = new GoogleGenerativeAI(process.env.GEMINI_API_KEY || "");
|
|
6
|
+
const result = await fetch(`https://generativelanguage.googleapis.com/v1beta/models?key=${process.env.GEMINI_API_KEY}`);
|
|
7
|
+
const models = await result.json();
|
|
8
|
+
console.log(JSON.stringify(models, null, 2));
|
|
9
|
+
}
|
|
10
|
+
listModels();
|
|
11
|
+
//# sourceMappingURL=list-models.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"list-models.js","sourceRoot":"","sources":["../src/list-models.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,kBAAkB,EAAE,MAAM,uBAAuB,CAAC;AAC3D,OAAO,MAAM,MAAM,QAAQ,CAAC;AAC5B,MAAM,CAAC,MAAM,EAAE,CAAC;AAEhB,KAAK,UAAU,UAAU;IACvB,MAAM,KAAK,GAAG,IAAI,kBAAkB,CAAC,OAAO,CAAC,GAAG,CAAC,cAAc,IAAI,EAAE,CAAC,CAAC;IACvE,MAAM,MAAM,GAAG,MAAM,KAAK,CAAC,+DAA+D,OAAO,CAAC,GAAG,CAAC,cAAc,EAAE,CAAC,CAAC;IACxH,MAAM,MAAM,GAAG,MAAM,MAAM,CAAC,IAAI,EAAE,CAAC;IACnC,OAAO,CAAC,GAAG,CAAC,IAAI,CAAC,SAAS,CAAC,MAAM,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC,CAAC;AAC/C,CAAC;AAED,UAAU,EAAE,CAAC"}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import type { Page } from 'playwright';
|
|
2
|
+
import { StateParser } from '../core/state-parser.js';
|
|
3
|
+
import type { SimplifiedState } from '../core/state-parser.js';
|
|
4
|
+
import { GeminiService } from '../utils/gemini.js';
|
|
5
|
+
export interface VerificationResult {
|
|
6
|
+
done: boolean;
|
|
7
|
+
success: boolean;
|
|
8
|
+
message: string;
|
|
9
|
+
confidence: number;
|
|
10
|
+
}
|
|
11
|
+
export declare class Verifier {
|
|
12
|
+
private page;
|
|
13
|
+
private stateParser;
|
|
14
|
+
private gemini;
|
|
15
|
+
constructor(page: Page, stateParser: StateParser, gemini: GeminiService);
|
|
16
|
+
verifyAction(action: string, stateBefore: SimplifiedState, stateAfter: SimplifiedState): Promise<VerificationResult>;
|
|
17
|
+
}
|
|
18
|
+
//# sourceMappingURL=verifier.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"verifier.d.ts","sourceRoot":"","sources":["../../src/reliability/verifier.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,YAAY,CAAC;AACvC,OAAO,EAAE,WAAW,EAAE,MAAM,yBAAyB,CAAC;AACtD,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,yBAAyB,CAAC;AAC/D,OAAO,EAAE,aAAa,EAAE,MAAM,oBAAoB,CAAC;AAEnD,MAAM,WAAW,kBAAkB;IACjC,IAAI,EAAE,OAAO,CAAC;IACd,OAAO,EAAE,OAAO,CAAC;IACjB,OAAO,EAAE,MAAM,CAAC;IAChB,UAAU,EAAE,MAAM,CAAC;CACpB;AAaD,qBAAa,QAAQ;IAEjB,OAAO,CAAC,IAAI;IACZ,OAAO,CAAC,WAAW;IACnB,OAAO,CAAC,MAAM;gBAFN,IAAI,EAAE,IAAI,EACV,WAAW,EAAE,WAAW,EACxB,MAAM,EAAE,aAAa;IAGzB,YAAY,CAChB,MAAM,EAAE,MAAM,EACd,WAAW,EAAE,eAAe,EAC5B,UAAU,EAAE,eAAe,GAC1B,OAAO,CAAC,kBAAkB,CAAC;CAkD/B"}
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
import { StateParser } from '../core/state-parser.js';
|
|
2
|
+
import { GeminiService } from '../utils/gemini.js';
|
|
3
|
+
/** Returns a short, meaningful summary of a state for comparison */
|
|
4
|
+
function summarizeState(state) {
|
|
5
|
+
return {
|
|
6
|
+
url: state.url,
|
|
7
|
+
title: state.title,
|
|
8
|
+
elementCount: state.elements.length,
|
|
9
|
+
// Include top-25 element names for semantic diff (not just count)
|
|
10
|
+
elementNames: state.elements.slice(0, 25).map(e => `${e.role}: ${e.name}`),
|
|
11
|
+
};
|
|
12
|
+
}
|
|
13
|
+
export class Verifier {
|
|
14
|
+
page;
|
|
15
|
+
stateParser;
|
|
16
|
+
gemini;
|
|
17
|
+
constructor(page, stateParser, gemini) {
|
|
18
|
+
this.page = page;
|
|
19
|
+
this.stateParser = stateParser;
|
|
20
|
+
this.gemini = gemini;
|
|
21
|
+
}
|
|
22
|
+
async verifyAction(action, stateBefore, stateAfter) {
|
|
23
|
+
// Fast path: URL changed → navigation happened → very likely success
|
|
24
|
+
if (stateBefore.url !== stateAfter.url) {
|
|
25
|
+
console.log(`[Verifier] URL changed: ${stateBefore.url} → ${stateAfter.url}. Auto-success.`);
|
|
26
|
+
return {
|
|
27
|
+
done: true,
|
|
28
|
+
success: true,
|
|
29
|
+
message: `Page navigated to ${stateAfter.url}`,
|
|
30
|
+
confidence: 0.95,
|
|
31
|
+
};
|
|
32
|
+
}
|
|
33
|
+
const prompt = `
|
|
34
|
+
I performed an action on a web page: "${action}"
|
|
35
|
+
|
|
36
|
+
State BEFORE the action:
|
|
37
|
+
${JSON.stringify(summarizeState(stateBefore), null, 2)}
|
|
38
|
+
|
|
39
|
+
State AFTER the action:
|
|
40
|
+
${JSON.stringify(summarizeState(stateAfter), null, 2)}
|
|
41
|
+
|
|
42
|
+
Based on the semantic differences between these states (URL, title, element changes),
|
|
43
|
+
did the action achieve its intended goal?
|
|
44
|
+
Rate your confidence between 0.0 and 1.0.
|
|
45
|
+
`;
|
|
46
|
+
const schema = {
|
|
47
|
+
type: "object",
|
|
48
|
+
properties: {
|
|
49
|
+
success: { type: "boolean" },
|
|
50
|
+
confidence: { type: "number" },
|
|
51
|
+
explanation: { type: "string" }
|
|
52
|
+
},
|
|
53
|
+
required: ["success", "confidence", "explanation"]
|
|
54
|
+
};
|
|
55
|
+
const result = await this.gemini.generateStructuredData(prompt, schema);
|
|
56
|
+
return {
|
|
57
|
+
done: true,
|
|
58
|
+
success: result.success,
|
|
59
|
+
message: result.explanation,
|
|
60
|
+
confidence: result.confidence,
|
|
61
|
+
};
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
//# sourceMappingURL=verifier.js.map
|