@xpert-ai/chatkit-host-automation 0.3.0 → 0.3.2

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/README.md CHANGED
@@ -23,3 +23,12 @@ The first version exposes a fixed tool allow-list:
23
23
  - `host_page_select`
24
24
  - `host_page_scroll`
25
25
  - `host_page_navigate`
26
+ - `host_page_hover`
27
+ - `host_page_focus`
28
+ - `host_page_pointer`
29
+ - `host_page_screenshot`
30
+ - `host_page_wait_for`
31
+
32
+ Plain web hosts use the DOM fallback executor. The Chrome browser extension can
33
+ upgrade the same client tools through the Chrome DevTools Protocol for richer
34
+ snapshots and browser-level input.
package/dist/index.d.ts CHANGED
@@ -2,7 +2,7 @@ import { ClientToolMessageInput } from '../../chatkit/src/index.ts';
2
2
 
3
3
  export declare function createHostPageAutomationClientToolHandler(options?: HostPageAutomationOptions): HostPageAutomationClientToolHandler;
4
4
 
5
- export declare const HOST_PAGE_AUTOMATION_TOOL_NAMES: readonly ["host_page_snapshot", "host_page_click", "host_page_fill", "host_page_press", "host_page_select", "host_page_scroll", "host_page_navigate"];
5
+ export declare const HOST_PAGE_AUTOMATION_TOOL_NAMES: readonly ["host_page_snapshot", "host_page_click", "host_page_fill", "host_page_press", "host_page_select", "host_page_scroll", "host_page_navigate", "host_page_hover", "host_page_focus", "host_page_pointer", "host_page_screenshot", "host_page_wait_for"];
6
6
 
7
7
  export declare type HostPageAutomationClientToolCall = {
8
8
  name: string;
@@ -19,17 +19,38 @@ export declare type HostPageAutomationElementSnapshot = {
19
19
  role?: string;
20
20
  name?: string;
21
21
  text?: string;
22
+ nearbyText?: string[];
23
+ testId?: string;
22
24
  value?: string;
23
25
  placeholder?: string;
24
26
  selector?: string;
25
27
  disabled?: boolean;
28
+ enabled?: boolean;
26
29
  checked?: boolean;
30
+ visible?: boolean;
31
+ actionable?: boolean;
27
32
  rect: {
28
33
  x: number;
29
34
  y: number;
30
35
  width: number;
31
36
  height: number;
32
37
  };
38
+ center?: {
39
+ x: number;
40
+ y: number;
41
+ };
42
+ hitTarget?: {
43
+ tag: string;
44
+ role?: string;
45
+ name?: string;
46
+ selector?: string;
47
+ };
48
+ hitStack?: Array<{
49
+ tag: string;
50
+ role?: string;
51
+ name?: string;
52
+ selector?: string;
53
+ }>;
33
54
  };
34
55
 
35
56
  export declare class HostPageAutomationExecutor {
@@ -40,7 +61,11 @@ export declare class HostPageAutomationExecutor {
40
61
  execute(name: HostPageAutomationToolName, params?: Record<string, unknown>): Promise<unknown>;
41
62
  snapshot(): HostPageSnapshot;
42
63
  private getRoot;
64
+ private getNavigationState;
65
+ private getFrameState;
43
66
  private snapshotElement;
67
+ private snapshotElementWithoutRef;
68
+ private createElementSnapshot;
44
69
  private resolveElement;
45
70
  private click;
46
71
  private fill;
@@ -48,6 +73,12 @@ export declare class HostPageAutomationExecutor {
48
73
  private select;
49
74
  private scroll;
50
75
  private navigate;
76
+ private hover;
77
+ private focus;
78
+ private pointer;
79
+ private screenshot;
80
+ private waitFor;
81
+ private tryResolveElement;
51
82
  private describeElement;
52
83
  }
53
84
 
@@ -62,14 +93,55 @@ export declare type HostPageAutomationToolName = (typeof HOST_PAGE_AUTOMATION_TO
62
93
  export declare type HostPageSnapshot = {
63
94
  url: string;
64
95
  title: string;
96
+ capabilities?: {
97
+ cdp?: boolean;
98
+ realInput?: boolean;
99
+ screenshot?: boolean;
100
+ accessibility?: boolean;
101
+ networkState?: boolean;
102
+ };
65
103
  viewport: {
66
104
  width: number;
67
105
  height: number;
106
+ devicePixelRatio?: number;
68
107
  };
69
108
  scroll: {
70
109
  x: number;
71
110
  y: number;
72
111
  };
112
+ page?: {
113
+ readyState?: string;
114
+ visibilityState?: string;
115
+ focusedElement?: HostPageAutomationElementSnapshot;
116
+ selection?: string;
117
+ };
118
+ navigation?: {
119
+ type?: string;
120
+ duration?: number;
121
+ domContentLoaded?: number;
122
+ loadEventEnd?: number;
123
+ };
124
+ frames?: Array<{
125
+ url?: string;
126
+ title?: string;
127
+ sameOrigin: boolean;
128
+ rect?: {
129
+ x: number;
130
+ y: number;
131
+ width: number;
132
+ height: number;
133
+ };
134
+ }>;
135
+ accessibility?: Array<{
136
+ ref?: string;
137
+ role?: string;
138
+ name?: string;
139
+ value?: string;
140
+ disabled?: boolean;
141
+ checked?: boolean;
142
+ expanded?: boolean;
143
+ focused?: boolean;
144
+ }>;
73
145
  elements: HostPageAutomationElementSnapshot[];
74
146
  };
75
147
 
package/dist/index.js CHANGED
@@ -1,14 +1,19 @@
1
- const S = [
1
+ const z = [
2
2
  "host_page_snapshot",
3
3
  "host_page_click",
4
4
  "host_page_fill",
5
5
  "host_page_press",
6
6
  "host_page_select",
7
7
  "host_page_scroll",
8
- "host_page_navigate"
9
- ], H = new Set(
10
- S
11
- ), k = [
8
+ "host_page_navigate",
9
+ "host_page_hover",
10
+ "host_page_focus",
11
+ "host_page_pointer",
12
+ "host_page_screenshot",
13
+ "host_page_wait_for"
14
+ ], G = new Set(
15
+ z
16
+ ), K = [
12
17
  "a[href]",
13
18
  "button",
14
19
  "input",
@@ -20,52 +25,64 @@ const S = [
20
25
  '[contenteditable=""]',
21
26
  '[contenteditable="true"]',
22
27
  "[aria-label]",
23
- "[data-testid]"
24
- ].join(","), x = 100;
25
- function N(t) {
26
- return H.has(t);
28
+ "[data-testid]",
29
+ "[data-test-id]",
30
+ "[data-qa]"
31
+ ].join(","), D = 100, J = 1e4, Q = 6e4;
32
+ function Z(t) {
33
+ return G.has(t);
27
34
  }
28
- function f(t) {
35
+ function g(t) {
29
36
  return t instanceof Document ? t : t.ownerDocument;
30
37
  }
31
- function p(t) {
32
- return f(t).defaultView ?? window;
38
+ function N(t) {
39
+ return g(t).defaultView ?? window;
33
40
  }
34
- function u(t) {
41
+ function h(t) {
35
42
  return typeof t == "object" && t !== null && !Array.isArray(t) ? t : {};
36
43
  }
37
- function g(t, e) {
44
+ function R(t, e) {
38
45
  if (typeof t != "string" || t.trim() === "")
39
46
  throw new Error(`${e} must be a non-empty string.`);
40
47
  return t;
41
48
  }
42
- function _(t) {
49
+ function p(t) {
43
50
  return typeof t == "string" && t.trim() ? t : void 0;
44
51
  }
45
- function v(t, e) {
52
+ function tt(t) {
53
+ return typeof t == "number" && Number.isInteger(t) ? t : void 0;
54
+ }
55
+ function M(t, e) {
46
56
  if (typeof t != "number" || !Number.isFinite(t))
47
57
  throw new Error(`${e} must be a finite number.`);
48
58
  return t;
49
59
  }
50
- function h(t) {
60
+ function T(t) {
51
61
  return typeof t == "number" && Number.isFinite(t) ? t : void 0;
52
62
  }
53
- function R(t) {
63
+ function F(t, e) {
64
+ return typeof t == "string" && e.includes(t) ? t : void 0;
65
+ }
66
+ function et(t) {
54
67
  if (typeof t == "string")
55
68
  return [t];
56
69
  if (Array.isArray(t) && t.every((e) => typeof e == "string"))
57
70
  return t;
58
71
  throw new Error("value or values must be a string or an array of strings.");
59
72
  }
60
- function y(t) {
73
+ function v(t) {
61
74
  const e = (t.textContent ?? "").replace(/\s+/g, " ").trim();
62
75
  return e ? e.slice(0, 160) : void 0;
63
76
  }
64
- function C(t) {
77
+ function V(t) {
78
+ const e = Array.from(t.childNodes).filter((n) => n.nodeType === Node.TEXT_NODE).map((n) => n.textContent ?? "").join(" ").replace(/\s+/g, " ").trim();
79
+ return e ? e.slice(0, 160) : void 0;
80
+ }
81
+ function nt(t) {
65
82
  if (t instanceof HTMLInputElement || t instanceof HTMLTextAreaElement || t instanceof HTMLSelectElement)
66
83
  return t.value;
67
84
  }
68
- function L(t) {
85
+ function E(t) {
69
86
  const e = t.getAttribute("aria-label");
70
87
  if (e != null && e.trim())
71
88
  return e.trim();
@@ -73,13 +90,54 @@ function L(t) {
73
90
  if (n != null && n.trim())
74
91
  return n.trim();
75
92
  if (t instanceof HTMLInputElement) {
76
- const r = Array.from(t.labels ?? []).map((o) => y(o)).filter(Boolean);
93
+ const r = Array.from(t.labels ?? []).map((c) => v(c)).filter(Boolean);
77
94
  if (r.length > 0)
78
95
  return r.join(" ").slice(0, 160);
79
96
  }
80
- return y(t);
97
+ const o = X(t)[0];
98
+ return o || v(t);
81
99
  }
82
- function A(t) {
100
+ function ot(t) {
101
+ if (!(t instanceof HTMLElement || t instanceof SVGElement) || !m(t) || t.matches(
102
+ 'input,textarea,select,button,a[href],[role="button"],[role="link"]'
103
+ ))
104
+ return !1;
105
+ const e = V(t) ?? v(t);
106
+ return !!(e && e.length <= 120);
107
+ }
108
+ function X(t) {
109
+ const e = t.ownerDocument, n = e.body;
110
+ if (!n)
111
+ return [];
112
+ const o = t.getBoundingClientRect();
113
+ if (o.width <= 0 || o.height <= 0)
114
+ return [];
115
+ const r = o.top + o.height / 2, c = o.left + o.width / 2, s = [], i = e.createTreeWalker(n, NodeFilter.SHOW_ELEMENT);
116
+ let a = i.nextNode();
117
+ for (; a; ) {
118
+ const l = a;
119
+ if (l !== t && ot(l)) {
120
+ const u = l.getBoundingClientRect(), f = V(l) ?? v(l);
121
+ if (f) {
122
+ const b = u.top + u.height / 2, w = u.left + u.width / 2, y = u.right <= o.left + 12 && Math.abs(b - r) <= Math.max(28, o.height * 1.25), S = u.bottom <= o.top + 8 && o.top - u.bottom <= 80 && w >= o.left - 80 && w <= o.right + 80;
123
+ if (y || S) {
124
+ const j = y ? o.left - u.right + Math.abs(b - r) : o.top - u.bottom + Math.abs(w - c);
125
+ s.push({
126
+ text: f,
127
+ score: j + (y ? 0 : 100)
128
+ });
129
+ }
130
+ }
131
+ }
132
+ a = i.nextNode();
133
+ }
134
+ const d = /* @__PURE__ */ new Set();
135
+ return s.sort((l, u) => l.score - u.score).map((l) => l.text).filter((l) => d.has(l) ? !1 : (d.add(l), !0)).slice(0, 4);
136
+ }
137
+ function rt(t) {
138
+ return t.getAttribute("data-testid") ?? t.getAttribute("data-test-id") ?? t.getAttribute("data-qa") ?? void 0;
139
+ }
140
+ function x(t) {
83
141
  const e = t.getAttribute("role");
84
142
  if (e != null && e.trim())
85
143
  return e.trim();
@@ -103,94 +161,221 @@ function A(t) {
103
161
  return "textbox";
104
162
  }
105
163
  }
106
- function O(t) {
164
+ function q(t) {
107
165
  return t instanceof HTMLButtonElement || t instanceof HTMLInputElement || t instanceof HTMLSelectElement || t instanceof HTMLTextAreaElement ? t.disabled || void 0 : t.getAttribute("aria-disabled") === "true" || void 0;
108
166
  }
109
- function I(t) {
167
+ function it(t) {
110
168
  return t instanceof HTMLInputElement && (t.type === "checkbox" || t.type === "radio") ? t.checked : void 0;
111
169
  }
112
- function D(t) {
170
+ function st(t) {
171
+ const e = k(t);
172
+ if (!(e.width <= 0 || e.height <= 0))
173
+ return {
174
+ x: e.x + e.width / 2,
175
+ y: e.y + e.height / 2
176
+ };
177
+ }
178
+ function $(t) {
179
+ return {
180
+ tag: t.tagName.toLowerCase(),
181
+ role: x(t),
182
+ name: E(t),
183
+ selector: P(t)
184
+ };
185
+ }
186
+ function m(t) {
113
187
  if (!(t instanceof HTMLElement || t instanceof SVGElement) || t instanceof HTMLElement && t.hidden)
114
188
  return !1;
115
189
  const e = t.ownerDocument.defaultView, n = e == null ? void 0 : e.getComputedStyle(t);
116
190
  return !(n && (n.display === "none" || n.visibility === "hidden" || n.opacity === "0"));
117
191
  }
118
- function $(t) {
119
- return t.matches(k) && D(t);
192
+ function U(t) {
193
+ return t.matches(K) && m(t);
120
194
  }
121
- function P(t) {
122
- var s, i;
195
+ function ct(t) {
196
+ return q(t) !== !0;
197
+ }
198
+ function at(t) {
199
+ var n;
200
+ if (U(t))
201
+ return !0;
202
+ if (!(t instanceof HTMLElement))
203
+ return !1;
204
+ const e = (n = t.ownerDocument.defaultView) == null ? void 0 : n.getComputedStyle(t);
205
+ return ((e == null ? void 0 : e.cursor) === "pointer" || !!t.onclick) && m(t);
206
+ }
207
+ function k(t) {
208
+ var c, s;
123
209
  const e = t.getBoundingClientRect();
124
- let n = e.left, r = e.top, o = null;
210
+ let n = e.left, o = e.top, r = null;
125
211
  try {
126
- o = ((s = t.ownerDocument.defaultView) == null ? void 0 : s.frameElement) ?? null;
212
+ r = ((c = t.ownerDocument.defaultView) == null ? void 0 : c.frameElement) ?? null;
127
213
  } catch {
128
- o = null;
214
+ r = null;
129
215
  }
130
- for (; o; ) {
131
- const c = o.getBoundingClientRect();
132
- n += c.left, r += c.top;
216
+ for (; r; ) {
217
+ const i = r.getBoundingClientRect();
218
+ n += i.left, o += i.top;
133
219
  try {
134
- o = ((i = o.ownerDocument.defaultView) == null ? void 0 : i.frameElement) ?? null;
220
+ r = ((s = r.ownerDocument.defaultView) == null ? void 0 : s.frameElement) ?? null;
135
221
  } catch {
136
- o = null;
222
+ r = null;
137
223
  }
138
224
  }
139
225
  return {
140
226
  x: n,
141
- y: r,
227
+ y: o,
142
228
  width: e.width,
143
229
  height: e.height
144
230
  };
145
231
  }
146
- function M(t) {
232
+ function P(t) {
147
233
  const e = (o) => typeof CSS < "u" && typeof CSS.escape == "function" ? CSS.escape(o) : o.replaceAll("\\", "\\\\").replaceAll('"', '\\"');
148
234
  if (t.id)
149
235
  return `#${e(t.id)}`;
150
- const n = t.getAttribute("data-testid");
151
- if (n)
152
- return `[data-testid="${e(n)}"]`;
153
- const r = t.getAttribute("name");
154
- return r ? `${t.tagName.toLowerCase()}[name="${e(
155
- r
236
+ for (const o of ["data-testid", "data-test-id", "data-qa"]) {
237
+ const r = t.getAttribute(o);
238
+ if (r)
239
+ return `[${o}="${e(r)}"]`;
240
+ }
241
+ const n = t.getAttribute("name");
242
+ return n ? `${t.tagName.toLowerCase()}[name="${e(
243
+ n
156
244
  )}"]` : t.tagName.toLowerCase();
157
245
  }
158
- function V(t) {
159
- const e = f(t), n = t instanceof Document ? t.body ?? t.documentElement : t;
246
+ function lt(t) {
247
+ const e = t.getBoundingClientRect();
248
+ if (!(e.width <= 0 || e.height <= 0))
249
+ return {
250
+ x: e.left + e.width / 2,
251
+ y: e.top + e.height / 2
252
+ };
253
+ }
254
+ function W(t, e) {
255
+ if (typeof t.elementsFromPoint == "function")
256
+ return t.elementsFromPoint(e.x, e.y);
257
+ if (typeof t.elementFromPoint != "function")
258
+ return [];
259
+ const n = t.elementFromPoint(e.x, e.y);
260
+ return n ? [n] : [];
261
+ }
262
+ function ut(t) {
263
+ return typeof t.elementsFromPoint == "function" || typeof t.elementFromPoint == "function";
264
+ }
265
+ function C(t, e) {
266
+ return t === e || t.contains(e);
267
+ }
268
+ function ft(t) {
269
+ let e = t;
270
+ for (; e; ) {
271
+ if (at(e))
272
+ return e;
273
+ e = e.parentElement;
274
+ }
275
+ return t;
276
+ }
277
+ function B(t) {
278
+ return (t ?? "").replace(/\([^)]*\)/g, "").replace(/\s+/g, "").trim().toLowerCase();
279
+ }
280
+ function Y(t) {
281
+ const e = t.getBoundingClientRect();
282
+ if (e.width <= 0 || e.height <= 0)
283
+ return;
284
+ const n = t.ownerDocument;
285
+ return [
286
+ { x: e.left + e.width / 2, y: e.top + e.height / 2 },
287
+ { x: e.left + Math.min(8, e.width / 2), y: e.top + e.height / 2 },
288
+ { x: e.right - Math.min(8, e.width / 2), y: e.top + e.height / 2 },
289
+ { x: e.left + e.width / 2, y: e.top + Math.min(8, e.height / 2) },
290
+ { x: e.left + e.width / 2, y: e.bottom - Math.min(8, e.height / 2) }
291
+ ].find(
292
+ (r) => W(n, r).some(
293
+ (c) => C(t, c) || C(c, t)
294
+ )
295
+ );
296
+ }
297
+ function dt(t, e) {
298
+ var c;
299
+ const n = x(e), o = B(E(e));
300
+ return o ? ((c = I(t).filter((s) => s !== e).filter((s) => {
301
+ const i = x(s), a = B(E(s));
302
+ return n && i && n !== i ? !1 : a.includes(o) || o.includes(a);
303
+ }).map((s) => ({
304
+ candidate: s,
305
+ point: Y(s),
306
+ rect: k(s)
307
+ })).filter(
308
+ (s) => !!s.point
309
+ ).sort((s, i) => i.rect.y - s.rect.y)[0]) == null ? void 0 : c.candidate) ?? null : null;
310
+ }
311
+ function L(t) {
312
+ const e = t.ownerDocument, n = lt(t), o = m(t) && !!n, r = ct(t), c = n ? W(e, n) : [], s = c[0], i = !!(s && (C(t, s) || C(s, t)));
313
+ return {
314
+ visible: o,
315
+ enabled: r,
316
+ receivesEvents: i,
317
+ actionable: o && r && i,
318
+ center: st(t),
319
+ hitTarget: s,
320
+ hitStack: c
321
+ };
322
+ }
323
+ function ht(t, e) {
324
+ var i, a, d;
325
+ const n = p(e.testId);
326
+ if (n) {
327
+ const l = typeof CSS < "u" && typeof CSS.escape == "function" ? CSS.escape(n) : n.replaceAll("\\", "\\\\").replaceAll('"', '\\"'), u = t.querySelector(
328
+ `[data-testid="${l}"],[data-test-id="${l}"],[data-qa="${l}"]`
329
+ );
330
+ if (u)
331
+ return u;
332
+ }
333
+ const o = (i = p(e.role)) == null ? void 0 : i.toLowerCase(), r = (a = p(e.name)) == null ? void 0 : a.toLowerCase(), c = (d = p(e.text)) == null ? void 0 : d.toLowerCase();
334
+ return !o && !r && !c ? null : I(t).find((l) => {
335
+ var w, y, S;
336
+ const u = (w = x(l)) == null ? void 0 : w.toLowerCase(), f = (y = E(l)) == null ? void 0 : y.toLowerCase(), b = (S = v(l)) == null ? void 0 : S.toLowerCase();
337
+ return !(o && u !== o || r && !(f != null && f.includes(r)) || c && !(b != null && b.includes(c)));
338
+ }) ?? null;
339
+ }
340
+ function I(t) {
341
+ const e = g(t), n = t instanceof Document ? t.body ?? t.documentElement : t;
160
342
  if (!n)
161
343
  return [];
162
- const r = [], o = (s) => {
163
- const i = e.createTreeWalker(s, NodeFilter.SHOW_ELEMENT);
164
- let c = i.nextNode();
165
- for (; c; ) {
166
- const l = c;
167
- $(l) && r.push(l);
168
- const d = l.shadowRoot;
169
- if (d && o(d), l instanceof HTMLIFrameElement)
344
+ const o = [], r = (c) => {
345
+ const s = e.createTreeWalker(c, NodeFilter.SHOW_ELEMENT);
346
+ let i = s.nextNode();
347
+ for (; i; ) {
348
+ const a = i;
349
+ U(a) && o.push(a);
350
+ const d = a.shadowRoot;
351
+ if (d && r(d), a instanceof HTMLIFrameElement)
170
352
  try {
171
- l.contentDocument && o(l.contentDocument);
353
+ a.contentDocument && r(a.contentDocument);
172
354
  } catch {
173
355
  }
174
- if (r.length >= x)
356
+ if (o.length >= D)
175
357
  return;
176
- c = i.nextNode();
358
+ i = s.nextNode();
177
359
  }
178
360
  };
179
- return o(n), r.slice(0, x);
361
+ return r(n), o.slice(0, D);
180
362
  }
181
- function U(t, e) {
182
- var o;
183
- const n = Object.getPrototypeOf(t), r = Object.getOwnPropertyDescriptor(n, "value");
184
- (o = r == null ? void 0 : r.set) == null || o.call(t, e);
363
+ function gt(t, e) {
364
+ var r;
365
+ const n = Object.getPrototypeOf(t), o = Object.getOwnPropertyDescriptor(n, "value");
366
+ (r = o == null ? void 0 : o.set) == null || r.call(t, e);
185
367
  }
186
- function E(t) {
368
+ function H(t) {
187
369
  t.dispatchEvent(new Event("input", { bubbles: !0 })), t.dispatchEvent(new Event("change", { bubbles: !0 }));
188
370
  }
189
- function b(t) {
371
+ function A(t) {
190
372
  var e, n;
191
373
  (t instanceof HTMLElement || t instanceof SVGElement) && ((e = t.scrollIntoView) == null || e.call(t, { block: "center", inline: "center" })), t instanceof HTMLElement && ((n = t.focus) == null || n.call(t));
192
374
  }
193
- class j {
375
+ function _(t, e) {
376
+ return typeof PointerEvent == "function" ? new PointerEvent(t, e) : new MouseEvent(t.replace("pointer", "mouse"), e);
377
+ }
378
+ class pt {
194
379
  constructor(e = {}) {
195
380
  this.options = e, this.refs = /* @__PURE__ */ new Map(), this.nextRef = 1;
196
381
  }
@@ -212,187 +397,365 @@ class j {
212
397
  return this.scroll(n);
213
398
  case "host_page_navigate":
214
399
  return this.navigate(n);
400
+ case "host_page_hover":
401
+ return this.hover(n);
402
+ case "host_page_focus":
403
+ return this.focus(n);
404
+ case "host_page_pointer":
405
+ return this.pointer(n);
406
+ case "host_page_screenshot":
407
+ return this.screenshot();
408
+ case "host_page_wait_for":
409
+ return this.waitFor(n);
215
410
  }
216
411
  }
217
412
  snapshot() {
413
+ var c, s;
218
414
  this.refs.clear(), this.nextRef = 1;
219
- const e = this.getRoot(), n = f(e), r = p(e), o = V(e).map(
220
- (s) => this.snapshotElement(s)
415
+ const e = this.getRoot(), n = g(e), o = N(e), r = I(e).map(
416
+ (i) => this.snapshotElement(i)
221
417
  );
222
418
  return {
223
- url: r.location.href,
419
+ url: o.location.href,
224
420
  title: n.title,
421
+ capabilities: {
422
+ cdp: !1,
423
+ realInput: !1,
424
+ screenshot: !1,
425
+ accessibility: !1,
426
+ networkState: !1
427
+ },
225
428
  viewport: {
226
- width: r.innerWidth,
227
- height: r.innerHeight
429
+ width: o.innerWidth,
430
+ height: o.innerHeight,
431
+ devicePixelRatio: o.devicePixelRatio
228
432
  },
229
433
  scroll: {
230
- x: r.scrollX,
231
- y: r.scrollY
434
+ x: o.scrollX,
435
+ y: o.scrollY
436
+ },
437
+ page: {
438
+ readyState: n.readyState,
439
+ visibilityState: n.visibilityState,
440
+ focusedElement: n.activeElement && n.activeElement !== n.body ? this.snapshotElementWithoutRef(n.activeElement) : void 0,
441
+ selection: ((s = (c = n.getSelection) == null ? void 0 : c.call(n)) == null ? void 0 : s.toString()) || void 0
232
442
  },
233
- elements: o
443
+ navigation: this.getNavigationState(o),
444
+ frames: this.getFrameState(n),
445
+ accessibility: r.map((i) => ({
446
+ ref: i.ref,
447
+ role: i.role,
448
+ name: i.name,
449
+ value: i.value,
450
+ disabled: i.disabled,
451
+ checked: i.checked,
452
+ focused: n.activeElement === this.refs.get(i.ref)
453
+ })),
454
+ elements: r
234
455
  };
235
456
  }
236
457
  getRoot() {
237
458
  return this.options.root ?? document;
238
459
  }
460
+ getNavigationState(e) {
461
+ var o, r, c;
462
+ const n = (c = (r = (o = e.performance) == null ? void 0 : o.getEntriesByType) == null ? void 0 : r.call(o, "navigation")) == null ? void 0 : c[0];
463
+ if (!(typeof PerformanceNavigationTiming != "function" || !(n instanceof PerformanceNavigationTiming)))
464
+ return {
465
+ type: n.type,
466
+ duration: n.duration,
467
+ domContentLoaded: n.domContentLoadedEventEnd - n.startTime,
468
+ loadEventEnd: n.loadEventEnd - n.startTime
469
+ };
470
+ }
471
+ getFrameState(e) {
472
+ return Array.from(e.querySelectorAll("iframe")).map((n) => {
473
+ var r, c;
474
+ const o = k(n);
475
+ try {
476
+ return {
477
+ url: (r = n.contentWindow) == null ? void 0 : r.location.href,
478
+ title: (c = n.contentDocument) == null ? void 0 : c.title,
479
+ sameOrigin: !0,
480
+ rect: o
481
+ };
482
+ } catch {
483
+ return {
484
+ url: n.getAttribute("src") ?? void 0,
485
+ sameOrigin: !1,
486
+ rect: o
487
+ };
488
+ }
489
+ });
490
+ }
239
491
  snapshotElement(e) {
240
492
  const n = `e${this.nextRef}`;
241
- this.nextRef += 1, this.refs.set(n, e);
242
- const r = (e instanceof HTMLInputElement || e instanceof HTMLTextAreaElement) && e.placeholder || void 0;
493
+ return this.nextRef += 1, this.refs.set(n, e), this.createElementSnapshot(e, n);
494
+ }
495
+ snapshotElementWithoutRef(e) {
496
+ return this.createElementSnapshot(e, "");
497
+ }
498
+ createElementSnapshot(e, n) {
499
+ const o = (e instanceof HTMLInputElement || e instanceof HTMLTextAreaElement) && e.placeholder || void 0, r = L(e);
243
500
  return {
244
501
  ref: n,
245
502
  tag: e.tagName.toLowerCase(),
246
- role: A(e),
247
- name: L(e),
248
- text: y(e),
249
- value: C(e),
250
- placeholder: r,
251
- selector: M(e),
252
- disabled: O(e),
253
- checked: I(e),
254
- rect: P(e)
503
+ role: x(e),
504
+ name: E(e),
505
+ text: v(e),
506
+ nearbyText: X(e),
507
+ testId: rt(e),
508
+ value: nt(e),
509
+ placeholder: o,
510
+ selector: P(e),
511
+ disabled: q(e),
512
+ enabled: r.enabled,
513
+ checked: it(e),
514
+ visible: r.visible,
515
+ actionable: r.actionable,
516
+ rect: k(e),
517
+ center: r.center,
518
+ hitTarget: r.hitTarget ? $(r.hitTarget) : void 0,
519
+ hitStack: r.hitStack.slice(0, 5).map($)
255
520
  };
256
521
  }
257
522
  resolveElement(e) {
258
- const n = _(e.ref);
523
+ const n = p(e.ref) ?? p(e.axRef);
259
524
  if (n) {
260
- const o = this.refs.get(n);
261
- if (!o)
525
+ const c = this.refs.get(n);
526
+ if (!c)
262
527
  throw new Error(`Unknown element ref: ${n}. Take a new snapshot.`);
263
- return o;
528
+ return c;
264
529
  }
265
- const r = _(e.selector);
266
- if (r) {
267
- const o = this.getRoot().querySelector(r);
268
- if (!o)
269
- throw new Error(`No element matches selector: ${r}.`);
270
- return o;
530
+ const o = p(e.selector);
531
+ if (o) {
532
+ const c = this.getRoot().querySelector(o);
533
+ if (!c)
534
+ throw new Error(`No element matches selector: ${o}.`);
535
+ return c;
271
536
  }
537
+ const r = ht(this.getRoot(), e);
538
+ if (r)
539
+ return r;
272
540
  if (typeof e.x < "u" || typeof e.y < "u") {
273
- const o = {
274
- x: v(e.x, "x"),
275
- y: v(e.y, "y")
276
- }, s = f(this.getRoot()).elementFromPoint(
277
- o.x,
278
- o.y
541
+ const c = {
542
+ x: M(e.x, "x"),
543
+ y: M(e.y, "y")
544
+ }, s = g(this.getRoot()).elementFromPoint(
545
+ c.x,
546
+ c.y
279
547
  );
280
548
  if (!s)
281
- throw new Error(`No element found at (${o.x}, ${o.y}).`);
549
+ throw new Error(`No element found at (${c.x}, ${c.y}).`);
282
550
  return s;
283
551
  }
284
- throw new Error("Expected one of ref, selector, or x/y.");
552
+ throw new Error(
553
+ "Expected one of ref, selector, role/name/text/testId, or x/y."
554
+ );
285
555
  }
286
556
  click(e) {
287
- const n = this.resolveElement(u(e));
288
- return b(n), n instanceof HTMLElement ? n.click() : n.dispatchEvent(new MouseEvent("click", { bubbles: !0 })), { clicked: this.describeElement(n) };
557
+ const n = h(e), o = this.resolveElement(n), r = L(o), s = (r.actionable ? null : dt(this.getRoot(), o)) ?? ft(o), i = Y(s);
558
+ if (!i && !r.actionable && ut(o.ownerDocument))
559
+ throw new Error(
560
+ `Target "${E(o) ?? o.tagName.toLowerCase()}" is not receiving pointer events. Take a screenshot or use host_page_pointer coordinates.`
561
+ );
562
+ return A(s), s instanceof HTMLElement ? s.click() : s.dispatchEvent(new MouseEvent("click", { bubbles: !0 })), {
563
+ clicked: this.describeElement(s),
564
+ requested: this.describeElement(o),
565
+ strategy: s === o ? "dom" : "semantic_visible_fallback",
566
+ point: i ?? r.center,
567
+ actionability: {
568
+ visible: r.visible,
569
+ enabled: r.enabled,
570
+ receivesEvents: r.receivesEvents
571
+ }
572
+ };
289
573
  }
290
574
  fill(e) {
291
- const n = u(e), r = g(n.value, "value"), o = this.resolveElement(n);
292
- if (b(o), o instanceof HTMLInputElement || o instanceof HTMLTextAreaElement)
293
- return U(o, r), E(o), { filled: this.describeElement(o), value: r };
294
- if (o instanceof HTMLElement && o.isContentEditable)
295
- return o.textContent = r, E(o), { filled: this.describeElement(o), value: r };
575
+ const n = h(e), o = R(n.value, "value"), r = this.resolveElement(n);
576
+ if (A(r), r instanceof HTMLInputElement || r instanceof HTMLTextAreaElement)
577
+ return gt(r, o), H(r), { filled: this.describeElement(r), value: o };
578
+ if (r instanceof HTMLElement && r.isContentEditable)
579
+ return r.textContent = o, H(r), { filled: this.describeElement(r), value: o };
296
580
  throw new Error("Target element cannot be filled.");
297
581
  }
298
582
  press(e) {
299
- const n = u(e), r = g(n.key, "key"), o = n.ref || n.selector || n.x !== void 0 && n.y !== void 0 ? this.resolveElement(n) : f(this.getRoot()).activeElement ?? f(this.getRoot()).body;
300
- if (!o)
583
+ const n = h(e), o = R(n.key, "key"), r = n.ref || n.selector || n.x !== void 0 && n.y !== void 0 ? this.resolveElement(n) : g(this.getRoot()).activeElement ?? g(this.getRoot()).body;
584
+ if (!r)
301
585
  throw new Error("No target element is available for key press.");
302
- b(o);
303
- const s = { key: r, bubbles: !0, cancelable: !0 };
304
- return o.dispatchEvent(new KeyboardEvent("keydown", s)), r.length === 1 && o.dispatchEvent(new KeyboardEvent("keypress", s)), o.dispatchEvent(new KeyboardEvent("keyup", s)), { pressed: r, target: this.describeElement(o) };
586
+ A(r);
587
+ const c = { key: o, bubbles: !0, cancelable: !0 };
588
+ return r.dispatchEvent(new KeyboardEvent("keydown", c)), o.length === 1 && r.dispatchEvent(new KeyboardEvent("keypress", c)), r.dispatchEvent(new KeyboardEvent("keyup", c)), { pressed: o, target: this.describeElement(r) };
305
589
  }
306
590
  select(e) {
307
- const n = u(e), r = R(n.values ?? n.value), o = this.resolveElement(n);
308
- if (!(o instanceof HTMLSelectElement))
591
+ const n = h(e), o = et(n.values ?? n.value), r = this.resolveElement(n);
592
+ if (!(r instanceof HTMLSelectElement))
309
593
  throw new Error("Target element is not a select.");
310
- const s = new Set(r);
311
- for (const i of Array.from(o.options))
312
- i.selected = s.has(i.value);
313
- return E(o), {
314
- selected: Array.from(o.selectedOptions).map(
315
- (i) => i.value
594
+ const c = new Set(o);
595
+ for (const s of Array.from(r.options))
596
+ s.selected = c.has(s.value);
597
+ return H(r), {
598
+ selected: Array.from(r.selectedOptions).map(
599
+ (s) => s.value
316
600
  ),
317
- target: this.describeElement(o)
601
+ target: this.describeElement(r)
318
602
  };
319
603
  }
320
604
  scroll(e) {
321
- var l, d, w, T;
322
- const n = u(e), r = n.x !== void 0 || n.y !== void 0 ? {
323
- x: h(n.x) ?? 0,
324
- y: h(n.y) ?? 0
325
- } : null, o = h(n.deltaX) ?? 0, s = h(n.deltaY) ?? 0, i = this.getRoot(), c = p(i);
326
- if (n.ref || n.selector) {
327
- const a = this.resolveElement(n);
328
- if (!(a instanceof HTMLElement))
605
+ var a, d, l, u;
606
+ const n = h(e), o = n.x !== void 0 || n.y !== void 0 ? {
607
+ x: T(n.x) ?? 0,
608
+ y: T(n.y) ?? 0
609
+ } : null, r = T(n.deltaX) ?? 0, c = T(n.deltaY) ?? 0, s = this.getRoot(), i = N(s);
610
+ if (n.ref || n.selector || n.role || n.name || n.text || n.testId) {
611
+ const f = this.resolveElement(n);
612
+ if (!(f instanceof HTMLElement))
329
613
  throw new Error("Target element cannot be scrolled.");
330
- return r ? (l = a.scrollTo) == null || l.call(a, r.x, r.y) : (d = a.scrollBy) == null || d.call(a, o, s), {
331
- scrolled: this.describeElement(a),
332
- scroll: { x: a.scrollLeft, y: a.scrollTop }
614
+ return o ? (a = f.scrollTo) == null || a.call(f, o.x, o.y) : (d = f.scrollBy) == null || d.call(f, r, c), {
615
+ scrolled: this.describeElement(f),
616
+ scroll: { x: f.scrollLeft, y: f.scrollTop }
333
617
  };
334
618
  }
335
- return r ? (w = c.scrollTo) == null || w.call(c, r.x, r.y) : (T = c.scrollBy) == null || T.call(c, o, s), { scroll: { x: c.scrollX, y: c.scrollY } };
619
+ return o ? (l = i.scrollTo) == null || l.call(i, o.x, o.y) : (u = i.scrollBy) == null || u.call(i, r, c), { scroll: { x: i.scrollX, y: i.scrollY } };
336
620
  }
337
621
  navigate(e) {
338
622
  if (this.options.allowNavigation === !1)
339
623
  throw new Error("Navigation is disabled for host page automation.");
340
- const n = u(e), r = this.getRoot(), o = p(r), s = g(n.url, "url"), i = new URL(s, o.location.href);
341
- if (i.protocol !== "http:" && i.protocol !== "https:")
624
+ const n = h(e), o = this.getRoot(), r = N(o), c = R(n.url, "url"), s = new URL(c, r.location.href);
625
+ if (s.protocol !== "http:" && s.protocol !== "https:")
342
626
  throw new Error("Navigation only supports HTTP(S) URLs.");
343
- return this.refs.clear(), o.location.assign(i.toString()), { navigated: i.toString() };
627
+ return this.refs.clear(), r.location.assign(s.toString()), { navigated: s.toString() };
628
+ }
629
+ hover(e) {
630
+ const n = h(e), o = this.resolveElement(n), r = L(o).center;
631
+ return o.dispatchEvent(
632
+ new MouseEvent("mouseover", {
633
+ bubbles: !0,
634
+ cancelable: !0,
635
+ clientX: r == null ? void 0 : r.x,
636
+ clientY: r == null ? void 0 : r.y
637
+ })
638
+ ), o.dispatchEvent(
639
+ new MouseEvent("mousemove", {
640
+ bubbles: !0,
641
+ cancelable: !0,
642
+ clientX: r == null ? void 0 : r.x,
643
+ clientY: r == null ? void 0 : r.y
644
+ })
645
+ ), { hovered: this.describeElement(o), point: r };
646
+ }
647
+ focus(e) {
648
+ const n = h(e), o = this.resolveElement(n);
649
+ return A(o), { focused: this.describeElement(o) };
650
+ }
651
+ pointer(e) {
652
+ const n = h(e), o = F(n.action, ["move", "down", "up", "click"]) ?? "click", r = tt(n.button) ?? 0, c = n.ref || n.selector || n.role || n.name || n.text || n.testId ? this.resolveElement(n) : null, s = c ? L(c).center : {
653
+ x: M(n.x, "x"),
654
+ y: M(n.y, "y")
655
+ };
656
+ if (!s)
657
+ throw new Error("Target element has no clickable point.");
658
+ const i = c ?? g(this.getRoot()).elementFromPoint(s.x, s.y);
659
+ if (!i)
660
+ throw new Error(`No element found at (${s.x}, ${s.y}).`);
661
+ const a = {
662
+ bubbles: !0,
663
+ cancelable: !0,
664
+ clientX: s.x,
665
+ clientY: s.y,
666
+ button: r
667
+ };
668
+ return o === "move" ? (i.dispatchEvent(_("pointermove", a)), i.dispatchEvent(new MouseEvent("mousemove", a))) : o === "down" ? (i.dispatchEvent(_("pointerdown", a)), i.dispatchEvent(new MouseEvent("mousedown", a))) : o === "up" ? (i.dispatchEvent(_("pointerup", a)), i.dispatchEvent(new MouseEvent("mouseup", a))) : (i.dispatchEvent(_("pointerdown", a)), i.dispatchEvent(new MouseEvent("mousedown", a)), i.dispatchEvent(_("pointerup", a)), i.dispatchEvent(new MouseEvent("mouseup", a)), i instanceof HTMLElement ? i.click() : i.dispatchEvent(new MouseEvent("click", a))), {
669
+ pointer: o,
670
+ point: s,
671
+ target: this.describeElement(i)
672
+ };
673
+ }
674
+ screenshot() {
675
+ throw new Error(
676
+ "host_page_screenshot requires the browser extension CDP adapter."
677
+ );
678
+ }
679
+ async waitFor(e) {
680
+ const n = h(e), o = F(n.state, [
681
+ "attached",
682
+ "visible",
683
+ "hidden",
684
+ "detached"
685
+ ]) ?? "visible", r = T(n.timeoutSeconds), c = Math.min(
686
+ Q,
687
+ Math.max(0, (r ?? J / 1e3) * 1e3)
688
+ ), s = Date.now();
689
+ for (; Date.now() - s <= c; ) {
690
+ const i = this.tryResolveElement(n);
691
+ if (o === "attached" ? !!i : o === "detached" ? !i : o === "hidden" ? !i || !m(i) : !!(i && m(i)))
692
+ return {
693
+ waitedFor: o,
694
+ elapsedMs: Date.now() - s,
695
+ target: i ? this.describeElement(i) : void 0
696
+ };
697
+ await new Promise((d) => setTimeout(d, 100));
698
+ }
699
+ throw new Error(`Timed out waiting for target to become ${o}.`);
700
+ }
701
+ tryResolveElement(e) {
702
+ try {
703
+ return this.resolveElement(e);
704
+ } catch {
705
+ return null;
706
+ }
344
707
  }
345
708
  describeElement(e) {
346
709
  return {
347
710
  tag: e.tagName.toLowerCase(),
348
- role: A(e),
349
- name: L(e),
350
- selector: M(e)
711
+ role: x(e),
712
+ name: E(e),
713
+ selector: P(e)
351
714
  };
352
715
  }
353
716
  }
354
- function B(t) {
717
+ function Et(t) {
355
718
  return typeof t == "object" && t !== null && !Array.isArray(t) ? t : {};
356
719
  }
357
- function F(t) {
720
+ function mt(t) {
358
721
  return JSON.stringify(t);
359
722
  }
360
- function m(t, e, n) {
723
+ function O(t, e, n) {
361
724
  return {
362
725
  tool_call_id: t.tool_call_id ?? t.id,
363
726
  name: t.name,
364
727
  status: e,
365
- content: F(n)
728
+ content: mt(n)
366
729
  };
367
730
  }
368
- function X(t) {
731
+ function bt(t) {
369
732
  return t instanceof Error ? t.message : String(t);
370
733
  }
371
- function G(t = {}) {
372
- const e = new j(t);
734
+ function wt(t = {}) {
735
+ const e = new pt(t);
373
736
  return async (n) => {
374
- if (!N(n.name))
375
- return m(n, "error", {
737
+ if (!Z(n.name))
738
+ return O(n, "error", {
376
739
  ok: !1,
377
740
  error: `Unknown host page automation tool: ${n.name}`
378
741
  });
379
742
  try {
380
- const r = await e.execute(
743
+ const o = await e.execute(
381
744
  n.name,
382
- B(n.params)
745
+ Et(n.params)
383
746
  );
384
- return m(n, "success", { ok: !0, result: r });
385
- } catch (r) {
386
- return m(n, "error", {
747
+ return O(n, "success", { ok: !0, result: o });
748
+ } catch (o) {
749
+ return O(n, "error", {
387
750
  ok: !1,
388
- error: X(r)
751
+ error: bt(o)
389
752
  });
390
753
  }
391
754
  };
392
755
  }
393
756
  export {
394
- S as HOST_PAGE_AUTOMATION_TOOL_NAMES,
395
- j as HostPageAutomationExecutor,
396
- G as createHostPageAutomationClientToolHandler,
397
- N as isHostPageAutomationToolName
757
+ z as HOST_PAGE_AUTOMATION_TOOL_NAMES,
758
+ pt as HostPageAutomationExecutor,
759
+ wt as createHostPageAutomationClientToolHandler,
760
+ Z as isHostPageAutomationToolName
398
761
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@xpert-ai/chatkit-host-automation",
3
- "version": "0.3.0",
3
+ "version": "0.3.2",
4
4
  "description": "Host page automation tools for Xpert ChatKit client tools",
5
5
  "sideEffects": false,
6
6
  "type": "module",
@@ -21,7 +21,7 @@
21
21
  "!**/*.tsbuildinfo"
22
22
  ],
23
23
  "dependencies": {
24
- "@xpert-ai/chatkit-types": "~0.3.1"
24
+ "@xpert-ai/chatkit-types": "~0.3.3"
25
25
  },
26
26
  "devDependencies": {
27
27
  "rimraf": "^6.0.0",