@nuanu-ai/agentbrowse 0.2.7 → 0.2.8
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 +36 -8
- package/dist/agentpay-stagehand-llm.d.ts.map +1 -1
- package/dist/agentpay-stagehand-llm.js +5 -1
- package/dist/commands/act.d.ts +6 -2
- package/dist/commands/act.d.ts.map +1 -1
- package/dist/commands/act.js +840 -55
- package/dist/commands/act.test-harness.d.ts +19 -0
- package/dist/commands/act.test-harness.d.ts.map +1 -0
- package/dist/commands/act.test-harness.js +245 -0
- package/dist/commands/action-acceptance.d.ts +90 -0
- package/dist/commands/action-acceptance.d.ts.map +1 -0
- package/dist/commands/action-acceptance.js +1411 -0
- package/dist/commands/action-artifacts.d.ts +33 -0
- package/dist/commands/action-artifacts.d.ts.map +1 -0
- package/dist/commands/action-artifacts.js +104 -0
- package/dist/commands/action-execution-guards.d.ts +5 -0
- package/dist/commands/action-execution-guards.d.ts.map +1 -0
- package/dist/commands/action-execution-guards.js +3 -0
- package/dist/commands/action-executor-helpers.d.ts +21 -0
- package/dist/commands/action-executor-helpers.d.ts.map +1 -0
- package/dist/commands/action-executor-helpers.js +242 -0
- package/dist/commands/action-executor.d.ts +12 -0
- package/dist/commands/action-executor.d.ts.map +1 -0
- package/dist/commands/action-executor.js +45 -0
- package/dist/commands/action-fallbacks.d.ts +6 -0
- package/dist/commands/action-fallbacks.d.ts.map +1 -0
- package/dist/commands/action-fallbacks.js +43 -0
- package/dist/commands/action-value-projection.d.ts +32 -0
- package/dist/commands/action-value-projection.d.ts.map +1 -0
- package/dist/commands/action-value-projection.js +151 -0
- package/dist/commands/browse-actions.d.ts +4 -0
- package/dist/commands/browse-actions.d.ts.map +1 -0
- package/dist/commands/browse-actions.js +4 -0
- package/dist/commands/captcha-solve.d.ts.map +1 -1
- package/dist/commands/captcha-solve.js +13 -3
- package/dist/commands/click-action-executor.d.ts +10 -0
- package/dist/commands/click-action-executor.d.ts.map +1 -0
- package/dist/commands/click-action-executor.js +68 -0
- package/dist/commands/create-intent.d.ts +6 -0
- package/dist/commands/create-intent.d.ts.map +1 -0
- package/dist/commands/create-intent.js +75 -0
- package/dist/commands/datepicker-action-executor.d.ts +12 -0
- package/dist/commands/datepicker-action-executor.d.ts.map +1 -0
- package/dist/commands/datepicker-action-executor.js +218 -0
- package/dist/commands/descriptor-validation.d.ts +27 -0
- package/dist/commands/descriptor-validation.d.ts.map +1 -0
- package/dist/commands/descriptor-validation.js +333 -0
- package/dist/commands/extract-scope-resolution.d.ts +20 -0
- package/dist/commands/extract-scope-resolution.d.ts.map +1 -0
- package/dist/commands/extract-scope-resolution.js +100 -0
- package/dist/commands/extract-stagehand-executor.d.ts +17 -0
- package/dist/commands/extract-stagehand-executor.d.ts.map +1 -0
- package/dist/commands/extract-stagehand-executor.js +18 -0
- package/dist/commands/extract.d.ts +3 -2
- package/dist/commands/extract.d.ts.map +1 -1
- package/dist/commands/extract.js +256 -39
- package/dist/commands/fill-secret.d.ts +7 -0
- package/dist/commands/fill-secret.d.ts.map +1 -0
- package/dist/commands/fill-secret.js +371 -0
- package/dist/commands/get-secrets-catalog.d.ts +6 -0
- package/dist/commands/get-secrets-catalog.d.ts.map +1 -0
- package/dist/commands/get-secrets-catalog.js +23 -0
- package/dist/commands/launch.d.ts.map +1 -1
- package/dist/commands/launch.js +41 -7
- package/dist/commands/navigate.d.ts +2 -1
- package/dist/commands/navigate.d.ts.map +1 -1
- package/dist/commands/navigate.js +49 -12
- package/dist/commands/observe-inventory.d.ts +109 -0
- package/dist/commands/observe-inventory.d.ts.map +1 -0
- package/dist/commands/observe-inventory.js +2837 -0
- package/dist/commands/observe-persistence.d.ts +14 -0
- package/dist/commands/observe-persistence.d.ts.map +1 -0
- package/dist/commands/observe-persistence.js +170 -0
- package/dist/commands/observe-projection.d.ts +84 -0
- package/dist/commands/observe-projection.d.ts.map +1 -0
- package/dist/commands/observe-projection.js +140 -0
- package/dist/commands/observe-protected.d.ts +5 -0
- package/dist/commands/observe-protected.d.ts.map +1 -0
- package/dist/commands/observe-protected.js +18 -0
- package/dist/commands/observe-semantics.d.ts +10 -0
- package/dist/commands/observe-semantics.d.ts.map +1 -0
- package/dist/commands/observe-semantics.js +338 -0
- package/dist/commands/observe-stagehand.d.ts +48 -0
- package/dist/commands/observe-stagehand.d.ts.map +1 -0
- package/dist/commands/observe-stagehand.js +105 -0
- package/dist/commands/observe-surfaces.d.ts +9 -0
- package/dist/commands/observe-surfaces.d.ts.map +1 -0
- package/dist/commands/observe-surfaces.js +195 -0
- package/dist/commands/observe.d.ts +47 -1
- package/dist/commands/observe.d.ts.map +1 -1
- package/dist/commands/observe.js +173 -20
- package/dist/commands/observe.test-harness.d.ts +67 -0
- package/dist/commands/observe.test-harness.d.ts.map +1 -0
- package/dist/commands/observe.test-harness.js +107 -0
- package/dist/commands/poll-intent.d.ts +6 -0
- package/dist/commands/poll-intent.d.ts.map +1 -0
- package/dist/commands/poll-intent.js +57 -0
- package/dist/commands/screenshot.d.ts +2 -1
- package/dist/commands/screenshot.d.ts.map +1 -1
- package/dist/commands/screenshot.js +44 -12
- package/dist/commands/select-action-executor.d.ts +10 -0
- package/dist/commands/select-action-executor.d.ts.map +1 -0
- package/dist/commands/select-action-executor.js +91 -0
- package/dist/commands/semantic-observe.d.ts +24 -0
- package/dist/commands/semantic-observe.d.ts.map +1 -0
- package/dist/commands/semantic-observe.js +344 -0
- package/dist/commands/status.d.ts.map +1 -1
- package/dist/commands/status.js +75 -2
- package/dist/commands/structured-grid-action-executor.d.ts +3 -0
- package/dist/commands/structured-grid-action-executor.d.ts.map +1 -0
- package/dist/commands/structured-grid-action-executor.js +4 -0
- package/dist/commands/target-resolution.d.ts +4 -0
- package/dist/commands/target-resolution.d.ts.map +1 -0
- package/dist/commands/target-resolution.js +33 -0
- package/dist/commands/text-input-action-executor.d.ts +5 -0
- package/dist/commands/text-input-action-executor.d.ts.map +1 -0
- package/dist/commands/text-input-action-executor.js +116 -0
- package/dist/commands/user-actionable.d.ts +4 -0
- package/dist/commands/user-actionable.d.ts.map +1 -0
- package/dist/commands/user-actionable.js +95 -0
- package/dist/control-semantics.d.ts +29 -0
- package/dist/control-semantics.d.ts.map +1 -0
- package/dist/control-semantics.js +299 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +95 -32
- package/dist/output.d.ts +14 -2
- package/dist/output.d.ts.map +1 -1
- package/dist/output.js +17 -29
- package/dist/playwright-runtime.d.ts +35 -0
- package/dist/playwright-runtime.d.ts.map +1 -0
- package/dist/playwright-runtime.js +224 -0
- package/dist/runtime-resolution.d.ts +9 -0
- package/dist/runtime-resolution.d.ts.map +1 -0
- package/dist/runtime-resolution.js +19 -0
- package/dist/runtime-state.d.ts +217 -0
- package/dist/runtime-state.d.ts.map +1 -0
- package/dist/runtime-state.js +629 -0
- package/dist/secrets/backend.d.ts +32 -0
- package/dist/secrets/backend.d.ts.map +1 -0
- package/dist/secrets/backend.js +169 -0
- package/dist/secrets/catalog-applicability.d.ts +5 -0
- package/dist/secrets/catalog-applicability.d.ts.map +1 -0
- package/dist/secrets/catalog-applicability.js +59 -0
- package/dist/secrets/catalog-sync.d.ts +14 -0
- package/dist/secrets/catalog-sync.d.ts.map +1 -0
- package/dist/secrets/catalog-sync.js +35 -0
- package/dist/secrets/field-policy.d.ts +3 -0
- package/dist/secrets/field-policy.d.ts.map +1 -0
- package/dist/secrets/field-policy.js +3 -0
- package/dist/secrets/fill-ordering.d.ts +11 -0
- package/dist/secrets/fill-ordering.d.ts.map +1 -0
- package/dist/secrets/fill-ordering.js +44 -0
- package/dist/secrets/form-matcher.d.ts +60 -0
- package/dist/secrets/form-matcher.d.ts.map +1 -0
- package/dist/secrets/form-matcher.js +596 -0
- package/dist/secrets/intent-output.d.ts +11 -0
- package/dist/secrets/intent-output.d.ts.map +1 -0
- package/dist/secrets/intent-output.js +64 -0
- package/dist/secrets/mock-agentpay-backend.d.ts +13 -0
- package/dist/secrets/mock-agentpay-backend.d.ts.map +1 -0
- package/dist/secrets/mock-agentpay-backend.js +87 -0
- package/dist/secrets/mock-agentpay-cabinet.d.ts +43 -0
- package/dist/secrets/mock-agentpay-cabinet.d.ts.map +1 -0
- package/dist/secrets/mock-agentpay-cabinet.js +195 -0
- package/dist/secrets/protected-artifact-guard.d.ts +25 -0
- package/dist/secrets/protected-artifact-guard.d.ts.map +1 -0
- package/dist/secrets/protected-artifact-guard.js +26 -0
- package/dist/secrets/protected-bindings.d.ts +10 -0
- package/dist/secrets/protected-bindings.d.ts.map +1 -0
- package/dist/secrets/protected-bindings.js +17 -0
- package/dist/secrets/protected-field-values.d.ts +13 -0
- package/dist/secrets/protected-field-values.d.ts.map +1 -0
- package/dist/secrets/protected-field-values.js +100 -0
- package/dist/secrets/protected-fill.d.ts +47 -0
- package/dist/secrets/protected-fill.d.ts.map +1 -0
- package/dist/secrets/protected-fill.js +512 -0
- package/dist/secrets/types.d.ts +84 -0
- package/dist/secrets/types.d.ts.map +1 -0
- package/dist/secrets/types.js +27 -0
- package/dist/session.d.ts +22 -0
- package/dist/session.d.ts.map +1 -1
- package/dist/session.js +74 -2
- package/dist/solver/browser-launcher.d.ts.map +1 -1
- package/dist/solver/browser-launcher.js +6 -3
- package/dist/stagehand-runtime.d.ts +4 -0
- package/dist/stagehand-runtime.d.ts.map +1 -0
- package/dist/stagehand-runtime.js +10 -0
- package/dist/stagehand.d.ts +0 -5
- package/dist/stagehand.d.ts.map +1 -1
- package/dist/stagehand.js +0 -6
- package/package.json +5 -2
|
@@ -0,0 +1,151 @@
|
|
|
1
|
+
const PHONE_PROJECTION_CONTEXT_SCRIPT = String.raw `
|
|
2
|
+
if (!(element instanceof HTMLElement)) {
|
|
3
|
+
return null;
|
|
4
|
+
}
|
|
5
|
+
|
|
6
|
+
const autocomplete = (element.getAttribute('autocomplete') || '').trim().toLowerCase();
|
|
7
|
+
const inputName = (element.getAttribute('name') || '').trim().toLowerCase();
|
|
8
|
+
const inputType = (element.getAttribute('type') || '').trim().toLowerCase();
|
|
9
|
+
const phoneLike =
|
|
10
|
+
inputType === 'tel' ||
|
|
11
|
+
autocomplete.includes('tel') ||
|
|
12
|
+
inputName.includes('phone');
|
|
13
|
+
const isNationalPhoneInput =
|
|
14
|
+
autocomplete.includes('tel-national') ||
|
|
15
|
+
autocomplete === 'tel-local' ||
|
|
16
|
+
autocomplete === 'tel';
|
|
17
|
+
if (!phoneLike || !isNationalPhoneInput) {
|
|
18
|
+
return null;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
const anchors = [];
|
|
22
|
+
let current = element.parentElement;
|
|
23
|
+
for (let depth = 0; current && depth < 8; depth += 1, current = current.parentElement) {
|
|
24
|
+
anchors.push(current);
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
const extractDialCode = (rawValue) => {
|
|
28
|
+
const normalized = String(rawValue ?? '').replace(/\s+/g, ' ').trim();
|
|
29
|
+
const matches = normalized.match(/\+\d{1,4}/g);
|
|
30
|
+
return matches && matches.length > 0 ? matches[matches.length - 1] : null;
|
|
31
|
+
};
|
|
32
|
+
|
|
33
|
+
for (const anchor of anchors) {
|
|
34
|
+
const companions = Array.from(
|
|
35
|
+
anchor.querySelectorAll('select, input, [role="combobox"]')
|
|
36
|
+
).filter((candidate) => candidate instanceof HTMLElement && candidate !== element);
|
|
37
|
+
|
|
38
|
+
for (const companion of companions) {
|
|
39
|
+
const companionAutocomplete = (
|
|
40
|
+
companion.getAttribute('autocomplete') || ''
|
|
41
|
+
).trim().toLowerCase();
|
|
42
|
+
const companionName = (companion.getAttribute('name') || '').trim().toLowerCase();
|
|
43
|
+
const companionTestId = (
|
|
44
|
+
companion.getAttribute('data-testid') || companion.getAttribute('data-test-id') || ''
|
|
45
|
+
)
|
|
46
|
+
.trim()
|
|
47
|
+
.toLowerCase();
|
|
48
|
+
const companionAriaLabel = (
|
|
49
|
+
companion.getAttribute('aria-label') || ''
|
|
50
|
+
).trim().toLowerCase();
|
|
51
|
+
const isCountryCodeCompanion =
|
|
52
|
+
companionAutocomplete.includes('tel-country-code') ||
|
|
53
|
+
companionName.includes('countrycode') ||
|
|
54
|
+
companionTestId.includes('phone-country-code') ||
|
|
55
|
+
companionAriaLabel.includes('country or region');
|
|
56
|
+
if (!isCountryCodeCompanion) {
|
|
57
|
+
continue;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
if (companion instanceof HTMLSelectElement) {
|
|
61
|
+
const selectedOption = companion.selectedOptions && companion.selectedOptions[0]
|
|
62
|
+
? companion.selectedOptions[0]
|
|
63
|
+
: null;
|
|
64
|
+
const selectedText = (selectedOption && selectedOption.textContent
|
|
65
|
+
? selectedOption.textContent
|
|
66
|
+
: ''
|
|
67
|
+
).replace(/\s+/g, ' ').trim();
|
|
68
|
+
return {
|
|
69
|
+
selectedDialCode: extractDialCode(selectedText),
|
|
70
|
+
selectedOptionText: selectedText || undefined,
|
|
71
|
+
};
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
const currentValue =
|
|
75
|
+
companion instanceof HTMLInputElement
|
|
76
|
+
? companion.value
|
|
77
|
+
: (companion.textContent || '').replace(/\s+/g, ' ').trim();
|
|
78
|
+
return {
|
|
79
|
+
selectedDialCode: extractDialCode(currentValue),
|
|
80
|
+
selectedOptionText: currentValue || undefined,
|
|
81
|
+
};
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
return null;
|
|
86
|
+
`;
|
|
87
|
+
function looksPhoneLike(target) {
|
|
88
|
+
const inputType = (target.inputType ?? '').trim().toLowerCase();
|
|
89
|
+
const autocomplete = (target.autocomplete ?? '').trim().toLowerCase();
|
|
90
|
+
const inputName = (target.inputName ?? '').trim().toLowerCase();
|
|
91
|
+
const label = (target.label ?? '').trim().toLowerCase();
|
|
92
|
+
return (inputType === 'tel' ||
|
|
93
|
+
autocomplete.includes('tel') ||
|
|
94
|
+
inputName.includes('phone') ||
|
|
95
|
+
/\bphone\b|\bтелефон\b|\bномер\b/.test(label));
|
|
96
|
+
}
|
|
97
|
+
function normalizePhoneComparable(value) {
|
|
98
|
+
const compact = value.replace(/[^\d+]/g, '');
|
|
99
|
+
if (!compact) {
|
|
100
|
+
return '';
|
|
101
|
+
}
|
|
102
|
+
if (!compact.startsWith('+')) {
|
|
103
|
+
return compact.replace(/\+/g, '');
|
|
104
|
+
}
|
|
105
|
+
return `+${compact.slice(1).replace(/\+/g, '')}`;
|
|
106
|
+
}
|
|
107
|
+
function readPhoneProjectionContextInBrowser(element) {
|
|
108
|
+
return Function('element', PHONE_PROJECTION_CONTEXT_SCRIPT)(element);
|
|
109
|
+
}
|
|
110
|
+
async function readPhoneProjectionContext(locator) {
|
|
111
|
+
return locator
|
|
112
|
+
.evaluate((element, source) => Function('element', source)(element), PHONE_PROJECTION_CONTEXT_SCRIPT)
|
|
113
|
+
.catch(() => null);
|
|
114
|
+
}
|
|
115
|
+
export async function projectActionValue(args) {
|
|
116
|
+
const { target, action, actionValue, locator, attempts } = args;
|
|
117
|
+
if (!actionValue || (action !== 'fill' && action !== 'type')) {
|
|
118
|
+
return null;
|
|
119
|
+
}
|
|
120
|
+
if (!looksPhoneLike(target)) {
|
|
121
|
+
return null;
|
|
122
|
+
}
|
|
123
|
+
const normalized = normalizePhoneComparable(actionValue);
|
|
124
|
+
if (!normalized.startsWith('+')) {
|
|
125
|
+
return null;
|
|
126
|
+
}
|
|
127
|
+
const context = await readPhoneProjectionContext(locator);
|
|
128
|
+
if (!context?.selectedDialCode) {
|
|
129
|
+
return null;
|
|
130
|
+
}
|
|
131
|
+
const selectedDialCode = normalizePhoneComparable(context.selectedDialCode);
|
|
132
|
+
if (!selectedDialCode.startsWith('+') || !normalized.startsWith(selectedDialCode)) {
|
|
133
|
+
return null;
|
|
134
|
+
}
|
|
135
|
+
const localPart = normalized.slice(selectedDialCode.length);
|
|
136
|
+
if (!/^\d{4,}$/.test(localPart)) {
|
|
137
|
+
return null;
|
|
138
|
+
}
|
|
139
|
+
attempts.push(`projection:phone.local-from-selected-code:${selectedDialCode}`);
|
|
140
|
+
return {
|
|
141
|
+
kind: 'phone-national',
|
|
142
|
+
executionValue: localPart,
|
|
143
|
+
acceptanceValue: localPart,
|
|
144
|
+
selectedDialCode,
|
|
145
|
+
selectedOptionText: context.selectedOptionText,
|
|
146
|
+
};
|
|
147
|
+
}
|
|
148
|
+
export const __testActionValueProjection = {
|
|
149
|
+
phoneProjectionContextScript: PHONE_PROJECTION_CONTEXT_SCRIPT,
|
|
150
|
+
readPhoneProjectionContextInBrowser,
|
|
151
|
+
};
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
export declare const BROWSE_ACTIONS: readonly ["click", "fill", "type", "select", "press"];
|
|
2
|
+
export type BrowseAction = (typeof BROWSE_ACTIONS)[number];
|
|
3
|
+
export declare function isBrowseAction(value: string): value is BrowseAction;
|
|
4
|
+
//# sourceMappingURL=browse-actions.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"browse-actions.d.ts","sourceRoot":"","sources":["../../src/commands/browse-actions.ts"],"names":[],"mappings":"AAAA,eAAO,MAAM,cAAc,uDAAwD,CAAC;AAEpF,MAAM,MAAM,YAAY,GAAG,CAAC,OAAO,cAAc,CAAC,CAAC,MAAM,CAAC,CAAC;AAE3D,wBAAgB,cAAc,CAAC,KAAK,EAAE,MAAM,GAAG,KAAK,IAAI,YAAY,CAEnE"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"captcha-solve.d.ts","sourceRoot":"","sources":["../../src/commands/captcha-solve.ts"],"names":[],"mappings":"AAAA;;GAEG;AAGH,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,eAAe,CAAC;AAOnD,wBAAsB,YAAY,CAChC,OAAO,EAAE,aAAa,GAAG,IAAI,EAC7B,cAAc,CAAC,EAAE,MAAM,GACtB,OAAO,CAAC,IAAI,CAAC,
|
|
1
|
+
{"version":3,"file":"captcha-solve.d.ts","sourceRoot":"","sources":["../../src/commands/captcha-solve.ts"],"names":[],"mappings":"AAAA;;GAEG;AAGH,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,eAAe,CAAC;AAOnD,wBAAsB,YAAY,CAChC,OAAO,EAAE,aAAa,GAAG,IAAI,EAC7B,cAAc,CAAC,EAAE,MAAM,GACtB,OAAO,CAAC,IAAI,CAAC,CAyCf"}
|
|
@@ -3,15 +3,25 @@
|
|
|
3
3
|
*/
|
|
4
4
|
import { solveCaptchasByCdp } from '../solver/captcha-runtime.js';
|
|
5
5
|
import { supportsCaptchaSolve } from '../session.js';
|
|
6
|
-
import { info,
|
|
6
|
+
import { info, outputContractFailure, outputJSON } from '../output.js';
|
|
7
7
|
import { applyAgentpayGatewayEnv, resolveAgentpayGatewayConfig } from '../agentpay-gateway.js';
|
|
8
8
|
const DEFAULT_TIMEOUT_SECONDS = 90;
|
|
9
9
|
export async function captchaSolve(session, timeoutSeconds) {
|
|
10
10
|
if (!session) {
|
|
11
|
-
|
|
11
|
+
outputContractFailure({
|
|
12
|
+
error: 'captcha_session_required',
|
|
13
|
+
outcomeType: 'blocked',
|
|
14
|
+
message: 'Captcha solving requires an active browser session.',
|
|
15
|
+
reason: 'Start a browser session with browse launch before running solve-captcha.',
|
|
16
|
+
});
|
|
12
17
|
}
|
|
13
18
|
if (!supportsCaptchaSolve(session)) {
|
|
14
|
-
|
|
19
|
+
outputContractFailure({
|
|
20
|
+
error: 'captcha_not_supported',
|
|
21
|
+
outcomeType: 'unsupported',
|
|
22
|
+
message: 'The current browser session does not support captcha solving.',
|
|
23
|
+
reason: 'This session was started without captcha-solving capability.',
|
|
24
|
+
});
|
|
15
25
|
}
|
|
16
26
|
const gateway = resolveAgentpayGatewayConfig();
|
|
17
27
|
applyAgentpayGatewayEnv(gateway);
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import type { Locator, Page } from 'playwright-core';
|
|
2
|
+
import { type ActionExecutionGuards } from './action-execution-guards.js';
|
|
3
|
+
type ClickRetryOptions = {
|
|
4
|
+
beforeRetry?: () => Promise<void>;
|
|
5
|
+
guards?: ActionExecutionGuards;
|
|
6
|
+
};
|
|
7
|
+
export declare function applyEditableClickAction(page: Page, locator: Locator, attempts: string[], options?: ClickRetryOptions): Promise<boolean>;
|
|
8
|
+
export declare function applyTriggerAction(page: Page, locator: Locator, attempts: string[], options?: ClickRetryOptions): Promise<boolean>;
|
|
9
|
+
export {};
|
|
10
|
+
//# sourceMappingURL=click-action-executor.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"click-action-executor.d.ts","sourceRoot":"","sources":["../../src/commands/click-action-executor.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,iBAAiB,CAAC;AAMrD,OAAO,EAEL,KAAK,qBAAqB,EAC3B,MAAM,8BAA8B,CAAC;AAEtC,KAAK,iBAAiB,GAAG;IACvB,WAAW,CAAC,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;IAClC,MAAM,CAAC,EAAE,qBAAqB,CAAC;CAChC,CAAC;AAwEF,wBAAsB,wBAAwB,CAC5C,IAAI,EAAE,IAAI,EACV,OAAO,EAAE,OAAO,EAChB,QAAQ,EAAE,MAAM,EAAE,EAClB,OAAO,CAAC,EAAE,iBAAiB,GAC1B,OAAO,CAAC,OAAO,CAAC,CAElB;AAED,wBAAsB,kBAAkB,CACtC,IAAI,EAAE,IAAI,EACV,OAAO,EAAE,OAAO,EAChB,QAAQ,EAAE,MAAM,EAAE,EAClB,OAAO,CAAC,EAAE,iBAAiB,GAC1B,OAAO,CAAC,OAAO,CAAC,CAElB"}
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
import { LOCATOR_CLICK_TIMEOUT_MS, dismissBlockingOverlay, looksLikeOverlayInterference, } from './action-executor-helpers.js';
|
|
2
|
+
import { runActionExecutionGuard, } from './action-execution-guards.js';
|
|
3
|
+
async function ensureLocatorRetryReady(locator, error, options) {
|
|
4
|
+
await runActionExecutionGuard(options?.guards, 'click.before-retry');
|
|
5
|
+
await options?.beforeRetry?.();
|
|
6
|
+
const count = await locator.count().catch(() => 0);
|
|
7
|
+
if (count === 0) {
|
|
8
|
+
throw error;
|
|
9
|
+
}
|
|
10
|
+
}
|
|
11
|
+
async function applyClickSequence(page, locator, attempts, options) {
|
|
12
|
+
attempts.push('locator.click');
|
|
13
|
+
try {
|
|
14
|
+
await locator.click({ timeout: LOCATOR_CLICK_TIMEOUT_MS });
|
|
15
|
+
return false;
|
|
16
|
+
}
|
|
17
|
+
catch (error) {
|
|
18
|
+
let lastError = error instanceof Error ? error : new Error(String(error));
|
|
19
|
+
await runActionExecutionGuard(options?.guards, 'click.after-error');
|
|
20
|
+
if (looksLikeOverlayInterference(error)) {
|
|
21
|
+
await runActionExecutionGuard(options?.guards, 'click.overlay-dismiss');
|
|
22
|
+
const dismissed = await dismissBlockingOverlay(page, attempts);
|
|
23
|
+
if (dismissed) {
|
|
24
|
+
await ensureLocatorRetryReady(locator, lastError, options);
|
|
25
|
+
attempts.push('locator.click.retry.afterOverlay');
|
|
26
|
+
try {
|
|
27
|
+
await runActionExecutionGuard(options?.guards, 'click.retry.after-overlay');
|
|
28
|
+
await locator.click({ timeout: LOCATOR_CLICK_TIMEOUT_MS });
|
|
29
|
+
return true;
|
|
30
|
+
}
|
|
31
|
+
catch (retryError) {
|
|
32
|
+
lastError = retryError instanceof Error ? retryError : new Error(String(retryError));
|
|
33
|
+
// Fall through to scroll / JS fallback.
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
await ensureLocatorRetryReady(locator, lastError, options);
|
|
38
|
+
await runActionExecutionGuard(options?.guards, 'click.scroll-into-view');
|
|
39
|
+
attempts.push('locator.scrollIntoViewIfNeeded');
|
|
40
|
+
await locator.scrollIntoViewIfNeeded();
|
|
41
|
+
await ensureLocatorRetryReady(locator, lastError, options);
|
|
42
|
+
attempts.push('locator.click.retry');
|
|
43
|
+
try {
|
|
44
|
+
await runActionExecutionGuard(options?.guards, 'click.retry');
|
|
45
|
+
await locator.click({ timeout: LOCATOR_CLICK_TIMEOUT_MS });
|
|
46
|
+
return true;
|
|
47
|
+
}
|
|
48
|
+
catch (retryError) {
|
|
49
|
+
lastError = retryError instanceof Error ? retryError : new Error(String(retryError));
|
|
50
|
+
await ensureLocatorRetryReady(locator, lastError, options);
|
|
51
|
+
await runActionExecutionGuard(options?.guards, 'click.evaluate');
|
|
52
|
+
attempts.push('locator.evaluate.click');
|
|
53
|
+
await locator.evaluate((element) => {
|
|
54
|
+
if (!(element instanceof HTMLElement)) {
|
|
55
|
+
throw new Error('unsupported_js_click_fallback');
|
|
56
|
+
}
|
|
57
|
+
element.click();
|
|
58
|
+
});
|
|
59
|
+
}
|
|
60
|
+
return true;
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
export async function applyEditableClickAction(page, locator, attempts, options) {
|
|
64
|
+
return applyClickSequence(page, locator, attempts, options);
|
|
65
|
+
}
|
|
66
|
+
export async function applyTriggerAction(page, locator, attempts, options) {
|
|
67
|
+
return applyClickSequence(page, locator, attempts, options);
|
|
68
|
+
}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* browse create-intent <fillRef> <storedSecretRef> — Create or reuse a stored-secret access intent.
|
|
3
|
+
*/
|
|
4
|
+
import type { BrowseSession } from '../session.js';
|
|
5
|
+
export declare function createIntent(session: BrowseSession, fillRef: string, storedSecretRef: string): Promise<void>;
|
|
6
|
+
//# sourceMappingURL=create-intent.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"create-intent.d.ts","sourceRoot":"","sources":["../../src/commands/create-intent.ts"],"names":[],"mappings":"AAAA;;GAEG;AAIH,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,eAAe,CAAC;AAmBnD,wBAAsB,YAAY,CAChC,OAAO,EAAE,aAAa,EACtB,OAAO,EAAE,MAAM,EACf,eAAe,EAAE,MAAM,GACtB,OAAO,CAAC,IAAI,CAAC,CAqEf"}
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* browse create-intent <fillRef> <storedSecretRef> — Create or reuse a stored-secret access intent.
|
|
3
|
+
*/
|
|
4
|
+
import { outputFailure, outputJSON } from '../output.js';
|
|
5
|
+
import { getFillableForm, saveSecretIntentSnapshot } from '../runtime-state.js';
|
|
6
|
+
import { saveSession } from '../session.js';
|
|
7
|
+
import { tryResolveCatalogHost } from '../secrets/catalog-sync.js';
|
|
8
|
+
import { getSecretBackend } from '../secrets/backend.js';
|
|
9
|
+
import { describeSecretIntentStatus, serializeSecretIntent } from '../secrets/intent-output.js';
|
|
10
|
+
function deriveRequestedFieldKeys(fillableForm, storedSecret) {
|
|
11
|
+
if (storedSecret.fieldKeys && storedSecret.fieldKeys.length > 0) {
|
|
12
|
+
return [...storedSecret.fieldKeys];
|
|
13
|
+
}
|
|
14
|
+
return fillableForm.fields.map((field) => field.fieldKey);
|
|
15
|
+
}
|
|
16
|
+
export async function createIntent(session, fillRef, storedSecretRef) {
|
|
17
|
+
const fillableForm = getFillableForm(session, fillRef);
|
|
18
|
+
if (!fillableForm) {
|
|
19
|
+
return outputFailure({
|
|
20
|
+
error: 'unknown_fill_ref',
|
|
21
|
+
outcomeType: 'blocked',
|
|
22
|
+
message: 'Protected intent was not created because the requested fill reference is unknown.',
|
|
23
|
+
reason: 'The provided fillRef does not match any currently observed protected fill target.',
|
|
24
|
+
fillRef,
|
|
25
|
+
});
|
|
26
|
+
}
|
|
27
|
+
const storedSecret = fillableForm.storedSecretCandidates.find((candidate) => candidate.storedSecretRef === storedSecretRef);
|
|
28
|
+
if (!storedSecret) {
|
|
29
|
+
return outputFailure({
|
|
30
|
+
error: 'stored_secret_not_available_for_fill_ref',
|
|
31
|
+
outcomeType: 'blocked',
|
|
32
|
+
message: 'Protected intent was not created because this stored secret is not available for the requested fill target.',
|
|
33
|
+
reason: 'The provided storedSecretRef is not a candidate for the requested protected fill target.',
|
|
34
|
+
fillRef,
|
|
35
|
+
storedSecretRef,
|
|
36
|
+
});
|
|
37
|
+
}
|
|
38
|
+
const pageUrl = session.runtime?.pages?.[fillableForm.pageRef]?.url;
|
|
39
|
+
const host = tryResolveCatalogHost(pageUrl);
|
|
40
|
+
if (!host) {
|
|
41
|
+
return outputFailure({
|
|
42
|
+
error: 'secret_intent_host_resolution_failed',
|
|
43
|
+
outcomeType: 'blocked',
|
|
44
|
+
message: 'Protected intent was not created because AgentBrowse could not determine the current site host.',
|
|
45
|
+
reason: 'The current page URL did not resolve to a usable host for protected intent creation.',
|
|
46
|
+
fillRef,
|
|
47
|
+
storedSecretRef,
|
|
48
|
+
});
|
|
49
|
+
}
|
|
50
|
+
const result = await getSecretBackend().createOrReuseSecretIntent({
|
|
51
|
+
fillRef,
|
|
52
|
+
storedSecretRef,
|
|
53
|
+
host,
|
|
54
|
+
pageRef: fillableForm.pageRef,
|
|
55
|
+
scopeRef: fillableForm.scopeRef,
|
|
56
|
+
purpose: fillableForm.purpose,
|
|
57
|
+
secretKind: storedSecret.kind,
|
|
58
|
+
requestedFieldKeys: deriveRequestedFieldKeys(fillableForm, storedSecret),
|
|
59
|
+
});
|
|
60
|
+
const snapshot = saveSecretIntentSnapshot(session, result.intent);
|
|
61
|
+
saveSession(session);
|
|
62
|
+
const statusContract = describeSecretIntentStatus(snapshot.status);
|
|
63
|
+
const reasonPrefix = result.reused
|
|
64
|
+
? 'AgentBrowse reused an existing intent for this fill target.'
|
|
65
|
+
: 'AgentBrowse created a new intent for this fill target.';
|
|
66
|
+
outputJSON({
|
|
67
|
+
success: true,
|
|
68
|
+
...serializeSecretIntent(snapshot),
|
|
69
|
+
reused: result.reused,
|
|
70
|
+
...(statusContract.outcomeType ? { outcomeType: statusContract.outcomeType } : {}),
|
|
71
|
+
message: statusContract.message,
|
|
72
|
+
reason: `${reasonPrefix} ${statusContract.reason}`,
|
|
73
|
+
...(statusContract.nextAction ? { nextAction: statusContract.nextAction } : {}),
|
|
74
|
+
});
|
|
75
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import type { Locator, Page } from 'playwright-core';
|
|
2
|
+
import { type ActionExecutionGuards } from './action-execution-guards.js';
|
|
3
|
+
type DatepickerActionHelpers = {
|
|
4
|
+
normalizeFillValue: (locator: Locator, value: string, attempts: string[]) => Promise<string>;
|
|
5
|
+
focusLocator: (page: Page, locator: Locator, attempts: string[]) => Promise<void>;
|
|
6
|
+
clearLocatorForReplacement: (locator: Locator, attempts: string[]) => Promise<void>;
|
|
7
|
+
applyValueWithJsFallback: (locator: Locator, value: string, attempts: string[]) => Promise<void>;
|
|
8
|
+
blurLocator: (locator: Locator, attempts: string[]) => Promise<void>;
|
|
9
|
+
};
|
|
10
|
+
export declare function applyDatepickerAction(page: Page, locator: Locator, value: string, attempts: string[], helpers: DatepickerActionHelpers, guards?: ActionExecutionGuards): Promise<boolean>;
|
|
11
|
+
export {};
|
|
12
|
+
//# sourceMappingURL=datepicker-action-executor.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"datepicker-action-executor.d.ts","sourceRoot":"","sources":["../../src/commands/datepicker-action-executor.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,iBAAiB,CAAC;AACrD,OAAO,EAEL,KAAK,qBAAqB,EAC3B,MAAM,8BAA8B,CAAC;AAmBtC,KAAK,uBAAuB,GAAG;IAC7B,kBAAkB,EAAE,CAAC,OAAO,EAAE,OAAO,EAAE,KAAK,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,KAAK,OAAO,CAAC,MAAM,CAAC,CAAC;IAC7F,YAAY,EAAE,CAAC,IAAI,EAAE,IAAI,EAAE,OAAO,EAAE,OAAO,EAAE,QAAQ,EAAE,MAAM,EAAE,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IAClF,0BAA0B,EAAE,CAAC,OAAO,EAAE,OAAO,EAAE,QAAQ,EAAE,MAAM,EAAE,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IACpF,wBAAwB,EAAE,CAAC,OAAO,EAAE,OAAO,EAAE,KAAK,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IACjG,WAAW,EAAE,CAAC,OAAO,EAAE,OAAO,EAAE,QAAQ,EAAE,MAAM,EAAE,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;CACtE,CAAC;AAkKF,wBAAsB,qBAAqB,CACzC,IAAI,EAAE,IAAI,EACV,OAAO,EAAE,OAAO,EAChB,KAAK,EAAE,MAAM,EACb,QAAQ,EAAE,MAAM,EAAE,EAClB,OAAO,EAAE,uBAAuB,EAChC,MAAM,CAAC,EAAE,qBAAqB,GAC7B,OAAO,CAAC,OAAO,CAAC,CAyElB"}
|
|
@@ -0,0 +1,218 @@
|
|
|
1
|
+
import { runActionExecutionGuard, } from './action-execution-guards.js';
|
|
2
|
+
const DATEPICKER_DAY_ROLES = [
|
|
3
|
+
'gridcell',
|
|
4
|
+
'button',
|
|
5
|
+
'option',
|
|
6
|
+
];
|
|
7
|
+
const DATEPICKER_CONTAINER_SELECTORS = [
|
|
8
|
+
'[role="dialog"]',
|
|
9
|
+
'[aria-modal="true"]',
|
|
10
|
+
'[data-testid*="calendar"]',
|
|
11
|
+
'[data-testid*="datepicker"]',
|
|
12
|
+
'[class*="calendar"]',
|
|
13
|
+
'[class*="datepicker"]',
|
|
14
|
+
];
|
|
15
|
+
const ISO_DATE_ENTRY_RE = /^\d{4}-\d{2}-\d{2}$/;
|
|
16
|
+
function isLiteralDateEntry(value) {
|
|
17
|
+
return /\d{1,2}[./-]\d{1,2}[./-]\d{2,4}/.test(value);
|
|
18
|
+
}
|
|
19
|
+
async function isNativeDateInput(locator) {
|
|
20
|
+
const type = await locator.getAttribute('type').catch(() => null);
|
|
21
|
+
return (type ?? '').toLowerCase() === 'date';
|
|
22
|
+
}
|
|
23
|
+
function datepickerDayLabels(value) {
|
|
24
|
+
const labels = new Set([value]);
|
|
25
|
+
const triplet = value.match(/^(\d{1,4})[./-](\d{1,2})[./-](\d{1,4})$/);
|
|
26
|
+
const parts = value.match(/\d+/g);
|
|
27
|
+
if (!parts || parts.length < 3) {
|
|
28
|
+
return [...labels];
|
|
29
|
+
}
|
|
30
|
+
let day;
|
|
31
|
+
if (triplet) {
|
|
32
|
+
const firstPart = triplet[1];
|
|
33
|
+
const lastPart = triplet[3];
|
|
34
|
+
if (firstPart && firstPart.length === 4) {
|
|
35
|
+
day = lastPart;
|
|
36
|
+
}
|
|
37
|
+
else if (lastPart && lastPart.length === 4) {
|
|
38
|
+
day = firstPart;
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
if (!day) {
|
|
42
|
+
day = parts.at(-1);
|
|
43
|
+
}
|
|
44
|
+
if (!day) {
|
|
45
|
+
return [...labels];
|
|
46
|
+
}
|
|
47
|
+
const normalized = String(Number(day));
|
|
48
|
+
if (normalized) {
|
|
49
|
+
labels.add(normalized);
|
|
50
|
+
}
|
|
51
|
+
if (day.length === 2) {
|
|
52
|
+
labels.add(day);
|
|
53
|
+
}
|
|
54
|
+
return [...labels];
|
|
55
|
+
}
|
|
56
|
+
async function trySelectDateFromPicker(page, anchor, value, attempts, guards) {
|
|
57
|
+
const labels = datepickerDayLabels(value);
|
|
58
|
+
if (labels.length === 1 && labels[0] === value && !/\d/.test(value)) {
|
|
59
|
+
return false;
|
|
60
|
+
}
|
|
61
|
+
const scopedContainers = [];
|
|
62
|
+
for (const selector of DATEPICKER_CONTAINER_SELECTORS) {
|
|
63
|
+
const containers = page.locator(selector);
|
|
64
|
+
const count = await containers.count().catch(() => 0);
|
|
65
|
+
for (let index = 0; index < count; index += 1) {
|
|
66
|
+
const container = containers.nth(index);
|
|
67
|
+
const visible = await container.isVisible().catch(() => false);
|
|
68
|
+
if (!visible) {
|
|
69
|
+
continue;
|
|
70
|
+
}
|
|
71
|
+
scopedContainers.push({ selector, container });
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
const scopedRoots = scopedContainers.length > 0
|
|
75
|
+
? scopedContainers
|
|
76
|
+
: [{ selector: 'page', container: page }];
|
|
77
|
+
const anchorBox = await anchor.boundingBox().catch(() => null);
|
|
78
|
+
async function clickBestMatch(root, descriptor, locatorFactory) {
|
|
79
|
+
const locator = locatorFactory();
|
|
80
|
+
const count = await locator.count().catch(() => 0);
|
|
81
|
+
let bestCandidate = null;
|
|
82
|
+
for (let index = 0; index < count; index += 1) {
|
|
83
|
+
const candidate = locator.nth(index);
|
|
84
|
+
const visible = await candidate.isVisible().catch(() => false);
|
|
85
|
+
if (!visible) {
|
|
86
|
+
continue;
|
|
87
|
+
}
|
|
88
|
+
const box = await candidate.boundingBox().catch(() => null);
|
|
89
|
+
const score = anchorBox && box
|
|
90
|
+
? Math.abs(box.y - anchorBox.y) * 10 + Math.abs(box.x - anchorBox.x)
|
|
91
|
+
: index;
|
|
92
|
+
if (!bestCandidate || score < bestCandidate.score) {
|
|
93
|
+
bestCandidate = { locator: candidate, score };
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
if (!bestCandidate) {
|
|
97
|
+
return false;
|
|
98
|
+
}
|
|
99
|
+
await bestCandidate.locator.evaluate((element) => {
|
|
100
|
+
if (!(element instanceof HTMLElement)) {
|
|
101
|
+
throw new Error('unsupported_datepicker_dom_click');
|
|
102
|
+
}
|
|
103
|
+
element.click();
|
|
104
|
+
});
|
|
105
|
+
attempts.push(`datepicker.click.dom:${descriptor}`);
|
|
106
|
+
return true;
|
|
107
|
+
}
|
|
108
|
+
for (const { selector, container } of scopedRoots) {
|
|
109
|
+
await runActionExecutionGuard(guards, `datepicker.scope:${selector}`);
|
|
110
|
+
attempts.push(`datepicker.scope:${selector}`);
|
|
111
|
+
for (const role of DATEPICKER_DAY_ROLES) {
|
|
112
|
+
for (const label of labels) {
|
|
113
|
+
const descriptor = `${role}:${label}`;
|
|
114
|
+
attempts.push(`datepicker.resolve:${descriptor}`);
|
|
115
|
+
try {
|
|
116
|
+
const clicked = await clickBestMatch(container, descriptor, () => container.getByRole(role, { name: label }));
|
|
117
|
+
if (!clicked) {
|
|
118
|
+
continue;
|
|
119
|
+
}
|
|
120
|
+
return true;
|
|
121
|
+
}
|
|
122
|
+
catch {
|
|
123
|
+
// Try the next label/role combination.
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
for (const label of labels) {
|
|
128
|
+
const descriptor = `text:${label}`;
|
|
129
|
+
attempts.push(`datepicker.resolve:${descriptor}`);
|
|
130
|
+
try {
|
|
131
|
+
const clicked = await clickBestMatch(container, descriptor, () => container.getByText(label));
|
|
132
|
+
if (!clicked) {
|
|
133
|
+
continue;
|
|
134
|
+
}
|
|
135
|
+
return true;
|
|
136
|
+
}
|
|
137
|
+
catch {
|
|
138
|
+
// Try next text label.
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
return false;
|
|
143
|
+
}
|
|
144
|
+
export async function applyDatepickerAction(page, locator, value, attempts, helpers, guards) {
|
|
145
|
+
const normalizedValue = await helpers.normalizeFillValue(locator, value, attempts);
|
|
146
|
+
const editable = await locator.isEditable().catch(() => false);
|
|
147
|
+
const nativeDateInput = await isNativeDateInput(locator);
|
|
148
|
+
if (nativeDateInput && editable && ISO_DATE_ENTRY_RE.test(normalizedValue)) {
|
|
149
|
+
attempts.push('datepicker.native:js-value');
|
|
150
|
+
await helpers.applyValueWithJsFallback(locator, normalizedValue, attempts);
|
|
151
|
+
await helpers.blurLocator(locator, attempts);
|
|
152
|
+
return true;
|
|
153
|
+
}
|
|
154
|
+
if (isLiteralDateEntry(normalizedValue)) {
|
|
155
|
+
await helpers.focusLocator(page, locator, attempts);
|
|
156
|
+
if (!editable) {
|
|
157
|
+
attempts.push('datepicker.readonly:picker-only');
|
|
158
|
+
return trySelectDateFromPicker(page, locator, normalizedValue, attempts, guards);
|
|
159
|
+
}
|
|
160
|
+
await helpers.clearLocatorForReplacement(locator, attempts);
|
|
161
|
+
if (typeof locator.pressSequentially === 'function') {
|
|
162
|
+
attempts.push('locator.pressSequentially.datepicker');
|
|
163
|
+
try {
|
|
164
|
+
await runActionExecutionGuard(guards, 'datepicker.press-sequentially');
|
|
165
|
+
await locator.pressSequentially(normalizedValue);
|
|
166
|
+
await helpers.blurLocator(locator, attempts);
|
|
167
|
+
return true;
|
|
168
|
+
}
|
|
169
|
+
catch {
|
|
170
|
+
// Fall through to fill/picker fallback.
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
attempts.push('locator.fill');
|
|
175
|
+
try {
|
|
176
|
+
await locator.fill(normalizedValue, { timeout: 1_500 });
|
|
177
|
+
if (isLiteralDateEntry(normalizedValue)) {
|
|
178
|
+
await helpers.blurLocator(locator, attempts);
|
|
179
|
+
}
|
|
180
|
+
return false;
|
|
181
|
+
}
|
|
182
|
+
catch {
|
|
183
|
+
await runActionExecutionGuard(guards, 'datepicker.after-error');
|
|
184
|
+
await helpers.focusLocator(page, locator, attempts);
|
|
185
|
+
if (await trySelectDateFromPicker(page, locator, normalizedValue, attempts, guards)) {
|
|
186
|
+
return true;
|
|
187
|
+
}
|
|
188
|
+
await helpers.clearLocatorForReplacement(locator, attempts);
|
|
189
|
+
if (typeof locator.pressSequentially === 'function') {
|
|
190
|
+
attempts.push('locator.pressSequentially');
|
|
191
|
+
try {
|
|
192
|
+
await runActionExecutionGuard(guards, 'datepicker.press-sequentially');
|
|
193
|
+
await locator.pressSequentially(normalizedValue);
|
|
194
|
+
if (isLiteralDateEntry(normalizedValue)) {
|
|
195
|
+
await helpers.blurLocator(locator, attempts);
|
|
196
|
+
}
|
|
197
|
+
return true;
|
|
198
|
+
}
|
|
199
|
+
catch {
|
|
200
|
+
// Fall through to retry and DOM-event fallback.
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
attempts.push('locator.fill.retry');
|
|
204
|
+
try {
|
|
205
|
+
await runActionExecutionGuard(guards, 'datepicker.fill.retry');
|
|
206
|
+
await locator.fill(normalizedValue, { timeout: 1_500 });
|
|
207
|
+
if (isLiteralDateEntry(normalizedValue)) {
|
|
208
|
+
await helpers.blurLocator(locator, attempts);
|
|
209
|
+
}
|
|
210
|
+
return true;
|
|
211
|
+
}
|
|
212
|
+
catch {
|
|
213
|
+
await runActionExecutionGuard(guards, 'datepicker.js-fallback');
|
|
214
|
+
await helpers.applyValueWithJsFallback(locator, normalizedValue, attempts);
|
|
215
|
+
}
|
|
216
|
+
return true;
|
|
217
|
+
}
|
|
218
|
+
}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import type { Locator } from 'playwright-core';
|
|
2
|
+
import type { TargetDescriptor } from '../runtime-state.js';
|
|
3
|
+
export declare function normalizePageSignature(url: string): string;
|
|
4
|
+
export declare const LOCATOR_DOM_SIGNATURE_SCRIPT: string;
|
|
5
|
+
declare function readLocatorDomSignatureInBrowser(element: Element): string | null;
|
|
6
|
+
export type LocatorBindingSnapshot = {
|
|
7
|
+
domSignature: string | null;
|
|
8
|
+
kind?: string;
|
|
9
|
+
role?: string;
|
|
10
|
+
label?: string;
|
|
11
|
+
placeholder?: string;
|
|
12
|
+
inputName?: string;
|
|
13
|
+
inputType?: string;
|
|
14
|
+
autocomplete?: string;
|
|
15
|
+
};
|
|
16
|
+
export declare function readLocatorDomSignature(locator: Locator): Promise<string | null>;
|
|
17
|
+
export declare function readLocatorBindingSnapshot(locator: Locator): Promise<LocatorBindingSnapshot | null>;
|
|
18
|
+
export declare function isCompatibleMutableFieldBinding(target: TargetDescriptor, snapshot: LocatorBindingSnapshot | null): boolean;
|
|
19
|
+
export declare const __testDescriptorValidation: {
|
|
20
|
+
readLocatorDomSignatureInBrowser: typeof readLocatorDomSignatureInBrowser;
|
|
21
|
+
locatorDomSignatureScript: string;
|
|
22
|
+
locatorBindingSnapshotScript: string;
|
|
23
|
+
isCompatibleMutableFieldBinding: typeof isCompatibleMutableFieldBinding;
|
|
24
|
+
};
|
|
25
|
+
export declare function readLocatorOuterHtml(locator: Locator): Promise<string | null>;
|
|
26
|
+
export {};
|
|
27
|
+
//# sourceMappingURL=descriptor-validation.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"descriptor-validation.d.ts","sourceRoot":"","sources":["../../src/commands/descriptor-validation.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,iBAAiB,CAAC;AAC/C,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,qBAAqB,CAAC;AAE5D,wBAAgB,sBAAsB,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAO1D;AAED,eAAO,MAAM,4BAA4B,QA+JxC,CAAC;AAEF,iBAAS,gCAAgC,CAAC,OAAO,EAAE,OAAO,GAAG,MAAM,GAAG,IAAI,CAEzE;AAED,MAAM,MAAM,sBAAsB,GAAG;IACnC,YAAY,EAAE,MAAM,GAAG,IAAI,CAAC;IAC5B,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,YAAY,CAAC,EAAE,MAAM,CAAC;CACvB,CAAC;AAgCF,wBAAsB,uBAAuB,CAAC,OAAO,EAAE,OAAO,GAAG,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAWtF;AAED,wBAAsB,0BAA0B,CAAC,OAAO,EAAE,OAAO,GAAG,OAAO,CAAC,sBAAsB,GAAG,IAAI,CAAC,CAWzG;AAOD,wBAAgB,+BAA+B,CAC7C,MAAM,EAAE,gBAAgB,EACxB,QAAQ,EAAE,sBAAsB,GAAG,IAAI,GACtC,OAAO,CAoCT;AAED,eAAO,MAAM,0BAA0B;;;;;CAKtC,CAAC;AAEF,wBAAsB,oBAAoB,CAAC,OAAO,EAAE,OAAO,GAAG,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAmFnF"}
|