@nuanu-ai/agentbrowse 0.2.23 → 0.2.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.
- package/dist/commands/act.d.ts.map +1 -1
- package/dist/commands/act.js +37 -23
- package/dist/commands/action-executor.d.ts +2 -0
- package/dist/commands/action-executor.d.ts.map +1 -1
- package/dist/commands/action-executor.js +1 -0
- package/dist/commands/click-action-executor.d.ts +2 -0
- package/dist/commands/click-action-executor.d.ts.map +1 -1
- package/dist/commands/click-action-executor.js +11 -0
- package/dist/commands/click-activation-policy.d.ts +5 -0
- package/dist/commands/click-activation-policy.d.ts.map +1 -0
- package/dist/commands/click-activation-policy.js +13 -0
- package/dist/commands/launch.js +16 -6
- package/dist/control-semantics.d.ts.map +1 -1
- package/dist/control-semantics.js +6 -1
- package/dist/owned-process.d.ts +10 -0
- package/dist/owned-process.d.ts.map +1 -1
- package/dist/owned-process.js +74 -0
- package/dist/solver/browser-launcher.js +35 -8
- package/package.json +1 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"act.d.ts","sourceRoot":"","sources":["../../src/commands/act.ts"],"names":[],"mappings":"AAAA;;GAEG;AAGH,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,eAAe,CAAC;
|
|
1
|
+
{"version":3,"file":"act.d.ts","sourceRoot":"","sources":["../../src/commands/act.ts"],"names":[],"mappings":"AAAA;;GAEG;AAGH,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,eAAe,CAAC;AAsCnD,OAAO,EAAE,cAAc,EAAE,KAAK,YAAY,EAAE,cAAc,EAAE,MAAM,qBAAqB,CAAC;AA2HxF,OAAO,EAAE,cAAc,EAAE,cAAc,EAAE,CAAC;AAC1C,YAAY,EAAE,YAAY,EAAE,CAAC;AA4V7B,wBAAsB,GAAG,CACvB,OAAO,EAAE,aAAa,EACtB,SAAS,EAAE,MAAM,EACjB,MAAM,EAAE,YAAY,EACpB,KAAK,CAAC,EAAE,MAAM,GACb,OAAO,CAAC,IAAI,CAAC,CAitBf"}
|
package/dist/commands/act.js
CHANGED
|
@@ -7,6 +7,7 @@ import { outputContractFailure, outputFailure, outputJSON } from '../output.js';
|
|
|
7
7
|
import { capturePageObservation, captureLocatorContextHash, captureLocatorState, createAcceptanceProbe, diagnoseNoObservableProgress, genericClickObservationChanged, locatorStateChanged, pageObservationChanged, rankLocatorCandidates, shouldVerifyObservableProgress, waitForAcceptanceProbe, } from './action-acceptance.js';
|
|
8
8
|
import { captureActionFailureArtifacts, startActionTrace } from './action-artifacts.js';
|
|
9
9
|
import { buildLocator, resolveLocatorRoot } from './action-fallbacks.js';
|
|
10
|
+
import { clickActivationStrategyForTarget } from './click-activation-policy.js';
|
|
10
11
|
import { resolveSubmitResult } from './action-result-resolution.js';
|
|
11
12
|
import { projectActionValue } from './action-value-projection.js';
|
|
12
13
|
import { applyActionWithFallbacks } from './action-executor.js';
|
|
@@ -23,6 +24,7 @@ function ensureValue(action, value) {
|
|
|
23
24
|
return value;
|
|
24
25
|
throw new Error(`Act value is required for action: ${action}`);
|
|
25
26
|
}
|
|
27
|
+
const MUTABLE_FIELD_REBIND_RETRY_DELAYS_MS = [0, 25, 50];
|
|
26
28
|
function emitActPreflightFailure(params) {
|
|
27
29
|
return outputContractFailure({
|
|
28
30
|
error: params.error,
|
|
@@ -465,30 +467,41 @@ export async function act(session, targetRef, action, value) {
|
|
|
465
467
|
if (!liveTarget.domSignature) {
|
|
466
468
|
return false;
|
|
467
469
|
}
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
470
|
+
for (let attemptIndex = 0; attemptIndex < MUTABLE_FIELD_REBIND_RETRY_DELAYS_MS.length; attemptIndex += 1) {
|
|
471
|
+
const delayMs = MUTABLE_FIELD_REBIND_RETRY_DELAYS_MS[attemptIndex] ?? 0;
|
|
472
|
+
if (attemptIndex > 0) {
|
|
473
|
+
attempts.push(`domSignature.rebind.retry:${strategy}:${attemptIndex + 1}`);
|
|
474
|
+
await new Promise((resolve) => setTimeout(resolve, delayMs));
|
|
475
|
+
}
|
|
476
|
+
const snapshot = await readLocatorBindingSnapshot(resolvedLocator).catch(() => null);
|
|
477
|
+
if (!snapshot?.domSignature) {
|
|
478
|
+
continue;
|
|
479
|
+
}
|
|
480
|
+
if (snapshot.domSignature === liveTarget.domSignature) {
|
|
481
|
+
return false;
|
|
482
|
+
}
|
|
483
|
+
if (!isCompatibleMutableFieldBinding(liveTarget, snapshot)) {
|
|
484
|
+
continue;
|
|
485
|
+
}
|
|
486
|
+
attempts.push(`domSignature.rebound:${strategy}`);
|
|
487
|
+
const updatedTarget = updateTarget(session, targetRef, {
|
|
488
|
+
domSignature: snapshot.domSignature,
|
|
489
|
+
label: snapshot.label ?? liveTarget.label,
|
|
490
|
+
lifecycle: 'live',
|
|
491
|
+
lifecycleReason: undefined,
|
|
492
|
+
availability: { state: 'available' },
|
|
493
|
+
semantics: {
|
|
494
|
+
...liveTarget.semantics,
|
|
495
|
+
name: snapshot.label ?? liveTarget.semantics?.name,
|
|
496
|
+
role: snapshot.role ?? liveTarget.semantics?.role,
|
|
497
|
+
},
|
|
498
|
+
});
|
|
499
|
+
if (updatedTarget) {
|
|
500
|
+
liveTarget = updatedTarget;
|
|
501
|
+
}
|
|
502
|
+
return true;
|
|
490
503
|
}
|
|
491
|
-
return
|
|
504
|
+
return false;
|
|
492
505
|
};
|
|
493
506
|
const assertResolvedTargetStillValid = async (resolvedLocator, stage) => {
|
|
494
507
|
if (liveTarget.pageSignature &&
|
|
@@ -596,6 +609,7 @@ export async function act(session, targetRef, action, value) {
|
|
|
596
609
|
});
|
|
597
610
|
attempts.push(`resolve:${strategy}`);
|
|
598
611
|
const usedFallback = await applyActionWithFallbacks(page, locatorRoot, resolvedLocator, action, executionValue, attempts, target.controlFamily, {
|
|
612
|
+
clickActivationStrategy: clickActivationStrategyForTarget(target, action),
|
|
599
613
|
guards: {
|
|
600
614
|
assertStillValid: async (stage) => {
|
|
601
615
|
await assertResolvedTargetStillValid(resolvedLocator, stage);
|
|
@@ -3,9 +3,11 @@ import type { TargetControlFamily } from '../runtime-state.js';
|
|
|
3
3
|
import type { BrowseAction } from './browse-actions.js';
|
|
4
4
|
import type { LocatorRoot } from './action-fallbacks.js';
|
|
5
5
|
import type { ActionExecutionGuards } from './action-execution-guards.js';
|
|
6
|
+
import type { ClickActivationStrategy } from './click-activation-policy.js';
|
|
6
7
|
type ActionExecutionOptions = {
|
|
7
8
|
beforeClickRetry?: () => Promise<void>;
|
|
8
9
|
guards?: ActionExecutionGuards;
|
|
10
|
+
clickActivationStrategy?: ClickActivationStrategy;
|
|
9
11
|
};
|
|
10
12
|
export declare function applyActionWithFallbacks(page: Page, root: LocatorRoot, locator: Locator, action: BrowseAction, value: string | undefined, attempts: string[], family?: TargetControlFamily, options?: ActionExecutionOptions): Promise<boolean>;
|
|
11
13
|
export {};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"action-executor.d.ts","sourceRoot":"","sources":["../../src/commands/action-executor.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,iBAAiB,CAAC;AACrD,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,qBAAqB,CAAC;AAC/D,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAC;AACxD,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,uBAAuB,CAAC;AACzD,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,8BAA8B,CAAC;
|
|
1
|
+
{"version":3,"file":"action-executor.d.ts","sourceRoot":"","sources":["../../src/commands/action-executor.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,iBAAiB,CAAC;AACrD,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,qBAAqB,CAAC;AAC/D,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAC;AACxD,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,uBAAuB,CAAC;AACzD,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,8BAA8B,CAAC;AAC1E,OAAO,KAAK,EAAE,uBAAuB,EAAE,MAAM,8BAA8B,CAAC;AAc5E,KAAK,sBAAsB,GAAG;IAC5B,gBAAgB,CAAC,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;IACvC,MAAM,CAAC,EAAE,qBAAqB,CAAC;IAC/B,uBAAuB,CAAC,EAAE,uBAAuB,CAAC;CACnD,CAAC;AAEF,wBAAsB,wBAAwB,CAC5C,IAAI,EAAE,IAAI,EACV,IAAI,EAAE,WAAW,EACjB,OAAO,EAAE,OAAO,EAChB,MAAM,EAAE,YAAY,EACpB,KAAK,EAAE,MAAM,GAAG,SAAS,EACzB,QAAQ,EAAE,MAAM,EAAE,EAClB,MAAM,CAAC,EAAE,mBAAmB,EAC5B,OAAO,CAAC,EAAE,sBAAsB,GAC/B,OAAO,CAAC,OAAO,CAAC,CAsDlB"}
|
|
@@ -14,6 +14,7 @@ export async function applyActionWithFallbacks(page, root, locator, action, valu
|
|
|
14
14
|
return applyTriggerAction(page, locator, attempts, {
|
|
15
15
|
beforeRetry: options?.beforeClickRetry,
|
|
16
16
|
guards: options?.guards,
|
|
17
|
+
clickActivationStrategy: options?.clickActivationStrategy,
|
|
17
18
|
});
|
|
18
19
|
}
|
|
19
20
|
return applyEditableClickAction(page, locator, attempts, {
|
|
@@ -1,8 +1,10 @@
|
|
|
1
1
|
import type { Locator, Page } from 'playwright-core';
|
|
2
|
+
import type { ClickActivationStrategy } from './click-activation-policy.js';
|
|
2
3
|
import { type ActionExecutionGuards } from './action-execution-guards.js';
|
|
3
4
|
type ClickRetryOptions = {
|
|
4
5
|
beforeRetry?: () => Promise<void>;
|
|
5
6
|
guards?: ActionExecutionGuards;
|
|
7
|
+
clickActivationStrategy?: ClickActivationStrategy;
|
|
6
8
|
};
|
|
7
9
|
export declare function applyEditableClickAction(page: Page, locator: Locator, attempts: string[], options?: ClickRetryOptions): Promise<boolean>;
|
|
8
10
|
export declare function applyTriggerAction(page: Page, locator: Locator, attempts: string[], options?: ClickRetryOptions): Promise<boolean>;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"click-action-executor.d.ts","sourceRoot":"","sources":["../../src/commands/click-action-executor.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,iBAAiB,CAAC;AAMrD,OAAO,EAA2B,KAAK,qBAAqB,EAAE,MAAM,8BAA8B,CAAC;AAEnG,KAAK,iBAAiB,GAAG;IACvB,WAAW,CAAC,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;IAClC,MAAM,CAAC,EAAE,qBAAqB,CAAC;
|
|
1
|
+
{"version":3,"file":"click-action-executor.d.ts","sourceRoot":"","sources":["../../src/commands/click-action-executor.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,iBAAiB,CAAC;AAMrD,OAAO,KAAK,EAAE,uBAAuB,EAAE,MAAM,8BAA8B,CAAC;AAC5E,OAAO,EAA2B,KAAK,qBAAqB,EAAE,MAAM,8BAA8B,CAAC;AAEnG,KAAK,iBAAiB,GAAG;IACvB,WAAW,CAAC,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;IAClC,MAAM,CAAC,EAAE,qBAAqB,CAAC;IAC/B,uBAAuB,CAAC,EAAE,uBAAuB,CAAC;CACnD,CAAC;AA2GF,wBAAsB,wBAAwB,CAC5C,IAAI,EAAE,IAAI,EACV,OAAO,EAAE,OAAO,EAChB,QAAQ,EAAE,MAAM,EAAE,EAClB,OAAO,CAAC,EAAE,iBAAiB,GAC1B,OAAO,CAAC,OAAO,CAAC,CAElB;AAED,wBAAsB,kBAAkB,CACtC,IAAI,EAAE,IAAI,EACV,OAAO,EAAE,OAAO,EAChB,QAAQ,EAAE,MAAM,EAAE,EAClB,OAAO,CAAC,EAAE,iBAAiB,GAC1B,OAAO,CAAC,OAAO,CAAC,CAElB"}
|
|
@@ -28,6 +28,17 @@ async function ensureLocatorRetryReady(locator, error, options) {
|
|
|
28
28
|
}
|
|
29
29
|
async function applyClickSequence(page, locator, attempts, options) {
|
|
30
30
|
await handoffFocusFromActiveIframe(locator, attempts);
|
|
31
|
+
if (options?.clickActivationStrategy === 'dom') {
|
|
32
|
+
await runActionExecutionGuard(options?.guards, 'click.evaluate.primary');
|
|
33
|
+
attempts.push('locator.evaluate.click.primary');
|
|
34
|
+
await locator.evaluate((element) => {
|
|
35
|
+
if (!(element instanceof HTMLElement)) {
|
|
36
|
+
throw new Error('unsupported_js_click_fallback');
|
|
37
|
+
}
|
|
38
|
+
element.click();
|
|
39
|
+
});
|
|
40
|
+
return false;
|
|
41
|
+
}
|
|
31
42
|
attempts.push('locator.click');
|
|
32
43
|
try {
|
|
33
44
|
await locator.click({ timeout: LOCATOR_CLICK_TIMEOUT_MS });
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import type { TargetDescriptor } from '../runtime-state.js';
|
|
2
|
+
import type { BrowseAction } from './browse-actions.js';
|
|
3
|
+
export type ClickActivationStrategy = 'pointer' | 'dom';
|
|
4
|
+
export declare function clickActivationStrategyForTarget(target: Pick<TargetDescriptor, 'kind' | 'framePath' | 'semantics'>, action: BrowseAction): ClickActivationStrategy;
|
|
5
|
+
//# sourceMappingURL=click-activation-policy.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"click-activation-policy.d.ts","sourceRoot":"","sources":["../../src/commands/click-activation-policy.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,qBAAqB,CAAC;AAC5D,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAC;AAExD,MAAM,MAAM,uBAAuB,GAAG,SAAS,GAAG,KAAK,CAAC;AAExD,wBAAgB,gCAAgC,CAC9C,MAAM,EAAE,IAAI,CAAC,gBAAgB,EAAE,MAAM,GAAG,WAAW,GAAG,WAAW,CAAC,EAClE,MAAM,EAAE,YAAY,GACnB,uBAAuB,CAezB"}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
export function clickActivationStrategyForTarget(target, action) {
|
|
2
|
+
if (action !== 'click') {
|
|
3
|
+
return 'pointer';
|
|
4
|
+
}
|
|
5
|
+
const withinIframe = Boolean(target.framePath?.length);
|
|
6
|
+
if (!withinIframe) {
|
|
7
|
+
return 'pointer';
|
|
8
|
+
}
|
|
9
|
+
const kind = target.kind?.trim().toLowerCase();
|
|
10
|
+
const role = target.semantics?.role?.trim().toLowerCase();
|
|
11
|
+
const isTab = kind === 'tab' || role === 'tab';
|
|
12
|
+
return isTab ? 'dom' : 'pointer';
|
|
13
|
+
}
|
package/dist/commands/launch.js
CHANGED
|
@@ -9,7 +9,7 @@ import { findChromePid } from '../stagehand.js';
|
|
|
9
9
|
import { applyAgentpayGatewayEnv, tryResolveAgentpayGatewayConfig } from '../agentpay-gateway.js';
|
|
10
10
|
import { connectPlaywright, disconnectPlaywright, syncLaunchPage } from '../playwright-runtime.js';
|
|
11
11
|
import { summarizeSecretCatalog, syncSecretCatalogForUrl } from '../secrets/catalog-sync.js';
|
|
12
|
-
import { terminateOwnedPid } from '../owned-process.js';
|
|
12
|
+
import { cleanupManagedBrowserPids, terminateOwnedPid } from '../owned-process.js';
|
|
13
13
|
import { info } from '../output.js';
|
|
14
14
|
const DEFAULT_PROFILE = 'default';
|
|
15
15
|
const COMPACT_WINDOW = {
|
|
@@ -30,12 +30,22 @@ export async function launch(url, opts) {
|
|
|
30
30
|
}
|
|
31
31
|
async function cleanupOwnedSession() {
|
|
32
32
|
const existingSession = loadSession();
|
|
33
|
-
|
|
34
|
-
|
|
33
|
+
const excludePids = new Set();
|
|
34
|
+
if (isOwnedSession(existingSession)) {
|
|
35
|
+
excludePids.add(existingSession.pid);
|
|
36
|
+
const termination = await terminateOwnedPid(existingSession.pid);
|
|
37
|
+
if (termination === 'still_alive') {
|
|
38
|
+
return buildLaunchFailure(new Error(`Existing owned browser pid ${existingSession.pid} did not exit after SIGTERM/SIGKILL.`));
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
// Phase-1 ownership removed port-global prekill, so launch needs a second guardrail
|
|
42
|
+
// for crashes that happen before a session record is persisted or cleaned up.
|
|
43
|
+
const orphanCleanup = await cleanupManagedBrowserPids({ excludePids });
|
|
44
|
+
if (orphanCleanup.blocked.length > 0) {
|
|
45
|
+
return buildLaunchFailure(new Error(`Orphaned managed browser pid${orphanCleanup.blocked.length === 1 ? '' : 's'} ${orphanCleanup.blocked.join(', ')} did not exit after SIGTERM/SIGKILL.`));
|
|
35
46
|
}
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
return buildLaunchFailure(new Error(`Existing owned browser pid ${existingSession.pid} did not exit after SIGTERM/SIGKILL.`));
|
|
47
|
+
if (orphanCleanup.terminated.length > 0) {
|
|
48
|
+
info(`[launch] cleaned up orphaned managed browser pid${orphanCleanup.terminated.length === 1 ? '' : 's'} ${orphanCleanup.terminated.join(', ')}`);
|
|
39
49
|
}
|
|
40
50
|
return null;
|
|
41
51
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"control-semantics.d.ts","sourceRoot":"","sources":["../src/control-semantics.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,sBAAsB,EACtB,mBAAmB,EACnB,uBAAuB,EACvB,mBAAmB,EACnB,eAAe,EAChB,MAAM,oBAAoB,CAAC;AAE5B,MAAM,MAAM,gBAAgB,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,GAAG,MAAM,CAAC,CAAC;AAEzE,MAAM,MAAM,oBAAoB,GAAG;IACjC,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,eAAe,CAAC,EAAE,OAAO,CAAC;IAC1B,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,uBAAuB,CAAC,EAAE,MAAM,CAAC;IACjC,MAAM,CAAC,EAAE,gBAAgB,CAAC;IAC1B,SAAS,CAAC,EAAE,eAAe,CAAC;IAC5B,YAAY,CAAC,EAAE,MAAM,CAAC;CACvB,CAAC;AAEF,MAAM,MAAM,mBAAmB,GAAG,OAAO,GAAG,aAAa,GAAG,QAAQ,GAAG,KAAK,GAAG,MAAM,CAAC;AAyCtF,wBAAgB,iCAAiC,CAC/C,KAAK,EAAE,oBAAoB,GAC1B,mBAAmB,GAAG,SAAS,CAuCjC;AAsCD,wBAAgB,qBAAqB,CAAC,KAAK,EAAE,MAAM,GAAG,SAAS,GAAG,OAAO,CAYxE;AAED,wBAAgB,4BAA4B,CAAC,KAAK,EAAE,oBAAoB,GAAG,mBAAmB,EAAE,
|
|
1
|
+
{"version":3,"file":"control-semantics.d.ts","sourceRoot":"","sources":["../src/control-semantics.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,sBAAsB,EACtB,mBAAmB,EACnB,uBAAuB,EACvB,mBAAmB,EACnB,eAAe,EAChB,MAAM,oBAAoB,CAAC;AAE5B,MAAM,MAAM,gBAAgB,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,GAAG,MAAM,CAAC,CAAC;AAEzE,MAAM,MAAM,oBAAoB,GAAG;IACjC,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,eAAe,CAAC,EAAE,OAAO,CAAC;IAC1B,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,uBAAuB,CAAC,EAAE,MAAM,CAAC;IACjC,MAAM,CAAC,EAAE,gBAAgB,CAAC;IAC1B,SAAS,CAAC,EAAE,eAAe,CAAC;IAC5B,YAAY,CAAC,EAAE,MAAM,CAAC;CACvB,CAAC;AAEF,MAAM,MAAM,mBAAmB,GAAG,OAAO,GAAG,aAAa,GAAG,QAAQ,GAAG,KAAK,GAAG,MAAM,CAAC;AAyCtF,wBAAgB,iCAAiC,CAC/C,KAAK,EAAE,oBAAoB,GAC1B,mBAAmB,GAAG,SAAS,CAuCjC;AAsCD,wBAAgB,qBAAqB,CAAC,KAAK,EAAE,MAAM,GAAG,SAAS,GAAG,OAAO,CAYxE;AAED,wBAAgB,4BAA4B,CAAC,KAAK,EAAE,oBAAoB,GAAG,mBAAmB,EAAE,CAoG/F;AAED,wBAAgB,0BAA0B,CACxC,MAAM,CAAC,EAAE,gBAAgB,EACzB,WAAW,CAAC,EAAE,MAAM,EACpB,OAAO,GAAE;IACP,mBAAmB,CAAC,EAAE,OAAO,CAAC;CAC1B,GACL,uBAAuB,CAyBzB;AAED,wBAAgB,2BAA2B,CACzC,KAAK,EAAE,oBAAoB,EAC3B,cAAc,EAAE,aAAa,CAAC,mBAAmB,CAAC,GACjD,mBAAmB,GAAG,SAAS,CA0EjC;AAED,wBAAgB,8BAA8B,CAC5C,KAAK,EAAE,oBAAoB,EAC3B,cAAc,EAAE,aAAa,CAAC,mBAAmB,CAAC,GACjD,sBAAsB,GAAG,SAAS,CAiDpC"}
|
|
@@ -148,7 +148,12 @@ export function inferAllowedActionsFromFacts(facts) {
|
|
|
148
148
|
actions.add('click');
|
|
149
149
|
actions.add('press');
|
|
150
150
|
}
|
|
151
|
-
if (kind === 'button' ||
|
|
151
|
+
if (kind === 'button' ||
|
|
152
|
+
kind === 'link' ||
|
|
153
|
+
kind === 'tab' ||
|
|
154
|
+
role === 'button' ||
|
|
155
|
+
role === 'link' ||
|
|
156
|
+
role === 'tab') {
|
|
152
157
|
actions.add('click');
|
|
153
158
|
actions.add('press');
|
|
154
159
|
}
|
package/dist/owned-process.d.ts
CHANGED
|
@@ -1,4 +1,14 @@
|
|
|
1
1
|
export type OwnedPidTerminationResult = 'not_found' | 'terminated' | 'sigkilled' | 'still_alive';
|
|
2
|
+
export type ManagedBrowserCleanupResult = {
|
|
3
|
+
terminated: number[];
|
|
4
|
+
blocked: number[];
|
|
5
|
+
};
|
|
2
6
|
export declare function isPidAlive(pid: number): boolean;
|
|
3
7
|
export declare function terminateOwnedPid(pid: number): Promise<OwnedPidTerminationResult>;
|
|
8
|
+
export declare function cleanupManagedBrowserPids(options?: {
|
|
9
|
+
excludePids?: Iterable<number>;
|
|
10
|
+
}): Promise<ManagedBrowserCleanupResult>;
|
|
11
|
+
export declare function listManagedBrowserPids(options?: {
|
|
12
|
+
processTable?: string;
|
|
13
|
+
}): number[];
|
|
4
14
|
//# sourceMappingURL=owned-process.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"owned-process.d.ts","sourceRoot":"","sources":["../src/owned-process.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"owned-process.d.ts","sourceRoot":"","sources":["../src/owned-process.ts"],"names":[],"mappings":"AAaA,MAAM,MAAM,yBAAyB,GAAG,WAAW,GAAG,YAAY,GAAG,WAAW,GAAG,aAAa,CAAC;AACjG,MAAM,MAAM,2BAA2B,GAAG;IACxC,UAAU,EAAE,MAAM,EAAE,CAAC;IACrB,OAAO,EAAE,MAAM,EAAE,CAAC;CACnB,CAAC;AAEF,wBAAgB,UAAU,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAW/C;AAED,wBAAsB,iBAAiB,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,yBAAyB,CAAC,CAsBvF;AAED,wBAAsB,yBAAyB,CAC7C,OAAO,GAAE;IAAE,WAAW,CAAC,EAAE,QAAQ,CAAC,MAAM,CAAC,CAAA;CAAO,GAC/C,OAAO,CAAC,2BAA2B,CAAC,CAqBtC;AAED,wBAAgB,sBAAsB,CACpC,OAAO,GAAE;IACP,YAAY,CAAC,EAAE,MAAM,CAAC;CAClB,GACL,MAAM,EAAE,CAuBV"}
|
package/dist/owned-process.js
CHANGED
|
@@ -1,7 +1,14 @@
|
|
|
1
|
+
import { execFileSync } from 'node:child_process';
|
|
2
|
+
import { homedir } from 'node:os';
|
|
3
|
+
import path from 'node:path';
|
|
1
4
|
const TERM_WAIT_ATTEMPTS = 10;
|
|
2
5
|
const TERM_WAIT_MS = 100;
|
|
3
6
|
const KILL_WAIT_ATTEMPTS = 5;
|
|
4
7
|
const KILL_WAIT_MS = 20;
|
|
8
|
+
const MANAGED_PROFILE_ROOTS = [
|
|
9
|
+
path.join(homedir(), '.agentpay', 'agentbrowse', 'profiles'),
|
|
10
|
+
path.join(homedir(), '.agentpay', 'solver', 'profiles'),
|
|
11
|
+
];
|
|
5
12
|
export function isPidAlive(pid) {
|
|
6
13
|
try {
|
|
7
14
|
process.kill(pid, 0);
|
|
@@ -36,6 +43,45 @@ export async function terminateOwnedPid(pid) {
|
|
|
36
43
|
}
|
|
37
44
|
return 'still_alive';
|
|
38
45
|
}
|
|
46
|
+
export async function cleanupManagedBrowserPids(options = {}) {
|
|
47
|
+
const excludePids = new Set(options.excludePids ?? []);
|
|
48
|
+
const terminated = [];
|
|
49
|
+
const blocked = [];
|
|
50
|
+
for (const pid of listManagedBrowserPids()) {
|
|
51
|
+
if (excludePids.has(pid)) {
|
|
52
|
+
continue;
|
|
53
|
+
}
|
|
54
|
+
const result = await terminateOwnedPid(pid);
|
|
55
|
+
if (result === 'still_alive') {
|
|
56
|
+
blocked.push(pid);
|
|
57
|
+
continue;
|
|
58
|
+
}
|
|
59
|
+
if (result !== 'not_found') {
|
|
60
|
+
terminated.push(pid);
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
return { terminated, blocked };
|
|
64
|
+
}
|
|
65
|
+
export function listManagedBrowserPids(options = {}) {
|
|
66
|
+
const processTable = options.processTable ?? readProcessTable();
|
|
67
|
+
const pids = new Set();
|
|
68
|
+
for (const line of processTable.split(/\r?\n/)) {
|
|
69
|
+
const match = line.trimStart().match(/^(\d+)\s+(.*)$/);
|
|
70
|
+
if (!match) {
|
|
71
|
+
continue;
|
|
72
|
+
}
|
|
73
|
+
const pid = Number(match[1]);
|
|
74
|
+
const command = match[2] ?? '';
|
|
75
|
+
if (!Number.isFinite(pid) || pid <= 0 || pid === process.pid) {
|
|
76
|
+
continue;
|
|
77
|
+
}
|
|
78
|
+
if (!isManagedBrowserCommand(command)) {
|
|
79
|
+
continue;
|
|
80
|
+
}
|
|
81
|
+
pids.add(pid);
|
|
82
|
+
}
|
|
83
|
+
return Array.from(pids);
|
|
84
|
+
}
|
|
39
85
|
async function waitForPidExit(pid, attempts, intervalMs) {
|
|
40
86
|
for (let attempt = 0; attempt < attempts; attempt++) {
|
|
41
87
|
if (!isPidAlive(pid)) {
|
|
@@ -55,3 +101,31 @@ function getErrorCode(error) {
|
|
|
55
101
|
function sleep(ms) {
|
|
56
102
|
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
57
103
|
}
|
|
104
|
+
function readProcessTable() {
|
|
105
|
+
try {
|
|
106
|
+
return execFileSync('ps', ['-Ao', 'pid=,command='], { encoding: 'utf-8' });
|
|
107
|
+
}
|
|
108
|
+
catch {
|
|
109
|
+
return '';
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
function isManagedBrowserCommand(command) {
|
|
113
|
+
if (!command.includes('--remote-debugging-address=127.0.0.1')) {
|
|
114
|
+
return false;
|
|
115
|
+
}
|
|
116
|
+
const userDataDir = readCommandFlag(command, '--user-data-dir');
|
|
117
|
+
if (!userDataDir) {
|
|
118
|
+
return false;
|
|
119
|
+
}
|
|
120
|
+
const normalizedUserDataDir = path.resolve(userDataDir);
|
|
121
|
+
return MANAGED_PROFILE_ROOTS.some((root) => isWithinRoot(normalizedUserDataDir, root));
|
|
122
|
+
}
|
|
123
|
+
function readCommandFlag(command, flag) {
|
|
124
|
+
const escapedFlag = flag.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
|
|
125
|
+
const match = command.match(new RegExp(`${escapedFlag}=(?:"([^"]+)"|'([^']+)'|(\\S+))`));
|
|
126
|
+
return match?.[1] ?? match?.[2] ?? match?.[3] ?? null;
|
|
127
|
+
}
|
|
128
|
+
function isWithinRoot(candidatePath, rootPath) {
|
|
129
|
+
const relative = path.relative(rootPath, candidatePath);
|
|
130
|
+
return relative.length > 0 && !relative.startsWith(`..${path.sep}`) && relative !== '..';
|
|
131
|
+
}
|
|
@@ -90,14 +90,9 @@ async function discoverCdpUrl(options) {
|
|
|
90
90
|
}
|
|
91
91
|
else {
|
|
92
92
|
const activePort = readDevToolsActivePort(activePortPath);
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
if (activePort?.port) {
|
|
97
|
-
const cdpUrl = await discoverCdpUrlOnPort(activePort.port);
|
|
98
|
-
if (cdpUrl) {
|
|
99
|
-
return cdpUrl;
|
|
100
|
-
}
|
|
93
|
+
const discoveredViaActivePort = await discoverCdpUrlFromActivePort(activePort);
|
|
94
|
+
if (discoveredViaActivePort) {
|
|
95
|
+
return discoveredViaActivePort;
|
|
101
96
|
}
|
|
102
97
|
}
|
|
103
98
|
await sleep(CDP_DISCOVERY_INTERVAL_MS);
|
|
@@ -117,6 +112,38 @@ async function discoverCdpUrlOnPort(port) {
|
|
|
117
112
|
return null;
|
|
118
113
|
}
|
|
119
114
|
}
|
|
115
|
+
async function discoverCdpUrlFromActivePort(activePort) {
|
|
116
|
+
if (!activePort) {
|
|
117
|
+
return null;
|
|
118
|
+
}
|
|
119
|
+
const candidatePorts = new Set();
|
|
120
|
+
if (activePort.browserWSEndpoint) {
|
|
121
|
+
const wsPort = portFromBrowserWSEndpoint(activePort.browserWSEndpoint);
|
|
122
|
+
if (wsPort) {
|
|
123
|
+
candidatePorts.add(wsPort);
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
if (activePort.port > 0) {
|
|
127
|
+
candidatePorts.add(activePort.port);
|
|
128
|
+
}
|
|
129
|
+
for (const port of candidatePorts) {
|
|
130
|
+
const cdpUrl = await discoverCdpUrlOnPort(port);
|
|
131
|
+
if (cdpUrl) {
|
|
132
|
+
return cdpUrl;
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
return null;
|
|
136
|
+
}
|
|
137
|
+
function portFromBrowserWSEndpoint(browserWSEndpoint) {
|
|
138
|
+
try {
|
|
139
|
+
const url = new URL(browserWSEndpoint);
|
|
140
|
+
const port = Number(url.port);
|
|
141
|
+
return Number.isFinite(port) && port > 0 ? port : null;
|
|
142
|
+
}
|
|
143
|
+
catch {
|
|
144
|
+
return null;
|
|
145
|
+
}
|
|
146
|
+
}
|
|
120
147
|
function readDevToolsActivePort(activePortPath) {
|
|
121
148
|
if (!existsSync(activePortPath)) {
|
|
122
149
|
return null;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@nuanu-ai/agentbrowse",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.24",
|
|
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": [
|