teamshare-bridge 0.26.17 → 0.26.19
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/dist/lib/browser/batch.d.ts +9 -0
- package/dist/lib/browser/batch.js +19 -0
- package/dist/lib/browser/batch.js.map +1 -1
- package/dist/lib/browser/index.d.ts +2 -0
- package/dist/lib/browser/index.js +4 -1
- package/dist/lib/browser/index.js.map +1 -1
- package/dist/lib/browser/network-log.d.ts +20 -1
- package/dist/lib/browser/network-log.js +144 -9
- package/dist/lib/browser/network-log.js.map +1 -1
- package/dist/lib/browser/otp.d.ts +61 -0
- package/dist/lib/browser/otp.js +329 -0
- package/dist/lib/browser/otp.js.map +1 -0
- package/dist/lib/browser/session.d.ts +35 -2
- package/dist/lib/browser/session.js +107 -3
- package/dist/lib/browser/session.js.map +1 -1
- package/dist/lib/harness/harness-adapter.js +41 -15
- package/dist/lib/harness/harness-adapter.js.map +1 -1
- package/dist/lib/harness/spawn.js +16 -3
- package/dist/lib/harness/spawn.js.map +1 -1
- package/dist/lib/orchestrator/handlers/browser.js +87 -1
- package/dist/lib/orchestrator/handlers/browser.js.map +1 -1
- package/dist/lib/scope-guard.d.ts +9 -26
- package/dist/lib/scope-guard.js +61 -32
- package/dist/lib/scope-guard.js.map +1 -1
- package/dist/lib/tools/browser-tool.js +56 -0
- package/dist/lib/tools/browser-tool.js.map +1 -1
- package/package.json +1 -1
|
@@ -0,0 +1,329 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.detectOtpGroup = detectOtpGroup;
|
|
4
|
+
exports.fillOtpGroup = fillOtpGroup;
|
|
5
|
+
/**
|
|
6
|
+
* Segmented OTP / one-time-code fill for the DEBUG browser (2026-10).
|
|
7
|
+
*
|
|
8
|
+
* Real OTP widgets are N sibling inputs (usually `maxlength="1"`,
|
|
9
|
+
* `inputmode="numeric"`, sometimes `autocomplete="new-password"`) plus a
|
|
10
|
+
* sensor that auto-submits when the last digit lands. Typing the whole code
|
|
11
|
+
* into ONE cell therefore fails `TYPE_NOT_VERIFIED`, and retrying without
|
|
12
|
+
* clearing appends into populated cells until the site rate-limits the run
|
|
13
|
+
* ("Too many invalid OTP attempts").
|
|
14
|
+
*
|
|
15
|
+
* `fillOtp` detects the group by SHAPE (N visible single-char inputs, never
|
|
16
|
+
* by autocomplete alone), clears every cell and verifies empty, types one
|
|
17
|
+
* digit per cell with real key events, re-reads values after each insert,
|
|
18
|
+
* and only submits when the widget did NOT auto-submit itself.
|
|
19
|
+
*/
|
|
20
|
+
const cdp_1 = require("./cdp");
|
|
21
|
+
const FILL_OTP_JS = `(() => {
|
|
22
|
+
const vis = (el) => {
|
|
23
|
+
if (!el) return false;
|
|
24
|
+
const r = el.getBoundingClientRect();
|
|
25
|
+
if (r.width === 0 || r.height === 0) return false;
|
|
26
|
+
const cs = getComputedStyle(el);
|
|
27
|
+
return cs.visibility !== 'hidden' && cs.display !== 'none' && cs.opacity !== '0';
|
|
28
|
+
};
|
|
29
|
+
const dialogSel = 'dialog[open],[role="dialog"],[aria-modal="true"],' +
|
|
30
|
+
'[class*="modal"],[class*="Modal"],[class*="dialog"],[class*="Dialog"]';
|
|
31
|
+
const dialogs = [...document.querySelectorAll(dialogSel)].filter(vis);
|
|
32
|
+
const scope = dialogs.length ? dialogs[dialogs.length - 1] : document;
|
|
33
|
+
const inputs = [...scope.querySelectorAll('input')]
|
|
34
|
+
.filter(vis)
|
|
35
|
+
.filter((el) => !el.disabled && !el.readOnly && (el.type || '').toLowerCase() !== 'hidden');
|
|
36
|
+
const single = inputs.filter((el) => {
|
|
37
|
+
const ml = el.getAttribute('maxlength') ?? el.getAttribute('maxLength');
|
|
38
|
+
const n = ml === null || ml === undefined ? NaN : Number(ml);
|
|
39
|
+
return n === 1;
|
|
40
|
+
});
|
|
41
|
+
// A segmented OTP widget: at least 4 single-char inputs, or an explicit
|
|
42
|
+
// one-time-code input with siblings of the same shape.
|
|
43
|
+
let group = [];
|
|
44
|
+
if (single.length >= 4) {
|
|
45
|
+
// Keep the largest run of adjacent single-char inputs in DOM order.
|
|
46
|
+
group = single;
|
|
47
|
+
} else {
|
|
48
|
+
const otpish = inputs.filter((el) => {
|
|
49
|
+
const ac = (el.getAttribute('autocomplete') || '').toLowerCase();
|
|
50
|
+
if (ac === 'one-time-code') return true;
|
|
51
|
+
const hint = [el.getAttribute('name'), el.id, el.getAttribute('aria-label')].filter(Boolean).join(' ');
|
|
52
|
+
return /otp|one[- ]?time|verif|code/i.test(hint);
|
|
53
|
+
});
|
|
54
|
+
if (otpish.length >= 4) group = otpish;
|
|
55
|
+
else if (otpish.length === 1 && single.length >= 2) group = [otpish[0], ...single.filter((el) => el !== otpish[0])];
|
|
56
|
+
}
|
|
57
|
+
if (group.length < 4) return null;
|
|
58
|
+
// Sort by on-screen position (top then left) so auto-advance order is right.
|
|
59
|
+
group = group.slice().sort((a, b) => {
|
|
60
|
+
const ra = a.getBoundingClientRect();
|
|
61
|
+
const rb = b.getBoundingClientRect();
|
|
62
|
+
if (Math.abs(ra.y - rb.y) > ra.height / 2) return ra.y - rb.y;
|
|
63
|
+
return ra.x - rb.x;
|
|
64
|
+
});
|
|
65
|
+
group.forEach((el, i) => el.setAttribute('data-ts-otp', String(i)));
|
|
66
|
+
return {
|
|
67
|
+
length: group.length,
|
|
68
|
+
tags: group.map((el) => String(el.tagName || '').toLowerCase()),
|
|
69
|
+
modal: scope !== document,
|
|
70
|
+
values: group.map((el) => typeof el.value === 'string' ? el.value : ''),
|
|
71
|
+
};
|
|
72
|
+
})()`;
|
|
73
|
+
async function detectOtpGroup(page) {
|
|
74
|
+
try {
|
|
75
|
+
const v = await (0, cdp_1.evaluateJs)(page, FILL_OTP_JS);
|
|
76
|
+
if (!v || typeof v !== 'object')
|
|
77
|
+
return null;
|
|
78
|
+
const rec = v;
|
|
79
|
+
const length = typeof rec.length === 'number' ? rec.length : 0;
|
|
80
|
+
if (!length || length < 4 || length > 12)
|
|
81
|
+
return null;
|
|
82
|
+
return {
|
|
83
|
+
length,
|
|
84
|
+
tags: Array.isArray(rec.tags) ? rec.tags : [],
|
|
85
|
+
modal: rec.modal === true,
|
|
86
|
+
values: Array.isArray(rec.values) ? rec.values : [],
|
|
87
|
+
};
|
|
88
|
+
}
|
|
89
|
+
catch {
|
|
90
|
+
return null;
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
async function otpValues(page) {
|
|
94
|
+
try {
|
|
95
|
+
const v = await (0, cdp_1.evaluateJs)(page, `(() => [...document.querySelectorAll('[data-ts-otp]')].sort((a,b)=>Number(a.getAttribute('data-ts-otp'))-Number(b.getAttribute('data-ts-otp'))).map((el)=>typeof el.value==='string'?el.value:''))()`);
|
|
96
|
+
return Array.isArray(v) ? v : [];
|
|
97
|
+
}
|
|
98
|
+
catch {
|
|
99
|
+
return [];
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
async function clearOtpGroup(page, length) {
|
|
103
|
+
for (let attempt = 0; attempt < 3; attempt++) {
|
|
104
|
+
try {
|
|
105
|
+
await (0, cdp_1.evaluateJs)(page, `(() => { const els=[...document.querySelectorAll('[data-ts-otp]')].sort((a,b)=>Number(a.getAttribute('data-ts-otp'))-Number(b.getAttribute('data-ts-otp'))); for (const el of els) { el.focus(); el.select && el.select(); const proto = el.tagName==='TEXTAREA'?window.HTMLTextAreaElement&&HTMLTextAreaElement.prototype:window.HTMLInputElement&&HTMLInputElement.prototype; try { const setter = proto && Object.getOwnPropertyDescriptor(proto,'value'); if (setter && setter.set) setter.set.call(el,''); } catch {} el.value=''; el.dispatchEvent(new Event('input',{bubbles:true})); el.dispatchEvent(new Event('change',{bubbles:true})); } return true; })()`);
|
|
106
|
+
}
|
|
107
|
+
catch {
|
|
108
|
+
/* retry */
|
|
109
|
+
}
|
|
110
|
+
await (0, cdp_1.sleep)(60);
|
|
111
|
+
const values = await otpValues(page);
|
|
112
|
+
if (values.length === length && values.every((v) => v === ''))
|
|
113
|
+
return true;
|
|
114
|
+
}
|
|
115
|
+
return false;
|
|
116
|
+
}
|
|
117
|
+
async function focusOtpCell(page, index) {
|
|
118
|
+
try {
|
|
119
|
+
const ok = await (0, cdp_1.evaluateJs)(page, `(() => { const els=[...document.querySelectorAll('[data-ts-otp]')].sort((a,b)=>Number(a.getAttribute('data-ts-otp'))-Number(b.getAttribute('data-ts-otp'))); const el=els[${index}]; if(!el){return false;} el.focus(); el.select && el.select(); return document.activeElement===el; })()`);
|
|
120
|
+
return ok === true;
|
|
121
|
+
}
|
|
122
|
+
catch {
|
|
123
|
+
return false;
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
async function typeOtpDigit(page, digit) {
|
|
127
|
+
// Real key events so framework listeners (React/Vue OTP sensors) observe
|
|
128
|
+
// the same sequence a hardware keyboard produces.
|
|
129
|
+
try {
|
|
130
|
+
await (0, cdp_1.cdpSend)(page, 'Input.insertText', { text: digit });
|
|
131
|
+
return;
|
|
132
|
+
}
|
|
133
|
+
catch {
|
|
134
|
+
/* fall through to key events */
|
|
135
|
+
}
|
|
136
|
+
const key = digit;
|
|
137
|
+
const code = digit.startsWith('Numpad') ? digit : `Digit${digit}`;
|
|
138
|
+
try {
|
|
139
|
+
await (0, cdp_1.cdpSend)(page, 'Input.dispatchKeyEvent', {
|
|
140
|
+
type: 'keyDown',
|
|
141
|
+
key,
|
|
142
|
+
code,
|
|
143
|
+
text: digit,
|
|
144
|
+
unmodifiedText: digit,
|
|
145
|
+
windowsVirtualKeyCode: 48 + Number(digit),
|
|
146
|
+
nativeVirtualKeyCode: 48 + Number(digit),
|
|
147
|
+
});
|
|
148
|
+
await (0, cdp_1.cdpSend)(page, 'Input.dispatchKeyEvent', { type: 'keyUp', key, code });
|
|
149
|
+
}
|
|
150
|
+
catch {
|
|
151
|
+
/* best-effort */
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
async function visibleOtpError(page) {
|
|
155
|
+
try {
|
|
156
|
+
const v = await (0, cdp_1.evaluateJs)(page, `(() => {
|
|
157
|
+
const sel = '[role="alert"],.error,.text-red-500,.text-destructive,[class*="error" i]';
|
|
158
|
+
const els = [...document.querySelectorAll(sel)].filter((el) => {
|
|
159
|
+
const r = el.getBoundingClientRect();
|
|
160
|
+
if (r.width === 0 || r.height === 0) return false;
|
|
161
|
+
const t = (el.innerText || '').trim();
|
|
162
|
+
return t.length > 0 && t.length < 300;
|
|
163
|
+
});
|
|
164
|
+
return els.length ? els[0].innerText.trim().slice(0, 300) : null;
|
|
165
|
+
})()`);
|
|
166
|
+
return typeof v === 'string' && v ? v : null;
|
|
167
|
+
}
|
|
168
|
+
catch {
|
|
169
|
+
return null;
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
async function clickOtpSubmit(page) {
|
|
173
|
+
try {
|
|
174
|
+
const clicked = await (0, cdp_1.evaluateJs)(page, `(() => {
|
|
175
|
+
const scope = document;
|
|
176
|
+
const btns = [...scope.querySelectorAll('button,input[type="submit"],[role="button"]')].filter((el) => {
|
|
177
|
+
const r = el.getBoundingClientRect();
|
|
178
|
+
if (r.width === 0 || r.height === 0) return false;
|
|
179
|
+
if (el.disabled) return false;
|
|
180
|
+
const t = ((el.innerText || el.textContent || el.value || '')).trim();
|
|
181
|
+
return /verify|confirm|continue|submit|next/i.test(t);
|
|
182
|
+
});
|
|
183
|
+
if (!btns.length) return false;
|
|
184
|
+
btns[0].click();
|
|
185
|
+
return true;
|
|
186
|
+
})()`);
|
|
187
|
+
return clicked === true;
|
|
188
|
+
}
|
|
189
|
+
catch {
|
|
190
|
+
return false;
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
/**
|
|
194
|
+
* Fill a segmented OTP widget digit-by-digit.
|
|
195
|
+
*
|
|
196
|
+
* Never types into a populated cell: every cell is cleared and verified
|
|
197
|
+
* empty first. After the last digit the page gets a short settle window so
|
|
198
|
+
* an auto-submit sensor (navigation / loading state) can fire before the
|
|
199
|
+
* caller decides whether to click submit itself.
|
|
200
|
+
*/
|
|
201
|
+
async function fillOtpGroup(page, opts) {
|
|
202
|
+
const code = String(opts.code ?? '').replace(/\D/g, '');
|
|
203
|
+
const group = await detectOtpGroup(page);
|
|
204
|
+
if (!group) {
|
|
205
|
+
return {
|
|
206
|
+
ok: false,
|
|
207
|
+
filled: 0,
|
|
208
|
+
expected: 0,
|
|
209
|
+
values: [],
|
|
210
|
+
autoSubmitted: false,
|
|
211
|
+
submitted: false,
|
|
212
|
+
modal: false,
|
|
213
|
+
error: 'OTP_GROUP_NOT_FOUND',
|
|
214
|
+
detail: 'No segmented OTP widget (4+ single-char inputs) is visible.',
|
|
215
|
+
};
|
|
216
|
+
}
|
|
217
|
+
const expected = opts.digits ?? group.length;
|
|
218
|
+
if (code.length !== expected || code.length !== group.length) {
|
|
219
|
+
return {
|
|
220
|
+
ok: false,
|
|
221
|
+
filled: 0,
|
|
222
|
+
expected: group.length,
|
|
223
|
+
values: group.values,
|
|
224
|
+
autoSubmitted: false,
|
|
225
|
+
submitted: false,
|
|
226
|
+
modal: group.modal,
|
|
227
|
+
error: 'OTP_LENGTH_MISMATCH',
|
|
228
|
+
detail: `Code has ${code.length} digits but the widget has ${group.length} cells. Count the boxes and pass digits.`,
|
|
229
|
+
};
|
|
230
|
+
}
|
|
231
|
+
const urlBefore = (() => {
|
|
232
|
+
try {
|
|
233
|
+
return page.url ?? '';
|
|
234
|
+
}
|
|
235
|
+
catch {
|
|
236
|
+
return '';
|
|
237
|
+
}
|
|
238
|
+
})();
|
|
239
|
+
if (!(await clearOtpGroup(page, group.length))) {
|
|
240
|
+
return {
|
|
241
|
+
ok: false,
|
|
242
|
+
filled: 0,
|
|
243
|
+
expected: group.length,
|
|
244
|
+
values: await otpValues(page),
|
|
245
|
+
autoSubmitted: false,
|
|
246
|
+
submitted: false,
|
|
247
|
+
modal: group.modal,
|
|
248
|
+
error: 'OTP_CLEAR_FAILED',
|
|
249
|
+
detail: 'The OTP cells could not be emptied (autofill is fighting the clear). Retry fillOtp.',
|
|
250
|
+
};
|
|
251
|
+
}
|
|
252
|
+
for (let i = 0; i < group.length; i++) {
|
|
253
|
+
await focusOtpCell(page, i);
|
|
254
|
+
await (0, cdp_1.sleep)(40);
|
|
255
|
+
await typeOtpDigit(page, code[i]);
|
|
256
|
+
await (0, cdp_1.sleep)(90);
|
|
257
|
+
const values = await otpValues(page);
|
|
258
|
+
if (values[i] !== code[i]) {
|
|
259
|
+
// Re-focus by DOM position and retry once — auto-advance may have
|
|
260
|
+
// moved focus past a slow render.
|
|
261
|
+
await focusOtpCell(page, i);
|
|
262
|
+
await (0, cdp_1.sleep)(40);
|
|
263
|
+
await typeOtpDigit(page, code[i]);
|
|
264
|
+
await (0, cdp_1.sleep)(90);
|
|
265
|
+
const retry = await otpValues(page);
|
|
266
|
+
if (retry[i] !== code[i]) {
|
|
267
|
+
return {
|
|
268
|
+
ok: false,
|
|
269
|
+
filled: i,
|
|
270
|
+
expected: group.length,
|
|
271
|
+
values: retry,
|
|
272
|
+
autoSubmitted: false,
|
|
273
|
+
submitted: false,
|
|
274
|
+
modal: group.modal,
|
|
275
|
+
error: 'OTP_DIGIT_NOT_VERIFIED',
|
|
276
|
+
detail: `Digit ${i + 1} did not land (cell ${i + 1} of ${group.length}).`,
|
|
277
|
+
visibleError: (await visibleOtpError(page)) ?? undefined,
|
|
278
|
+
};
|
|
279
|
+
}
|
|
280
|
+
}
|
|
281
|
+
}
|
|
282
|
+
const values = await otpValues(page);
|
|
283
|
+
// Settle: an auto-submit sensor typically navigates or shows a spinner
|
|
284
|
+
// within ~1.5s. Poll cheaply so `submit:"auto"` sees it.
|
|
285
|
+
let autoSubmitted = false;
|
|
286
|
+
const deadline = Date.now() + 1500;
|
|
287
|
+
while (Date.now() < deadline) {
|
|
288
|
+
await (0, cdp_1.sleep)(150);
|
|
289
|
+
try {
|
|
290
|
+
const current = await (0, cdp_1.evaluateJs)(page, `(() => location.href)()`);
|
|
291
|
+
if (typeof current === 'string' && urlBefore && current !== urlBefore) {
|
|
292
|
+
autoSubmitted = true;
|
|
293
|
+
break;
|
|
294
|
+
}
|
|
295
|
+
}
|
|
296
|
+
catch {
|
|
297
|
+
// "Inspected target navigated" means the sensor already submitted.
|
|
298
|
+
autoSubmitted = true;
|
|
299
|
+
break;
|
|
300
|
+
}
|
|
301
|
+
try {
|
|
302
|
+
const busy = await (0, cdp_1.evaluateJs)(page, `(() => !!document.querySelector('[aria-busy="true"],[data-loading="true"],.animate-spin'))()`);
|
|
303
|
+
if (busy === true) {
|
|
304
|
+
autoSubmitted = true;
|
|
305
|
+
break;
|
|
306
|
+
}
|
|
307
|
+
}
|
|
308
|
+
catch {
|
|
309
|
+
break;
|
|
310
|
+
}
|
|
311
|
+
}
|
|
312
|
+
let submitted = false;
|
|
313
|
+
const mode = opts.submit ?? 'auto';
|
|
314
|
+
if (mode === 'always' || (mode === 'auto' && !autoSubmitted)) {
|
|
315
|
+
submitted = await clickOtpSubmit(page);
|
|
316
|
+
}
|
|
317
|
+
const visibleError = await visibleOtpError(page);
|
|
318
|
+
return {
|
|
319
|
+
ok: true,
|
|
320
|
+
filled: group.length,
|
|
321
|
+
expected: group.length,
|
|
322
|
+
values,
|
|
323
|
+
autoSubmitted,
|
|
324
|
+
submitted,
|
|
325
|
+
modal: group.modal,
|
|
326
|
+
...(visibleError ? { visibleError } : {}),
|
|
327
|
+
};
|
|
328
|
+
}
|
|
329
|
+
//# sourceMappingURL=otp.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"otp.js","sourceRoot":"","sources":["../../../src/lib/browser/otp.ts"],"names":[],"mappings":"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"}
|
|
@@ -2,6 +2,7 @@ import type { CdpPage, ElementInfo, PageInfo, PageText, PageLink } from './cdp';
|
|
|
2
2
|
import { type NetworkEntry, type ConsoleEntry, type WebSocketFrame, type NetworkLogStatus } from './network-log';
|
|
3
3
|
import { type ReadStagedResult, type FillExcelResult } from './xlsx-template';
|
|
4
4
|
import { type LoginResult, type PerformLoginOptions, type TypeResult } from './login';
|
|
5
|
+
import { type FillOtpOptions, type FillOtpResult } from './otp';
|
|
5
6
|
import { type NativeDialogState, type PagePaginationState, type PageSnapshot, type WaitForKind } from './page-context';
|
|
6
7
|
import { type PageEvent } from './page-events';
|
|
7
8
|
import { type RefElement } from './refs';
|
|
@@ -270,19 +271,43 @@ export declare class BrowserSession {
|
|
|
270
271
|
annotatedScreenshot(format?: 'jpeg' | 'png', quality?: number): Promise<string>;
|
|
271
272
|
/**
|
|
272
273
|
* Read the network + console evidence collected for this session (QA).
|
|
273
|
-
* `onlyProblems` narrows to failures/redirects; `clear`
|
|
274
|
-
*
|
|
274
|
+
* `onlyProblems` narrows to failures/redirects; `clear` advances the read
|
|
275
|
+
* cursor (history is never deleted); `since`/`afterId`/`offset` page it.
|
|
275
276
|
*/
|
|
276
277
|
networkLog(opts?: {
|
|
277
278
|
limit?: number;
|
|
278
279
|
clear?: boolean;
|
|
279
280
|
onlyProblems?: boolean;
|
|
280
281
|
maxBodyChars?: number;
|
|
282
|
+
offset?: number;
|
|
283
|
+
since?: number;
|
|
284
|
+
afterId?: string;
|
|
281
285
|
}): Promise<{
|
|
282
286
|
requests: NetworkEntry[];
|
|
283
287
|
console: ConsoleEntry[];
|
|
284
288
|
websockets: WebSocketFrame[];
|
|
285
289
|
status: NetworkLogStatus;
|
|
290
|
+
truncated?: boolean;
|
|
291
|
+
bodyCap?: number;
|
|
292
|
+
}>;
|
|
293
|
+
/**
|
|
294
|
+
* Bounded per-action network delta (2026-10): the API requests that started
|
|
295
|
+
* at/after `since`, newest last. Attached to every mutating action result
|
|
296
|
+
* so the agent sees the rejected OTP POST (or any API mismatch) without a
|
|
297
|
+
* second `networkLog` call.
|
|
298
|
+
*/
|
|
299
|
+
networkDelta(since: number, limit?: number): Promise<{
|
|
300
|
+
requests: Array<{
|
|
301
|
+
method: string;
|
|
302
|
+
url: string;
|
|
303
|
+
status?: number;
|
|
304
|
+
failed?: string;
|
|
305
|
+
blockedReason?: string;
|
|
306
|
+
durationMs?: number;
|
|
307
|
+
requestBody?: string;
|
|
308
|
+
responseBody?: string;
|
|
309
|
+
}>;
|
|
310
|
+
pendingRequests: number;
|
|
286
311
|
}>;
|
|
287
312
|
/**
|
|
288
313
|
* Click at (x, y) CSS pixels. Coordinates OUTSIDE the current viewport are
|
|
@@ -388,6 +413,14 @@ export declare class BrowserSession {
|
|
|
388
413
|
type(text: string, opts?: {
|
|
389
414
|
clearFirst?: boolean;
|
|
390
415
|
}): Promise<TypeResult>;
|
|
416
|
+
/**
|
|
417
|
+
* Fill a segmented OTP / one-time-code widget digit-by-digit (2026-10).
|
|
418
|
+
*
|
|
419
|
+
* Detects the group by shape (N visible single-char inputs), clears every
|
|
420
|
+
* cell, types one digit per cell with verification, waits for the site's
|
|
421
|
+
* auto-submit sensor, and only clicks submit when it did not fire.
|
|
422
|
+
*/
|
|
423
|
+
fillOtp(opts: FillOtpOptions): Promise<FillOtpResult>;
|
|
391
424
|
/**
|
|
392
425
|
* Deterministically fill a login form (username + password) and submit.
|
|
393
426
|
* Handles the common two-step email -> Continue -> password flow. Requires
|
|
@@ -55,6 +55,7 @@ const launch_1 = require("./launch");
|
|
|
55
55
|
const owned_registry_1 = require("./owned-registry");
|
|
56
56
|
const process_1 = require("./process");
|
|
57
57
|
const login_1 = require("./login");
|
|
58
|
+
const otp_1 = require("./otp");
|
|
58
59
|
const page_context_1 = require("./page-context");
|
|
59
60
|
const page_events_1 = require("./page-events");
|
|
60
61
|
const refs_1 = require("./refs");
|
|
@@ -729,13 +730,50 @@ class BrowserSession {
|
|
|
729
730
|
}
|
|
730
731
|
/**
|
|
731
732
|
* Read the network + console evidence collected for this session (QA).
|
|
732
|
-
* `onlyProblems` narrows to failures/redirects; `clear`
|
|
733
|
-
*
|
|
733
|
+
* `onlyProblems` narrows to failures/redirects; `clear` advances the read
|
|
734
|
+
* cursor (history is never deleted); `since`/`afterId`/`offset` page it.
|
|
734
735
|
*/
|
|
735
736
|
async networkLog(opts) {
|
|
736
737
|
this.ensureConnected();
|
|
737
738
|
return this.network.snapshot(this.page, opts);
|
|
738
739
|
}
|
|
740
|
+
/**
|
|
741
|
+
* Bounded per-action network delta (2026-10): the API requests that started
|
|
742
|
+
* at/after `since`, newest last. Attached to every mutating action result
|
|
743
|
+
* so the agent sees the rejected OTP POST (or any API mismatch) without a
|
|
744
|
+
* second `networkLog` call.
|
|
745
|
+
*/
|
|
746
|
+
async networkDelta(since, limit = 10) {
|
|
747
|
+
try {
|
|
748
|
+
const snap = await this.network.snapshot(null, {
|
|
749
|
+
limit,
|
|
750
|
+
since,
|
|
751
|
+
maxBodyChars: 2000,
|
|
752
|
+
});
|
|
753
|
+
return {
|
|
754
|
+
requests: snap.requests.map((e) => ({
|
|
755
|
+
method: e.method,
|
|
756
|
+
url: e.url.length > 500 ? `${e.url.slice(0, 500)}…` : e.url,
|
|
757
|
+
...(typeof e.status === 'number' ? { status: e.status } : {}),
|
|
758
|
+
...(e.failed ? { failed: e.failed } : {}),
|
|
759
|
+
...(e.blockedReason ? { blockedReason: e.blockedReason } : {}),
|
|
760
|
+
...(typeof e.durationMs === 'number'
|
|
761
|
+
? { durationMs: e.durationMs }
|
|
762
|
+
: {}),
|
|
763
|
+
...(e.requestBody
|
|
764
|
+
? { requestBody: e.requestBody.slice(0, 2000) }
|
|
765
|
+
: {}),
|
|
766
|
+
...(e.responseBody
|
|
767
|
+
? { responseBody: e.responseBody.slice(0, 2000) }
|
|
768
|
+
: {}),
|
|
769
|
+
})),
|
|
770
|
+
pendingRequests: this.network.pendingCount(),
|
|
771
|
+
};
|
|
772
|
+
}
|
|
773
|
+
catch {
|
|
774
|
+
return { requests: [], pendingRequests: 0 };
|
|
775
|
+
}
|
|
776
|
+
}
|
|
739
777
|
/**
|
|
740
778
|
* Click at (x, y) CSS pixels. Coordinates OUTSIDE the current viewport are
|
|
741
779
|
* refused loudly — a screenshot pixel is not a CSS pixel (the capture is
|
|
@@ -955,16 +993,68 @@ class BrowserSession {
|
|
|
955
993
|
*/
|
|
956
994
|
async type(text, opts) {
|
|
957
995
|
this.ensureConnected();
|
|
996
|
+
// Segmented OTP widgets (N maxlength=1 cells): typing the whole code into
|
|
997
|
+
// one cell fails verification and appends on retry. Delegate to the OTP
|
|
998
|
+
// fill instead of failing with a bare TYPE_NOT_VERIFIED.
|
|
999
|
+
const digitsOnly = String(text ?? '').replace(/\D/g, '');
|
|
1000
|
+
if (digitsOnly.length >= 4 &&
|
|
1001
|
+
digitsOnly.length <= 12 &&
|
|
1002
|
+
digitsOnly.length === String(text ?? '').trim().length) {
|
|
1003
|
+
try {
|
|
1004
|
+
const group = await (0, otp_1.detectOtpGroup)(this.page);
|
|
1005
|
+
if (group && group.length === digitsOnly.length) {
|
|
1006
|
+
const filled = await (0, otp_1.fillOtpGroup)(this.page, { code: digitsOnly });
|
|
1007
|
+
if (filled.ok) {
|
|
1008
|
+
return {
|
|
1009
|
+
ok: true,
|
|
1010
|
+
beforeLength: 0,
|
|
1011
|
+
afterLength: filled.filled,
|
|
1012
|
+
focused: 'otp-group',
|
|
1013
|
+
contentEditable: false,
|
|
1014
|
+
};
|
|
1015
|
+
}
|
|
1016
|
+
throw new Error(`OTP_FILL_FAILED: ${filled.error ?? 'the code did not land'} — ${filled.detail ?? 'use browser_action({action:"fillOtp", code}) instead of type.'} ${filled.visibleError ? `Site says: "${filled.visibleError}". ` : ''}Cells: [${filled.values.map((v) => (v ? '"1-char"' : 'empty')).join(', ')}].`);
|
|
1017
|
+
}
|
|
1018
|
+
}
|
|
1019
|
+
catch (err) {
|
|
1020
|
+
if (err instanceof Error && err.message.startsWith('OTP_FILL_FAILED'))
|
|
1021
|
+
throw err;
|
|
1022
|
+
/* detection is best-effort — fall through to normal typing */
|
|
1023
|
+
}
|
|
1024
|
+
}
|
|
958
1025
|
const credential = await (0, login_1.isCredentialFocused)(this.page);
|
|
959
1026
|
if (credential && !this._allowCredentials) {
|
|
960
1027
|
throw new Error('LOGIN_BLOCKED: refusing to type into a password/credential field. Pass allowCredentials:true on browser_open (debug browser only), use browser_action({action:"login"}) with supplied credentials, or ask the human to sign in.');
|
|
961
1028
|
}
|
|
962
1029
|
const result = await (0, login_1.typeTextVerified)(this.page, text, opts);
|
|
963
1030
|
if (!result.ok) {
|
|
964
|
-
|
|
1031
|
+
// Hint the OTP path when the focused field looks like one cell of many:
|
|
1032
|
+
// the agent otherwise retries `type` and burns OTP attempts.
|
|
1033
|
+
let hint = '';
|
|
1034
|
+
try {
|
|
1035
|
+
const group = await (0, otp_1.detectOtpGroup)(this.page);
|
|
1036
|
+
if (group) {
|
|
1037
|
+
hint = ` This looks like cell 1 of a ${group.length}-cell OTP widget — call browser_action({action:"fillOtp", code:"<${group.length} digits>"}) instead of type.`;
|
|
1038
|
+
}
|
|
1039
|
+
}
|
|
1040
|
+
catch {
|
|
1041
|
+
/* ignore */
|
|
1042
|
+
}
|
|
1043
|
+
throw new Error(`TYPE_NOT_VERIFIED: the typed text did not land (focused: ${result.focused ?? 'nothing focusable'}${result.contentEditable ? ' [contenteditable]' : ''}, before: ${result.beforeLength ?? 'unreadable'} chars, after: ${result.afterLength ?? 'unreadable'} chars). Read the field back via snapshot (focused/valueLength) and retry with clearFirst:true, or fill it with evaluate's native value setter + input/change events.${hint}`);
|
|
965
1044
|
}
|
|
966
1045
|
return result;
|
|
967
1046
|
}
|
|
1047
|
+
/**
|
|
1048
|
+
* Fill a segmented OTP / one-time-code widget digit-by-digit (2026-10).
|
|
1049
|
+
*
|
|
1050
|
+
* Detects the group by shape (N visible single-char inputs), clears every
|
|
1051
|
+
* cell, types one digit per cell with verification, waits for the site's
|
|
1052
|
+
* auto-submit sensor, and only clicks submit when it did not fire.
|
|
1053
|
+
*/
|
|
1054
|
+
async fillOtp(opts) {
|
|
1055
|
+
this.ensureConnected();
|
|
1056
|
+
return (0, otp_1.fillOtpGroup)(this.page, opts);
|
|
1057
|
+
}
|
|
968
1058
|
/**
|
|
969
1059
|
* Deterministically fill a login form (username + password) and submit.
|
|
970
1060
|
* Handles the common two-step email -> Continue -> password flow. Requires
|
|
@@ -1301,10 +1391,14 @@ class BrowserSession {
|
|
|
1301
1391
|
};
|
|
1302
1392
|
if (opts?.refs === true && !this._nativeDialog) {
|
|
1303
1393
|
const scanned = await this.getRefs();
|
|
1394
|
+
const otp = await (0, otp_1.detectOtpGroup)(this.page).catch(() => null);
|
|
1304
1395
|
return {
|
|
1305
1396
|
...base,
|
|
1306
1397
|
refs: scanned.refs,
|
|
1307
1398
|
refsText: scanned.lines,
|
|
1399
|
+
...(otp
|
|
1400
|
+
? { oneTimeCode: { length: otp.length, modal: otp.modal } }
|
|
1401
|
+
: {}),
|
|
1308
1402
|
};
|
|
1309
1403
|
}
|
|
1310
1404
|
return base;
|
|
@@ -1343,8 +1437,18 @@ class BrowserSession {
|
|
|
1343
1437
|
const state = await (0, page_context_1.readCompactState)(this.page);
|
|
1344
1438
|
if (!state)
|
|
1345
1439
|
return null;
|
|
1440
|
+
let oneTimeCode;
|
|
1441
|
+
try {
|
|
1442
|
+
const otp = await (0, otp_1.detectOtpGroup)(this.page);
|
|
1443
|
+
if (otp)
|
|
1444
|
+
oneTimeCode = { length: otp.length, modal: otp.modal };
|
|
1445
|
+
}
|
|
1446
|
+
catch {
|
|
1447
|
+
/* OTP detection is best-effort — never fail the observation */
|
|
1448
|
+
}
|
|
1346
1449
|
return {
|
|
1347
1450
|
...state,
|
|
1451
|
+
...(oneTimeCode ? { oneTimeCode } : {}),
|
|
1348
1452
|
nativeDialog: this._nativeDialog,
|
|
1349
1453
|
pendingRequests: this.network.pendingCount(),
|
|
1350
1454
|
consoleErrors: this.network.recentErrors(3).map((c) => ({
|