@ai-support-agent/cli 0.1.31 → 0.1.32-beta.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -10,189 +10,21 @@ exports.getElementAtPoint = getElementAtPoint;
10
10
  exports.getFocusedElementInfo = getFocusedElementInfo;
11
11
  exports.getCursorAt = getCursorAt;
12
12
  exports.formatElementInfo = formatElementInfo;
13
- /**
14
- * JavaScript to run in the browser via page.evaluate() to extract element info.
15
- * Returns ElementInfo-compatible object or null.
16
- */
17
- const ELEMENT_AT_POINT_SCRIPT = `(point) => {
18
- const el = document.elementFromPoint(point.x, point.y);
19
- if (!el) return null;
20
- return extractInfo(el);
21
-
22
- function extractInfo(el) {
23
- const tag = el.tagName.toLowerCase();
24
- const id = el.id;
25
- const name = el.getAttribute('name');
26
- const type = el.getAttribute('type');
27
- const role = el.getAttribute('role') || implicitRole(tag, type);
28
- const ariaLabel = el.getAttribute('aria-label');
29
- const placeholder = el.getAttribute('placeholder');
30
- const title = el.getAttribute('title');
31
- const href = el.getAttribute('href');
32
- const text = getVisibleText(el);
33
- const testId = el.getAttribute('data-testid') || el.getAttribute('data-test-id');
34
-
35
- const selector = buildSelector(el, tag, id, name, testId, text, role, ariaLabel, placeholder, type);
36
- const label = ariaLabel || placeholder || name || title || undefined;
37
-
38
- return {
39
- selector,
40
- tagName: tag,
41
- type: type || undefined,
42
- text: text || undefined,
43
- role: role || undefined,
44
- label: label || undefined,
45
- href: href || undefined,
46
- };
47
- }
48
-
49
- function buildSelector(el, tag, id, name, testId, text, role, ariaLabel, placeholder, type) {
50
- // Priority 1: data-testid (most stable)
51
- if (testId) return '[data-testid="' + testId + '"]';
52
-
53
- // Priority 2: id (unique)
54
- if (id) return '#' + CSS.escape(id);
55
-
56
- // Priority 3: role + name (accessible selectors)
57
- if (role && ariaLabel) return tag + '[role="' + role + '"][aria-label="' + ariaLabel + '"]';
58
-
59
- // Priority 4: name attribute (forms)
60
- if (name && (tag === 'input' || tag === 'select' || tag === 'textarea')) {
61
- return tag + '[name="' + name + '"]';
62
- }
63
-
64
- // Priority 5: Playwright text selector for buttons/links
65
- if (text && text.length <= 50 && (tag === 'button' || tag === 'a' || role === 'button' || role === 'link')) {
66
- return tag + ':has-text("' + text.replace(/"/g, '\\\\"') + '")';
67
- }
68
-
69
- // Priority 6: placeholder for inputs
70
- if (placeholder && tag === 'input') {
71
- return 'input[placeholder="' + placeholder + '"]';
72
- }
73
-
74
- // Priority 7: type for inputs
75
- if (type && tag === 'input') {
76
- // Check if unique enough by adding parent context
77
- const parent = el.parentElement;
78
- if (parent && parent.id) {
79
- return '#' + CSS.escape(parent.id) + ' > input[type="' + type + '"]';
80
- }
81
- return 'input[type="' + type + '"]';
82
- }
83
-
84
- // Priority 8: nth-of-type with class
85
- const classes = Array.from(el.classList).filter(c => !c.match(/^(js-|is-|has-)/)).slice(0, 2);
86
- if (classes.length > 0) {
87
- const classSelector = tag + '.' + classes.map(c => CSS.escape(c)).join('.');
88
- const siblings = document.querySelectorAll(classSelector);
89
- if (siblings.length === 1) return classSelector;
90
- const index = Array.from(siblings).indexOf(el);
91
- if (index >= 0) return classSelector + ':nth-of-type(' + (index + 1) + ')';
92
- }
93
-
94
- // Fallback: tag with index
95
- return tag;
96
- }
97
-
98
- function getVisibleText(el) {
99
- const text = (el.textContent || '').trim();
100
- return text.length > 80 ? text.substring(0, 77) + '...' : text;
101
- }
102
-
103
- function implicitRole(tag, type) {
104
- if (tag === 'button') return 'button';
105
- if (tag === 'a') return 'link';
106
- if (tag === 'input') {
107
- if (type === 'checkbox') return 'checkbox';
108
- if (type === 'radio') return 'radio';
109
- if (type === 'submit') return 'button';
110
- if (!type || type === 'text' || type === 'email' || type === 'password' || type === 'search' || type === 'tel' || type === 'url' || type === 'number') return 'textbox';
111
- }
112
- if (tag === 'textarea') return 'textbox';
113
- if (tag === 'select') return 'combobox';
114
- if (tag === 'img') return 'img';
115
- return '';
116
- }
117
- }`;
118
- /**
119
- * JavaScript to read the CSS `cursor` value of the element at a point.
120
- * Mirrors ELEMENT_AT_POINT_SCRIPT's string-script convention so the browser
121
- * context (not Node) resolves DOM types. Returns 'default' when no element is
122
- * found or the computed cursor is empty.
123
- */
124
- const CURSOR_AT_POINT_SCRIPT = `(point) => {
125
- const el = document.elementFromPoint(point.x, point.y);
126
- if (!el) return 'default';
127
- return getComputedStyle(el).cursor || 'default';
128
- }`;
129
- /**
130
- * JavaScript to extract info about the currently focused element.
131
- */
132
- const FOCUSED_ELEMENT_SCRIPT = `() => {
133
- const el = document.activeElement;
134
- if (!el || el === document.body) return null;
135
-
136
- const tag = el.tagName.toLowerCase();
137
- const id = el.id;
138
- const name = el.getAttribute('name');
139
- const type = el.getAttribute('type');
140
- const role = el.getAttribute('role') || implicitRole(tag, type);
141
- const ariaLabel = el.getAttribute('aria-label');
142
- const placeholder = el.getAttribute('placeholder');
143
- const title = el.getAttribute('title');
144
- const testId = el.getAttribute('data-testid') || el.getAttribute('data-test-id');
145
-
146
- const selector = buildSelector(el, tag, id, name, testId, '', role, ariaLabel, placeholder, type);
147
- const label = ariaLabel || placeholder || name || title || undefined;
148
-
149
- return {
150
- selector,
151
- tagName: tag,
152
- type: type || undefined,
153
- role: role || undefined,
154
- label: label || undefined,
155
- };
156
-
157
- function buildSelector(el, tag, id, name, testId, text, role, ariaLabel, placeholder, type) {
158
- if (testId) return '[data-testid="' + testId + '"]';
159
- if (id) return '#' + CSS.escape(id);
160
- if (role && ariaLabel) return tag + '[role="' + role + '"][aria-label="' + ariaLabel + '"]';
161
- if (name && (tag === 'input' || tag === 'select' || tag === 'textarea')) {
162
- return tag + '[name="' + name + '"]';
163
- }
164
- if (placeholder && tag === 'input') {
165
- return 'input[placeholder="' + placeholder + '"]';
166
- }
167
- if (type && tag === 'input') return 'input[type="' + type + '"]';
168
- const classes = Array.from(el.classList).filter(c => !c.match(/^(js-|is-|has-)/)).slice(0, 2);
169
- if (classes.length > 0) return tag + '.' + classes.map(c => CSS.escape(c)).join('.');
170
- return tag;
171
- }
172
-
173
- function implicitRole(tag, type) {
174
- if (tag === 'button') return 'button';
175
- if (tag === 'a') return 'link';
176
- if (tag === 'input') {
177
- if (type === 'checkbox') return 'checkbox';
178
- if (type === 'radio') return 'radio';
179
- if (type === 'submit') return 'button';
180
- if (!type || type === 'text' || type === 'email' || type === 'password' || type === 'search' || type === 'tel' || type === 'url' || type === 'number') return 'textbox';
181
- }
182
- if (tag === 'textarea') return 'textbox';
183
- if (tag === 'select') return 'combobox';
184
- return '';
185
- }
186
- }`;
13
+ const logger_1 = require("../../../logger");
14
+ const page_scripts_1 = require("./page-scripts");
187
15
  /**
188
16
  * Get element info at the given coordinates via page.evaluate().
189
17
  * Returns null if no element found or on error.
190
18
  */
191
19
  async function getElementAtPoint(page, x, y) {
192
20
  try {
193
- return await page.evaluate(ELEMENT_AT_POINT_SCRIPT, { x, y });
21
+ return await page.evaluate(page_scripts_1.ELEMENT_AT_POINT_SCRIPT, { x, y });
194
22
  }
195
- catch {
23
+ catch (error) {
24
+ // The script now actually executes in the page, so real eval errors reach
25
+ // here; log them (debug — this is best-effort enrichment) instead of
26
+ // silently dropping them.
27
+ logger_1.logger.debug(`[browser] getElementAtPoint evaluate failed: ${String(error)}`);
196
28
  return null;
197
29
  }
198
30
  }
@@ -202,9 +34,10 @@ async function getElementAtPoint(page, x, y) {
202
34
  */
203
35
  async function getFocusedElementInfo(page) {
204
36
  try {
205
- return await page.evaluate(FOCUSED_ELEMENT_SCRIPT);
37
+ return await page.evaluate(page_scripts_1.FOCUSED_ELEMENT_SCRIPT);
206
38
  }
207
- catch {
39
+ catch (error) {
40
+ logger_1.logger.debug(`[browser] getFocusedElementInfo evaluate failed: ${String(error)}`);
208
41
  return null;
209
42
  }
210
43
  }
@@ -217,7 +50,7 @@ async function getFocusedElementInfo(page) {
217
50
  * cursor update for that frame instead of reporting a misleading 'default'.
218
51
  */
219
52
  async function getCursorAt(page, x, y) {
220
- const result = await page.evaluate(CURSOR_AT_POINT_SCRIPT, { x, y });
53
+ const result = await page.evaluate(page_scripts_1.CURSOR_AT_POINT_SCRIPT, { x, y });
221
54
  return typeof result === 'string' ? result : 'default';
222
55
  }
223
56
  /**
@@ -1 +1 @@
1
- {"version":3,"file":"element-info.js","sourceRoot":"","sources":["../../../../src/mcp/tools/browser/element-info.ts"],"names":[],"mappings":";AAAA;;;;;GAKG;;AA2MH,8CAMC;AAMD,sDAMC;AAUD,kCAGC;AAKD,8CAiBC;AA1OD;;;GAGG;AACH,MAAM,uBAAuB,GAAG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAoG9B,CAAA;AAEF;;;;;GAKG;AACH,MAAM,sBAAsB,GAAG;;;;EAI7B,CAAA;AAEF;;GAEG;AACH,MAAM,sBAAsB,GAAG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAsD7B,CAAA;AAEF;;;GAGG;AACI,KAAK,UAAU,iBAAiB,CAAC,IAAU,EAAE,CAAS,EAAE,CAAS;IACtE,IAAI,CAAC;QACH,OAAO,MAAM,IAAI,CAAC,QAAQ,CAAC,uBAAuB,EAAE,EAAE,CAAC,EAAE,CAAC,EAAE,CAAuB,CAAA;IACrF,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,IAAI,CAAA;IACb,CAAC;AACH,CAAC;AAED;;;GAGG;AACI,KAAK,UAAU,qBAAqB,CAAC,IAAU;IACpD,IAAI,CAAC;QACH,OAAO,MAAM,IAAI,CAAC,QAAQ,CAAC,sBAAsB,CAAuB,CAAA;IAC1E,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,IAAI,CAAA;IACb,CAAC;AACH,CAAC;AAED;;;;;;;GAOG;AACI,KAAK,UAAU,WAAW,CAAC,IAAU,EAAE,CAAS,EAAE,CAAS;IAChE,MAAM,MAAM,GAAG,MAAM,IAAI,CAAC,QAAQ,CAAC,sBAAsB,EAAE,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,CAAA;IACpE,OAAO,OAAO,MAAM,KAAK,QAAQ,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,SAAS,CAAA;AACxD,CAAC;AAED;;GAEG;AACH,SAAgB,iBAAiB,CAAC,IAAiB;IACjD,MAAM,KAAK,GAAa,EAAE,CAAA;IAE1B,oEAAoE;IACpE,KAAK,CAAC,IAAI,CAAC,aAAa,IAAI,CAAC,QAAQ,GAAG,CAAC,CAAA;IAEzC,sBAAsB;IACtB,MAAM,IAAI,GAAa,CAAC,IAAI,CAAC,OAAO,CAAC,CAAA;IACrC,IAAI,IAAI,CAAC,IAAI;QAAE,IAAI,CAAC,IAAI,CAAC,QAAQ,IAAI,CAAC,IAAI,EAAE,CAAC,CAAA;IAC7C,IAAI,IAAI,CAAC,IAAI;QAAE,IAAI,CAAC,IAAI,CAAC,QAAQ,IAAI,CAAC,IAAI,EAAE,CAAC,CAAA;IAC7C,KAAK,CAAC,IAAI,CAAC,IAAI,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC,CAAA;IAEjC,IAAI,IAAI,CAAC,KAAK;QAAE,KAAK,CAAC,IAAI,CAAC,UAAU,IAAI,CAAC,KAAK,GAAG,CAAC,CAAA;IACnD,IAAI,IAAI,CAAC,IAAI;QAAE,KAAK,CAAC,IAAI,CAAC,SAAS,IAAI,CAAC,IAAI,GAAG,CAAC,CAAA;IAChD,IAAI,IAAI,CAAC,IAAI;QAAE,KAAK,CAAC,IAAI,CAAC,SAAS,IAAI,CAAC,IAAI,GAAG,CAAC,CAAA;IAEhD,OAAO,KAAK,CAAC,IAAI,CAAC,GAAG,CAAC,CAAA;AACxB,CAAC"}
1
+ {"version":3,"file":"element-info.js","sourceRoot":"","sources":["../../../../src/mcp/tools/browser/element-info.ts"],"names":[],"mappings":";AAAA;;;;;GAKG;;AAiCH,8CAUC;AAMD,sDAOC;AAUD,kCAGC;AAKD,8CAiBC;AAvFD,4CAAwC;AACxC,iDAIuB;AAoBvB;;;GAGG;AACI,KAAK,UAAU,iBAAiB,CAAC,IAAU,EAAE,CAAS,EAAE,CAAS;IACtE,IAAI,CAAC;QACH,OAAO,MAAM,IAAI,CAAC,QAAQ,CAAC,sCAAuB,EAAE,EAAE,CAAC,EAAE,CAAC,EAAE,CAAuB,CAAA;IACrF,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,0EAA0E;QAC1E,qEAAqE;QACrE,0BAA0B;QAC1B,eAAM,CAAC,KAAK,CAAC,gDAAgD,MAAM,CAAC,KAAK,CAAC,EAAE,CAAC,CAAA;QAC7E,OAAO,IAAI,CAAA;IACb,CAAC;AACH,CAAC;AAED;;;GAGG;AACI,KAAK,UAAU,qBAAqB,CAAC,IAAU;IACpD,IAAI,CAAC;QACH,OAAO,MAAM,IAAI,CAAC,QAAQ,CAAC,qCAAsB,CAAuB,CAAA;IAC1E,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,eAAM,CAAC,KAAK,CAAC,oDAAoD,MAAM,CAAC,KAAK,CAAC,EAAE,CAAC,CAAA;QACjF,OAAO,IAAI,CAAA;IACb,CAAC;AACH,CAAC;AAED;;;;;;;GAOG;AACI,KAAK,UAAU,WAAW,CAAC,IAAU,EAAE,CAAS,EAAE,CAAS;IAChE,MAAM,MAAM,GAAG,MAAM,IAAI,CAAC,QAAQ,CAAC,qCAAsB,EAAE,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,CAAA;IACpE,OAAO,OAAO,MAAM,KAAK,QAAQ,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,SAAS,CAAA;AACxD,CAAC;AAED;;GAEG;AACH,SAAgB,iBAAiB,CAAC,IAAiB;IACjD,MAAM,KAAK,GAAa,EAAE,CAAA;IAE1B,oEAAoE;IACpE,KAAK,CAAC,IAAI,CAAC,aAAa,IAAI,CAAC,QAAQ,GAAG,CAAC,CAAA;IAEzC,sBAAsB;IACtB,MAAM,IAAI,GAAa,CAAC,IAAI,CAAC,OAAO,CAAC,CAAA;IACrC,IAAI,IAAI,CAAC,IAAI;QAAE,IAAI,CAAC,IAAI,CAAC,QAAQ,IAAI,CAAC,IAAI,EAAE,CAAC,CAAA;IAC7C,IAAI,IAAI,CAAC,IAAI;QAAE,IAAI,CAAC,IAAI,CAAC,QAAQ,IAAI,CAAC,IAAI,EAAE,CAAC,CAAA;IAC7C,KAAK,CAAC,IAAI,CAAC,IAAI,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC,CAAA;IAEjC,IAAI,IAAI,CAAC,KAAK;QAAE,KAAK,CAAC,IAAI,CAAC,UAAU,IAAI,CAAC,KAAK,GAAG,CAAC,CAAA;IACnD,IAAI,IAAI,CAAC,IAAI;QAAE,KAAK,CAAC,IAAI,CAAC,SAAS,IAAI,CAAC,IAAI,GAAG,CAAC,CAAA;IAChD,IAAI,IAAI,CAAC,IAAI;QAAE,KAAK,CAAC,IAAI,CAAC,SAAS,IAAI,CAAC,IAAI,GAAG,CAAC,CAAA;IAEhD,OAAO,KAAK,CAAC,IAAI,CAAC,GAAG,CAAC,CAAA;AACxB,CAAC"}
@@ -0,0 +1,87 @@
1
+ /**
2
+ * page-scripts.ts — Browser-context functions passed to Playwright's
3
+ * page.evaluate() / page.addInitScript().
4
+ *
5
+ * IMPORTANT: every function in this module is serialized by Playwright and
6
+ * executed INSIDE the browser page, NOT in Node. They must therefore be
7
+ * self-contained (no references to Node/module-scope variables) and may only
8
+ * use browser globals (document, window, getComputedStyle, CSS, …).
9
+ *
10
+ * They are REAL functions (not strings): Playwright treats a string
11
+ * pageFunction as a JS expression and never calls it, so a string arrow
12
+ * function would silently do nothing and ignore its argument. Passing the
13
+ * function directly lets Playwright serialize and invoke it with the arg.
14
+ *
15
+ * DOM typing is provided by the MODULE-SCOPED ambient declarations below. They
16
+ * intentionally avoid `/// <reference lib="dom" />` and `declare global`, both
17
+ * of which leak DOM types into the rest of the (Node) codebase; module-scoped
18
+ * `declare const` bindings are visible only within this file, so the global
19
+ * tsconfig `lib: ["ES2022"]` (no "DOM") still catches stray DOM usage elsewhere.
20
+ */
21
+ /**
22
+ * Extract Playwright-friendly element info for the element at a point.
23
+ * Returns an ElementInfo-compatible object or null.
24
+ */
25
+ export declare const ELEMENT_AT_POINT_SCRIPT: (point: {
26
+ x: number;
27
+ y: number;
28
+ }) => {
29
+ selector: string;
30
+ tagName: string;
31
+ type: string | undefined;
32
+ text: string | undefined;
33
+ role: string | undefined;
34
+ label: string | undefined;
35
+ href: string | undefined;
36
+ } | null;
37
+ /**
38
+ * Read the CSS `cursor` value of the element at a point. Returns 'default' when
39
+ * no element is found or the computed cursor is empty.
40
+ */
41
+ export declare const CURSOR_AT_POINT_SCRIPT: (point: {
42
+ x: number;
43
+ y: number;
44
+ }) => string;
45
+ /**
46
+ * Extract info about the currently focused element.
47
+ */
48
+ export declare const FOCUSED_ELEMENT_SCRIPT: () => {
49
+ selector: string;
50
+ tagName: string;
51
+ type: string | undefined;
52
+ role: string | undefined;
53
+ label: string | undefined;
54
+ } | null;
55
+ /**
56
+ * Read the current selection. Prefers the selection range of a focused
57
+ * input/textarea, falling back to the document selection. Returns an empty
58
+ * string when nothing is selected.
59
+ */
60
+ export declare const GET_SELECTED_TEXT_SCRIPT: () => string;
61
+ /**
62
+ * Install (idempotently) focus/value/selection reporting for simple
63
+ * input/textarea elements. Reports through the `window.__onBrowserFocus`
64
+ * binding exposed by Playwright.
65
+ *
66
+ * Listener registration is guarded by `window.__browserFocusReportingInstalled`
67
+ * so re-evaluating the script does not double-register handlers. The INITIAL
68
+ * report runs on EVERY evaluation (outside the guard) so an element already
69
+ * focused at injection time (e.g. an autofocused login field) is surfaced.
70
+ *
71
+ * Browser-side failures of the exposed binding are warned at most once here;
72
+ * the Node side (browser-session.ts exposeBinding wrapper) adds server-log
73
+ * observability for binding callback failures.
74
+ */
75
+ export declare const FOCUS_REPORTING_SCRIPT: () => void;
76
+ /**
77
+ * Reflect a value into the currently-focused reporting-target input/textarea
78
+ * using the native value setter + InputEvent('input') dispatch, so React-style
79
+ * controlled components do not roll the value back. Optionally applies a
80
+ * selection range. No-op when the active element is not a reporting target.
81
+ */
82
+ export declare const SET_FOCUSED_INPUT_VALUE_SCRIPT: (args: {
83
+ value: string;
84
+ selectionStart?: number;
85
+ selectionEnd?: number;
86
+ }) => void;
87
+ //# sourceMappingURL=page-scripts.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"page-scripts.d.ts","sourceRoot":"","sources":["../../../../src/mcp/tools/browser/page-scripts.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;GAmBG;AA+EH;;;GAGG;AACH,eAAO,MAAM,uBAAuB,GAAI,OAAO;IAAE,CAAC,EAAE,MAAM,CAAC;IAAC,CAAC,EAAE,MAAM,CAAA;CAAE;;;;;;;;QA+GtE,CAAA;AAED;;;GAGG;AACH,eAAO,MAAM,sBAAsB,GAAI,OAAO;IAAE,CAAC,EAAE,MAAM,CAAC;IAAC,CAAC,EAAE,MAAM,CAAA;CAAE,KAAG,MAIxE,CAAA;AAED;;GAEG;AACH,eAAO,MAAM,sBAAsB;;;;;;QAiElC,CAAA;AAED;;;;GAIG;AACH,eAAO,MAAM,wBAAwB,QAAO,MAa3C,CAAA;AAED;;;;;;;;;;;;;GAaG;AACH,eAAO,MAAM,sBAAsB,YAqHlC,CAAA;AAED;;;;;GAKG;AACH,eAAO,MAAM,8BAA8B,GAAI,MAAM;IACnD,KAAK,EAAE,MAAM,CAAA;IACb,cAAc,CAAC,EAAE,MAAM,CAAA;IACvB,YAAY,CAAC,EAAE,MAAM,CAAA;CACtB,SA4BA,CAAA"}