@mercuryo-ai/agentbrowse 0.2.56 → 0.2.60
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/LICENSE +21 -0
- package/README.md +52 -58
- package/dist/command-name.js +1 -1
- package/dist/commands/act.d.ts.map +1 -1
- package/dist/commands/act.js +13 -5
- package/dist/commands/action-executor-helpers.d.ts.map +1 -1
- package/dist/commands/action-executor-helpers.js +10 -8
- package/dist/commands/click-activation-policy.d.ts.map +1 -1
- package/dist/commands/click-activation-policy.js +6 -2
- package/dist/commands/launch.d.ts +0 -1
- package/dist/commands/launch.d.ts.map +1 -1
- package/dist/commands/launch.js +2 -8
- package/dist/commands/observe-inventory.d.ts +5 -1
- package/dist/commands/observe-inventory.d.ts.map +1 -1
- package/dist/commands/observe-inventory.js +316 -5
- package/dist/commands/observe-persistence.d.ts.map +1 -1
- package/dist/commands/observe-persistence.js +2 -0
- package/dist/commands/observe-projection.d.ts +3 -2
- package/dist/commands/observe-projection.d.ts.map +1 -1
- package/dist/commands/observe-projection.js +1 -0
- package/dist/commands/observe-protected.d.ts +3 -1
- package/dist/commands/observe-protected.d.ts.map +1 -1
- package/dist/commands/observe-protected.js +23 -1
- package/dist/commands/observe-semantics.d.ts.map +1 -1
- package/dist/commands/observe-semantics.js +70 -0
- package/dist/commands/observe.d.ts +1 -0
- package/dist/commands/observe.d.ts.map +1 -1
- package/dist/commands/observe.js +4 -2
- package/dist/control-semantics.d.ts.map +1 -1
- package/dist/control-semantics.js +5 -0
- package/dist/date-value-normalization.d.ts +16 -0
- package/dist/date-value-normalization.d.ts.map +1 -0
- package/dist/date-value-normalization.js +117 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +5 -24
- package/dist/library.d.ts +3 -0
- package/dist/library.d.ts.map +1 -1
- package/dist/library.js +2 -0
- package/dist/protected-fill.d.ts +3 -2
- package/dist/protected-fill.d.ts.map +1 -1
- package/dist/runtime-protected-state.d.ts.map +1 -1
- package/dist/runtime-protected-state.js +8 -1
- package/dist/runtime-state.d.ts +11 -0
- package/dist/runtime-state.d.ts.map +1 -1
- package/dist/secrets/form-matcher.d.ts +1 -2
- package/dist/secrets/form-matcher.d.ts.map +1 -1
- package/dist/secrets/form-matcher.js +125 -119
- package/dist/secrets/matching-helpers.d.ts +13 -0
- package/dist/secrets/matching-helpers.d.ts.map +1 -0
- package/dist/secrets/matching-helpers.js +147 -0
- package/dist/secrets/observed-field-resolution.d.ts +43 -0
- package/dist/secrets/observed-field-resolution.d.ts.map +1 -0
- package/dist/secrets/observed-field-resolution.js +223 -0
- package/dist/secrets/protected-field-semantics.d.ts.map +1 -1
- package/dist/secrets/protected-field-semantics.js +3 -2
- package/dist/secrets/protected-fill.d.ts +3 -1
- package/dist/secrets/protected-fill.d.ts.map +1 -1
- package/dist/secrets/protected-fill.js +31 -0
- package/dist/secrets/protected-value-adapters.d.ts.map +1 -1
- package/dist/secrets/protected-value-adapters.js +14 -22
- package/dist/secrets/types.d.ts +3 -0
- package/dist/secrets/types.d.ts.map +1 -1
- package/docs/README.md +15 -2
- package/docs/api-reference.md +13 -3
- package/docs/assistive-runtime.md +63 -7
- package/docs/configuration.md +16 -8
- package/docs/getting-started.md +17 -8
- package/docs/integration-checklist.md +8 -7
- package/docs/protected-fill.md +40 -7
- package/docs/testing.md +4 -3
- package/docs/troubleshooting.md +88 -34
- package/examples/README.md +9 -2
- package/package.json +8 -3
|
@@ -0,0 +1,223 @@
|
|
|
1
|
+
import { canonicalFieldKeyOf, directFieldSignalValuesOf, fieldKeyEvidenceTerms, normalizeText, selectorRootOf, signalValuesOf, } from './matching-helpers.js';
|
|
2
|
+
function tokensOf(value) {
|
|
3
|
+
return value.split(/[^a-z0-9]+/).filter((token) => token.length > 0);
|
|
4
|
+
}
|
|
5
|
+
function evidenceMatchScore(term, signals) {
|
|
6
|
+
const termTokens = tokensOf(term);
|
|
7
|
+
let best = 0;
|
|
8
|
+
for (const signal of signals) {
|
|
9
|
+
if (signal === term) {
|
|
10
|
+
return 6;
|
|
11
|
+
}
|
|
12
|
+
if (signal.includes(term) || term.includes(signal)) {
|
|
13
|
+
best = Math.max(best, 4);
|
|
14
|
+
continue;
|
|
15
|
+
}
|
|
16
|
+
const signalTokens = new Set(tokensOf(signal));
|
|
17
|
+
const sharedTokenCount = termTokens.filter((token) => signalTokens.has(token)).length;
|
|
18
|
+
if (sharedTokenCount >= 2) {
|
|
19
|
+
best = Math.max(best, 3);
|
|
20
|
+
continue;
|
|
21
|
+
}
|
|
22
|
+
if (sharedTokenCount === 1 &&
|
|
23
|
+
termTokens.some((token) => token.length >= 4 && signalTokens.has(token))) {
|
|
24
|
+
best = Math.max(best, 2);
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
return best;
|
|
28
|
+
}
|
|
29
|
+
function candidateEvidenceTerms(candidate) {
|
|
30
|
+
const terms = new Set();
|
|
31
|
+
const push = (value) => {
|
|
32
|
+
const normalized = normalizeText(value);
|
|
33
|
+
if (normalized) {
|
|
34
|
+
terms.add(normalized);
|
|
35
|
+
}
|
|
36
|
+
};
|
|
37
|
+
for (const term of fieldKeyEvidenceTerms(candidate.fieldKey)) {
|
|
38
|
+
push(term);
|
|
39
|
+
}
|
|
40
|
+
push(candidate.label);
|
|
41
|
+
for (const tag of candidate.semanticTags ?? []) {
|
|
42
|
+
push(tag);
|
|
43
|
+
}
|
|
44
|
+
return [...terms];
|
|
45
|
+
}
|
|
46
|
+
function targetValueTypeOf(target) {
|
|
47
|
+
const inputType = normalizeText(target.inputType);
|
|
48
|
+
const autocomplete = normalizeText(target.autocomplete);
|
|
49
|
+
const signals = directFieldSignalValuesOf(target);
|
|
50
|
+
if (inputType === 'email' || autocomplete.includes('email')) {
|
|
51
|
+
return 'email';
|
|
52
|
+
}
|
|
53
|
+
if (inputType === 'url') {
|
|
54
|
+
return 'url';
|
|
55
|
+
}
|
|
56
|
+
if (inputType === 'number') {
|
|
57
|
+
return 'number';
|
|
58
|
+
}
|
|
59
|
+
if (inputType === 'date' ||
|
|
60
|
+
autocomplete === 'bday' ||
|
|
61
|
+
signals.some((signal) => /\b(date of birth|birth date|dob|birthday)\b/.test(signal))) {
|
|
62
|
+
return 'date';
|
|
63
|
+
}
|
|
64
|
+
return 'text';
|
|
65
|
+
}
|
|
66
|
+
function isEmailValue(value) {
|
|
67
|
+
return typeof value === 'string' && /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(value.trim());
|
|
68
|
+
}
|
|
69
|
+
function isDateValue(value) {
|
|
70
|
+
return typeof value === 'string' && /^\d{4}-\d{2}-\d{2}$/.test(value.trim());
|
|
71
|
+
}
|
|
72
|
+
function isNumberValue(value) {
|
|
73
|
+
if (typeof value === 'number') {
|
|
74
|
+
return Number.isFinite(value);
|
|
75
|
+
}
|
|
76
|
+
return typeof value === 'string' && /^-?\d+(?:\.\d+)?$/.test(value.trim());
|
|
77
|
+
}
|
|
78
|
+
function isUrlValue(value) {
|
|
79
|
+
return typeof value === 'string' && /^https?:\/\//.test(value.trim());
|
|
80
|
+
}
|
|
81
|
+
function isShapeCompatible(target, candidate) {
|
|
82
|
+
if (candidate.type === 'secret') {
|
|
83
|
+
return false;
|
|
84
|
+
}
|
|
85
|
+
const targetValueType = targetValueTypeOf(target);
|
|
86
|
+
switch (targetValueType) {
|
|
87
|
+
case 'email':
|
|
88
|
+
return (isEmailValue(candidate.value) &&
|
|
89
|
+
(!candidate.type || candidate.type === 'email' || candidate.type === 'text'));
|
|
90
|
+
case 'date':
|
|
91
|
+
return (isDateValue(candidate.value) &&
|
|
92
|
+
(!candidate.type || candidate.type === 'date' || candidate.type === 'text'));
|
|
93
|
+
case 'number':
|
|
94
|
+
return (isNumberValue(candidate.value) &&
|
|
95
|
+
(!candidate.type || candidate.type === 'number' || candidate.type === 'text'));
|
|
96
|
+
case 'url':
|
|
97
|
+
return (isUrlValue(candidate.value) &&
|
|
98
|
+
(!candidate.type || candidate.type === 'url' || candidate.type === 'text'));
|
|
99
|
+
case 'text':
|
|
100
|
+
return ((typeof candidate.value === 'string' || typeof candidate.value === 'number') &&
|
|
101
|
+
(!candidate.type ||
|
|
102
|
+
candidate.type === 'text' ||
|
|
103
|
+
candidate.type === 'email' ||
|
|
104
|
+
candidate.type === 'number' ||
|
|
105
|
+
candidate.type === 'date' ||
|
|
106
|
+
candidate.type === 'url'));
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
function isScopeApplicable(candidate, host) {
|
|
110
|
+
if (!candidate.applicability || candidate.applicability.target === 'global') {
|
|
111
|
+
return true;
|
|
112
|
+
}
|
|
113
|
+
return Boolean(host && normalizeText(candidate.applicability.value) === normalizeText(host));
|
|
114
|
+
}
|
|
115
|
+
function contextCompatibilityScore(target, candidate) {
|
|
116
|
+
let score = 0;
|
|
117
|
+
const targetSelectorRoot = normalizeText(selectorRootOf(target) ?? undefined);
|
|
118
|
+
if (targetSelectorRoot && normalizeText(candidate.selectorRoot) === targetSelectorRoot) {
|
|
119
|
+
score += 2;
|
|
120
|
+
}
|
|
121
|
+
const targetPageFormSelector = normalizeText(target.pageFormSelector?.trim());
|
|
122
|
+
if (targetPageFormSelector &&
|
|
123
|
+
normalizeText(candidate.pageFormSelector?.trim()) === targetPageFormSelector) {
|
|
124
|
+
score += 2;
|
|
125
|
+
}
|
|
126
|
+
return score;
|
|
127
|
+
}
|
|
128
|
+
function candidatePriorityScore(candidate, host) {
|
|
129
|
+
let score = 0;
|
|
130
|
+
if (candidate.applicability?.target === 'host' &&
|
|
131
|
+
normalizeText(candidate.applicability.value) === normalizeText(host)) {
|
|
132
|
+
score += 3;
|
|
133
|
+
}
|
|
134
|
+
if (candidate.source === 'session_open_value') {
|
|
135
|
+
score += 2;
|
|
136
|
+
}
|
|
137
|
+
return score;
|
|
138
|
+
}
|
|
139
|
+
function evidenceScore(target, candidate) {
|
|
140
|
+
const directSignals = directFieldSignalValuesOf(target);
|
|
141
|
+
const broadSignals = signalValuesOf(target);
|
|
142
|
+
const terms = candidateEvidenceTerms(candidate);
|
|
143
|
+
let bestTermScore = 0;
|
|
144
|
+
for (const term of terms) {
|
|
145
|
+
bestTermScore = Math.max(bestTermScore, evidenceMatchScore(term, directSignals));
|
|
146
|
+
bestTermScore = Math.max(bestTermScore, evidenceMatchScore(term, broadSignals) - 1);
|
|
147
|
+
}
|
|
148
|
+
return bestTermScore + contextCompatibilityScore(target, candidate) + 1;
|
|
149
|
+
}
|
|
150
|
+
function rankCandidates(target, candidates, options = {}) {
|
|
151
|
+
return candidates
|
|
152
|
+
.map((candidate) => ({
|
|
153
|
+
candidate,
|
|
154
|
+
canonicalFieldKey: canonicalFieldKeyOf(candidate.fieldKey) || candidate.fieldKey,
|
|
155
|
+
score: evidenceScore(target, candidate) + candidatePriorityScore(candidate, options.host),
|
|
156
|
+
}))
|
|
157
|
+
.sort((left, right) => {
|
|
158
|
+
if (right.score !== left.score) {
|
|
159
|
+
return right.score - left.score;
|
|
160
|
+
}
|
|
161
|
+
return left.candidate.candidateRef.localeCompare(right.candidate.candidateRef);
|
|
162
|
+
});
|
|
163
|
+
}
|
|
164
|
+
export function resolveObservedField(target, candidates, options = {}) {
|
|
165
|
+
if (options.protectedTargetRefs?.has(target.ref)) {
|
|
166
|
+
return {
|
|
167
|
+
status: 'no_match',
|
|
168
|
+
targetRef: target.ref,
|
|
169
|
+
reason: 'protected_target',
|
|
170
|
+
};
|
|
171
|
+
}
|
|
172
|
+
if (candidates.length === 0) {
|
|
173
|
+
return {
|
|
174
|
+
status: 'no_match',
|
|
175
|
+
targetRef: target.ref,
|
|
176
|
+
reason: 'no_candidate',
|
|
177
|
+
};
|
|
178
|
+
}
|
|
179
|
+
const scopeEligible = candidates.filter((candidate) => isScopeApplicable(candidate, options.host));
|
|
180
|
+
if (scopeEligible.length === 0) {
|
|
181
|
+
return {
|
|
182
|
+
status: 'no_match',
|
|
183
|
+
targetRef: target.ref,
|
|
184
|
+
reason: 'scope_ineligible',
|
|
185
|
+
};
|
|
186
|
+
}
|
|
187
|
+
const shapeEligible = scopeEligible.filter((candidate) => isShapeCompatible(target, candidate));
|
|
188
|
+
if (shapeEligible.length === 0) {
|
|
189
|
+
return {
|
|
190
|
+
status: 'no_match',
|
|
191
|
+
targetRef: target.ref,
|
|
192
|
+
reason: 'incompatible_shape',
|
|
193
|
+
};
|
|
194
|
+
}
|
|
195
|
+
const minimumScore = options.minimumScore ?? 5;
|
|
196
|
+
const clearWinnerDelta = options.clearWinnerDelta ?? 2;
|
|
197
|
+
const ranked = rankCandidates(target, shapeEligible, { host: options.host });
|
|
198
|
+
const best = ranked[0];
|
|
199
|
+
if (!best || best.score < minimumScore) {
|
|
200
|
+
return {
|
|
201
|
+
status: 'no_match',
|
|
202
|
+
targetRef: target.ref,
|
|
203
|
+
reason: 'low_confidence',
|
|
204
|
+
};
|
|
205
|
+
}
|
|
206
|
+
const ambiguousCandidates = ranked.filter((candidate) => best.score - candidate.score < clearWinnerDelta);
|
|
207
|
+
if (ambiguousCandidates.length > 1) {
|
|
208
|
+
return {
|
|
209
|
+
status: 'ambiguous',
|
|
210
|
+
targetRef: target.ref,
|
|
211
|
+
candidates: ambiguousCandidates.map((candidate) => candidate.candidate.candidateRef),
|
|
212
|
+
};
|
|
213
|
+
}
|
|
214
|
+
return {
|
|
215
|
+
status: 'matched',
|
|
216
|
+
targetRef: target.ref,
|
|
217
|
+
fieldKey: best.canonicalFieldKey,
|
|
218
|
+
value: best.candidate.value,
|
|
219
|
+
source: best.candidate.source,
|
|
220
|
+
confidence: best.score >= 7 ? 'high' : 'medium',
|
|
221
|
+
candidateRef: best.candidate.candidateRef,
|
|
222
|
+
};
|
|
223
|
+
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"protected-field-semantics.d.ts","sourceRoot":"","sources":["../../src/secrets/protected-field-semantics.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,qBAAqB,CAAC;AAC5D,OAAO,KAAK,EAAE,yBAAyB,EAAE,oBAAoB,EAAE,gBAAgB,EAAE,MAAM,YAAY,CAAC;AAEpG,MAAM,WAAW,yBAAyB;IACxC,IAAI,EAAE,gBAAgB,CAAC;IACvB,QAAQ,EAAE,oBAAoB,CAAC;IAC/B,SAAS,EAAE,yBAAyB,CAAC;CACtC;AAiED,wBAAgB,oCAAoC,CAClD,MAAM,EAAE,gBAAgB,GACvB,yBAAyB,EAAE,
|
|
1
|
+
{"version":3,"file":"protected-field-semantics.d.ts","sourceRoot":"","sources":["../../src/secrets/protected-field-semantics.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,qBAAqB,CAAC;AAC5D,OAAO,KAAK,EAAE,yBAAyB,EAAE,oBAAoB,EAAE,gBAAgB,EAAE,MAAM,YAAY,CAAC;AAEpG,MAAM,WAAW,yBAAyB;IACxC,IAAI,EAAE,gBAAgB,CAAC;IACvB,QAAQ,EAAE,oBAAoB,CAAC;IAC/B,SAAS,EAAE,yBAAyB,CAAC;CACtC;AAiED,wBAAgB,oCAAoC,CAClD,MAAM,EAAE,gBAAgB,GACvB,yBAAyB,EAAE,CAyJ7B"}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { inferComparableValueTypeFromFacts } from '../control-semantics.js';
|
|
2
|
-
const LOGIN_USERNAME_SIGNAL_RE = /\b(email|e-mail|username|user name|login|account(?: email|
|
|
2
|
+
const LOGIN_USERNAME_SIGNAL_RE = /\b(email|e-mail|username|user name|login|account(?:[ _-]?(?:email|name|id|login|username))|member(?:[ _-]?(?:email|name|id)))\b/i;
|
|
3
3
|
const LOGIN_PASSWORD_SIGNAL_RE = /\b(password|passcode|pass word)\b/i;
|
|
4
4
|
const PAYMENT_CARD_NAME_SIGNAL_RE = /\b(cardholder|card holder|name on card|cardholder name|full name)\b/i;
|
|
5
5
|
const PAYMENT_CARD_PAN_SIGNAL_RE = /\b(card number|card no|cc-number|cc number)\b/i;
|
|
@@ -65,7 +65,8 @@ export function inferProtectedFieldMeaningFromTarget(target) {
|
|
|
65
65
|
if (autocomplete.includes('username') ||
|
|
66
66
|
autocomplete.includes('email') ||
|
|
67
67
|
inputType === 'email' ||
|
|
68
|
-
/\b(user(name)?|login|email
|
|
68
|
+
/\b(user(name)?|login|email)\b/.test(inputName) ||
|
|
69
|
+
/\b(account|member)(?:_?(?:email|name|id|login|username))\b/.test(inputName) ||
|
|
69
70
|
signals.some((signal) => LOGIN_USERNAME_SIGNAL_RE.test(signal))) {
|
|
70
71
|
hints.push({ kind: 'login', fieldKey: 'username', valueHint: 'direct' });
|
|
71
72
|
}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import type { Page } from 'playwright-core';
|
|
2
2
|
import type { BrowserSessionState } from '../browser-session-state.js';
|
|
3
|
+
import { type DateValueNormalizationErrorReason } from '../date-value-normalization.js';
|
|
3
4
|
import { type TargetDescriptor } from '../runtime-state.js';
|
|
4
5
|
import type { PersistedFillableForm, StoredSecretFieldPolicies, StoredSecretFieldKey } from './types.js';
|
|
5
6
|
type ProtectedFillAction = 'fill' | 'select' | 'type';
|
|
@@ -11,6 +12,7 @@ export interface ProtectedFieldError extends ProtectedFilledField {
|
|
|
11
12
|
reason: 'client_validation_rejected' | 'value_not_applied';
|
|
12
13
|
validationTextRedacted?: true;
|
|
13
14
|
}
|
|
15
|
+
type ProtectedUnexpectedErrorReason = 'missing_protected_value' | 'unsupported_protected_field_group' | 'deterministic_only_resolution_failed' | 'assisted_value_resolution_failed' | 'action_failed' | DateValueNormalizationErrorReason;
|
|
14
16
|
type ProtectedBindingStaleReason = 'target_missing' | 'target_not_live' | 'page_signature_mismatch' | 'dom_signature_mismatch' | 'locator_resolution_failed' | 'target_blocked';
|
|
15
17
|
export type ProtectedFillExecutionResult = {
|
|
16
18
|
kind: 'success';
|
|
@@ -27,7 +29,7 @@ export type ProtectedFillExecutionResult = {
|
|
|
27
29
|
fieldErrors: ProtectedFieldError[];
|
|
28
30
|
} | {
|
|
29
31
|
kind: 'unexpected_error';
|
|
30
|
-
reason:
|
|
32
|
+
reason: ProtectedUnexpectedErrorReason;
|
|
31
33
|
};
|
|
32
34
|
declare function actionForTarget(target: TargetDescriptor): ProtectedFillAction;
|
|
33
35
|
declare function formatCardExpiry(month: string, year: string): string;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"protected-fill.d.ts","sourceRoot":"","sources":["../../src/secrets/protected-fill.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAW,IAAI,EAAE,MAAM,iBAAiB,CAAC;AAErD,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,6BAA6B,CAAC;AACvE,OAAO,EAAyB,KAAK,gBAAgB,EAAE,MAAM,qBAAqB,CAAC;AAiBnF,OAAO,KAAK,EAEV,qBAAqB,EACrB,yBAAyB,EACzB,oBAAoB,EACrB,MAAM,YAAY,CAAC;AAGpB,KAAK,mBAAmB,GAAG,MAAM,GAAG,QAAQ,GAAG,MAAM,CAAC;AAEtD,MAAM,WAAW,oBAAoB;IACnC,QAAQ,EAAE,oBAAoB,CAAC;IAC/B,SAAS,EAAE,MAAM,CAAC;CACnB;AAED,MAAM,WAAW,mBAAoB,SAAQ,oBAAoB;IAC/D,MAAM,EAAE,4BAA4B,GAAG,mBAAmB,CAAC;IAC3D,sBAAsB,CAAC,EAAE,IAAI,CAAC;CAC/B;AAED,KAAK,2BAA2B,GAC5B,gBAAgB,GAChB,iBAAiB,GACjB,yBAAyB,GACzB,wBAAwB,GACxB,2BAA2B,GAC3B,gBAAgB,CAAC;AAarB,MAAM,MAAM,4BAA4B,GACpC;IACE,IAAI,EAAE,SAAS,CAAC;IAChB,YAAY,EAAE,oBAAoB,EAAE,CAAC;CACtC,GACD;IACE,IAAI,EAAE,eAAe,CAAC;IACtB,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,EAAE,oBAAoB,EAAE,CAAC;IAClC,MAAM,EAAE,2BAA2B,CAAC;IACpC,QAAQ,EAAE,MAAM,EAAE,CAAC;CACpB,GACD;IACE,IAAI,EAAE,mBAAmB,CAAC;IAC1B,YAAY,EAAE,oBAAoB,EAAE,CAAC;IACrC,WAAW,EAAE,mBAAmB,EAAE,CAAC;CACpC,GACD;IACE,IAAI,EAAE,kBAAkB,CAAC;IACzB,MAAM,
|
|
1
|
+
{"version":3,"file":"protected-fill.d.ts","sourceRoot":"","sources":["../../src/secrets/protected-fill.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAW,IAAI,EAAE,MAAM,iBAAiB,CAAC;AAErD,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,6BAA6B,CAAC;AACvE,OAAO,EAEL,KAAK,iCAAiC,EAEvC,MAAM,gCAAgC,CAAC;AACxC,OAAO,EAAyB,KAAK,gBAAgB,EAAE,MAAM,qBAAqB,CAAC;AAiBnF,OAAO,KAAK,EAEV,qBAAqB,EACrB,yBAAyB,EACzB,oBAAoB,EACrB,MAAM,YAAY,CAAC;AAGpB,KAAK,mBAAmB,GAAG,MAAM,GAAG,QAAQ,GAAG,MAAM,CAAC;AAEtD,MAAM,WAAW,oBAAoB;IACnC,QAAQ,EAAE,oBAAoB,CAAC;IAC/B,SAAS,EAAE,MAAM,CAAC;CACnB;AAED,MAAM,WAAW,mBAAoB,SAAQ,oBAAoB;IAC/D,MAAM,EAAE,4BAA4B,GAAG,mBAAmB,CAAC;IAC3D,sBAAsB,CAAC,EAAE,IAAI,CAAC;CAC/B;AAED,KAAK,8BAA8B,GAC/B,yBAAyB,GACzB,mCAAmC,GACnC,sCAAsC,GACtC,kCAAkC,GAClC,eAAe,GACf,iCAAiC,CAAC;AAEtC,KAAK,2BAA2B,GAC5B,gBAAgB,GAChB,iBAAiB,GACjB,yBAAyB,GACzB,wBAAwB,GACxB,2BAA2B,GAC3B,gBAAgB,CAAC;AAarB,MAAM,MAAM,4BAA4B,GACpC;IACE,IAAI,EAAE,SAAS,CAAC;IAChB,YAAY,EAAE,oBAAoB,EAAE,CAAC;CACtC,GACD;IACE,IAAI,EAAE,eAAe,CAAC;IACtB,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,EAAE,oBAAoB,EAAE,CAAC;IAClC,MAAM,EAAE,2BAA2B,CAAC;IACpC,QAAQ,EAAE,MAAM,EAAE,CAAC;CACpB,GACD;IACE,IAAI,EAAE,mBAAmB,CAAC;IAC1B,YAAY,EAAE,oBAAoB,EAAE,CAAC;IACrC,WAAW,EAAE,mBAAmB,EAAE,CAAC;CACpC,GACD;IACE,IAAI,EAAE,kBAAkB,CAAC;IACzB,MAAM,EAAE,8BAA8B,CAAC;CACxC,CAAC;AAEN,iBAAS,eAAe,CAAC,MAAM,EAAE,gBAAgB,GAAG,mBAAmB,CActE;AAED,iBAAS,gBAAgB,CAAC,KAAK,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,MAAM,CAK7D;AAwUD,wBAAsB,oBAAoB,CAAC,MAAM,EAAE;IACjD,OAAO,EAAE,mBAAmB,CAAC;IAC7B,IAAI,EAAE,IAAI,CAAC;IACX,YAAY,EAAE,qBAAqB,CAAC;IACpC,eAAe,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACxC,aAAa,CAAC,EAAE,yBAAyB,CAAC;CAC3C,GAAG,OAAO,CAAC,4BAA4B,CAAC,CAgOxC;AAED,eAAO,MAAM,mBAAmB;;;qCAIjB,aAAa,CAAC,oBAAoB,CAAC,mBAC7B,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,WAC9B,gBAAgB;CAuB5B,CAAC"}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { AgentbrowseAssistiveRuntimeMissingError } from '../assistive-runtime.js';
|
|
2
|
+
import { isDateValueNormalizationErrorReason, normalizeStructuredDateValue, } from '../date-value-normalization.js';
|
|
2
3
|
import { getSurface, getTarget } from '../runtime-state.js';
|
|
3
4
|
import { createAcceptanceProbe, waitForAcceptanceProbe } from '../commands/action-acceptance.js';
|
|
4
5
|
import { applyActionWithFallbacks } from '../commands/action-executor.js';
|
|
@@ -44,6 +45,18 @@ function resolveBindingValue(fields, target, protectedValues, fieldPolicies, ass
|
|
|
44
45
|
if (typeof deterministicValue === 'string' && deterministicValue.length > 0) {
|
|
45
46
|
return deterministicValue;
|
|
46
47
|
}
|
|
48
|
+
if (field.fieldKey === 'date_of_birth') {
|
|
49
|
+
const rawDateValue = protectedValues.date_of_birth;
|
|
50
|
+
if (typeof rawDateValue === 'string' && rawDateValue.trim().length > 0) {
|
|
51
|
+
const normalizedDate = normalizeStructuredDateValue(rawDateValue);
|
|
52
|
+
if (normalizedDate.kind === 'error') {
|
|
53
|
+
throw new Error(normalizedDate.reason);
|
|
54
|
+
}
|
|
55
|
+
if (normalizedDate.kind === 'not_date_like') {
|
|
56
|
+
throw new Error('invalid_date_value');
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
}
|
|
47
60
|
if ((field.valueHint ?? 'direct') !== 'direct') {
|
|
48
61
|
throw new Error('deterministic_only_resolution_failed');
|
|
49
62
|
}
|
|
@@ -253,6 +266,10 @@ function staleReasonFromError(error) {
|
|
|
253
266
|
}
|
|
254
267
|
return null;
|
|
255
268
|
}
|
|
269
|
+
function unexpectedReasonFromError(error) {
|
|
270
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
271
|
+
return isDateValueNormalizationErrorReason(message) ? message : null;
|
|
272
|
+
}
|
|
256
273
|
export async function executeProtectedFill(params) {
|
|
257
274
|
const preparedBindings = await prepareBindings(params.session, params.page, params.fillableForm);
|
|
258
275
|
if (!Array.isArray(preparedBindings)) {
|
|
@@ -305,6 +322,13 @@ export async function executeProtectedFill(params) {
|
|
|
305
322
|
actionValue = resolveBindingValue(binding.fields, binding.target, params.protectedValues, params.fieldPolicies, assistedValues);
|
|
306
323
|
}
|
|
307
324
|
catch (error) {
|
|
325
|
+
const unexpectedReason = unexpectedReasonFromError(error);
|
|
326
|
+
if (unexpectedReason) {
|
|
327
|
+
return {
|
|
328
|
+
kind: 'unexpected_error',
|
|
329
|
+
reason: unexpectedReason,
|
|
330
|
+
};
|
|
331
|
+
}
|
|
308
332
|
const message = error instanceof Error ? error.message : String(error);
|
|
309
333
|
if (message === 'missing_protected_value') {
|
|
310
334
|
return {
|
|
@@ -383,6 +407,13 @@ export async function executeProtectedFill(params) {
|
|
|
383
407
|
filledFields.push(...flattenFilledFields(binding.fieldKeys, binding.targetRef));
|
|
384
408
|
}
|
|
385
409
|
catch (error) {
|
|
410
|
+
const unexpectedReason = unexpectedReasonFromError(error);
|
|
411
|
+
if (unexpectedReason) {
|
|
412
|
+
return {
|
|
413
|
+
kind: 'unexpected_error',
|
|
414
|
+
reason: unexpectedReason,
|
|
415
|
+
};
|
|
416
|
+
}
|
|
386
417
|
if (acceptanceProbe) {
|
|
387
418
|
acceptanceResult =
|
|
388
419
|
acceptanceResult ?? (await waitForAcceptanceProbe(acceptanceProbe).catch(() => null));
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"protected-value-adapters.d.ts","sourceRoot":"","sources":["../../src/secrets/protected-value-adapters.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"protected-value-adapters.d.ts","sourceRoot":"","sources":["../../src/secrets/protected-value-adapters.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,qBAAqB,CAAC;AAC5D,OAAO,KAAK,EAAE,wBAAwB,EAAwB,MAAM,YAAY,CAAC;AA+EjF,wBAAgB,yCAAyC,CACvD,OAAO,EAAE,IAAI,CAAC,wBAAwB,EAAE,UAAU,GAAG,WAAW,CAAC,EACjE,eAAe,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,EACvC,MAAM,CAAC,EAAE,IAAI,CAAC,gBAAgB,EAAE,OAAO,GAAG,cAAc,GAAG,SAAS,CAAC,GACpE,MAAM,GAAG,IAAI,CAqEf"}
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { normalizeStructuredDateValue } from '../date-value-normalization.js';
|
|
1
2
|
function normalizeWhitespace(value) {
|
|
2
3
|
return value.replace(/\s+/g, ' ').trim();
|
|
3
4
|
}
|
|
@@ -11,18 +12,9 @@ function directStoredValue(protectedValues, fieldKey) {
|
|
|
11
12
|
const value = protectedValues[fieldKey];
|
|
12
13
|
return typeof value === 'string' && value.trim().length > 0 ? value : null;
|
|
13
14
|
}
|
|
14
|
-
function
|
|
15
|
-
const
|
|
16
|
-
|
|
17
|
-
return null;
|
|
18
|
-
}
|
|
19
|
-
const year = match[1];
|
|
20
|
-
const month = match[2];
|
|
21
|
-
const day = match[3];
|
|
22
|
-
if (!year || !month || !day) {
|
|
23
|
-
return null;
|
|
24
|
-
}
|
|
25
|
-
return { year, month, day };
|
|
15
|
+
function normalizeDateValue(value) {
|
|
16
|
+
const normalized = normalizeStructuredDateValue(value);
|
|
17
|
+
return normalized.kind === 'normalized' ? normalized : null;
|
|
26
18
|
}
|
|
27
19
|
function monthName(month) {
|
|
28
20
|
const names = [
|
|
@@ -65,29 +57,29 @@ export function resolveDeterministicProtectedBindingValue(binding, protectedValu
|
|
|
65
57
|
if (!dateOfBirth) {
|
|
66
58
|
return null;
|
|
67
59
|
}
|
|
60
|
+
const normalizedDate = normalizeDateValue(dateOfBirth);
|
|
61
|
+
if (!normalizedDate) {
|
|
62
|
+
return null;
|
|
63
|
+
}
|
|
68
64
|
const valueHint = binding.valueHint ?? 'direct';
|
|
69
65
|
if (valueHint === 'direct') {
|
|
70
|
-
return
|
|
71
|
-
}
|
|
72
|
-
const parsed = parseIsoDate(dateOfBirth);
|
|
73
|
-
if (!parsed) {
|
|
74
|
-
return null;
|
|
66
|
+
return normalizedDate.iso;
|
|
75
67
|
}
|
|
76
68
|
if (valueHint === 'date_of_birth.day') {
|
|
77
|
-
return String(Number(
|
|
69
|
+
return String(Number(normalizedDate.day));
|
|
78
70
|
}
|
|
79
71
|
if (valueHint === 'date_of_birth.year') {
|
|
80
|
-
return
|
|
72
|
+
return normalizedDate.year;
|
|
81
73
|
}
|
|
82
74
|
if (valueHint === 'date_of_birth.month') {
|
|
83
75
|
const style = monthProjectionStyle(target);
|
|
84
76
|
if (style === 'name') {
|
|
85
|
-
return monthName(
|
|
77
|
+
return monthName(normalizedDate.month);
|
|
86
78
|
}
|
|
87
79
|
if (style === 'short') {
|
|
88
|
-
return monthShortName(
|
|
80
|
+
return monthShortName(normalizedDate.month);
|
|
89
81
|
}
|
|
90
|
-
return
|
|
82
|
+
return normalizedDate.month;
|
|
91
83
|
}
|
|
92
84
|
return null;
|
|
93
85
|
}
|
package/dist/secrets/types.d.ts
CHANGED
|
@@ -11,6 +11,7 @@ export type LoginFieldKey = (typeof LOGIN_FIELD_KEYS)[number];
|
|
|
11
11
|
export type IdentityFieldKey = (typeof IDENTITY_FIELD_KEYS)[number];
|
|
12
12
|
export type PaymentCardFieldKey = (typeof PAYMENT_CARD_FIELD_KEYS)[number];
|
|
13
13
|
export type StoredSecretFieldKey = LoginFieldKey | IdentityFieldKey | PaymentCardFieldKey;
|
|
14
|
+
export type LoginStepKind = 'full' | 'identifier' | 'password';
|
|
14
15
|
export type StoredSecretScope = 'site' | 'global';
|
|
15
16
|
export type ProtectedFieldPolicy = 'deterministic_only' | 'llm_assisted';
|
|
16
17
|
export declare const PROTECTED_BINDING_VALUE_HINTS: readonly ["direct", "full_name.given", "full_name.family", "date_of_birth.day", "date_of_birth.month", "date_of_birth.year"];
|
|
@@ -60,7 +61,9 @@ export interface PersistedFillableForm {
|
|
|
60
61
|
fillRef: string;
|
|
61
62
|
pageRef: string;
|
|
62
63
|
scopeRef?: string;
|
|
64
|
+
pageFormSelector?: string;
|
|
63
65
|
purpose: string;
|
|
66
|
+
loginStep?: LoginStepKind;
|
|
64
67
|
presence?: FillableFormPresence;
|
|
65
68
|
scopeEpoch?: number;
|
|
66
69
|
fields: FillableFormFieldBinding[];
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/secrets/types.ts"],"names":[],"mappings":"AAAA,eAAO,MAAM,gBAAgB,mCAAoC,CAAC;AAClE,eAAO,MAAM,mBAAmB,2HAQtB,CAAC;AACX,eAAO,MAAM,uBAAuB,gEAM1B,CAAC;AAEX,MAAM,MAAM,gBAAgB,GAAG,OAAO,GAAG,UAAU,GAAG,cAAc,CAAC;AAErE,eAAO,MAAM,gCAAgC;;;;CAInC,CAAC;AAEX,MAAM,MAAM,aAAa,GAAG,CAAC,OAAO,gBAAgB,CAAC,CAAC,MAAM,CAAC,CAAC;AAC9D,MAAM,MAAM,gBAAgB,GAAG,CAAC,OAAO,mBAAmB,CAAC,CAAC,MAAM,CAAC,CAAC;AACpE,MAAM,MAAM,mBAAmB,GAAG,CAAC,OAAO,uBAAuB,CAAC,CAAC,MAAM,CAAC,CAAC;AAC3E,MAAM,MAAM,oBAAoB,GAAG,aAAa,GAAG,gBAAgB,GAAG,mBAAmB,CAAC;
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/secrets/types.ts"],"names":[],"mappings":"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"}
|
package/docs/README.md
CHANGED
|
@@ -2,7 +2,20 @@
|
|
|
2
2
|
|
|
3
3
|
Public guides for `@mercuryo-ai/agentbrowse`.
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
**Reading order for a new integration:**
|
|
6
|
+
|
|
7
|
+
1. [Package README](../README.md) — 5-minute overview and Quick Start.
|
|
8
|
+
2. [getting-started.md](./getting-started.md) — extended tutorial that
|
|
9
|
+
turns the Quick Start into a working model for every main API.
|
|
10
|
+
3. [api-reference.md](./api-reference.md) — lookup for types, result
|
|
11
|
+
shapes, and error codes (keep open while coding).
|
|
12
|
+
4. Only-when-needed: [configuration.md](./configuration.md),
|
|
13
|
+
[assistive-runtime.md](./assistive-runtime.md),
|
|
14
|
+
[protected-fill.md](./protected-fill.md).
|
|
15
|
+
5. Before shipping: [integration-checklist.md](./integration-checklist.md)
|
|
16
|
+
and [troubleshooting.md](./troubleshooting.md).
|
|
17
|
+
|
|
18
|
+
Full list:
|
|
6
19
|
|
|
7
20
|
- [getting-started.md](./getting-started.md)
|
|
8
21
|
The first guide to read after the package README. It explains the normal
|
|
@@ -18,7 +31,7 @@ Start here:
|
|
|
18
31
|
- [protected-fill.md](./protected-fill.md)
|
|
19
32
|
How protected fill works and when to use it instead of a normal fill action.
|
|
20
33
|
- [integration-checklist.md](./integration-checklist.md)
|
|
21
|
-
|
|
34
|
+
Checklist for packages and services that integrate AgentBrowse.
|
|
22
35
|
- [testing.md](./testing.md)
|
|
23
36
|
Stable testing helpers for packages that wrap AgentBrowse.
|
|
24
37
|
- [troubleshooting.md](./troubleshooting.md)
|
package/docs/api-reference.md
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
# AgentBrowse API Reference
|
|
2
2
|
|
|
3
3
|
This page is the compact reference for the public `@mercuryo-ai/agentbrowse`
|
|
4
|
-
library
|
|
4
|
+
library API.
|
|
5
5
|
|
|
6
6
|
## Main Functions
|
|
7
7
|
|
|
@@ -113,6 +113,16 @@ branching:
|
|
|
113
113
|
|
|
114
114
|
These arrays back the exported `*ErrorCode` types.
|
|
115
115
|
|
|
116
|
+
## Error Classes
|
|
117
|
+
|
|
118
|
+
For code paths that want `instanceof` checks instead of string matching on
|
|
119
|
+
`error` codes:
|
|
120
|
+
|
|
121
|
+
- `AgentbrowseAssistiveRuntimeMissingError` — thrown when an assistive-only
|
|
122
|
+
command runs without an assistive runtime configured.
|
|
123
|
+
- `AssistiveStructuredOutputTruncatedError` — thrown when the assistive
|
|
124
|
+
runtime returns a structured output that was cut off mid-response.
|
|
125
|
+
|
|
116
126
|
## Core Result Shapes
|
|
117
127
|
|
|
118
128
|
All main commands use the same top-level pattern:
|
|
@@ -213,7 +223,7 @@ fields and does not treat the schema stringification format as part of the API.
|
|
|
213
223
|
|
|
214
224
|
## Assistive Runtime Types
|
|
215
225
|
|
|
216
|
-
The root package exports the
|
|
226
|
+
The root package exports the types that describe the assistive runtime:
|
|
217
227
|
|
|
218
228
|
- `AgentbrowseAssistiveChatCompletionRequest`
|
|
219
229
|
- `AgentbrowseAssistiveChatCompletionOptions`
|
|
@@ -229,7 +239,7 @@ Your adapter receives `args.options.messages`, optional
|
|
|
229
239
|
|
|
230
240
|
## Testing Subpath
|
|
231
241
|
|
|
232
|
-
The package publishes a dedicated testing
|
|
242
|
+
The package publishes a dedicated `/testing` subpath:
|
|
233
243
|
|
|
234
244
|
```ts
|
|
235
245
|
import {
|
|
@@ -18,15 +18,13 @@ Today, assistive behavior matters mainly for:
|
|
|
18
18
|
If you only need browser actions and normal page inspection, you can ignore
|
|
19
19
|
assistive runtime completely.
|
|
20
20
|
|
|
21
|
-
## The Runtime
|
|
21
|
+
## The Runtime Shape
|
|
22
22
|
|
|
23
23
|
AgentBrowse does not ship a built-in OpenAI or OpenRouter adapter.
|
|
24
24
|
|
|
25
25
|
Instead, you provide a small runtime object with one responsibility:
|
|
26
26
|
create a chat-completions client.
|
|
27
27
|
|
|
28
|
-
The shape is:
|
|
29
|
-
|
|
30
28
|
```ts
|
|
31
29
|
{
|
|
32
30
|
createLlmClient: () => ({
|
|
@@ -38,8 +36,66 @@ The shape is:
|
|
|
38
36
|
}
|
|
39
37
|
```
|
|
40
38
|
|
|
41
|
-
|
|
42
|
-
|
|
39
|
+
Any OpenAI-compatible chat-completions backend can work, as long as your
|
|
40
|
+
adapter returns the expected response shape.
|
|
41
|
+
|
|
42
|
+
### `options.response_model`
|
|
43
|
+
|
|
44
|
+
When AgentBrowse needs structured JSON (from `extract(...)` or certain
|
|
45
|
+
goal-driven `observe(...)` paths), it passes `response_model` describing
|
|
46
|
+
the expected output:
|
|
47
|
+
|
|
48
|
+
```ts
|
|
49
|
+
{
|
|
50
|
+
name: string; // short identifier for the schema (e.g. "checkout_total")
|
|
51
|
+
schema: ZodType; // a Zod schema AgentBrowse wants the LLM to conform to
|
|
52
|
+
}
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
Your adapter is responsible for translating `response_model.schema` into
|
|
56
|
+
whatever structured-output mechanism your provider supports. For OpenAI,
|
|
57
|
+
this usually means:
|
|
58
|
+
|
|
59
|
+
1. Convert the Zod schema to JSON Schema (e.g. with
|
|
60
|
+
`@browserbasehq/stagehand`'s `toJsonSchema`, or your own helper).
|
|
61
|
+
2. Pass it as `response_format: { type: 'json_schema', json_schema: { ... } }`.
|
|
62
|
+
3. Parse `choices[0].message.content` and return `{ data, usage? }`.
|
|
63
|
+
|
|
64
|
+
If your provider does not support structured outputs, you can prompt the
|
|
65
|
+
model to return JSON and parse the result yourself — as long as the
|
|
66
|
+
returned `data` conforms to the schema, AgentBrowse does not care how it
|
|
67
|
+
was produced.
|
|
68
|
+
|
|
69
|
+
### `options.image`
|
|
70
|
+
|
|
71
|
+
For vision-assisted extraction, AgentBrowse passes a screenshot:
|
|
72
|
+
|
|
73
|
+
```ts
|
|
74
|
+
{
|
|
75
|
+
buffer: Buffer; // raw image bytes
|
|
76
|
+
description?: string; // short hint about what's in the image
|
|
77
|
+
}
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
Most adapters encode the buffer as a base64 data URL and inject it as a
|
|
81
|
+
user-turn content block. See the helper below.
|
|
82
|
+
|
|
83
|
+
### Errors
|
|
84
|
+
|
|
85
|
+
Your adapter throws on provider errors; AgentBrowse maps them to typed
|
|
86
|
+
failures on the calling command:
|
|
87
|
+
|
|
88
|
+
- HTTP errors (401/403/429/5xx): throw a normal `Error` with a descriptive
|
|
89
|
+
message. AgentBrowse surfaces it as a command failure.
|
|
90
|
+
- Missing/empty content: throw; do not return `{ data: null }`.
|
|
91
|
+
- Truncated structured output: AgentBrowse detects this and throws
|
|
92
|
+
`AssistiveStructuredOutputTruncatedError` — usually a signal to raise
|
|
93
|
+
`maxOutputTokens`.
|
|
94
|
+
|
|
95
|
+
The `AgentbrowseAssistiveLlmClient` type exported from the root package
|
|
96
|
+
describes the client object returned by `createLlmClient()`. Use it when you
|
|
97
|
+
want your adapter to conform to the expected shape without redefining it
|
|
98
|
+
by hand.
|
|
43
99
|
|
|
44
100
|
## Recommended Setup
|
|
45
101
|
|
|
@@ -203,5 +259,5 @@ import {
|
|
|
203
259
|
} from '@mercuryo-ai/agentbrowse/testing';
|
|
204
260
|
```
|
|
205
261
|
|
|
206
|
-
That helper installs a fetch-backed runtime with the same public
|
|
207
|
-
runtime
|
|
262
|
+
That helper installs a fetch-backed runtime with the same public
|
|
263
|
+
assistive-runtime shape used by the main package.
|
package/docs/configuration.md
CHANGED
|
@@ -40,8 +40,8 @@ const attached = await attach(remoteCdpUrl, {
|
|
|
40
40
|
});
|
|
41
41
|
```
|
|
42
42
|
|
|
43
|
-
|
|
44
|
-
generic CDP-attached browser session.
|
|
43
|
+
The provider label is metadata only — AgentBrowse treats the connection as
|
|
44
|
+
a generic CDP-attached browser session regardless of the label.
|
|
45
45
|
|
|
46
46
|
## Client Configuration
|
|
47
47
|
|
|
@@ -71,10 +71,19 @@ after a process restart.
|
|
|
71
71
|
### Default Store
|
|
72
72
|
|
|
73
73
|
```ts
|
|
74
|
-
import { loadBrowserSession, saveBrowserSession } from '@mercuryo-ai/agentbrowse';
|
|
74
|
+
import { loadBrowserSession, saveBrowserSession, status } from '@mercuryo-ai/agentbrowse';
|
|
75
75
|
|
|
76
76
|
saveBrowserSession(session);
|
|
77
77
|
const restored = loadBrowserSession();
|
|
78
|
+
|
|
79
|
+
// Always check a restored session before using it — the browser it points
|
|
80
|
+
// at may already be gone.
|
|
81
|
+
if (restored) {
|
|
82
|
+
const check = await status(restored);
|
|
83
|
+
if (!check.success) {
|
|
84
|
+
// The session is no longer reachable. Discard and relaunch.
|
|
85
|
+
}
|
|
86
|
+
}
|
|
78
87
|
```
|
|
79
88
|
|
|
80
89
|
Default path:
|
|
@@ -276,12 +285,11 @@ if your diagnostics implementation throws.
|
|
|
276
285
|
|
|
277
286
|
## Process-Global Convenience Helpers
|
|
278
287
|
|
|
279
|
-
AgentBrowse
|
|
288
|
+
For small scripts and quick experiments, AgentBrowse exposes process-global
|
|
289
|
+
helpers:
|
|
280
290
|
|
|
281
291
|
- `configureAgentbrowseAssistiveRuntime(...)`
|
|
282
292
|
- `configureAgentbrowseDiagnostics(...)`
|
|
283
293
|
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
For embedded production usage, client-scoped configuration is the better
|
|
287
|
-
default.
|
|
294
|
+
For embedded production usage, prefer client-scoped configuration through
|
|
295
|
+
`createAgentbrowseClient(...)`.
|