teamshare-bridge 0.26.23 → 0.26.24

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.
Files changed (81) hide show
  1. package/README.md +383 -383
  2. package/dist/bridge/install-verify.js +8 -8
  3. package/dist/lib/agent-scope.js +10 -10
  4. package/dist/lib/browser/browse-llm.js +44 -44
  5. package/dist/lib/browser/human-input-guard.js +106 -106
  6. package/dist/lib/browser/login.js +141 -141
  7. package/dist/lib/browser/otp.js +72 -72
  8. package/dist/lib/browser/page-context.js +592 -592
  9. package/dist/lib/browser/page-events.js +91 -91
  10. package/dist/lib/browser/refs.js +264 -264
  11. package/dist/lib/browser/screen-change.js +79 -79
  12. package/dist/lib/human-style.js +32 -32
  13. package/dist/lib/llm/usage-reader.js +4 -4
  14. package/dist/lib/orchestrator/worktree.js +118 -50
  15. package/dist/lib/orchestrator/worktree.js.map +1 -1
  16. package/dist/lib/workspace/mirror.d.ts +13 -0
  17. package/dist/lib/workspace/mirror.js +200 -43
  18. package/dist/lib/workspace/mirror.js.map +1 -1
  19. package/dist/lib/workspace/workspace-audit.d.ts +2 -2
  20. package/dist/lib/workspace/workspace-audit.js +27 -17
  21. package/dist/lib/workspace/workspace-audit.js.map +1 -1
  22. package/package.json +1 -1
  23. package/skills/THIRD_PARTY_NOTICES.md +26 -26
  24. package/skills/architect/LICENSE +21 -21
  25. package/skills/codebase-design/LICENSE +21 -21
  26. package/skills/diagnosing-bugs/LICENSE +21 -21
  27. package/skills/domain-modeling/LICENSE +21 -21
  28. package/skills/improve-codebase-architecture/LICENSE +21 -21
  29. package/skills/recover/LICENSE +21 -21
  30. package/skills/review/LICENSE +21 -21
  31. package/skills/tdd/LICENSE +21 -21
  32. package/skills/teamshare-auth-rbac/SKILL.md +78 -78
  33. package/skills/teamshare-cdd/SKILL.md +215 -215
  34. package/skills/teamshare-modularity/SKILL.md +146 -146
  35. package/skills/teamshare-orchestrator-ask/SKILL.md +77 -77
  36. package/skills/teamshare-scaffold/SKILL.md +116 -116
  37. package/templates/backend/.env.example.tpl +13 -13
  38. package/templates/backend/nest-cli.json.tpl +8 -8
  39. package/templates/backend/package.json.tpl +41 -41
  40. package/templates/backend/prisma/schema.prisma.tpl +39 -39
  41. package/templates/backend/src/app.module.ts.tpl +24 -24
  42. package/templates/backend/src/common/filters/all-exceptions.filter.ts.tpl +59 -59
  43. package/templates/backend/src/common/guards/jwt-auth.guard.ts.tpl +27 -27
  44. package/templates/backend/src/common/guards/rbac.guard.ts.tpl +48 -48
  45. package/templates/backend/src/common/interceptors/transform.interceptor.ts.tpl +44 -44
  46. package/templates/backend/src/common/pipes/zod-validation.pipe.ts.tpl +29 -29
  47. package/templates/backend/src/common/swagger/envelope.schemas.ts.tpl +53 -53
  48. package/templates/backend/src/common/swagger/error.schemas.ts.tpl +62 -62
  49. package/templates/backend/src/main.ts.tpl +27 -27
  50. package/templates/backend/src/modules/example/example.controller.ts.tpl +102 -102
  51. package/templates/backend/src/modules/example/example.dto.ts.tpl +14 -14
  52. package/templates/backend/src/modules/example/example.module.ts.tpl +10 -10
  53. package/templates/backend/src/modules/example/example.service.ts.tpl +45 -45
  54. package/templates/backend/tsconfig.json.tpl +26 -26
  55. package/templates/common/.gitignore.tpl +42 -42
  56. package/templates/common/AGENTS.md.tpl +58 -58
  57. package/templates/common/docs/adr/README.md +25 -25
  58. package/templates/common/docs/api-contract.md.tpl +72 -72
  59. package/templates/web/next.config.ts.tpl +7 -7
  60. package/templates/web/package.json.tpl +37 -37
  61. package/templates/web/src/app/globals.css.tpl +48 -48
  62. package/templates/web/src/app/layout.tsx.tpl +25 -25
  63. package/templates/web/src/components/shared/app-button.tsx.tpl +59 -59
  64. package/templates/web/src/components/shared/app-empty-state.tsx.tpl +39 -39
  65. package/templates/web/src/components/shared/app-list-toolbar.tsx.tpl +63 -63
  66. package/templates/web/src/components/shared/app-skeleton.tsx.tpl +14 -14
  67. package/templates/web/src/components/shared/app-table.tsx.tpl +128 -128
  68. package/templates/web/src/components/shared/index.ts.tpl +5 -5
  69. package/templates/web/src/hooks/use-list-state.ts.tpl +72 -72
  70. package/templates/web/src/hooks/use-url-tab.ts.tpl +25 -25
  71. package/templates/web/src/lib/api/client.ts.tpl +123 -123
  72. package/templates/web/src/lib/providers.tsx.tpl +35 -35
  73. package/templates/web/src/lib/query-keys.ts.tpl +21 -21
  74. package/templates/web/src/lib/utils.ts.tpl +7 -7
  75. package/templates/web/src/lib/validation/schemas.ts.tpl +15 -15
  76. package/templates/web/src/modules/example/views/example-list-view.tsx.tpl +97 -97
  77. package/templates/web/tsconfig.json.tpl +23 -23
  78. package/tools/workspace-arbitration-check.mjs +359 -359
  79. package/dist/lib/git/ensure-excludes.d.ts +0 -5
  80. package/dist/lib/git/ensure-excludes.js +0 -55
  81. package/dist/lib/git/ensure-excludes.js.map +0 -1
@@ -39,102 +39,102 @@ exports.isCredentialFocused = isCredentialFocused;
39
39
  */
40
40
  const cdp_1 = require("./cdp");
41
41
  /** DOM heuristic: locate the login controls, scoped to the topmost modal. */
42
- exports.FIND_LOGIN_FIELDS_JS = `(() => {
43
- const vis = (el) => {
44
- if (!el) return false;
45
- const r = el.getBoundingClientRect();
46
- if (r.width === 0 || r.height === 0) return false;
47
- const cs = getComputedStyle(el);
48
- return cs.visibility !== 'hidden' && cs.display !== 'none' && cs.opacity !== '0';
49
- };
50
- const hintOf = (el) => [el.getAttribute('name'), el.id, el.getAttribute('autocomplete')].filter(Boolean).join('/');
51
- const lowerType = (el) => (el.type || '').toLowerCase();
52
- const autocomplete = (el) => (el.getAttribute('autocomplete') || '').toLowerCase();
53
-
54
- // MODAL SCOPE (2026-10): a login opened in a dialog must win over a footer
55
- // newsletter input or the page's nav Login button. Prefer the first visible
56
- // dialog/frame that actually contains a login-ish field; otherwise the last
57
- // visible dialog; otherwise the document.
58
- const dialogSel = 'dialog[open],[role="dialog"],[aria-modal="true"],' +
59
- '[class*="modal"],[class*="Modal"],[class*="dialog"],[class*="Dialog"]';
60
- const dialogEls = [...document.querySelectorAll(dialogSel)].filter(vis);
61
- const hasStrongLogin = (d) => !!d.querySelector('input[type="password"],input[autocomplete="username"],input[autocomplete="current-password"],input[autocomplete="new-password"],input[autocomplete="one-time-code"]');
62
- const hasEmailAndContinue = (d) => {
63
- if (!d.querySelector('input[type="email"],input[autocomplete="email"]')) return false;
64
- return [...d.querySelectorAll('button,[role="button"],input[type="submit"]')].some((el) =>
65
- /continue|next|sign|log\\s?in|verify|submit/i.test((el.innerText || el.textContent || el.value || '').trim()),
66
- );
67
- };
68
- let scope =
69
- dialogEls.find(hasStrongLogin) ||
70
- dialogEls.find(hasEmailAndContinue) ||
71
- dialogEls.find((d) => d.querySelector('input,textarea')) ||
72
- null;
73
- if (!scope && dialogEls.length) scope = dialogEls[dialogEls.length - 1];
74
- const root = scope || document;
75
-
76
- // Clear the previous scan's tags so a re-rendered modal cannot be addressed
77
- // through a stale element.
78
- document.querySelectorAll('[data-ts-login]').forEach((e) => e.removeAttribute('data-ts-login'));
79
-
80
- const inputs = [...root.querySelectorAll('input,textarea')]
81
- .filter(vis)
82
- .filter((el) => lowerType(el) !== 'hidden' && !el.disabled && !el.readOnly);
83
-
84
- const pw =
85
- inputs.find((el) => lowerType(el) === 'password') ||
86
- inputs.find((el) => ['current-password', 'new-password'].includes(autocomplete(el))) ||
87
- null;
88
-
89
- let user =
90
- inputs.find((el) => autocomplete(el) === 'username') ||
91
- inputs.find((el) => lowerType(el) === 'email') ||
92
- null;
93
- if (!user) {
94
- const textish = inputs.filter((el) => ['', 'text', 'email', 'tel'].includes(lowerType(el)));
95
- if (pw) {
96
- user = textish.find((el) => (el.compareDocumentPosition(pw) & Node.DOCUMENT_POSITION_PRECEDING)) || null;
97
- }
98
- if (!user && textish.length) user = textish[0];
99
- }
100
-
101
- const otp =
102
- inputs.find((el) => autocomplete(el) === 'one-time-code') ||
103
- inputs.find((el) => /otp|one[- ]?time|verif|code/i.test(hintOf(el) || '')) ||
104
- null;
105
-
106
- // The same element can match several heuristics; one role wins.
107
- if (user && otp && user === otp) user = null;
108
- if (user && pw && user === pw) user = null;
109
-
110
- const submitRe = /sign\\s?in|log\\s?in|login|continue|next|submit|log\\s?on|enter|proceed|verify|confirm/i;
111
- const clickable = [...root.querySelectorAll('button,input[type=submit],input[type=button],[role=button],a')]
112
- .filter(vis)
113
- .filter((el) => el.disabled !== true && el.getAttribute('aria-disabled') !== 'true');
114
- let submit =
115
- clickable.find((el) => (el.getAttribute('type') || '').toLowerCase() === 'submit') ||
116
- clickable.find((el) => submitRe.test((el.innerText || el.textContent || el.value || '').trim())) ||
117
- clickable.find((el) => /submit/i.test(hintOf(el) || '')) ||
118
- null;
119
-
120
- const box = (el, hint, role) => {
121
- el.setAttribute('data-ts-login', role);
122
- const r = el.getBoundingClientRect();
123
- return {
124
- x: Math.round(r.x + r.width / 2),
125
- y: Math.round(r.y + r.height / 2),
126
- hint: hint || '',
127
- role: role,
128
- };
129
- };
130
-
131
- return {
132
- username: user && vis(user) ? box(user, hintOf(user), 'username') : null,
133
- password: pw && vis(pw) ? box(pw, hintOf(pw), 'password') : null,
134
- submit: submit ? box(submit, (submit.innerText || submit.textContent || submit.value || '').trim().slice(0, 40), 'submit') : null,
135
- otp: otp && vis(otp) ? box(otp, hintOf(otp), 'otp') : null,
136
- modal: !!scope,
137
- };
42
+ exports.FIND_LOGIN_FIELDS_JS = `(() => {
43
+ const vis = (el) => {
44
+ if (!el) return false;
45
+ const r = el.getBoundingClientRect();
46
+ if (r.width === 0 || r.height === 0) return false;
47
+ const cs = getComputedStyle(el);
48
+ return cs.visibility !== 'hidden' && cs.display !== 'none' && cs.opacity !== '0';
49
+ };
50
+ const hintOf = (el) => [el.getAttribute('name'), el.id, el.getAttribute('autocomplete')].filter(Boolean).join('/');
51
+ const lowerType = (el) => (el.type || '').toLowerCase();
52
+ const autocomplete = (el) => (el.getAttribute('autocomplete') || '').toLowerCase();
53
+
54
+ // MODAL SCOPE (2026-10): a login opened in a dialog must win over a footer
55
+ // newsletter input or the page's nav Login button. Prefer the first visible
56
+ // dialog/frame that actually contains a login-ish field; otherwise the last
57
+ // visible dialog; otherwise the document.
58
+ const dialogSel = 'dialog[open],[role="dialog"],[aria-modal="true"],' +
59
+ '[class*="modal"],[class*="Modal"],[class*="dialog"],[class*="Dialog"]';
60
+ const dialogEls = [...document.querySelectorAll(dialogSel)].filter(vis);
61
+ const hasStrongLogin = (d) => !!d.querySelector('input[type="password"],input[autocomplete="username"],input[autocomplete="current-password"],input[autocomplete="new-password"],input[autocomplete="one-time-code"]');
62
+ const hasEmailAndContinue = (d) => {
63
+ if (!d.querySelector('input[type="email"],input[autocomplete="email"]')) return false;
64
+ return [...d.querySelectorAll('button,[role="button"],input[type="submit"]')].some((el) =>
65
+ /continue|next|sign|log\\s?in|verify|submit/i.test((el.innerText || el.textContent || el.value || '').trim()),
66
+ );
67
+ };
68
+ let scope =
69
+ dialogEls.find(hasStrongLogin) ||
70
+ dialogEls.find(hasEmailAndContinue) ||
71
+ dialogEls.find((d) => d.querySelector('input,textarea')) ||
72
+ null;
73
+ if (!scope && dialogEls.length) scope = dialogEls[dialogEls.length - 1];
74
+ const root = scope || document;
75
+
76
+ // Clear the previous scan's tags so a re-rendered modal cannot be addressed
77
+ // through a stale element.
78
+ document.querySelectorAll('[data-ts-login]').forEach((e) => e.removeAttribute('data-ts-login'));
79
+
80
+ const inputs = [...root.querySelectorAll('input,textarea')]
81
+ .filter(vis)
82
+ .filter((el) => lowerType(el) !== 'hidden' && !el.disabled && !el.readOnly);
83
+
84
+ const pw =
85
+ inputs.find((el) => lowerType(el) === 'password') ||
86
+ inputs.find((el) => ['current-password', 'new-password'].includes(autocomplete(el))) ||
87
+ null;
88
+
89
+ let user =
90
+ inputs.find((el) => autocomplete(el) === 'username') ||
91
+ inputs.find((el) => lowerType(el) === 'email') ||
92
+ null;
93
+ if (!user) {
94
+ const textish = inputs.filter((el) => ['', 'text', 'email', 'tel'].includes(lowerType(el)));
95
+ if (pw) {
96
+ user = textish.find((el) => (el.compareDocumentPosition(pw) & Node.DOCUMENT_POSITION_PRECEDING)) || null;
97
+ }
98
+ if (!user && textish.length) user = textish[0];
99
+ }
100
+
101
+ const otp =
102
+ inputs.find((el) => autocomplete(el) === 'one-time-code') ||
103
+ inputs.find((el) => /otp|one[- ]?time|verif|code/i.test(hintOf(el) || '')) ||
104
+ null;
105
+
106
+ // The same element can match several heuristics; one role wins.
107
+ if (user && otp && user === otp) user = null;
108
+ if (user && pw && user === pw) user = null;
109
+
110
+ const submitRe = /sign\\s?in|log\\s?in|login|continue|next|submit|log\\s?on|enter|proceed|verify|confirm/i;
111
+ const clickable = [...root.querySelectorAll('button,input[type=submit],input[type=button],[role=button],a')]
112
+ .filter(vis)
113
+ .filter((el) => el.disabled !== true && el.getAttribute('aria-disabled') !== 'true');
114
+ let submit =
115
+ clickable.find((el) => (el.getAttribute('type') || '').toLowerCase() === 'submit') ||
116
+ clickable.find((el) => submitRe.test((el.innerText || el.textContent || el.value || '').trim())) ||
117
+ clickable.find((el) => /submit/i.test(hintOf(el) || '')) ||
118
+ null;
119
+
120
+ const box = (el, hint, role) => {
121
+ el.setAttribute('data-ts-login', role);
122
+ const r = el.getBoundingClientRect();
123
+ return {
124
+ x: Math.round(r.x + r.width / 2),
125
+ y: Math.round(r.y + r.height / 2),
126
+ hint: hint || '',
127
+ role: role,
128
+ };
129
+ };
130
+
131
+ return {
132
+ username: user && vis(user) ? box(user, hintOf(user), 'username') : null,
133
+ password: pw && vis(pw) ? box(pw, hintOf(pw), 'password') : null,
134
+ submit: submit ? box(submit, (submit.innerText || submit.textContent || submit.value || '').trim().slice(0, 40), 'submit') : null,
135
+ otp: otp && vis(otp) ? box(otp, hintOf(otp), 'otp') : null,
136
+ modal: !!scope,
137
+ };
138
138
  })()`;
139
139
  async function findLoginFields(page) {
140
140
  const empty = {
@@ -223,16 +223,16 @@ async function contentText(page) {
223
223
  */
224
224
  async function setNativeActiveValue(page, value) {
225
225
  try {
226
- const ok = await (0, cdp_1.evaluateJs)(page, `(() => {
227
- const a = document.activeElement;
228
- if (!a || typeof a.value !== 'string') return false;
229
- const proto = a.tagName === 'TEXTAREA' ? HTMLTextAreaElement.prototype : HTMLInputElement.prototype;
230
- const desc = Object.getOwnPropertyDescriptor(proto, 'value');
231
- if (!desc || !desc.set) return false;
232
- desc.set.call(a, ${JSON.stringify(value)});
233
- a.dispatchEvent(new Event('input', { bubbles: true }));
234
- a.dispatchEvent(new Event('change', { bubbles: true }));
235
- return true;
226
+ const ok = await (0, cdp_1.evaluateJs)(page, `(() => {
227
+ const a = document.activeElement;
228
+ if (!a || typeof a.value !== 'string') return false;
229
+ const proto = a.tagName === 'TEXTAREA' ? HTMLTextAreaElement.prototype : HTMLInputElement.prototype;
230
+ const desc = Object.getOwnPropertyDescriptor(proto, 'value');
231
+ if (!desc || !desc.set) return false;
232
+ desc.set.call(a, ${JSON.stringify(value)});
233
+ a.dispatchEvent(new Event('input', { bubbles: true }));
234
+ a.dispatchEvent(new Event('change', { bubbles: true }));
235
+ return true;
236
236
  })()`);
237
237
  return ok === true;
238
238
  }
@@ -311,16 +311,16 @@ async function pressBackspace(page) {
311
311
  /** Set a tagged input's value through the prototype setter (React-safe). */
312
312
  async function setNativeControlValue(page, role, value) {
313
313
  try {
314
- const ok = await (0, cdp_1.evaluateJs)(page, `(() => {
315
- const el = document.querySelector('[data-ts-login="${role}"]');
316
- if (!el || typeof el.value !== 'string') return false;
317
- const proto = el.tagName === 'TEXTAREA' ? HTMLTextAreaElement.prototype : HTMLInputElement.prototype;
318
- const desc = Object.getOwnPropertyDescriptor(proto, 'value');
319
- if (!desc || !desc.set) return false;
320
- desc.set.call(el, ${JSON.stringify(value)});
321
- el.dispatchEvent(new Event('input', { bubbles: true }));
322
- el.dispatchEvent(new Event('change', { bubbles: true }));
323
- return true;
314
+ const ok = await (0, cdp_1.evaluateJs)(page, `(() => {
315
+ const el = document.querySelector('[data-ts-login="${role}"]');
316
+ if (!el || typeof el.value !== 'string') return false;
317
+ const proto = el.tagName === 'TEXTAREA' ? HTMLTextAreaElement.prototype : HTMLInputElement.prototype;
318
+ const desc = Object.getOwnPropertyDescriptor(proto, 'value');
319
+ if (!desc || !desc.set) return false;
320
+ desc.set.call(el, ${JSON.stringify(value)});
321
+ el.dispatchEvent(new Event('input', { bubbles: true }));
322
+ el.dispatchEvent(new Event('change', { bubbles: true }));
323
+ return true;
324
324
  })()`);
325
325
  return ok === true;
326
326
  }
@@ -581,31 +581,31 @@ async function pressEnter(page) {
581
581
  */
582
582
  async function visibleLoginError(page) {
583
583
  try {
584
- const value = await (0, cdp_1.evaluateJs)(page, `(() => {
585
- const vis = (el) => {
586
- if (!el) return false;
587
- const r = el.getBoundingClientRect();
588
- if (r.width === 0 || r.height === 0) return false;
589
- const cs = getComputedStyle(el);
590
- return cs.visibility !== 'hidden' && cs.display !== 'none';
591
- };
592
- const dialogs = [...document.querySelectorAll('dialog[open],[role="dialog"],[aria-modal="true"]')].filter(vis);
593
- const scope = dialogs.length ? dialogs[dialogs.length - 1] : document;
594
- const out = [];
595
- const add = (t) => {
596
- const text = String(t == null ? '' : t).replace(/\\s+/g, ' ').trim().slice(0, 240);
597
- if (text.length < 3) return;
598
- if (/^(error|errors|warning|warnings)$/i.test(text)) return;
599
- if (!out.includes(text) && out.length < 3) out.push(text);
600
- };
601
- for (const el of [...scope.querySelectorAll('[role="alert"],[aria-invalid="true"]')].filter(vis).slice(0, 4)) {
602
- add(el.innerText || el.textContent || el.getAttribute('aria-label') || '');
603
- }
604
- const sels = '[class*="field-error"],[class*="form-error"],[class*="validation-error"],[class*="error-message"],[class*="invalid-feedback"],[class*="error"],[class*="danger"]';
605
- for (const el of [...scope.querySelectorAll(sels)].filter(vis).slice(0, 12)) {
606
- add(el.innerText || el.textContent || '');
607
- }
608
- return out.join(' | ');
584
+ const value = await (0, cdp_1.evaluateJs)(page, `(() => {
585
+ const vis = (el) => {
586
+ if (!el) return false;
587
+ const r = el.getBoundingClientRect();
588
+ if (r.width === 0 || r.height === 0) return false;
589
+ const cs = getComputedStyle(el);
590
+ return cs.visibility !== 'hidden' && cs.display !== 'none';
591
+ };
592
+ const dialogs = [...document.querySelectorAll('dialog[open],[role="dialog"],[aria-modal="true"]')].filter(vis);
593
+ const scope = dialogs.length ? dialogs[dialogs.length - 1] : document;
594
+ const out = [];
595
+ const add = (t) => {
596
+ const text = String(t == null ? '' : t).replace(/\\s+/g, ' ').trim().slice(0, 240);
597
+ if (text.length < 3) return;
598
+ if (/^(error|errors|warning|warnings)$/i.test(text)) return;
599
+ if (!out.includes(text) && out.length < 3) out.push(text);
600
+ };
601
+ for (const el of [...scope.querySelectorAll('[role="alert"],[aria-invalid="true"]')].filter(vis).slice(0, 4)) {
602
+ add(el.innerText || el.textContent || el.getAttribute('aria-label') || '');
603
+ }
604
+ const sels = '[class*="field-error"],[class*="form-error"],[class*="validation-error"],[class*="error-message"],[class*="invalid-feedback"],[class*="error"],[class*="danger"]';
605
+ for (const el of [...scope.querySelectorAll(sels)].filter(vis).slice(0, 12)) {
606
+ add(el.innerText || el.textContent || '');
607
+ }
608
+ return out.join(' | ');
609
609
  })()`);
610
610
  return typeof value === 'string' && value.trim() ? value.trim() : null;
611
611
  }
@@ -18,57 +18,57 @@ exports.fillOtpGroup = fillOtpGroup;
18
18
  * and only submits when the widget did NOT auto-submit itself.
19
19
  */
20
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
- };
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
72
  })()`;
73
73
  async function detectOtpGroup(page) {
74
74
  try {
@@ -153,15 +153,15 @@ async function typeOtpDigit(page, digit) {
153
153
  }
154
154
  async function visibleOtpError(page) {
155
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;
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
165
  })()`);
166
166
  return typeof v === 'string' && v ? v : null;
167
167
  }
@@ -171,18 +171,18 @@ async function visibleOtpError(page) {
171
171
  }
172
172
  async function clickOtpSubmit(page) {
173
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;
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
186
  })()`);
187
187
  return clicked === true;
188
188
  }