@nuanu-ai/agentbrowse 0.2.21 → 0.2.23
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 +52 -9
- package/dist/commands/act.d.ts.map +1 -1
- package/dist/commands/act.js +100 -73
- package/dist/commands/action-acceptance.d.ts +4 -1
- package/dist/commands/action-acceptance.d.ts.map +1 -1
- package/dist/commands/action-acceptance.js +127 -37
- package/dist/commands/action-executor-helpers.d.ts.map +1 -1
- package/dist/commands/action-executor-helpers.js +12 -4
- package/dist/commands/action-fallbacks.d.ts.map +1 -1
- package/dist/commands/action-result-resolution.d.ts.map +1 -1
- package/dist/commands/action-result-resolution.js +1 -1
- package/dist/commands/action-value-projection.d.ts.map +1 -1
- package/dist/commands/close.d.ts +12 -1
- package/dist/commands/close.d.ts.map +1 -1
- package/dist/commands/close.js +19 -21
- package/dist/commands/datepicker-action-executor.d.ts.map +1 -1
- package/dist/commands/datepicker-action-executor.js +1 -1
- package/dist/commands/descriptor-validation.d.ts.map +1 -1
- package/dist/commands/descriptor-validation.js +13 -172
- package/dist/commands/extract-scoped-dialog-text.d.ts.map +1 -1
- package/dist/commands/extract-scoped-dialog-text.js +1 -4
- package/dist/commands/extract-snapshot-sanitizer.d.ts.map +1 -1
- package/dist/commands/extract-stagehand-executor.d.ts.map +1 -1
- package/dist/commands/extract.d.ts.map +1 -1
- package/dist/commands/extract.js +23 -4
- package/dist/commands/launch.d.ts +22 -1
- package/dist/commands/launch.d.ts.map +1 -1
- package/dist/commands/launch.js +122 -59
- package/dist/commands/observe-accessibility.d.ts +22 -0
- package/dist/commands/observe-accessibility.d.ts.map +1 -0
- package/dist/commands/observe-accessibility.js +497 -0
- package/dist/commands/observe-display-label.d.ts +4 -0
- package/dist/commands/observe-display-label.d.ts.map +1 -0
- package/dist/commands/observe-display-label.js +26 -0
- package/dist/commands/observe-dom-label-contract.d.ts +2 -0
- package/dist/commands/observe-dom-label-contract.d.ts.map +1 -0
- package/dist/commands/observe-dom-label-contract.js +521 -0
- package/dist/commands/observe-fallback-semantics.d.ts +6 -0
- package/dist/commands/observe-fallback-semantics.d.ts.map +1 -0
- package/dist/commands/observe-fallback-semantics.js +86 -0
- package/dist/commands/observe-inventory.d.ts +23 -18
- package/dist/commands/observe-inventory.d.ts.map +1 -1
- package/dist/commands/observe-inventory.js +172 -719
- package/dist/commands/observe-label-policy.d.ts +8 -0
- package/dist/commands/observe-label-policy.d.ts.map +1 -0
- package/dist/commands/observe-label-policy.js +21 -0
- package/dist/commands/observe-page-state.d.ts +1 -1
- package/dist/commands/observe-page-state.d.ts.map +1 -1
- package/dist/commands/observe-persistence.d.ts.map +1 -1
- package/dist/commands/observe-persistence.js +10 -3
- package/dist/commands/observe-projection.d.ts +2 -1
- package/dist/commands/observe-projection.d.ts.map +1 -1
- package/dist/commands/observe-projection.js +5 -2
- package/dist/commands/observe-semantics.d.ts.map +1 -1
- package/dist/commands/observe-semantics.js +18 -1
- package/dist/commands/observe-signals.d.ts +48 -0
- package/dist/commands/observe-signals.d.ts.map +1 -0
- package/dist/commands/observe-signals.js +461 -0
- package/dist/commands/observe-stagehand.d.ts.map +1 -1
- package/dist/commands/observe-stagehand.js +5 -18
- package/dist/commands/observe-surfaces.d.ts.map +1 -1
- package/dist/commands/observe-surfaces.js +5 -4
- package/dist/commands/observe.d.ts +0 -6
- package/dist/commands/observe.d.ts.map +1 -1
- package/dist/commands/observe.js +30 -6
- package/dist/commands/observe.test-harness.d.ts +0 -6
- package/dist/commands/observe.test-harness.d.ts.map +1 -1
- package/dist/commands/screenshot.d.ts.map +1 -1
- package/dist/commands/select-action-executor.d.ts.map +1 -1
- package/dist/commands/select-action-executor.js +1 -1
- package/dist/commands/semantic-observe.d.ts.map +1 -1
- package/dist/commands/semantic-observe.js +1 -4
- package/dist/commands/status.d.ts.map +1 -1
- package/dist/commands/text-input-action-executor.d.ts.map +1 -1
- package/dist/commands/text-input-action-executor.js +1 -1
- package/dist/control-semantics.d.ts.map +1 -1
- package/dist/control-semantics.js +2 -6
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +30 -9
- package/dist/owned-process.d.ts +4 -0
- package/dist/owned-process.d.ts.map +1 -0
- package/dist/owned-process.js +57 -0
- package/dist/playwright-runtime.d.ts.map +1 -1
- package/dist/playwright-runtime.js +3 -4
- package/dist/runtime-state.d.ts +3 -0
- package/dist/runtime-state.d.ts.map +1 -1
- package/dist/runtime-state.js +3 -0
- package/dist/secrets/catalog-applicability.d.ts.map +1 -1
- package/dist/secrets/form-matcher.d.ts.map +1 -1
- package/dist/secrets/form-matcher.js +2 -1
- package/dist/secrets/mock-agentpay-backend.d.ts +1 -1
- package/dist/secrets/mock-agentpay-backend.d.ts.map +1 -1
- package/dist/secrets/mock-agentpay-backend.js +2 -4
- package/dist/secrets/mock-agentpay-cabinet.d.ts.map +1 -1
- package/dist/secrets/mock-agentpay-cabinet.js +6 -1
- package/dist/secrets/protected-field-values.d.ts.map +1 -1
- package/dist/secrets/protected-field-values.js +1 -1
- package/dist/secrets/protected-fill.d.ts.map +1 -1
- package/dist/secrets/protected-fill.js +16 -4
- package/dist/session.d.ts +13 -0
- package/dist/session.d.ts.map +1 -1
- package/dist/session.js +52 -7
- package/dist/solver/captcha-detector.d.ts.map +1 -1
- package/dist/solver/types.d.ts +2 -0
- package/dist/solver/types.d.ts.map +1 -1
- package/package.json +3 -3
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { OBSERVE_DOM_LABEL_CONTRACT_HELPER_SCRIPT } from './observe-dom-label-contract.js';
|
|
1
2
|
export function normalizePageSignature(url) {
|
|
2
3
|
try {
|
|
3
4
|
const parsed = new URL(url);
|
|
@@ -8,184 +9,30 @@ export function normalizePageSignature(url) {
|
|
|
8
9
|
}
|
|
9
10
|
}
|
|
10
11
|
export const LOCATOR_DOM_SIGNATURE_SCRIPT = String.raw `
|
|
12
|
+
${OBSERVE_DOM_LABEL_CONTRACT_HELPER_SCRIPT}
|
|
11
13
|
if (!(element instanceof HTMLElement)) {
|
|
12
14
|
return null;
|
|
13
15
|
}
|
|
14
16
|
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
['button', 'submit', 'reset'].includes(inputTypeOf());
|
|
23
|
-
};
|
|
24
|
-
|
|
25
|
-
const inferRole = () => {
|
|
26
|
-
const explicitRole = element.getAttribute('role')?.trim();
|
|
27
|
-
if (explicitRole) return explicitRole;
|
|
28
|
-
|
|
29
|
-
const tag = element.tagName.toLowerCase();
|
|
30
|
-
if (tag === 'button') return 'button';
|
|
31
|
-
if (tag === 'a' && element.getAttribute('href')) return 'link';
|
|
32
|
-
if (tag === 'select') return 'combobox';
|
|
33
|
-
if (tag === 'textarea') return 'textbox';
|
|
34
|
-
if (tag === 'input') {
|
|
35
|
-
if (isButtonLikeInput()) return 'button';
|
|
36
|
-
return 'textbox';
|
|
37
|
-
}
|
|
38
|
-
return '';
|
|
39
|
-
};
|
|
40
|
-
|
|
41
|
-
const textOf = (target) => {
|
|
42
|
-
if (target === element && isButtonLikeInput()) {
|
|
43
|
-
const value = (element.getAttribute('value') || element.value || '').trim();
|
|
44
|
-
if (value) {
|
|
45
|
-
return value.replace(/\s+/g, ' ');
|
|
46
|
-
}
|
|
47
|
-
}
|
|
48
|
-
const value = target?.innerText?.trim() || target?.textContent?.trim() || '';
|
|
49
|
-
return value.replace(/\s+/g, ' ');
|
|
50
|
-
};
|
|
51
|
-
|
|
52
|
-
const isMeaningfulLabel = (value) => {
|
|
53
|
-
const normalized = (value || '').replace(/\s+/g, ' ').trim();
|
|
54
|
-
return Boolean(normalized) && normalized !== '[object Object]';
|
|
55
|
-
};
|
|
56
|
-
|
|
57
|
-
const composedParentElement = (node) => {
|
|
58
|
-
if (!node) {
|
|
59
|
-
return null;
|
|
60
|
-
}
|
|
61
|
-
if (node.parentElement) {
|
|
62
|
-
return node.parentElement;
|
|
63
|
-
}
|
|
64
|
-
const root = node.getRootNode?.();
|
|
65
|
-
return root instanceof ShadowRoot && root.host instanceof HTMLElement ? root.host : null;
|
|
66
|
-
};
|
|
67
|
-
|
|
68
|
-
const ariaLabelledbyTextOf = () => {
|
|
69
|
-
const labelledBy = element.getAttribute('aria-labelledby')?.trim();
|
|
70
|
-
if (!labelledBy) return undefined;
|
|
71
|
-
|
|
72
|
-
const text = labelledBy
|
|
73
|
-
.split(/\s+/)
|
|
74
|
-
.map((id) => textOf(document.getElementById(id)))
|
|
75
|
-
.filter(Boolean)
|
|
76
|
-
.join(' ')
|
|
77
|
-
.trim();
|
|
78
|
-
|
|
79
|
-
return isMeaningfulLabel(text) ? text : undefined;
|
|
80
|
-
};
|
|
81
|
-
|
|
82
|
-
const nearestFieldLabelOf = () => {
|
|
83
|
-
const anchors = [
|
|
84
|
-
composedParentElement(element),
|
|
85
|
-
composedParentElement(composedParentElement(element)),
|
|
86
|
-
composedParentElement(composedParentElement(composedParentElement(element))),
|
|
87
|
-
].filter(Boolean);
|
|
88
|
-
|
|
89
|
-
for (const anchor of anchors) {
|
|
90
|
-
if (!(anchor instanceof HTMLElement)) continue;
|
|
91
|
-
|
|
92
|
-
const explicitLabels = Array.from(
|
|
93
|
-
anchor.querySelectorAll('label, legend, [class*="label"], [data-testid*="label"]')
|
|
94
|
-
).filter((candidate) => {
|
|
95
|
-
return (
|
|
96
|
-
candidate instanceof HTMLElement &&
|
|
97
|
-
candidate !== element &&
|
|
98
|
-
!candidate.contains(element) &&
|
|
99
|
-
!element.contains(candidate)
|
|
100
|
-
);
|
|
101
|
-
});
|
|
102
|
-
|
|
103
|
-
for (const candidate of explicitLabels) {
|
|
104
|
-
const candidateText = textOf(candidate);
|
|
105
|
-
if (isMeaningfulLabel(candidateText)) {
|
|
106
|
-
return candidateText;
|
|
107
|
-
}
|
|
108
|
-
}
|
|
109
|
-
|
|
110
|
-
for (const child of Array.from(anchor.children).slice(0, 8)) {
|
|
111
|
-
if (!(child instanceof HTMLElement)) continue;
|
|
112
|
-
if (child === element || child.contains(element) || element.contains(child)) continue;
|
|
113
|
-
|
|
114
|
-
const candidateText = textOf(child);
|
|
115
|
-
if (isMeaningfulLabel(candidateText)) {
|
|
116
|
-
return candidateText;
|
|
117
|
-
}
|
|
118
|
-
}
|
|
119
|
-
}
|
|
120
|
-
|
|
121
|
-
return undefined;
|
|
122
|
-
};
|
|
123
|
-
|
|
124
|
-
const syntheticLabelOf = () => {
|
|
125
|
-
const tag = element.tagName.toLowerCase();
|
|
126
|
-
const explicitRole = element.getAttribute('role')?.trim();
|
|
127
|
-
if (tag === 'input') {
|
|
128
|
-
const inputType = inputTypeOf();
|
|
129
|
-
if (isButtonLikeInput()) return 'Button';
|
|
130
|
-
if (inputType === 'tel') return 'Phone input';
|
|
131
|
-
if (inputType === 'email') return 'Email input';
|
|
132
|
-
if (inputType === 'password') return 'Password input';
|
|
133
|
-
if (inputType === 'search') return 'Search input';
|
|
134
|
-
if (inputType === 'date') return 'Date input';
|
|
135
|
-
return 'Text input';
|
|
136
|
-
}
|
|
137
|
-
if (tag === 'textarea') return 'Text area';
|
|
138
|
-
if (tag === 'select' || explicitRole === 'combobox') return 'Combobox';
|
|
139
|
-
if (explicitRole === 'textbox') return 'Text input';
|
|
140
|
-
return undefined;
|
|
141
|
-
};
|
|
142
|
-
|
|
143
|
-
const labelOf = () => {
|
|
144
|
-
const ariaLabel = element.getAttribute('aria-label')?.trim();
|
|
145
|
-
if (isMeaningfulLabel(ariaLabel)) return ariaLabel;
|
|
146
|
-
|
|
147
|
-
const customLabel = element.getAttribute('label')?.trim();
|
|
148
|
-
if (isMeaningfulLabel(customLabel)) return customLabel;
|
|
149
|
-
|
|
150
|
-
const ariaLabelledbyText = ariaLabelledbyTextOf();
|
|
151
|
-
if (isMeaningfulLabel(ariaLabelledbyText)) return ariaLabelledbyText;
|
|
152
|
-
|
|
153
|
-
if ('labels' in element && Array.isArray(Array.from(element.labels ?? []))) {
|
|
154
|
-
const labels = element.labels;
|
|
155
|
-
const firstLabel = labels && labels.length > 0 ? textOf(labels[0]) : '';
|
|
156
|
-
if (isMeaningfulLabel(firstLabel)) return firstLabel;
|
|
157
|
-
}
|
|
158
|
-
|
|
159
|
-
const text = textOf(element);
|
|
160
|
-
if (isMeaningfulLabel(text)) return text;
|
|
161
|
-
|
|
162
|
-
const nearestFieldLabel = isButtonLikeInput() ? undefined : nearestFieldLabelOf();
|
|
163
|
-
if (isMeaningfulLabel(nearestFieldLabel)) return nearestFieldLabel;
|
|
164
|
-
|
|
165
|
-
return syntheticLabelOf() || '';
|
|
166
|
-
};
|
|
167
|
-
|
|
168
|
-
return element.tagName.toLowerCase() + '|' + inferRole() + '|' + labelOf();
|
|
17
|
+
return (
|
|
18
|
+
element.tagName.toLowerCase() +
|
|
19
|
+
'|' +
|
|
20
|
+
observedInferRole(element) +
|
|
21
|
+
'|' +
|
|
22
|
+
observedLabelOf(element)
|
|
23
|
+
);
|
|
169
24
|
`;
|
|
170
25
|
function readLocatorDomSignatureInBrowser(element) {
|
|
171
26
|
return Function('element', LOCATOR_DOM_SIGNATURE_SCRIPT)(element);
|
|
172
27
|
}
|
|
173
28
|
const LOCATOR_BINDING_SNAPSHOT_SCRIPT = String.raw `
|
|
29
|
+
${OBSERVE_DOM_LABEL_CONTRACT_HELPER_SCRIPT}
|
|
174
30
|
if (!(element instanceof HTMLElement)) {
|
|
175
31
|
return null;
|
|
176
32
|
}
|
|
177
33
|
|
|
178
34
|
const read = Function('element', ${JSON.stringify(LOCATOR_DOM_SIGNATURE_SCRIPT)});
|
|
179
|
-
const
|
|
180
|
-
const role =
|
|
181
|
-
explicitRole ||
|
|
182
|
-
(element.tagName.toLowerCase() === 'select'
|
|
183
|
-
? 'combobox'
|
|
184
|
-
: element.tagName.toLowerCase() === 'textarea'
|
|
185
|
-
? 'textbox'
|
|
186
|
-
: element.tagName.toLowerCase() === 'input'
|
|
187
|
-
? 'textbox'
|
|
188
|
-
: '');
|
|
35
|
+
const role = observedInferRole(element);
|
|
189
36
|
const domSignature = read(element);
|
|
190
37
|
const label = typeof domSignature === 'string' ? domSignature.split('|').slice(2).join('|') : undefined;
|
|
191
38
|
return {
|
|
@@ -287,14 +134,8 @@ export async function readLocatorOuterHtml(locator) {
|
|
|
287
134
|
'track',
|
|
288
135
|
'wbr',
|
|
289
136
|
]);
|
|
290
|
-
const escapeText = (value) => value
|
|
291
|
-
|
|
292
|
-
.replace(/</g, '<')
|
|
293
|
-
.replace(/>/g, '>');
|
|
294
|
-
const escapeAttribute = (value) => value
|
|
295
|
-
.replace(/&/g, '&')
|
|
296
|
-
.replace(/"/g, '"')
|
|
297
|
-
.replace(/</g, '<');
|
|
137
|
+
const escapeText = (value) => value.replace(/&/g, '&').replace(/</g, '<').replace(/>/g, '>');
|
|
138
|
+
const escapeAttribute = (value) => value.replace(/&/g, '&').replace(/"/g, '"').replace(/</g, '<');
|
|
298
139
|
const serializeChildren = (parent) => Array.from(parent.childNodes)
|
|
299
140
|
.map((node) => serializeNode(node))
|
|
300
141
|
.join('');
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"extract-scoped-dialog-text.d.ts","sourceRoot":"","sources":["../../src/commands/extract-scoped-dialog-text.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,iBAAiB,CAAC;AAuM5C,wBAAsB,oBAAoB,CAAC,IAAI,EAAE,IAAI,EAAE,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,
|
|
1
|
+
{"version":3,"file":"extract-scoped-dialog-text.d.ts","sourceRoot":"","sources":["../../src/commands/extract-scoped-dialog-text.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,iBAAiB,CAAC;AAuM5C,wBAAsB,oBAAoB,CAAC,IAAI,EAAE,IAAI,EAAE,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAsB/F"}
|
|
@@ -196,10 +196,7 @@ export async function readScopedDialogText(page, selector) {
|
|
|
196
196
|
return null;
|
|
197
197
|
}
|
|
198
198
|
try {
|
|
199
|
-
const result = await page
|
|
200
|
-
.locator(selector)
|
|
201
|
-
.first()
|
|
202
|
-
.evaluate(readScopedDialogTextInBrowser, {
|
|
199
|
+
const result = await page.locator(selector).first().evaluate(readScopedDialogTextInBrowser, {
|
|
203
200
|
marker: BLOCK_MARKER,
|
|
204
201
|
anchorSelector: ANCHOR_SELECTOR,
|
|
205
202
|
minBlockTextLength: 20,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"extract-snapshot-sanitizer.d.ts","sourceRoot":"","sources":["../../src/commands/extract-snapshot-sanitizer.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"extract-snapshot-sanitizer.d.ts","sourceRoot":"","sources":["../../src/commands/extract-snapshot-sanitizer.ts"],"names":[],"mappings":"AAuEA,wBAAgB,uBAAuB,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,CAQhE"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"extract-stagehand-executor.d.ts","sourceRoot":"","sources":["../../src/commands/extract-stagehand-executor.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,iBAAiB,CAAC;AAI5C,OAAO,KAAK,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAC7B,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,eAAe,CAAC;AAEnD,OAAO,EAA8B,KAAK,iBAAiB,EAAE,MAAM,0BAA0B,CAAC;AAI9F,MAAM,MAAM,yBAAyB,GAAG,iBAAiB,GAAG;IAC1D,IAAI,EAAE,OAAO,CAAC;IACd,UAAU,EAAE,mBAAmB,CAAC;CACjC,CAAC;
|
|
1
|
+
{"version":3,"file":"extract-stagehand-executor.d.ts","sourceRoot":"","sources":["../../src/commands/extract-stagehand-executor.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,iBAAiB,CAAC;AAI5C,OAAO,KAAK,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAC7B,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,eAAe,CAAC;AAEnD,OAAO,EAA8B,KAAK,iBAAiB,EAAE,MAAM,0BAA0B,CAAC;AAI9F,MAAM,MAAM,yBAAyB,GAAG,iBAAiB,GAAG;IAC1D,IAAI,EAAE,OAAO,CAAC;IACd,UAAU,EAAE,mBAAmB,CAAC;CACjC,CAAC;AAiBF,wBAAsB,uBAAuB,CAAC,IAAI,EAAE;IAClD,OAAO,EAAE,aAAa,CAAC;IACvB,WAAW,EAAE,MAAM,CAAC;IACpB,MAAM,EAAE,CAAC,CAAC,OAAO,CAAC;IAClB,IAAI,EAAE,IAAI,CAAC;IACX,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,iBAAiB,CAAC,EAAE,MAAM,CAAC;CAC5B,GAAG,OAAO,CAAC,yBAAyB,CAAC,CAsDrC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"extract.d.ts","sourceRoot":"","sources":["../../src/commands/extract.ts"],"names":[],"mappings":"AAAA;;GAEG;AAIH,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,eAAe,CAAC;
|
|
1
|
+
{"version":3,"file":"extract.d.ts","sourceRoot":"","sources":["../../src/commands/extract.ts"],"names":[],"mappings":"AAAA;;GAEG;AAIH,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,eAAe,CAAC;AAmMnD,wBAAsB,OAAO,CAC3B,OAAO,EAAE,aAAa,EACtB,UAAU,EAAE,MAAM,EAClB,QAAQ,CAAC,EAAE,MAAM,GAChB,OAAO,CAAC,IAAI,CAAC,CAkMf"}
|
package/dist/commands/extract.js
CHANGED
|
@@ -44,10 +44,14 @@ function describeSchema(descriptor, prefix) {
|
|
|
44
44
|
}
|
|
45
45
|
if (Array.isArray(value)) {
|
|
46
46
|
const itemDescriptor = value[0];
|
|
47
|
-
if (itemDescriptor === 'string' ||
|
|
47
|
+
if (itemDescriptor === 'string' ||
|
|
48
|
+
itemDescriptor === 'number' ||
|
|
49
|
+
itemDescriptor === 'boolean') {
|
|
48
50
|
lines.push(`${fieldPath}[]: ${itemDescriptor}`);
|
|
49
51
|
}
|
|
50
|
-
else if (typeof itemDescriptor === 'object' &&
|
|
52
|
+
else if (typeof itemDescriptor === 'object' &&
|
|
53
|
+
itemDescriptor !== null &&
|
|
54
|
+
!Array.isArray(itemDescriptor)) {
|
|
51
55
|
lines.push(...describeSchema(itemDescriptor, `${fieldPath}[]`));
|
|
52
56
|
}
|
|
53
57
|
else {
|
|
@@ -98,8 +102,23 @@ function canUseTargetAsExtractScope(target) {
|
|
|
98
102
|
'form',
|
|
99
103
|
'group',
|
|
100
104
|
]);
|
|
101
|
-
const leafInteractiveKinds = new Set([
|
|
102
|
-
|
|
105
|
+
const leafInteractiveKinds = new Set([
|
|
106
|
+
'input',
|
|
107
|
+
'textarea',
|
|
108
|
+
'select',
|
|
109
|
+
'option',
|
|
110
|
+
'button',
|
|
111
|
+
'link',
|
|
112
|
+
'combobox',
|
|
113
|
+
]);
|
|
114
|
+
const leafInteractiveRoles = new Set([
|
|
115
|
+
'textbox',
|
|
116
|
+
'combobox',
|
|
117
|
+
'option',
|
|
118
|
+
'menuitem',
|
|
119
|
+
'button',
|
|
120
|
+
'link',
|
|
121
|
+
]);
|
|
103
122
|
const iframeFieldLike = Boolean(target.framePath?.length) &&
|
|
104
123
|
(target.allowedActions.includes('fill') ||
|
|
105
124
|
target.allowedActions.includes('type') ||
|
|
@@ -1,10 +1,31 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* browse launch [url] — Start browser session, optionally navigate.
|
|
3
3
|
*/
|
|
4
|
+
import type { SecretCatalogSummary } from '../secrets/catalog-sync.js';
|
|
4
5
|
export type LaunchCliOptions = {
|
|
5
6
|
compact?: boolean;
|
|
6
7
|
profile?: string;
|
|
7
8
|
headless?: boolean;
|
|
9
|
+
proxy?: string;
|
|
10
|
+
noProxy?: boolean;
|
|
8
11
|
};
|
|
9
|
-
export
|
|
12
|
+
export type LaunchSuccessResult = {
|
|
13
|
+
success: true;
|
|
14
|
+
runtime: 'managed';
|
|
15
|
+
captchaSolveCapable: true;
|
|
16
|
+
profile: string;
|
|
17
|
+
cdpUrl: string;
|
|
18
|
+
url: string;
|
|
19
|
+
title: string;
|
|
20
|
+
secretCatalog?: SecretCatalogSummary;
|
|
21
|
+
};
|
|
22
|
+
export type LaunchFailureResult = {
|
|
23
|
+
success: false;
|
|
24
|
+
error: 'browser_launch_failed';
|
|
25
|
+
outcomeType: 'blocked';
|
|
26
|
+
message: 'Browser launch failed.';
|
|
27
|
+
reason: string;
|
|
28
|
+
};
|
|
29
|
+
export type LaunchResult = LaunchSuccessResult | LaunchFailureResult;
|
|
30
|
+
export declare function launch(url?: string, opts?: LaunchCliOptions): Promise<LaunchResult>;
|
|
10
31
|
//# sourceMappingURL=launch.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"launch.d.ts","sourceRoot":"","sources":["../../src/commands/launch.ts"],"names":[],"mappings":"AAAA;;GAEG;
|
|
1
|
+
{"version":3,"file":"launch.d.ts","sourceRoot":"","sources":["../../src/commands/launch.ts"],"names":[],"mappings":"AAAA;;GAEG;AAWH,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,4BAA4B,CAAC;AAWvE,MAAM,MAAM,gBAAgB,GAAG;IAC7B,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,OAAO,CAAC,EAAE,OAAO,CAAC;CACnB,CAAC;AAEF,MAAM,MAAM,mBAAmB,GAAG;IAChC,OAAO,EAAE,IAAI,CAAC;IACd,OAAO,EAAE,SAAS,CAAC;IACnB,mBAAmB,EAAE,IAAI,CAAC;IAC1B,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,EAAE,MAAM,CAAC;IACf,GAAG,EAAE,MAAM,CAAC;IACZ,KAAK,EAAE,MAAM,CAAC;IACd,aAAa,CAAC,EAAE,oBAAoB,CAAC;CACtC,CAAC;AAEF,MAAM,MAAM,mBAAmB,GAAG;IAChC,OAAO,EAAE,KAAK,CAAC;IACf,KAAK,EAAE,uBAAuB,CAAC;IAC/B,WAAW,EAAE,SAAS,CAAC;IACvB,OAAO,EAAE,wBAAwB,CAAC;IAClC,MAAM,EAAE,MAAM,CAAC;CAChB,CAAC;AAEF,MAAM,MAAM,YAAY,GAAG,mBAAmB,GAAG,mBAAmB,CAAC;AAErE,wBAAsB,MAAM,CAAC,GAAG,CAAC,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,gBAAgB,GAAG,OAAO,CAAC,YAAY,CAAC,CAazF"}
|
package/dist/commands/launch.js
CHANGED
|
@@ -1,92 +1,112 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* browse launch [url] — Start browser session, optionally navigate.
|
|
3
3
|
*/
|
|
4
|
-
import { execSync } from 'node:child_process';
|
|
5
4
|
import { readConfig } from '../solver/config.js';
|
|
6
5
|
import { ensureProfile } from '../solver/profile-manager.js';
|
|
7
6
|
import { launchSolver } from '../solver/browser-launcher.js';
|
|
8
|
-
import { saveSession } from '../session.js';
|
|
7
|
+
import { buildOwnedSession, isOwnedSession, loadSession, saveSession } from '../session.js';
|
|
9
8
|
import { findChromePid } from '../stagehand.js';
|
|
10
|
-
import { outputContractFailure, outputJSON } from '../output.js';
|
|
11
9
|
import { applyAgentpayGatewayEnv, tryResolveAgentpayGatewayConfig } from '../agentpay-gateway.js';
|
|
12
10
|
import { connectPlaywright, disconnectPlaywright, syncLaunchPage } from '../playwright-runtime.js';
|
|
13
11
|
import { summarizeSecretCatalog, syncSecretCatalogForUrl } from '../secrets/catalog-sync.js';
|
|
14
|
-
|
|
12
|
+
import { terminateOwnedPid } from '../owned-process.js';
|
|
13
|
+
import { info } from '../output.js';
|
|
15
14
|
const DEFAULT_PROFILE = 'default';
|
|
16
15
|
const COMPACT_WINDOW = {
|
|
17
16
|
width: 1280,
|
|
18
17
|
height: 900,
|
|
19
18
|
};
|
|
20
19
|
export async function launch(url, opts) {
|
|
21
|
-
await
|
|
20
|
+
const cleanupFailure = await cleanupOwnedSession();
|
|
21
|
+
if (cleanupFailure) {
|
|
22
|
+
return cleanupFailure;
|
|
23
|
+
}
|
|
22
24
|
const compact = opts?.compact ?? true;
|
|
23
25
|
const profileName = opts?.profile ?? DEFAULT_PROFILE;
|
|
24
26
|
const headless = opts?.headless ?? false;
|
|
25
|
-
|
|
27
|
+
const proxyOverride = opts?.proxy;
|
|
28
|
+
const noProxy = opts?.noProxy ?? false;
|
|
29
|
+
return launchManaged(url, profileName, headless, compact, proxyOverride, noProxy);
|
|
26
30
|
}
|
|
27
|
-
async function
|
|
28
|
-
const
|
|
29
|
-
if (
|
|
30
|
-
return;
|
|
31
|
-
for (const pid of pids) {
|
|
32
|
-
try {
|
|
33
|
-
process.kill(pid, 'SIGTERM');
|
|
34
|
-
}
|
|
35
|
-
catch {
|
|
36
|
-
// Skip stale PID.
|
|
37
|
-
}
|
|
31
|
+
async function cleanupOwnedSession() {
|
|
32
|
+
const existingSession = loadSession();
|
|
33
|
+
if (!isOwnedSession(existingSession)) {
|
|
34
|
+
return null;
|
|
38
35
|
}
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
await sleep(100);
|
|
36
|
+
const termination = await terminateOwnedPid(existingSession.pid);
|
|
37
|
+
if (termination === 'still_alive') {
|
|
38
|
+
return buildLaunchFailure(new Error(`Existing owned browser pid ${existingSession.pid} did not exit after SIGTERM/SIGKILL.`));
|
|
43
39
|
}
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
40
|
+
return null;
|
|
41
|
+
}
|
|
42
|
+
function buildLaunchFailure(err) {
|
|
43
|
+
return {
|
|
44
|
+
success: false,
|
|
45
|
+
error: 'browser_launch_failed',
|
|
46
|
+
outcomeType: 'blocked',
|
|
47
|
+
message: 'Browser launch failed.',
|
|
48
|
+
reason: formatUnknownError(err),
|
|
49
|
+
};
|
|
50
|
+
}
|
|
51
|
+
function getPortFromCdpUrl(cdpUrl) {
|
|
52
|
+
try {
|
|
53
|
+
const url = new URL(cdpUrl);
|
|
54
|
+
if (!url.port) {
|
|
55
|
+
return undefined;
|
|
50
56
|
}
|
|
57
|
+
const port = Number(url.port);
|
|
58
|
+
return Number.isFinite(port) && port > 0 ? port : undefined;
|
|
59
|
+
}
|
|
60
|
+
catch {
|
|
61
|
+
return undefined;
|
|
51
62
|
}
|
|
52
63
|
}
|
|
53
|
-
async function launchManaged(url, profileName, headless, compact) {
|
|
64
|
+
async function launchManaged(url, profileName, headless, compact, proxyOverride, noProxy = false) {
|
|
54
65
|
let session;
|
|
55
66
|
let browser = null;
|
|
56
67
|
let secretCatalogSummary;
|
|
57
68
|
try {
|
|
58
|
-
const
|
|
69
|
+
const baseProfile = ensureProfile(profileName);
|
|
59
70
|
const config = readConfig();
|
|
71
|
+
const runtimeProxy = resolveLaunchProxy({
|
|
72
|
+
profileProxy: baseProfile.fingerprint.proxy,
|
|
73
|
+
configProxy: config.defaults?.proxy,
|
|
74
|
+
cliProxy: proxyOverride,
|
|
75
|
+
noProxy,
|
|
76
|
+
});
|
|
77
|
+
const profile = withLaunchProxy(baseProfile, runtimeProxy);
|
|
60
78
|
const gateway = tryResolveAgentpayGatewayConfig();
|
|
61
79
|
if (gateway) {
|
|
62
80
|
applyAgentpayGatewayEnv(gateway);
|
|
63
81
|
}
|
|
82
|
+
if (runtimeProxy) {
|
|
83
|
+
info(`[launch] starting browser with proxy ${runtimeProxy.server}`);
|
|
84
|
+
}
|
|
64
85
|
session = await launchSolver(profile, {
|
|
65
86
|
headless: headless || config.defaults?.headless,
|
|
66
87
|
url,
|
|
67
|
-
cdpPort:
|
|
88
|
+
cdpPort: undefined,
|
|
68
89
|
windowSize: compact ? COMPACT_WINDOW : undefined,
|
|
69
90
|
});
|
|
70
91
|
}
|
|
71
92
|
catch (err) {
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
93
|
+
return buildLaunchFailure(err);
|
|
94
|
+
}
|
|
95
|
+
const cdpPort = getPortFromCdpUrl(session.cdpUrl);
|
|
96
|
+
const chromePid = findChromePid(cdpPort);
|
|
97
|
+
if (!chromePid) {
|
|
98
|
+
await session.close().catch(() => undefined);
|
|
99
|
+
return buildLaunchFailure(new Error('Launched browser PID could not be resolved.'));
|
|
78
100
|
}
|
|
79
|
-
const
|
|
80
|
-
const persistedSession = {
|
|
101
|
+
const persistedSession = buildOwnedSession({
|
|
81
102
|
cdpUrl: session.cdpUrl,
|
|
82
103
|
pid: chromePid,
|
|
83
|
-
port: CDP_PORT,
|
|
84
104
|
profile: profileName,
|
|
85
105
|
launchedAt: new Date().toISOString(),
|
|
86
106
|
capabilities: {
|
|
87
107
|
captchaSolve: true,
|
|
88
108
|
},
|
|
89
|
-
};
|
|
109
|
+
});
|
|
90
110
|
saveSession(persistedSession);
|
|
91
111
|
let currentUrl = session.page.url();
|
|
92
112
|
let title = await session.page.title().catch(() => '');
|
|
@@ -111,10 +131,12 @@ async function launchManaged(url, profileName, headless, compact) {
|
|
|
111
131
|
}
|
|
112
132
|
if (url) {
|
|
113
133
|
const snapshot = await syncSecretCatalogForUrl(persistedSession, currentUrl || url);
|
|
114
|
-
|
|
134
|
+
if (snapshot) {
|
|
135
|
+
secretCatalogSummary = summarizeSecretCatalog(snapshot);
|
|
136
|
+
}
|
|
115
137
|
}
|
|
116
138
|
saveSession(persistedSession);
|
|
117
|
-
|
|
139
|
+
return {
|
|
118
140
|
success: true,
|
|
119
141
|
runtime: 'managed',
|
|
120
142
|
captchaSolveCapable: true,
|
|
@@ -123,21 +145,7 @@ async function launchManaged(url, profileName, headless, compact) {
|
|
|
123
145
|
url: currentUrl,
|
|
124
146
|
title,
|
|
125
147
|
secretCatalog: secretCatalogSummary,
|
|
126
|
-
}
|
|
127
|
-
}
|
|
128
|
-
function getPidsOnPort(port) {
|
|
129
|
-
try {
|
|
130
|
-
const out = execSync(`lsof -ti :${port}`, { encoding: 'utf-8' }).trim();
|
|
131
|
-
if (!out)
|
|
132
|
-
return [];
|
|
133
|
-
return out
|
|
134
|
-
.split('\n')
|
|
135
|
-
.map((value) => Number(value))
|
|
136
|
-
.filter((value) => Number.isFinite(value) && value > 0);
|
|
137
|
-
}
|
|
138
|
-
catch {
|
|
139
|
-
return [];
|
|
140
|
-
}
|
|
148
|
+
};
|
|
141
149
|
}
|
|
142
150
|
function formatUnknownError(err) {
|
|
143
151
|
if (err instanceof Error)
|
|
@@ -154,6 +162,61 @@ function formatUnknownError(err) {
|
|
|
154
162
|
}
|
|
155
163
|
return String(err);
|
|
156
164
|
}
|
|
157
|
-
function
|
|
158
|
-
|
|
165
|
+
function resolveLaunchProxy(options) {
|
|
166
|
+
if (options.noProxy) {
|
|
167
|
+
return undefined;
|
|
168
|
+
}
|
|
169
|
+
if (options.cliProxy) {
|
|
170
|
+
return normalizeProxySetting(options.cliProxy);
|
|
171
|
+
}
|
|
172
|
+
if (options.configProxy) {
|
|
173
|
+
return normalizeProxySetting(options.configProxy);
|
|
174
|
+
}
|
|
175
|
+
if (options.profileProxy) {
|
|
176
|
+
return normalizeProxySetting(options.profileProxy);
|
|
177
|
+
}
|
|
178
|
+
return undefined;
|
|
179
|
+
}
|
|
180
|
+
function withLaunchProxy(profile, proxy) {
|
|
181
|
+
return {
|
|
182
|
+
...profile,
|
|
183
|
+
fingerprint: {
|
|
184
|
+
...profile.fingerprint,
|
|
185
|
+
proxy,
|
|
186
|
+
},
|
|
187
|
+
};
|
|
188
|
+
}
|
|
189
|
+
function normalizeProxySetting(value) {
|
|
190
|
+
if (typeof value === 'string') {
|
|
191
|
+
return parseProxyString(value);
|
|
192
|
+
}
|
|
193
|
+
const normalized = parseProxyString(value.server);
|
|
194
|
+
const username = value.username?.trim() || normalized.username;
|
|
195
|
+
const password = value.password ?? normalized.password;
|
|
196
|
+
return {
|
|
197
|
+
server: normalized.server,
|
|
198
|
+
...(username ? { username } : {}),
|
|
199
|
+
...(password ? { password } : {}),
|
|
200
|
+
};
|
|
201
|
+
}
|
|
202
|
+
function parseProxyString(value) {
|
|
203
|
+
const trimmed = value.trim();
|
|
204
|
+
if (!trimmed) {
|
|
205
|
+
throw new Error('Proxy value must not be empty.');
|
|
206
|
+
}
|
|
207
|
+
try {
|
|
208
|
+
const parsed = new URL(trimmed);
|
|
209
|
+
const server = `${parsed.protocol}//${parsed.host}`;
|
|
210
|
+
if (!parsed.hostname) {
|
|
211
|
+
throw new Error('missing hostname');
|
|
212
|
+
}
|
|
213
|
+
return {
|
|
214
|
+
server,
|
|
215
|
+
...(parsed.username ? { username: decodeURIComponent(parsed.username) } : {}),
|
|
216
|
+
...(parsed.password ? { password: decodeURIComponent(parsed.password) } : {}),
|
|
217
|
+
};
|
|
218
|
+
}
|
|
219
|
+
catch {
|
|
220
|
+
return { server: trimmed };
|
|
221
|
+
}
|
|
159
222
|
}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import type { Page } from 'playwright-core';
|
|
2
|
+
import type { DomObservedTarget } from './observe-inventory.js';
|
|
3
|
+
type ObserveAccessibilitySemantics = {
|
|
4
|
+
role?: string;
|
|
5
|
+
name?: string;
|
|
6
|
+
states?: Record<string, string | boolean | number>;
|
|
7
|
+
};
|
|
8
|
+
export type ObserveAccessibilityStats = {
|
|
9
|
+
axAttempts: number;
|
|
10
|
+
axHits: number;
|
|
11
|
+
fallbackUses: number;
|
|
12
|
+
};
|
|
13
|
+
type ObserveAccessibilityOptions = {
|
|
14
|
+
onStats?: (stats: ObserveAccessibilityStats) => void;
|
|
15
|
+
};
|
|
16
|
+
export declare function parseObserveAriaSnapshot(snapshot: string | undefined | null): ObserveAccessibilitySemantics | null;
|
|
17
|
+
export declare function enrichDomTargetsWithAccessibility(page: Page, targets: ReadonlyArray<DomObservedTarget>, options?: ObserveAccessibilityOptions): Promise<DomObservedTarget[]>;
|
|
18
|
+
export declare const __testObserveAccessibility: {
|
|
19
|
+
parseObserveAriaSnapshot: typeof parseObserveAriaSnapshot;
|
|
20
|
+
};
|
|
21
|
+
export {};
|
|
22
|
+
//# sourceMappingURL=observe-accessibility.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"observe-accessibility.d.ts","sourceRoot":"","sources":["../../src/commands/observe-accessibility.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,iBAAiB,CAAC;AAS5C,OAAO,KAAK,EAEV,iBAAiB,EAElB,MAAM,wBAAwB,CAAC;AAEhC,KAAK,6BAA6B,GAAG;IACnC,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,GAAG,MAAM,CAAC,CAAC;CACpD,CAAC;AAOF,MAAM,MAAM,yBAAyB,GAAG;IACtC,UAAU,EAAE,MAAM,CAAC;IACnB,MAAM,EAAE,MAAM,CAAC;IACf,YAAY,EAAE,MAAM,CAAC;CACtB,CAAC;AAEF,KAAK,2BAA2B,GAAG;IACjC,OAAO,CAAC,EAAE,CAAC,KAAK,EAAE,yBAAyB,KAAK,IAAI,CAAC;CACtD,CAAC;AAyDF,wBAAgB,wBAAwB,CACtC,QAAQ,EAAE,MAAM,GAAG,SAAS,GAAG,IAAI,GAClC,6BAA6B,GAAG,IAAI,CAoDtC;AA+nBD,wBAAsB,iCAAiC,CACrD,IAAI,EAAE,IAAI,EACV,OAAO,EAAE,aAAa,CAAC,iBAAiB,CAAC,EACzC,OAAO,CAAC,EAAE,2BAA2B,GACpC,OAAO,CAAC,iBAAiB,EAAE,CAAC,CAY9B;AAED,eAAO,MAAM,0BAA0B;;CAEtC,CAAC"}
|