@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.
Files changed (63) hide show
  1. package/LICENSE +15 -0
  2. package/README.md +122 -0
  3. package/dist/api/act.d.ts +22 -0
  4. package/dist/api/act.d.ts.map +1 -0
  5. package/dist/api/act.js +116 -0
  6. package/dist/api/act.js.map +1 -0
  7. package/dist/api/extract.d.ts +13 -0
  8. package/dist/api/extract.d.ts.map +1 -0
  9. package/dist/api/extract.js +52 -0
  10. package/dist/api/extract.js.map +1 -0
  11. package/dist/api/observe.d.ts +17 -0
  12. package/dist/api/observe.d.ts.map +1 -0
  13. package/dist/api/observe.js +56 -0
  14. package/dist/api/observe.js.map +1 -0
  15. package/dist/complex-test.d.ts +2 -0
  16. package/dist/complex-test.d.ts.map +1 -0
  17. package/dist/complex-test.js +58 -0
  18. package/dist/complex-test.js.map +1 -0
  19. package/dist/core/driver.d.ts +23 -0
  20. package/dist/core/driver.d.ts.map +1 -0
  21. package/dist/core/driver.js +61 -0
  22. package/dist/core/driver.js.map +1 -0
  23. package/dist/core/state-parser.d.ts +38 -0
  24. package/dist/core/state-parser.d.ts.map +1 -0
  25. package/dist/core/state-parser.js +103 -0
  26. package/dist/core/state-parser.js.map +1 -0
  27. package/dist/debug-aom.d.ts +2 -0
  28. package/dist/debug-aom.d.ts.map +1 -0
  29. package/dist/debug-aom.js +22 -0
  30. package/dist/debug-aom.js.map +1 -0
  31. package/dist/demo.d.ts +2 -0
  32. package/dist/demo.d.ts.map +1 -0
  33. package/dist/demo.js +41 -0
  34. package/dist/demo.js.map +1 -0
  35. package/dist/index.d.ts +85 -0
  36. package/dist/index.d.ts.map +1 -0
  37. package/dist/index.js +143 -0
  38. package/dist/index.js.map +1 -0
  39. package/dist/list-all.d.ts +2 -0
  40. package/dist/list-all.d.ts.map +1 -0
  41. package/dist/list-all.js +25 -0
  42. package/dist/list-all.js.map +1 -0
  43. package/dist/list-models.d.ts +2 -0
  44. package/dist/list-models.d.ts.map +1 -0
  45. package/dist/list-models.js +11 -0
  46. package/dist/list-models.js.map +1 -0
  47. package/dist/reliability/verifier.d.ts +18 -0
  48. package/dist/reliability/verifier.d.ts.map +1 -0
  49. package/dist/reliability/verifier.js +64 -0
  50. package/dist/reliability/verifier.js.map +1 -0
  51. package/dist/test-versions.d.ts +2 -0
  52. package/dist/test-versions.d.ts.map +1 -0
  53. package/dist/test-versions.js +33 -0
  54. package/dist/test-versions.js.map +1 -0
  55. package/dist/utils/gemini.d.ts +12 -0
  56. package/dist/utils/gemini.d.ts.map +1 -0
  57. package/dist/utils/gemini.js +103 -0
  58. package/dist/utils/gemini.js.map +1 -0
  59. package/dist/whatsapp-test.d.ts +2 -0
  60. package/dist/whatsapp-test.d.ts.map +1 -0
  61. package/dist/whatsapp-test.js +56 -0
  62. package/dist/whatsapp-test.js.map +1 -0
  63. 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,2 @@
1
+ export {};
2
+ //# sourceMappingURL=debug-aom.d.ts.map
@@ -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,2 @@
1
+ export {};
2
+ //# sourceMappingURL=demo.d.ts.map
@@ -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
@@ -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"}
@@ -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,2 @@
1
+ export {};
2
+ //# sourceMappingURL=list-all.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"list-all.d.ts","sourceRoot":"","sources":["../src/list-all.ts"],"names":[],"mappings":""}
@@ -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,2 @@
1
+ export {};
2
+ //# sourceMappingURL=list-models.d.ts.map
@@ -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