@nuanu-ai/agentbrowse 0.2.24 → 0.2.26
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/commands/action-result-resolution.d.ts +2 -2
- package/dist/commands/action-result-resolution.d.ts.map +1 -1
- package/dist/commands/action-result-resolution.js +26 -6
- package/dist/commands/extract.d.ts.map +1 -1
- package/dist/commands/extract.js +11 -3
- package/dist/commands/navigate.d.ts.map +1 -1
- package/dist/commands/navigate.js +7 -7
- package/dist/commands/observe.d.ts.map +1 -1
- package/dist/commands/observe.js +15 -6
- package/dist/commands/screenshot.d.ts.map +1 -1
- package/dist/commands/screenshot.js +28 -12
- package/dist/commands/status.d.ts.map +1 -1
- package/dist/commands/status.js +92 -6
- package/dist/playwright-runtime.d.ts +6 -0
- package/dist/playwright-runtime.d.ts.map +1 -1
- package/dist/playwright-runtime.js +76 -0
- package/package.json +1 -1
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { type AcceptanceProbe, type PageObservation } from './action-acceptance.js';
|
|
2
|
-
export type SubmitResultClaimKind = 'hard_result' | 'hard_blocker' | 'soft_result_candidate' | 'noisy_change';
|
|
2
|
+
export type SubmitResultClaimKind = 'hard_result' | 'hard_blocker' | 'intermediate_progress' | 'soft_result_candidate' | 'noisy_change';
|
|
3
3
|
export type SubmitResultClaim = {
|
|
4
4
|
kind: SubmitResultClaimKind;
|
|
5
5
|
source: string;
|
|
@@ -7,7 +7,7 @@ export type SubmitResultClaim = {
|
|
|
7
7
|
ownerScopeRef?: string;
|
|
8
8
|
};
|
|
9
9
|
export type SubmitResultResolution = {
|
|
10
|
-
finalVerdict: 'outcome' | 'blocked' | 'none';
|
|
10
|
+
finalVerdict: 'outcome' | 'blocked' | 'progress' | 'none';
|
|
11
11
|
claims: SubmitResultClaim[];
|
|
12
12
|
decisiveClaims: SubmitResultClaim[];
|
|
13
13
|
acceptAsProgress: boolean;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"action-result-resolution.d.ts","sourceRoot":"","sources":["../../src/commands/action-result-resolution.ts"],"names":[],"mappings":"AAAA,OAAO,EAEL,KAAK,eAAe,EACpB,KAAK,eAAe,EACrB,MAAM,wBAAwB,CAAC;AAGhC,MAAM,MAAM,qBAAqB,GAC7B,aAAa,GACb,cAAc,GACd,uBAAuB,GACvB,cAAc,CAAC;AAEnB,MAAM,MAAM,iBAAiB,GAAG;IAC9B,IAAI,EAAE,qBAAqB,CAAC;IAC5B,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,MAAM,CAAC;IACf,aAAa,CAAC,EAAE,MAAM,CAAC;CACxB,CAAC;AAEF,MAAM,MAAM,sBAAsB,GAAG;IACnC,YAAY,EAAE,SAAS,GAAG,SAAS,GAAG,MAAM,CAAC;
|
|
1
|
+
{"version":3,"file":"action-result-resolution.d.ts","sourceRoot":"","sources":["../../src/commands/action-result-resolution.ts"],"names":[],"mappings":"AAAA,OAAO,EAEL,KAAK,eAAe,EACpB,KAAK,eAAe,EACrB,MAAM,wBAAwB,CAAC;AAGhC,MAAM,MAAM,qBAAqB,GAC7B,aAAa,GACb,cAAc,GACd,uBAAuB,GACvB,uBAAuB,GACvB,cAAc,CAAC;AAEnB,MAAM,MAAM,iBAAiB,GAAG;IAC9B,IAAI,EAAE,qBAAqB,CAAC;IAC5B,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,MAAM,CAAC;IACf,aAAa,CAAC,EAAE,MAAM,CAAC;CACxB,CAAC;AAEF,MAAM,MAAM,sBAAsB,GAAG;IACnC,YAAY,EAAE,SAAS,GAAG,SAAS,GAAG,UAAU,GAAG,MAAM,CAAC;IAC1D,MAAM,EAAE,iBAAiB,EAAE,CAAC;IAC5B,cAAc,EAAE,iBAAiB,EAAE,CAAC;IACpC,gBAAgB,EAAE,OAAO,CAAC;CAC3B,CAAC;AAEF,wBAAgB,wBAAwB,CACtC,MAAM,EAAE,aAAa,CAAC,iBAAiB,CAAC,GACvC,IAAI,CAAC,sBAAsB,EAAE,QAAQ,GAAG,kBAAkB,CAAC,CA+B7D;AAmBD,wBAAsB,mBAAmB,CACvC,KAAK,EAAE,eAAe,EACtB,oBAAoB,EAAE,eAAe,GAAG,IAAI,GAC3C,OAAO,CAAC,sBAAsB,CAAC,CAoFjC"}
|
|
@@ -15,6 +15,13 @@ export function reduceSubmitResultClaims(claims) {
|
|
|
15
15
|
decisiveClaims: hardResults,
|
|
16
16
|
};
|
|
17
17
|
}
|
|
18
|
+
const intermediateProgressClaims = claims.filter((claim) => claim.kind === 'intermediate_progress');
|
|
19
|
+
if (intermediateProgressClaims.length > 0) {
|
|
20
|
+
return {
|
|
21
|
+
finalVerdict: 'progress',
|
|
22
|
+
decisiveClaims: intermediateProgressClaims,
|
|
23
|
+
};
|
|
24
|
+
}
|
|
18
25
|
return {
|
|
19
26
|
finalVerdict: 'none',
|
|
20
27
|
decisiveClaims: [],
|
|
@@ -62,6 +69,8 @@ export async function resolveSubmitResult(probe, afterPageObservation) {
|
|
|
62
69
|
}
|
|
63
70
|
const resultSignalChanged = beforePageObservation.resultSignalHash !== afterPageObservation.resultSignalHash &&
|
|
64
71
|
afterPageObservation.resultSignalHash !== null;
|
|
72
|
+
const submitSignalChanged = beforePageObservation.submitSignalHash !== afterPageObservation.submitSignalHash &&
|
|
73
|
+
afterPageObservation.submitSignalHash !== null;
|
|
65
74
|
const afterSurfaceContextHash = await captureAfterSurfaceContextHash(probe);
|
|
66
75
|
const ownerScopeChanged = probe.beforeSurfaceContextHash !== null &&
|
|
67
76
|
afterSurfaceContextHash !== null &&
|
|
@@ -77,18 +86,29 @@ export async function resolveSubmitResult(probe, afterPageObservation) {
|
|
|
77
86
|
});
|
|
78
87
|
}
|
|
79
88
|
else if (ownerScopeChanged) {
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
89
|
+
if (submitSignalChanged) {
|
|
90
|
+
claims.push({
|
|
91
|
+
kind: 'intermediate_progress',
|
|
92
|
+
source: 'owner-scope',
|
|
93
|
+
reason: 'Owner scope changed after submit with a submit-relevant follow-up state change.',
|
|
94
|
+
ownerScopeRef: probe.surface?.ref,
|
|
95
|
+
});
|
|
96
|
+
}
|
|
97
|
+
else {
|
|
98
|
+
claims.push({
|
|
99
|
+
kind: 'noisy_change',
|
|
100
|
+
source: 'owner-scope',
|
|
101
|
+
reason: 'Owner scope changed after submit without a result-bearing signal.',
|
|
102
|
+
ownerScopeRef: probe.surface?.ref,
|
|
103
|
+
});
|
|
104
|
+
}
|
|
86
105
|
}
|
|
87
106
|
const reduced = reduceSubmitResultClaims(claims);
|
|
88
107
|
return {
|
|
89
108
|
...reduced,
|
|
90
109
|
claims,
|
|
91
110
|
acceptAsProgress: reduced.finalVerdict === 'outcome' ||
|
|
111
|
+
reduced.finalVerdict === 'progress' ||
|
|
92
112
|
(reduced.finalVerdict === 'none' &&
|
|
93
113
|
claims.some((claim) => claim.kind === 'soft_result_candidate')),
|
|
94
114
|
};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"extract.d.ts","sourceRoot":"","sources":["../../src/commands/extract.ts"],"names":[],"mappings":"AAAA;;GAEG;AAIH,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,eAAe,CAAC;
|
|
1
|
+
{"version":3,"file":"extract.d.ts","sourceRoot":"","sources":["../../src/commands/extract.ts"],"names":[],"mappings":"AAAA;;GAEG;AAIH,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,eAAe,CAAC;AAoMnD,wBAAsB,OAAO,CAC3B,OAAO,EAAE,aAAa,EACtB,UAAU,EAAE,MAAM,EAClB,QAAQ,CAAC,EAAE,MAAM,GAChB,OAAO,CAAC,IAAI,CAAC,CAyMf"}
|
package/dist/commands/extract.js
CHANGED
|
@@ -6,7 +6,7 @@ import { AgentpayStructuredOutputTruncatedError } from '../agentpay-stagehand-ll
|
|
|
6
6
|
import { saveSession } from '../session.js';
|
|
7
7
|
import { getSurface, getTarget, markSurfaceLifecycle, markTargetLifecycle, setCurrentPage, } from '../runtime-state.js';
|
|
8
8
|
import { outputContractFailure, outputJSON } from '../output.js';
|
|
9
|
-
import { connectPlaywright, disconnectPlaywright, resolvePageByRef as resolvePlaywrightPageByRef, syncSessionPage, } from '../playwright-runtime.js';
|
|
9
|
+
import { connectPlaywright, disconnectPlaywright, resolveCurrentPageContext, resolvePageByRef as resolvePlaywrightPageByRef, syncSessionPage, } from '../playwright-runtime.js';
|
|
10
10
|
import { normalizePageSignature } from './descriptor-validation.js';
|
|
11
11
|
import { readScopedDialogText } from './extract-scoped-dialog-text.js';
|
|
12
12
|
import { resolveScopedExtractContext } from './extract-scope-resolution.js';
|
|
@@ -174,7 +174,7 @@ export async function extract(session, schemaJson, scopeRef) {
|
|
|
174
174
|
const targetScope = scopeRef ? getTarget(session, scopeRef) : null;
|
|
175
175
|
const surfaceScope = !targetScope && scopeRef ? getSurface(session, scopeRef) : null;
|
|
176
176
|
const scopeTarget = targetScope ?? surfaceScope;
|
|
177
|
-
|
|
177
|
+
let pageRef = scopeTarget?.pageRef ?? session.runtime?.currentPageRef ?? 'p0';
|
|
178
178
|
if (scopeRef && !scopeTarget) {
|
|
179
179
|
return outputContractFailure({
|
|
180
180
|
error: 'unknown_scope_ref',
|
|
@@ -221,7 +221,15 @@ export async function extract(session, schemaJson, scopeRef) {
|
|
|
221
221
|
});
|
|
222
222
|
}
|
|
223
223
|
try {
|
|
224
|
-
|
|
224
|
+
let sourcePage;
|
|
225
|
+
if (scopeTarget) {
|
|
226
|
+
sourcePage = await resolvePlaywrightPageByRef(browser, session, pageRef);
|
|
227
|
+
}
|
|
228
|
+
else {
|
|
229
|
+
const resolvedPage = await resolveCurrentPageContext(browser, session);
|
|
230
|
+
pageRef = resolvedPage.pageRef;
|
|
231
|
+
sourcePage = resolvedPage.page;
|
|
232
|
+
}
|
|
225
233
|
let page = sourcePage;
|
|
226
234
|
let scopedResolution = null;
|
|
227
235
|
const { url, title } = await syncSessionPage(session, pageRef, sourcePage);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"navigate.d.ts","sourceRoot":"","sources":["../../src/commands/navigate.ts"],"names":[],"mappings":"AAAA;;GAEG;AAGH,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,eAAe,CAAC;
|
|
1
|
+
{"version":3,"file":"navigate.d.ts","sourceRoot":"","sources":["../../src/commands/navigate.ts"],"names":[],"mappings":"AAAA;;GAEG;AAGH,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,eAAe,CAAC;AAgBnD,wBAAsB,QAAQ,CAAC,OAAO,EAAE,aAAa,EAAE,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAwDvF"}
|
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* browse navigate <url> — Navigate the current tab to a URL.
|
|
3
3
|
*/
|
|
4
|
-
import { clearProtectedExposure } from '../runtime-state.js';
|
|
4
|
+
import { clearProtectedExposure, setCurrentPage } from '../runtime-state.js';
|
|
5
5
|
import { saveSession } from '../session.js';
|
|
6
6
|
import { outputContractFailure, outputJSON, info } from '../output.js';
|
|
7
7
|
import { summarizeSecretCatalog, syncSecretCatalogForUrl, tryResolveCatalogHost, } from '../secrets/catalog-sync.js';
|
|
8
|
-
import { connectPlaywright, disconnectPlaywright,
|
|
8
|
+
import { connectPlaywright, disconnectPlaywright, resolveCurrentPageContext, syncSessionPage, } from '../playwright-runtime.js';
|
|
9
9
|
export async function navigate(session, targetUrl) {
|
|
10
10
|
let browser = null;
|
|
11
11
|
let failureMessage = null;
|
|
@@ -22,18 +22,18 @@ export async function navigate(session, targetUrl) {
|
|
|
22
22
|
});
|
|
23
23
|
}
|
|
24
24
|
try {
|
|
25
|
-
|
|
25
|
+
let pageRef = session.runtime?.currentPageRef ?? 'p0';
|
|
26
|
+
const resolvedPage = await resolveCurrentPageContext(browser, session);
|
|
27
|
+
pageRef = resolvedPage.pageRef;
|
|
28
|
+
const page = resolvedPage.page;
|
|
26
29
|
const previousUrl = session.runtime?.pages?.[pageRef]?.url;
|
|
27
30
|
const previousHost = tryResolveCatalogHost(previousUrl);
|
|
28
|
-
const pages = listPages(browser);
|
|
29
|
-
const page = session.runtime?.pages[pageRef] || pages.length > 1
|
|
30
|
-
? await resolvePageByRef(browser, session, pageRef)
|
|
31
|
-
: pages[0];
|
|
32
31
|
if (!page) {
|
|
33
32
|
throw new Error('no_open_pages');
|
|
34
33
|
}
|
|
35
34
|
await page.goto(targetUrl, { waitUntil: 'domcontentloaded' });
|
|
36
35
|
const { url, title } = await syncSessionPage(session, pageRef, page);
|
|
36
|
+
setCurrentPage(session, pageRef);
|
|
37
37
|
clearProtectedExposure(session, pageRef);
|
|
38
38
|
const nextHost = tryResolveCatalogHost(url);
|
|
39
39
|
if (nextHost && nextHost !== previousHost) {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"observe.d.ts","sourceRoot":"","sources":["../../src/commands/observe.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,eAAe,CAAC;
|
|
1
|
+
{"version":3,"file":"observe.d.ts","sourceRoot":"","sources":["../../src/commands/observe.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,eAAe,CAAC;AA0DnD,OAAO,EAAE,qBAAqB,EAAE,MAAM,wBAAwB,CAAC;AAU/D,eAAO,MAAM,yBAAyB;;;;iBA6T7B,CAAC;gBAEE,CAAA;;;;CA/TyD,CAAC;AACtE,eAAO,MAAM,yBAAyB;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAGrC,CAAC;AAEF,wBAAsB,OAAO,CAAC,OAAO,EAAE,aAAa,EAAE,WAAW,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CA8TzF"}
|
package/dist/commands/observe.js
CHANGED
|
@@ -2,8 +2,8 @@
|
|
|
2
2
|
* browse observe ["<instruction>"] — Discover available actions on the page.
|
|
3
3
|
*/
|
|
4
4
|
import { saveSession } from '../session.js';
|
|
5
|
-
import { ensureRuntimeState, incrementMetric, replaceTargetsForPage } from '../runtime-state.js';
|
|
6
|
-
import { connectPlaywright, disconnectPlaywright,
|
|
5
|
+
import { ensureRuntimeState, incrementMetric, replaceTargetsForPage, setCurrentPage, } from '../runtime-state.js';
|
|
6
|
+
import { connectPlaywright, disconnectPlaywright, resolveCurrentPageContext, syncSessionPage, } from '../playwright-runtime.js';
|
|
7
7
|
import { outputContractFailure, outputJSON } from '../output.js';
|
|
8
8
|
import { domRuntimeResolution, stagehandRuntimeResolution } from '../runtime-resolution.js';
|
|
9
9
|
import { withStagehand } from '../stagehand-runtime.js';
|
|
@@ -26,7 +26,7 @@ export const __testStagehandDescriptor = {
|
|
|
26
26
|
};
|
|
27
27
|
export async function observe(session, instruction) {
|
|
28
28
|
const runtime = ensureRuntimeState(session);
|
|
29
|
-
|
|
29
|
+
let pageRef = runtime.currentPageRef;
|
|
30
30
|
let domPassError = null;
|
|
31
31
|
let stagehandFallbackReason = null;
|
|
32
32
|
let browser = null;
|
|
@@ -34,8 +34,11 @@ export async function observe(session, instruction) {
|
|
|
34
34
|
if (!instruction) {
|
|
35
35
|
try {
|
|
36
36
|
browser = await connectPlaywright(session.cdpUrl);
|
|
37
|
-
const
|
|
37
|
+
const resolvedPage = await resolveCurrentPageContext(browser, session);
|
|
38
|
+
pageRef = resolvedPage.pageRef;
|
|
39
|
+
const page = resolvedPage.page;
|
|
38
40
|
const { url, title } = await syncSessionPage(session, pageRef, page);
|
|
41
|
+
setCurrentPage(session, pageRef);
|
|
39
42
|
const collectedTargets = await collectDomTargets(page);
|
|
40
43
|
let observeAccessibilityStats;
|
|
41
44
|
const domTargets = compressSemanticallyDuplicateTargets(orderBySurfaceCompetition(annotateDomTargets(await enrichDomTargetsWithAccessibility(page, collectedTargets, {
|
|
@@ -96,8 +99,11 @@ export async function observe(session, instruction) {
|
|
|
96
99
|
if (!browser) {
|
|
97
100
|
browser = await connectPlaywright(session.cdpUrl);
|
|
98
101
|
}
|
|
99
|
-
const
|
|
102
|
+
const resolvedPage = await resolveCurrentPageContext(browser, session);
|
|
103
|
+
pageRef = resolvedPage.pageRef;
|
|
104
|
+
const page = resolvedPage.page;
|
|
100
105
|
const { url, title } = await syncSessionPage(session, pageRef, page);
|
|
106
|
+
setCurrentPage(session, pageRef);
|
|
101
107
|
const collectedTargets = await collectDomTargets(page, {
|
|
102
108
|
includeActivationAffordances: true,
|
|
103
109
|
});
|
|
@@ -198,8 +204,11 @@ export async function observe(session, instruction) {
|
|
|
198
204
|
});
|
|
199
205
|
}
|
|
200
206
|
try {
|
|
201
|
-
const
|
|
207
|
+
const resolvedPage = await resolveCurrentPageContext(browser, session);
|
|
208
|
+
pageRef = resolvedPage.pageRef;
|
|
209
|
+
const page = resolvedPage.page;
|
|
202
210
|
const { url, title } = await syncSessionPage(session, pageRef, page);
|
|
211
|
+
setCurrentPage(session, pageRef);
|
|
203
212
|
const signals = compactSignals(await collectPageSignals(page).catch(() => []));
|
|
204
213
|
const actions = await withStagehand(session, async (stagehand) => {
|
|
205
214
|
incrementMetric(session, 'stagehandCalls');
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"screenshot.d.ts","sourceRoot":"","sources":["../../src/commands/screenshot.ts"],"names":[],"mappings":"AAAA;;GAEG;AAGH,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,eAAe,CAAC;
|
|
1
|
+
{"version":3,"file":"screenshot.d.ts","sourceRoot":"","sources":["../../src/commands/screenshot.ts"],"names":[],"mappings":"AAAA;;GAEG;AAGH,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,eAAe,CAAC;AAWnD,wBAAsB,UAAU,CAAC,OAAO,EAAE,aAAa,EAAE,QAAQ,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAsEzF"}
|
|
@@ -1,20 +1,19 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* browse screenshot [--path <file>] — Capture a screenshot of the current page.
|
|
3
3
|
*/
|
|
4
|
-
import { getProtectedExposure } from '../runtime-state.js';
|
|
4
|
+
import { getProtectedExposure, setCurrentPage } from '../runtime-state.js';
|
|
5
5
|
import { saveSession } from '../session.js';
|
|
6
6
|
import { outputFailure, outputJSON, outputContractFailure, info } from '../output.js';
|
|
7
|
-
import { connectPlaywright, disconnectPlaywright,
|
|
7
|
+
import { connectPlaywright, disconnectPlaywright, resolveCurrentPageContext, syncSessionPage, } from '../playwright-runtime.js';
|
|
8
8
|
import { buildProtectedScreenshotBlockedResult } from '../secrets/protected-artifact-guard.js';
|
|
9
9
|
export async function screenshot(session, filePath) {
|
|
10
10
|
const outputPath = filePath ?? `/tmp/browse-screenshot-${Date.now()}.png`;
|
|
11
|
-
const
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
return outputFailure(buildProtectedScreenshotBlockedResult(protectedExposure));
|
|
15
|
-
}
|
|
11
|
+
const initialPageRef = session.runtime?.currentPageRef ?? 'p0';
|
|
12
|
+
let pageRef = initialPageRef;
|
|
13
|
+
const initialProtectedExposure = getProtectedExposure(session, initialPageRef);
|
|
16
14
|
let browser = null;
|
|
17
15
|
let failureMessage = null;
|
|
16
|
+
let page = null;
|
|
18
17
|
try {
|
|
19
18
|
browser = await connectPlaywright(session.cdpUrl);
|
|
20
19
|
}
|
|
@@ -27,16 +26,33 @@ export async function screenshot(session, filePath) {
|
|
|
27
26
|
});
|
|
28
27
|
}
|
|
29
28
|
try {
|
|
30
|
-
const
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
29
|
+
const resolvedPage = await resolveCurrentPageContext(browser, session);
|
|
30
|
+
pageRef = resolvedPage.pageRef;
|
|
31
|
+
page = resolvedPage.page;
|
|
32
|
+
}
|
|
33
|
+
catch (err) {
|
|
34
|
+
failureMessage = `Screenshot failed: ${err instanceof Error ? err.message : String(err)}`;
|
|
35
|
+
}
|
|
36
|
+
const recoveredProtectedExposure = getProtectedExposure(session, pageRef) ?? (!page ? initialProtectedExposure : null);
|
|
37
|
+
if (recoveredProtectedExposure) {
|
|
38
|
+
if (page && pageRef !== initialPageRef) {
|
|
39
|
+
setCurrentPage(session, pageRef);
|
|
40
|
+
saveSession(session);
|
|
41
|
+
}
|
|
42
|
+
if (browser) {
|
|
43
|
+
disconnectPlaywright(browser);
|
|
44
|
+
browser = null;
|
|
45
|
+
}
|
|
46
|
+
return outputFailure(buildProtectedScreenshotBlockedResult(recoveredProtectedExposure));
|
|
47
|
+
}
|
|
48
|
+
try {
|
|
34
49
|
if (!page) {
|
|
35
|
-
throw new Error('no_open_pages');
|
|
50
|
+
throw new Error(failureMessage?.replace(/^Screenshot failed:\s*/, '') ?? 'no_open_pages');
|
|
36
51
|
}
|
|
37
52
|
await page.screenshot({ path: outputPath });
|
|
38
53
|
info(`Screenshot saved: ${outputPath}`);
|
|
39
54
|
const { url, title } = await syncSessionPage(session, pageRef, page);
|
|
55
|
+
setCurrentPage(session, pageRef);
|
|
40
56
|
saveSession(session);
|
|
41
57
|
outputJSON({ success: true, pageRef, path: outputPath, url, title });
|
|
42
58
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"status.d.ts","sourceRoot":"","sources":["../../src/commands/status.ts"],"names":[],"mappings":"AAAA;;GAEG;
|
|
1
|
+
{"version":3,"file":"status.d.ts","sourceRoot":"","sources":["../../src/commands/status.ts"],"names":[],"mappings":"AAAA;;GAEG;AAmKH,wBAAsB,MAAM,IAAI,OAAO,CAAC,IAAI,CAAC,CAgG5C"}
|
package/dist/commands/status.js
CHANGED
|
@@ -3,6 +3,21 @@
|
|
|
3
3
|
*/
|
|
4
4
|
import { loadSession, isSessionAlive, getSessionPort, supportsCaptchaSolve } from '../session.js';
|
|
5
5
|
import { outputJSON } from '../output.js';
|
|
6
|
+
function normalizeComparableUrl(url) {
|
|
7
|
+
const raw = url?.trim();
|
|
8
|
+
if (!raw) {
|
|
9
|
+
return null;
|
|
10
|
+
}
|
|
11
|
+
try {
|
|
12
|
+
const parsed = new URL(raw);
|
|
13
|
+
parsed.hash = '';
|
|
14
|
+
parsed.search = '';
|
|
15
|
+
return `${parsed.origin}${parsed.pathname}`;
|
|
16
|
+
}
|
|
17
|
+
catch {
|
|
18
|
+
return raw;
|
|
19
|
+
}
|
|
20
|
+
}
|
|
6
21
|
function tryResolveHost(url) {
|
|
7
22
|
if (!url) {
|
|
8
23
|
return undefined;
|
|
@@ -23,6 +38,7 @@ function buildProtectedStatusPayload(params) {
|
|
|
23
38
|
? { captchaSolveCapable: params.captchaSolveCapable }
|
|
24
39
|
: {}),
|
|
25
40
|
runtime: params.runtimeSummary,
|
|
41
|
+
...(params.currentPageMismatch ? { currentPageMismatch: params.currentPageMismatch } : {}),
|
|
26
42
|
protectedExposureActive: true,
|
|
27
43
|
pageRef: params.protectedExposure.pageRef,
|
|
28
44
|
url: params.pageUrl ?? 'unknown',
|
|
@@ -36,6 +52,70 @@ function buildProtectedStatusPayload(params) {
|
|
|
36
52
|
reason: 'AgentBrowse is treating the current page as sensitive because a protected fill was executed and values may still be visible.',
|
|
37
53
|
};
|
|
38
54
|
}
|
|
55
|
+
function matchesStoredPage(page, livePage) {
|
|
56
|
+
const storedUrl = normalizeComparableUrl(page?.url);
|
|
57
|
+
const currentUrl = normalizeComparableUrl(livePage.url);
|
|
58
|
+
if (!storedUrl || !currentUrl || storedUrl !== currentUrl) {
|
|
59
|
+
return null;
|
|
60
|
+
}
|
|
61
|
+
if (page?.title && livePage.title && page.title === livePage.title) {
|
|
62
|
+
return 'url-title-match';
|
|
63
|
+
}
|
|
64
|
+
return 'url-match';
|
|
65
|
+
}
|
|
66
|
+
function resolveCurrentPageMismatch(session, livePage) {
|
|
67
|
+
const runtime = session?.runtime;
|
|
68
|
+
if (!runtime || !livePage) {
|
|
69
|
+
return undefined;
|
|
70
|
+
}
|
|
71
|
+
const persistedPageRef = runtime.currentPageRef;
|
|
72
|
+
const currentPage = runtime.pages[persistedPageRef];
|
|
73
|
+
if (!currentPage) {
|
|
74
|
+
return {
|
|
75
|
+
persistedPageRef,
|
|
76
|
+
};
|
|
77
|
+
}
|
|
78
|
+
if (matchesStoredPage(currentPage, livePage)) {
|
|
79
|
+
return undefined;
|
|
80
|
+
}
|
|
81
|
+
const openerPageRef = currentPage.openerPageRef;
|
|
82
|
+
if (openerPageRef && matchesStoredPage(runtime.pages[openerPageRef], livePage)) {
|
|
83
|
+
return {
|
|
84
|
+
persistedPageRef,
|
|
85
|
+
livePageRef: openerPageRef,
|
|
86
|
+
recoveredVia: 'opener',
|
|
87
|
+
};
|
|
88
|
+
}
|
|
89
|
+
const exactMatches = Object.values(runtime.pages).filter((page) => {
|
|
90
|
+
if (page.pageRef === persistedPageRef || page.pageRef === openerPageRef) {
|
|
91
|
+
return false;
|
|
92
|
+
}
|
|
93
|
+
return matchesStoredPage(page, livePage) === 'url-title-match';
|
|
94
|
+
});
|
|
95
|
+
if (exactMatches.length === 1) {
|
|
96
|
+
return {
|
|
97
|
+
persistedPageRef,
|
|
98
|
+
livePageRef: exactMatches[0].pageRef,
|
|
99
|
+
recoveredVia: 'url-title-match',
|
|
100
|
+
};
|
|
101
|
+
}
|
|
102
|
+
const urlOnlyMatches = Object.values(runtime.pages).filter((page) => {
|
|
103
|
+
if (page.pageRef === persistedPageRef || page.pageRef === openerPageRef) {
|
|
104
|
+
return false;
|
|
105
|
+
}
|
|
106
|
+
return matchesStoredPage(page, livePage) === 'url-match';
|
|
107
|
+
});
|
|
108
|
+
if (urlOnlyMatches.length === 1) {
|
|
109
|
+
return {
|
|
110
|
+
persistedPageRef,
|
|
111
|
+
livePageRef: urlOnlyMatches[0].pageRef,
|
|
112
|
+
recoveredVia: 'url-match',
|
|
113
|
+
};
|
|
114
|
+
}
|
|
115
|
+
return {
|
|
116
|
+
persistedPageRef,
|
|
117
|
+
};
|
|
118
|
+
}
|
|
39
119
|
export async function status() {
|
|
40
120
|
const session = loadSession();
|
|
41
121
|
const port = getSessionPort(session);
|
|
@@ -57,15 +137,17 @@ export async function status() {
|
|
|
57
137
|
const listRes = await fetch(`http://localhost:${port}/json/list`);
|
|
58
138
|
const targets = (await listRes.json());
|
|
59
139
|
const page = targets.find((t) => t.type === 'page' && !t.url.startsWith('devtools://'));
|
|
60
|
-
const
|
|
61
|
-
const
|
|
62
|
-
|
|
140
|
+
const currentPageMismatch = resolveCurrentPageMismatch(session, page);
|
|
141
|
+
const effectivePageRef = currentPageMismatch?.livePageRef ?? session?.runtime?.currentPageRef;
|
|
142
|
+
const protectedExposure = effectivePageRef
|
|
143
|
+
? session?.runtime?.protectedExposureByPage?.[effectivePageRef]
|
|
63
144
|
: undefined;
|
|
64
145
|
if (protectedExposure) {
|
|
65
146
|
outputJSON(buildProtectedStatusPayload({
|
|
66
147
|
runtimeSummary,
|
|
67
148
|
pageUrl: page?.url,
|
|
68
149
|
pageTitle: page?.title,
|
|
150
|
+
currentPageMismatch,
|
|
69
151
|
protectedExposure,
|
|
70
152
|
}));
|
|
71
153
|
return;
|
|
@@ -76,6 +158,7 @@ export async function status() {
|
|
|
76
158
|
url: page?.url ?? 'unknown',
|
|
77
159
|
title: page?.title ?? 'unknown',
|
|
78
160
|
runtime: runtimeSummary,
|
|
161
|
+
...(currentPageMismatch ? { currentPageMismatch } : {}),
|
|
79
162
|
});
|
|
80
163
|
return;
|
|
81
164
|
}
|
|
@@ -90,9 +173,10 @@ export async function status() {
|
|
|
90
173
|
const listRes = await fetch(`http://localhost:${port}/json/list`);
|
|
91
174
|
const targets = (await listRes.json());
|
|
92
175
|
const page = targets.find((t) => t.type === 'page' && !t.url.startsWith('devtools://'));
|
|
93
|
-
const
|
|
94
|
-
const
|
|
95
|
-
|
|
176
|
+
const currentPageMismatch = resolveCurrentPageMismatch(session, page);
|
|
177
|
+
const effectivePageRef = currentPageMismatch?.livePageRef ?? session.runtime?.currentPageRef;
|
|
178
|
+
const protectedExposure = effectivePageRef
|
|
179
|
+
? session.runtime?.protectedExposureByPage?.[effectivePageRef]
|
|
96
180
|
: undefined;
|
|
97
181
|
if (protectedExposure) {
|
|
98
182
|
outputJSON(buildProtectedStatusPayload({
|
|
@@ -100,6 +184,7 @@ export async function status() {
|
|
|
100
184
|
pageUrl: page?.url,
|
|
101
185
|
pageTitle: page?.title,
|
|
102
186
|
captchaSolveCapable: supportsCaptchaSolve(session),
|
|
187
|
+
currentPageMismatch,
|
|
103
188
|
protectedExposure,
|
|
104
189
|
}));
|
|
105
190
|
return;
|
|
@@ -111,6 +196,7 @@ export async function status() {
|
|
|
111
196
|
url: page?.url ?? 'unknown',
|
|
112
197
|
title: page?.title ?? 'unknown',
|
|
113
198
|
runtime: runtimeSummary,
|
|
199
|
+
...(currentPageMismatch ? { currentPageMismatch } : {}),
|
|
114
200
|
});
|
|
115
201
|
return;
|
|
116
202
|
}
|
|
@@ -9,6 +9,11 @@ type LaunchPageMetadata = {
|
|
|
9
9
|
title: string;
|
|
10
10
|
targetId?: string;
|
|
11
11
|
};
|
|
12
|
+
export type ResolvedCurrentPageContext = {
|
|
13
|
+
pageRef: string;
|
|
14
|
+
page: Page;
|
|
15
|
+
recoveredVia?: 'opener' | 'sole-live-page';
|
|
16
|
+
};
|
|
12
17
|
export declare function readLaunchPageMetadata(browser: Browser, options?: {
|
|
13
18
|
requestedUrl?: string;
|
|
14
19
|
fallbackUrl?: string;
|
|
@@ -26,6 +31,7 @@ export declare function syncLaunchPage(session: BrowseSession, browser: Browser,
|
|
|
26
31
|
targetId?: string;
|
|
27
32
|
}>;
|
|
28
33
|
export declare function resolvePageByRef(browser: Browser, session: BrowseSession, pageRef: string): Promise<Page>;
|
|
34
|
+
export declare function resolveCurrentPageContext(browser: Browser, session: BrowseSession): Promise<ResolvedCurrentPageContext>;
|
|
29
35
|
export declare function syncSessionPage(session: BrowseSession, pageRef: string, page: Page, options?: {
|
|
30
36
|
settleTimeoutMs?: number;
|
|
31
37
|
}): Promise<{
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"playwright-runtime.d.ts","sourceRoot":"","sources":["../src/playwright-runtime.ts"],"names":[],"mappings":"AAAA,OAAO,EAAY,KAAK,OAAO,EAAE,KAAK,IAAI,EAAE,MAAM,iBAAiB,CAAC;AACpE,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,cAAc,CAAC;AAGlD,wBAAsB,iBAAiB,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAExE;AAED,wBAAgB,oBAAoB,CAAC,OAAO,EAAE,OAAO,GAAG,IAAI,CAO3D;AAED,wBAAgB,SAAS,CAAC,OAAO,EAAE,OAAO,GAAG,IAAI,EAAE,CAElD;
|
|
1
|
+
{"version":3,"file":"playwright-runtime.d.ts","sourceRoot":"","sources":["../src/playwright-runtime.ts"],"names":[],"mappings":"AAAA,OAAO,EAAY,KAAK,OAAO,EAAE,KAAK,IAAI,EAAE,MAAM,iBAAiB,CAAC;AACpE,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,cAAc,CAAC;AAGlD,wBAAsB,iBAAiB,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAExE;AAED,wBAAgB,oBAAoB,CAAC,OAAO,EAAE,OAAO,GAAG,IAAI,CAO3D;AAED,wBAAgB,SAAS,CAAC,OAAO,EAAE,OAAO,GAAG,IAAI,EAAE,CAElD;AA8CD,KAAK,kBAAkB,GAAG;IACxB,IAAI,EAAE,IAAI,GAAG,IAAI,CAAC;IAClB,GAAG,EAAE,MAAM,CAAC;IACZ,KAAK,EAAE,MAAM,CAAC;IACd,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB,CAAC;AAQF,MAAM,MAAM,0BAA0B,GAAG;IACvC,OAAO,EAAE,MAAM,CAAC;IAChB,IAAI,EAAE,IAAI,CAAC;IACX,YAAY,CAAC,EAAE,QAAQ,GAAG,gBAAgB,CAAC;CAC5C,CAAC;AA4IF,wBAAsB,sBAAsB,CAC1C,OAAO,EAAE,OAAO,EAChB,OAAO,GAAE;IACP,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,SAAS,CAAC,EAAE,MAAM,CAAC;CACf,GACL,OAAO,CAAC,kBAAkB,CAAC,CA+C7B;AAED,wBAAsB,cAAc,CAClC,OAAO,EAAE,aAAa,EACtB,OAAO,EAAE,OAAO,EAChB,OAAO,GAAE;IACP,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,SAAS,CAAC,EAAE,MAAM,CAAC;CACf,GACL,OAAO,CAAC;IAAE,GAAG,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,EAAE,MAAM,CAAA;CAAE,CAAC,CAgC5D;AAED,wBAAsB,gBAAgB,CACpC,OAAO,EAAE,OAAO,EAChB,OAAO,EAAE,aAAa,EACtB,OAAO,EAAE,MAAM,GACd,OAAO,CAAC,IAAI,CAAC,CA8Cf;AA+CD,wBAAsB,yBAAyB,CAC7C,OAAO,EAAE,OAAO,EAChB,OAAO,EAAE,aAAa,GACrB,OAAO,CAAC,0BAA0B,CAAC,CAqCrC;AAED,wBAAsB,eAAe,CACnC,OAAO,EAAE,aAAa,EACtB,OAAO,EAAE,MAAM,EACf,IAAI,EAAE,IAAI,EACV,OAAO,GAAE;IACP,eAAe,CAAC,EAAE,MAAM,CAAC;CACrB,GACL,OAAO,CAAC;IAAE,GAAG,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,EAAE,MAAM,CAAA;CAAE,CAAC,CAgB5D"}
|
|
@@ -17,6 +17,10 @@ export function listPages(browser) {
|
|
|
17
17
|
function buildPageResolutionError(kind, pageRef) {
|
|
18
18
|
return new Error(`${kind === 'unknown' ? 'unknown_page_ref' : 'stale_page_ref'}:${pageRef}`);
|
|
19
19
|
}
|
|
20
|
+
function isPageResolutionError(error, kind, pageRef) {
|
|
21
|
+
return (error instanceof Error &&
|
|
22
|
+
error.message === `${kind === 'unknown' ? 'unknown_page_ref' : 'stale_page_ref'}:${pageRef}`);
|
|
23
|
+
}
|
|
20
24
|
function sleep(ms) {
|
|
21
25
|
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
22
26
|
}
|
|
@@ -250,6 +254,78 @@ export async function resolvePageByRef(browser, session, pageRef) {
|
|
|
250
254
|
}
|
|
251
255
|
throw buildPageResolutionError('stale', pageRef);
|
|
252
256
|
}
|
|
257
|
+
async function findRecoverableCurrentPageRef(browser, session, stalePageRef) {
|
|
258
|
+
const runtimePages = session.runtime?.pages ?? {};
|
|
259
|
+
const stalePageState = runtimePages[stalePageRef];
|
|
260
|
+
const openerPageRef = stalePageState?.openerPageRef;
|
|
261
|
+
if (openerPageRef && openerPageRef !== stalePageRef) {
|
|
262
|
+
try {
|
|
263
|
+
await resolvePageByRef(browser, session, openerPageRef);
|
|
264
|
+
return {
|
|
265
|
+
pageRef: openerPageRef,
|
|
266
|
+
recoveredVia: 'opener',
|
|
267
|
+
};
|
|
268
|
+
}
|
|
269
|
+
catch {
|
|
270
|
+
// Keep recovery fail-closed unless opener or a single live alternative is provably valid.
|
|
271
|
+
}
|
|
272
|
+
}
|
|
273
|
+
const liveCandidateRefs = [];
|
|
274
|
+
for (const candidatePageRef of Object.keys(runtimePages)) {
|
|
275
|
+
if (candidatePageRef === stalePageRef || candidatePageRef === openerPageRef) {
|
|
276
|
+
continue;
|
|
277
|
+
}
|
|
278
|
+
try {
|
|
279
|
+
await resolvePageByRef(browser, session, candidatePageRef);
|
|
280
|
+
liveCandidateRefs.push(candidatePageRef);
|
|
281
|
+
}
|
|
282
|
+
catch {
|
|
283
|
+
// Ignore stale or unknown candidates while looking for a single surviving live page.
|
|
284
|
+
}
|
|
285
|
+
}
|
|
286
|
+
if (liveCandidateRefs.length === 1) {
|
|
287
|
+
return {
|
|
288
|
+
pageRef: liveCandidateRefs[0],
|
|
289
|
+
recoveredVia: 'sole-live-page',
|
|
290
|
+
};
|
|
291
|
+
}
|
|
292
|
+
return null;
|
|
293
|
+
}
|
|
294
|
+
export async function resolveCurrentPageContext(browser, session) {
|
|
295
|
+
const currentPageRef = session.runtime?.currentPageRef ?? 'p0';
|
|
296
|
+
try {
|
|
297
|
+
const page = await resolvePageByRef(browser, session, currentPageRef);
|
|
298
|
+
return {
|
|
299
|
+
pageRef: currentPageRef,
|
|
300
|
+
page,
|
|
301
|
+
};
|
|
302
|
+
}
|
|
303
|
+
catch (error) {
|
|
304
|
+
if (isPageResolutionError(error, 'unknown', currentPageRef)) {
|
|
305
|
+
const pages = listPages(browser);
|
|
306
|
+
if (pages.length === 1) {
|
|
307
|
+
return {
|
|
308
|
+
pageRef: currentPageRef,
|
|
309
|
+
page: pages[0],
|
|
310
|
+
recoveredVia: 'sole-live-page',
|
|
311
|
+
};
|
|
312
|
+
}
|
|
313
|
+
throw error;
|
|
314
|
+
}
|
|
315
|
+
if (!isPageResolutionError(error, 'stale', currentPageRef)) {
|
|
316
|
+
throw error;
|
|
317
|
+
}
|
|
318
|
+
const recovered = await findRecoverableCurrentPageRef(browser, session, currentPageRef);
|
|
319
|
+
if (!recovered) {
|
|
320
|
+
throw error;
|
|
321
|
+
}
|
|
322
|
+
return {
|
|
323
|
+
pageRef: recovered.pageRef,
|
|
324
|
+
page: await resolvePageByRef(browser, session, recovered.pageRef),
|
|
325
|
+
recoveredVia: recovered.recoveredVia,
|
|
326
|
+
};
|
|
327
|
+
}
|
|
328
|
+
}
|
|
253
329
|
export async function syncSessionPage(session, pageRef, page, options = {}) {
|
|
254
330
|
ensureRuntimeState(session);
|
|
255
331
|
const { url, title, targetId } = await readSettledSessionPageMetadata(session, pageRef, page, options);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@nuanu-ai/agentbrowse",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.26",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "Browser automation CLI for AI agents: control a CDP browser, observe UI surfaces, act on refs, extract data, capture screenshots, complete protected fills, and solve captchas",
|
|
6
6
|
"keywords": [
|