@mearl/native-host 2.6.1 → 2.7.1
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/README.md +11 -0
- package/dist/agentbay-browser.d.ts +12 -1
- package/dist/agentbay-lifecycle.d.ts +38 -0
- package/dist/cdp-backend/cdp-utils.d.ts +4 -1
- package/dist/cdp-backend/interaction-handlers.d.ts +2 -1
- package/dist/index.js +722 -49
- package/dist/internal-extension-actions.d.ts +3 -2
- package/package.json +4 -4
package/README.md
CHANGED
|
@@ -45,6 +45,8 @@ mearl-native-host [options]
|
|
|
45
45
|
--daemon 独立 CDP daemon
|
|
46
46
|
--cdp-port <port> 指定 CDP 端口
|
|
47
47
|
--cdp-user-data-dir <dir> 指定 Chrome Profile
|
|
48
|
+
--agentbay-session-id <id> 连接 AgentBay CDP Session
|
|
49
|
+
--agentbay-idle-release-minutes <minutes> AgentBay 空闲回收时间(内部参数)
|
|
48
50
|
--browser-id <id> 指定浏览器注册 ID
|
|
49
51
|
--browser-name <name> 指定浏览器显示名称
|
|
50
52
|
--version, -v 查看版本
|
|
@@ -63,6 +65,15 @@ Chrome Extension
|
|
|
63
65
|
|
|
64
66
|
`browser_launch` 会为每个托管 Chrome 启动一个带独立 CDP 配置的 native-host 子进程,使多个 headless/Profile/TDBank 账号实例可以同时被现有浏览器注册表寻址。设计细节见 [托管浏览器与 TDBank 多账号设计](../../docs/managed-browsers.md)。
|
|
65
67
|
|
|
68
|
+
AgentBay 会话创建时使用官方 `LifecyclePolicy`:默认 30 分钟没有 Mearl 指令时由本地宿主执行优雅关闭,120 分钟达到云端最大运行时长时由 AgentBay 强制释放。优雅关闭会先同步持久化 Browser Context,再删除 Session 和本地注册记录;执行中的 Mearl action 不计为空闲。正常任务仍应主动调用 `browser_close`,超时只用于异常退出兜底。
|
|
69
|
+
|
|
70
|
+
运维环境可覆盖默认值;两项均为正整数分钟,空闲时间至少为 3,最大运行时长不得小于空闲时间:
|
|
71
|
+
|
|
72
|
+
```bash
|
|
73
|
+
MEARL_AGENTBAY_IDLE_RELEASE_MINUTES=30
|
|
74
|
+
MEARL_AGENTBAY_MAX_RUNTIME_MINUTES=120
|
|
75
|
+
```
|
|
76
|
+
|
|
66
77
|
## 侧边栏 Agent Chat
|
|
67
78
|
|
|
68
79
|
扩展侧边栏通过 Native Messaging 调用 native-host,再按用户选择启动持久的 Codex app-server 或 Qoder ACP 进程。两种 Provider 都支持流式消息、思考与工具进度、中断和审批,并复用各自 CLI 已保存的本地登录态。
|
|
@@ -9,6 +9,7 @@ export interface AgentBayBrowserRecord extends DaemonRecord {
|
|
|
9
9
|
name: string;
|
|
10
10
|
sessionId: string;
|
|
11
11
|
imageId?: string;
|
|
12
|
+
userAgentMode?: 'default' | 'desktop';
|
|
12
13
|
persistent?: boolean;
|
|
13
14
|
contextId?: string;
|
|
14
15
|
contextName?: string;
|
|
@@ -93,12 +94,21 @@ export interface AgentBayBrowserSummary {
|
|
|
93
94
|
copiedCookies?: AgentBayBrowserRecord['copiedCookies'];
|
|
94
95
|
running: boolean;
|
|
95
96
|
}
|
|
97
|
+
export interface AgentBayBrowserLaunchResult extends AgentBayBrowserSummary {
|
|
98
|
+
created: boolean;
|
|
99
|
+
reused: boolean;
|
|
100
|
+
}
|
|
96
101
|
export interface AgentBayBrowserManagerDeps {
|
|
97
102
|
nativeHostScript: string;
|
|
98
103
|
exportCookies: (domains: string[]) => Promise<TransferableCookie[]>;
|
|
99
104
|
getBrowserUserAgent: () => Promise<string>;
|
|
100
105
|
log: (...args: any[]) => void;
|
|
101
106
|
createAgentBayClient?: (apiKey: string) => Promise<AgentBayClientLike>;
|
|
107
|
+
connectCdp?: ConnectAgentBayCdp;
|
|
108
|
+
findBrowserRecord?: (selector: string) => {
|
|
109
|
+
record: AgentBayBrowserRecord;
|
|
110
|
+
running: boolean;
|
|
111
|
+
} | null;
|
|
102
112
|
persistBrowserRecord?: (record: AgentBayBrowserRecord) => void;
|
|
103
113
|
resolveApiKey?: () => string;
|
|
104
114
|
}
|
|
@@ -124,7 +134,8 @@ export declare function getAgentBayAccessUrl(sessionId: string, dependencies?: P
|
|
|
124
134
|
export declare function resolveAgentBayEndpoint(sessionId: string): Promise<string>;
|
|
125
135
|
export declare function keepAgentBaySessionAlive(sessionId: string): Promise<void>;
|
|
126
136
|
export declare function initializeAgentBayBrowserSession(session: Pick<AgentBaySessionLike, 'browser'>, input: Pick<BrowserLaunchParams, 'url'>, cookies: TransferableCookie[], options?: AgentBayBrowserSessionInitOptions): Promise<number>;
|
|
127
|
-
export declare function
|
|
137
|
+
export declare function reuseAgentBayBrowserSession(session: Pick<AgentBaySessionLike, 'browser'>, input: Pick<BrowserLaunchParams, 'url'>, cookies: TransferableCookie[], options?: Pick<AgentBayBrowserSessionInitOptions, 'connectCdp'>): Promise<number>;
|
|
138
|
+
export declare function launchAgentBayBrowser(input: BrowserLaunchParams, deps: AgentBayBrowserManagerDeps): Promise<AgentBayBrowserLaunchResult>;
|
|
128
139
|
export declare function releaseAgentBayBrowserResources(record: AgentBayBrowserRecord, agentBay: AgentBayClientLike, options?: {
|
|
129
140
|
deleteProfile?: boolean;
|
|
130
141
|
persistRecord?: (record: AgentBayBrowserRecord) => void;
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
export declare const MIN_AGENTBAY_IDLE_RELEASE_MINUTES = 3;
|
|
2
|
+
export declare const DEFAULT_AGENTBAY_IDLE_RELEASE_MINUTES = 30;
|
|
3
|
+
export declare const DEFAULT_AGENTBAY_MAX_RUNTIME_MINUTES = 120;
|
|
4
|
+
export declare const DEFAULT_AGENTBAY_LIFECYCLE_CHECK_INTERVAL_MS = 60000;
|
|
5
|
+
export interface AgentBayLifecycleConfig {
|
|
6
|
+
idleReleaseTimeout: number;
|
|
7
|
+
maxRuntime: number;
|
|
8
|
+
}
|
|
9
|
+
interface AgentBaySessionWatchdogOptions {
|
|
10
|
+
idleTimeoutMs: number;
|
|
11
|
+
checkIntervalMs?: number;
|
|
12
|
+
now?: () => number;
|
|
13
|
+
keepAlive: () => Promise<void>;
|
|
14
|
+
close: () => Promise<void>;
|
|
15
|
+
log: (...args: unknown[]) => void;
|
|
16
|
+
}
|
|
17
|
+
export declare function resolveAgentBayLifecycleConfig(env?: NodeJS.ProcessEnv): AgentBayLifecycleConfig;
|
|
18
|
+
export declare class AgentBaySessionWatchdog {
|
|
19
|
+
private readonly idleTimeoutMs;
|
|
20
|
+
private readonly checkIntervalMs;
|
|
21
|
+
private readonly now;
|
|
22
|
+
private readonly keepAlive;
|
|
23
|
+
private readonly close;
|
|
24
|
+
private readonly log;
|
|
25
|
+
private activeRequests;
|
|
26
|
+
private lastActivityAt;
|
|
27
|
+
private timer;
|
|
28
|
+
private checking;
|
|
29
|
+
private closing;
|
|
30
|
+
private stopped;
|
|
31
|
+
constructor(options: AgentBaySessionWatchdogOptions);
|
|
32
|
+
start(): void;
|
|
33
|
+
stop(): void;
|
|
34
|
+
beginActivity(): boolean;
|
|
35
|
+
endActivity(): void;
|
|
36
|
+
checkNow(): Promise<void>;
|
|
37
|
+
}
|
|
38
|
+
export {};
|
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
* Contains: HTTP helper (nodeFetch), cookie utilities, page-load waiters,
|
|
5
5
|
* viewport helpers, and serialized function sources for page injection.
|
|
6
6
|
*/
|
|
7
|
-
import { type PageClickPointerType } from '@mearl/browser-core';
|
|
7
|
+
import { type PageClickPointerType, type PageDragPoint, type PageDragPointerType } from '@mearl/browser-core';
|
|
8
8
|
import type { PageSession } from './cdp-session.js';
|
|
9
9
|
export declare const TYPE_INTO_ELEMENT_SRC: string;
|
|
10
10
|
export declare const CLICK_ELEMENT_VIA_JS_SRC: string;
|
|
@@ -61,4 +61,7 @@ export declare function toCdpInputPoint(session: PageSession, x: number, y: numb
|
|
|
61
61
|
y: number;
|
|
62
62
|
}>;
|
|
63
63
|
export declare function dispatchCdpClick(session: PageSession, x: number, y: number, pointerType: PageClickPointerType): Promise<void>;
|
|
64
|
+
export declare function dispatchCdpDrag(session: PageSession, from: PageDragPoint, to: PageDragPoint, pointerType: PageDragPointerType, durationMs: number): Promise<{
|
|
65
|
+
steps: number;
|
|
66
|
+
}>;
|
|
64
67
|
export declare function resolveBackendNodeObjectId(session: PageSession, backendNodeId: number): Promise<string>;
|
|
@@ -3,9 +3,10 @@
|
|
|
3
3
|
*
|
|
4
4
|
* Covers: click, type, scroll, eval, press, wait, navigate, upload, device emulation.
|
|
5
5
|
*/
|
|
6
|
-
import { type PageClickParams, type PageEvalParams, type PageHoverParams, type PageNavigateParams, type PagePressParams, type PageScrollParams, type PageTypeParams, type PageUploadParams, type PageWaitParams, type SetDeviceEmulationParams } from '@mearl/browser-core';
|
|
6
|
+
import { type PageClickParams, type PageDragParams, type PageDragResultData, type PageEvalParams, type PageHoverParams, type PageNavigateParams, type PagePressParams, type PageScrollParams, type PageTypeParams, type PageUploadParams, type PageWaitParams, type SetDeviceEmulationParams } from '@mearl/browser-core';
|
|
7
7
|
import type { PageSession } from './cdp-session.js';
|
|
8
8
|
export declare function cdpPageClick(session: PageSession, data: PageClickParams): Promise<any>;
|
|
9
|
+
export declare function cdpPageDrag(session: PageSession, data: PageDragParams): Promise<PageDragResultData>;
|
|
9
10
|
export declare function cdpPageType(session: PageSession, data: PageTypeParams): Promise<any>;
|
|
10
11
|
export declare function cdpPageScroll(session: PageSession, data: PageScrollParams): Promise<any>;
|
|
11
12
|
export declare function cdpPageEval(session: PageSession, data: PageEvalParams): Promise<any>;
|
package/dist/index.js
CHANGED
|
@@ -2068,6 +2068,203 @@ function getClickCoordsFromNode() {
|
|
|
2068
2068
|
};
|
|
2069
2069
|
}
|
|
2070
2070
|
|
|
2071
|
+
// ../browser-core/dist/pageDragging.js
|
|
2072
|
+
var PAGE_DRAG_DEFAULT_DURATION_MS = 400;
|
|
2073
|
+
var PAGE_DRAG_MAX_DURATION_MS = 5e3;
|
|
2074
|
+
var PAGE_DRAG_STEP_INTERVAL_MS = 25;
|
|
2075
|
+
var PAGE_DRAG_MIN_STEPS = 2;
|
|
2076
|
+
var PAGE_DRAG_MAX_STEPS = 60;
|
|
2077
|
+
function isPageDragPoint(value) {
|
|
2078
|
+
if (!value || typeof value !== "object")
|
|
2079
|
+
return false;
|
|
2080
|
+
const point = value;
|
|
2081
|
+
return Number.isFinite(point.x) && Number.isFinite(point.y) && point.x >= 0 && point.y >= 0;
|
|
2082
|
+
}
|
|
2083
|
+
function resolveEndpoint(value, name) {
|
|
2084
|
+
if (typeof value === "string" && value.trim().length > 0)
|
|
2085
|
+
return value.trim();
|
|
2086
|
+
if (isPageDragPoint(value))
|
|
2087
|
+
return { x: value.x, y: value.y };
|
|
2088
|
+
throw new Error(`page_drag: "${name}" must be a non-empty CSS/@ref selector or { x, y } viewport point.`);
|
|
2089
|
+
}
|
|
2090
|
+
function resolvePageDragInput(data) {
|
|
2091
|
+
if (!data || typeof data !== "object") {
|
|
2092
|
+
throw new Error("page_drag: payload is required");
|
|
2093
|
+
}
|
|
2094
|
+
if (data.dragMode !== void 0 && !["auto", "dom", "mouse", "touch"].includes(data.dragMode)) {
|
|
2095
|
+
throw new Error('page_drag: "dragMode" must be one of auto, dom, mouse, touch');
|
|
2096
|
+
}
|
|
2097
|
+
const durationMs = data.durationMs ?? PAGE_DRAG_DEFAULT_DURATION_MS;
|
|
2098
|
+
if (!Number.isFinite(durationMs) || durationMs < 0 || durationMs > PAGE_DRAG_MAX_DURATION_MS) {
|
|
2099
|
+
throw new Error(`page_drag: "durationMs" must be between 0 and ${PAGE_DRAG_MAX_DURATION_MS}.`);
|
|
2100
|
+
}
|
|
2101
|
+
return {
|
|
2102
|
+
from: resolveEndpoint(data.from, "from"),
|
|
2103
|
+
to: resolveEndpoint(data.to, "to"),
|
|
2104
|
+
dragMode: data.dragMode ?? "auto",
|
|
2105
|
+
durationMs: Math.round(durationMs)
|
|
2106
|
+
};
|
|
2107
|
+
}
|
|
2108
|
+
function resolvePageDragDispatchPolicy(dragMode, visibilityState, touchEmulationEnabled) {
|
|
2109
|
+
const requestedMode = dragMode ?? "auto";
|
|
2110
|
+
const pointerType = requestedMode === "mouse" || requestedMode === "touch" ? requestedMode : touchEmulationEnabled ? "touch" : "mouse";
|
|
2111
|
+
if (requestedMode === "dom")
|
|
2112
|
+
return { dispatchMode: "dom", pointerType };
|
|
2113
|
+
if (requestedMode === "auto" && visibilityState === "hidden") {
|
|
2114
|
+
return { dispatchMode: "dom", pointerType, fallbackReason: "page-hidden" };
|
|
2115
|
+
}
|
|
2116
|
+
return { dispatchMode: "trusted-input", pointerType };
|
|
2117
|
+
}
|
|
2118
|
+
function buildPageDragPath(from, to, durationMs) {
|
|
2119
|
+
const steps = Math.max(PAGE_DRAG_MIN_STEPS, Math.min(PAGE_DRAG_MAX_STEPS, Math.ceil(durationMs / PAGE_DRAG_STEP_INTERVAL_MS)));
|
|
2120
|
+
const points = Array.from({ length: steps }, (_, index) => {
|
|
2121
|
+
const ratio = (index + 1) / steps;
|
|
2122
|
+
return {
|
|
2123
|
+
x: from.x + (to.x - from.x) * ratio,
|
|
2124
|
+
y: from.y + (to.y - from.y) * ratio
|
|
2125
|
+
};
|
|
2126
|
+
});
|
|
2127
|
+
return { points, intervalMs: durationMs / steps };
|
|
2128
|
+
}
|
|
2129
|
+
function dragBetweenPointsInPage(from, points, pointerType) {
|
|
2130
|
+
const target = document.elementFromPoint(from.x, from.y);
|
|
2131
|
+
if (!target) {
|
|
2132
|
+
return {
|
|
2133
|
+
dragged: false,
|
|
2134
|
+
steps: 0,
|
|
2135
|
+
error: `No element found at drag start point (${from.x}, ${from.y})`
|
|
2136
|
+
};
|
|
2137
|
+
}
|
|
2138
|
+
if (points.length === 0) {
|
|
2139
|
+
return { dragged: false, steps: 0, error: "Drag path must contain at least one point" };
|
|
2140
|
+
}
|
|
2141
|
+
const view = target.ownerDocument.defaultView ?? window;
|
|
2142
|
+
const realm = view;
|
|
2143
|
+
const pointerId = 1;
|
|
2144
|
+
const pointerInit = (point, buttons, pressure) => ({
|
|
2145
|
+
bubbles: true,
|
|
2146
|
+
cancelable: true,
|
|
2147
|
+
composed: true,
|
|
2148
|
+
view,
|
|
2149
|
+
clientX: point.x,
|
|
2150
|
+
clientY: point.y,
|
|
2151
|
+
screenX: point.x,
|
|
2152
|
+
screenY: point.y,
|
|
2153
|
+
button: 0,
|
|
2154
|
+
buttons,
|
|
2155
|
+
pointerId,
|
|
2156
|
+
pointerType,
|
|
2157
|
+
isPrimary: true,
|
|
2158
|
+
pressure,
|
|
2159
|
+
width: 1,
|
|
2160
|
+
height: 1
|
|
2161
|
+
});
|
|
2162
|
+
const dispatchPointer = (type, point, buttons, pressure) => {
|
|
2163
|
+
if (typeof realm.PointerEvent !== "function")
|
|
2164
|
+
return false;
|
|
2165
|
+
target.dispatchEvent(new realm.PointerEvent(type, pointerInit(point, buttons, pressure)));
|
|
2166
|
+
return true;
|
|
2167
|
+
};
|
|
2168
|
+
const dispatchMouse = (type, point, buttons) => {
|
|
2169
|
+
if (typeof realm.MouseEvent !== "function")
|
|
2170
|
+
return false;
|
|
2171
|
+
target.dispatchEvent(new realm.MouseEvent(type, {
|
|
2172
|
+
bubbles: true,
|
|
2173
|
+
cancelable: true,
|
|
2174
|
+
composed: true,
|
|
2175
|
+
view,
|
|
2176
|
+
clientX: point.x,
|
|
2177
|
+
clientY: point.y,
|
|
2178
|
+
screenX: point.x,
|
|
2179
|
+
screenY: point.y,
|
|
2180
|
+
button: 0,
|
|
2181
|
+
buttons
|
|
2182
|
+
}));
|
|
2183
|
+
return true;
|
|
2184
|
+
};
|
|
2185
|
+
const createTouch = (point) => {
|
|
2186
|
+
const init = {
|
|
2187
|
+
identifier: pointerId,
|
|
2188
|
+
target,
|
|
2189
|
+
clientX: point.x,
|
|
2190
|
+
clientY: point.y,
|
|
2191
|
+
screenX: point.x,
|
|
2192
|
+
screenY: point.y,
|
|
2193
|
+
pageX: point.x + view.scrollX,
|
|
2194
|
+
pageY: point.y + view.scrollY,
|
|
2195
|
+
radiusX: 1,
|
|
2196
|
+
radiusY: 1,
|
|
2197
|
+
rotationAngle: 0,
|
|
2198
|
+
force: 0.5
|
|
2199
|
+
};
|
|
2200
|
+
return typeof realm.Touch === "function" ? new realm.Touch(init) : init;
|
|
2201
|
+
};
|
|
2202
|
+
const dispatchTouch = (type, point) => {
|
|
2203
|
+
if (typeof realm.TouchEvent !== "function")
|
|
2204
|
+
return false;
|
|
2205
|
+
const touch = createTouch(point);
|
|
2206
|
+
const activeTouches = type === "touchend" ? [] : [touch];
|
|
2207
|
+
target.dispatchEvent(new realm.TouchEvent(type, {
|
|
2208
|
+
bubbles: true,
|
|
2209
|
+
cancelable: true,
|
|
2210
|
+
composed: true,
|
|
2211
|
+
touches: activeTouches,
|
|
2212
|
+
targetTouches: activeTouches,
|
|
2213
|
+
changedTouches: [touch]
|
|
2214
|
+
}));
|
|
2215
|
+
return true;
|
|
2216
|
+
};
|
|
2217
|
+
let dispatched = dispatchPointer("pointerdown", from, 1, 0.5);
|
|
2218
|
+
dispatched = pointerType === "touch" ? dispatchTouch("touchstart", from) || dispatched : dispatchMouse("mousedown", from, 1) || dispatched;
|
|
2219
|
+
for (const point of points) {
|
|
2220
|
+
dispatched = dispatchPointer("pointermove", point, 1, 0.5) || dispatched;
|
|
2221
|
+
dispatched = pointerType === "touch" ? dispatchTouch("touchmove", point) || dispatched : dispatchMouse("mousemove", point, 1) || dispatched;
|
|
2222
|
+
}
|
|
2223
|
+
const end = points[points.length - 1];
|
|
2224
|
+
dispatched = pointerType === "touch" ? dispatchTouch("touchend", end) || dispatched : dispatchMouse("mouseup", end, 0) || dispatched;
|
|
2225
|
+
dispatched = dispatchPointer("pointerup", end, 0, 0) || dispatched;
|
|
2226
|
+
return dispatched ? { dragged: true, steps: points.length } : { dragged: false, steps: 0, error: "This page does not support synthetic drag events" };
|
|
2227
|
+
}
|
|
2228
|
+
var DRAG_BETWEEN_POINTS_IN_PAGE_SOURCE = dragBetweenPointsInPage.toString();
|
|
2229
|
+
function buildPageDragDomExpression(from, points, pointerType) {
|
|
2230
|
+
return `(${DRAG_BETWEEN_POINTS_IN_PAGE_SOURCE})(${JSON.stringify(from)}, ${JSON.stringify(points)}, ${JSON.stringify(pointerType)})`;
|
|
2231
|
+
}
|
|
2232
|
+
function unwrapPageDragDomResult(result) {
|
|
2233
|
+
if (!result)
|
|
2234
|
+
throw new Error("page_drag: DOM simulation returned no result");
|
|
2235
|
+
if (!result.dragged)
|
|
2236
|
+
throw new Error(`page_drag: ${result.error}`);
|
|
2237
|
+
return result;
|
|
2238
|
+
}
|
|
2239
|
+
function getDragCoordsFromNode() {
|
|
2240
|
+
const el = this.nodeType === Node.TEXT_NODE ? this.parentElement : this;
|
|
2241
|
+
if (!el || typeof el.getBoundingClientRect !== "function")
|
|
2242
|
+
return null;
|
|
2243
|
+
const rect = el.getBoundingClientRect();
|
|
2244
|
+
const viewportWidth = window.visualViewport?.width ?? window.innerWidth;
|
|
2245
|
+
const viewportHeight = window.visualViewport?.height ?? window.innerHeight;
|
|
2246
|
+
if (rect.width <= 0 || rect.height <= 0) {
|
|
2247
|
+
return { x: 0, y: 0, tag: "", text: "", error: "Element has no visible bounds" };
|
|
2248
|
+
}
|
|
2249
|
+
if (rect.right <= 0 || rect.bottom <= 0 || rect.left >= viewportWidth || rect.top >= viewportHeight) {
|
|
2250
|
+
return {
|
|
2251
|
+
x: 0,
|
|
2252
|
+
y: 0,
|
|
2253
|
+
tag: "",
|
|
2254
|
+
text: "",
|
|
2255
|
+
error: "Element is outside the current viewport; scroll it into view before dragging"
|
|
2256
|
+
};
|
|
2257
|
+
}
|
|
2258
|
+
const x = Math.min(Math.max(rect.left + rect.width / 2, 0), Math.max(viewportWidth - 1, 0));
|
|
2259
|
+
const y = Math.min(Math.max(rect.top + rect.height / 2, 0), Math.max(viewportHeight - 1, 0));
|
|
2260
|
+
return {
|
|
2261
|
+
x,
|
|
2262
|
+
y,
|
|
2263
|
+
tag: el.tagName?.toLowerCase() ?? "",
|
|
2264
|
+
text: (el.textContent ?? "").trim().slice(0, 100)
|
|
2265
|
+
};
|
|
2266
|
+
}
|
|
2267
|
+
|
|
2071
2268
|
// ../browser-core/dist/axSnapshot.js
|
|
2072
2269
|
var PAGE_SNAPSHOT_QUERY_MAX_MATCHES = 100;
|
|
2073
2270
|
var PAGE_SNAPSHOT_QUERY_MAX_BYTES = 256 * 1024;
|
|
@@ -3372,6 +3569,7 @@ var SEND_REQUEST_MAX_TOTAL_FILE_BYTES = 40 * 1024 * 1024;
|
|
|
3372
3569
|
var SEND_REQUEST_MAX_TOTAL_BASE64_FILE_BYTES = 7 * 1024 * 1024;
|
|
3373
3570
|
var PAGE_ACT_ACTIONS = [
|
|
3374
3571
|
"page_click",
|
|
3572
|
+
"page_drag",
|
|
3375
3573
|
"page_type",
|
|
3376
3574
|
"page_hover",
|
|
3377
3575
|
"page_scroll",
|
|
@@ -3402,6 +3600,7 @@ var BROWSER_COMMAND_ACTIONS = {
|
|
|
3402
3600
|
tab_close: true,
|
|
3403
3601
|
tab_list: true,
|
|
3404
3602
|
page_click: true,
|
|
3603
|
+
page_drag: true,
|
|
3405
3604
|
page_type: true,
|
|
3406
3605
|
page_scroll: true,
|
|
3407
3606
|
page_hover: true,
|
|
@@ -3442,6 +3641,7 @@ var EXTENSION_BROWSER_ACTIONS = {
|
|
|
3442
3641
|
tab_close: true,
|
|
3443
3642
|
tab_list: true,
|
|
3444
3643
|
page_click: true,
|
|
3644
|
+
page_drag: true,
|
|
3445
3645
|
page_type: true,
|
|
3446
3646
|
page_scroll: true,
|
|
3447
3647
|
page_hover: true,
|
|
@@ -4339,6 +4539,12 @@ function resolveSetCookieTarget(currentUrl, input) {
|
|
|
4339
4539
|
}
|
|
4340
4540
|
|
|
4341
4541
|
// ../browser-core/dist/controlledFavicon.js
|
|
4542
|
+
function normalizeControlSource(source) {
|
|
4543
|
+
return source === "remote" || source === "cloud" ? "remote" : "local";
|
|
4544
|
+
}
|
|
4545
|
+
function toControlSourceWireValue(source) {
|
|
4546
|
+
return source === "remote" ? "cloud" : "local";
|
|
4547
|
+
}
|
|
4342
4548
|
var BASE_ICON = `
|
|
4343
4549
|
<rect width="128" height="128" rx="28" fill="#3273E8"/>
|
|
4344
4550
|
<path d="M36 90V40L64 68L92 40V90" stroke="#FFFFFF" stroke-width="14"
|
|
@@ -4348,7 +4554,7 @@ var SOURCE_BADGES = {
|
|
|
4348
4554
|
local: `
|
|
4349
4555
|
<circle cx="104" cy="104" r="21" fill="#16A34A" stroke="#FFFFFF" stroke-width="6"/>
|
|
4350
4556
|
`,
|
|
4351
|
-
|
|
4557
|
+
remote: `
|
|
4352
4558
|
<circle cx="104" cy="104" r="21" fill="#FF7A00" stroke="#FFFFFF" stroke-width="6"/>
|
|
4353
4559
|
`
|
|
4354
4560
|
};
|
|
@@ -5949,6 +6155,85 @@ async function dispatchCdpClick(session, x, y, pointerType) {
|
|
|
5949
6155
|
clickCount: 1
|
|
5950
6156
|
});
|
|
5951
6157
|
}
|
|
6158
|
+
async function dispatchCdpDrag(session, from, to, pointerType, durationMs) {
|
|
6159
|
+
const [start, end] = await Promise.all([
|
|
6160
|
+
toCdpInputPoint(session, from.x, from.y),
|
|
6161
|
+
toCdpInputPoint(session, to.x, to.y)
|
|
6162
|
+
]);
|
|
6163
|
+
const path15 = buildPageDragPath(start, end, durationMs);
|
|
6164
|
+
const waitForNextPoint = async () => {
|
|
6165
|
+
if (path15.intervalMs <= 0) return;
|
|
6166
|
+
await new Promise((resolve2) => setTimeout(resolve2, path15.intervalMs));
|
|
6167
|
+
};
|
|
6168
|
+
if (pointerType === "touch") {
|
|
6169
|
+
await sendCdpInputCommand(session, "Input.dispatchTouchEvent", {
|
|
6170
|
+
type: "touchStart",
|
|
6171
|
+
touchPoints: [start]
|
|
6172
|
+
});
|
|
6173
|
+
let primaryError2;
|
|
6174
|
+
try {
|
|
6175
|
+
for (const point of path15.points) {
|
|
6176
|
+
await waitForNextPoint();
|
|
6177
|
+
await sendCdpInputCommand(session, "Input.dispatchTouchEvent", {
|
|
6178
|
+
type: "touchMove",
|
|
6179
|
+
touchPoints: [point]
|
|
6180
|
+
});
|
|
6181
|
+
}
|
|
6182
|
+
} catch (error48) {
|
|
6183
|
+
primaryError2 = error48;
|
|
6184
|
+
throw error48;
|
|
6185
|
+
} finally {
|
|
6186
|
+
await sendCdpInputCommand(session, "Input.dispatchTouchEvent", {
|
|
6187
|
+
type: "touchEnd",
|
|
6188
|
+
touchPoints: []
|
|
6189
|
+
}).catch((error48) => {
|
|
6190
|
+
if (primaryError2 === void 0) throw error48;
|
|
6191
|
+
});
|
|
6192
|
+
}
|
|
6193
|
+
return { steps: path15.points.length };
|
|
6194
|
+
}
|
|
6195
|
+
await sendCdpInputCommand(session, "Input.dispatchMouseEvent", {
|
|
6196
|
+
type: "mouseMoved",
|
|
6197
|
+
x: start.x,
|
|
6198
|
+
y: start.y
|
|
6199
|
+
});
|
|
6200
|
+
await sendCdpInputCommand(session, "Input.dispatchMouseEvent", {
|
|
6201
|
+
type: "mousePressed",
|
|
6202
|
+
x: start.x,
|
|
6203
|
+
y: start.y,
|
|
6204
|
+
button: "left",
|
|
6205
|
+
buttons: 1,
|
|
6206
|
+
clickCount: 1
|
|
6207
|
+
});
|
|
6208
|
+
let primaryError;
|
|
6209
|
+
try {
|
|
6210
|
+
for (const point of path15.points) {
|
|
6211
|
+
await waitForNextPoint();
|
|
6212
|
+
await sendCdpInputCommand(session, "Input.dispatchMouseEvent", {
|
|
6213
|
+
type: "mouseMoved",
|
|
6214
|
+
x: point.x,
|
|
6215
|
+
y: point.y,
|
|
6216
|
+
button: "left",
|
|
6217
|
+
buttons: 1
|
|
6218
|
+
});
|
|
6219
|
+
}
|
|
6220
|
+
} catch (error48) {
|
|
6221
|
+
primaryError = error48;
|
|
6222
|
+
throw error48;
|
|
6223
|
+
} finally {
|
|
6224
|
+
await sendCdpInputCommand(session, "Input.dispatchMouseEvent", {
|
|
6225
|
+
type: "mouseReleased",
|
|
6226
|
+
x: end.x,
|
|
6227
|
+
y: end.y,
|
|
6228
|
+
button: "left",
|
|
6229
|
+
buttons: 0,
|
|
6230
|
+
clickCount: 1
|
|
6231
|
+
}).catch((error48) => {
|
|
6232
|
+
if (primaryError === void 0) throw error48;
|
|
6233
|
+
});
|
|
6234
|
+
}
|
|
6235
|
+
return { steps: path15.points.length };
|
|
6236
|
+
}
|
|
5952
6237
|
async function sendCdpInputCommand(session, method, params) {
|
|
5953
6238
|
try {
|
|
5954
6239
|
await session.client.sendCommand(method, params, PAGE_CLICK_INPUT_TIMEOUT_MS);
|
|
@@ -7940,6 +8225,7 @@ registerActions([
|
|
|
7940
8225
|
]);
|
|
7941
8226
|
|
|
7942
8227
|
// src/cdp-backend/interaction-handlers.ts
|
|
8228
|
+
var GET_DRAG_COORDS_FROM_NODE_SRC = getDragCoordsFromNode.toString();
|
|
7943
8229
|
function resolveSnapshotRef(session, ref) {
|
|
7944
8230
|
const entry = resolvePageRef(session, ref);
|
|
7945
8231
|
if (!entry) {
|
|
@@ -8251,6 +8537,136 @@ ${candidates}`
|
|
|
8251
8537
|
)
|
|
8252
8538
|
);
|
|
8253
8539
|
}
|
|
8540
|
+
async function scrollDragSourceIntoView(session, endpoint) {
|
|
8541
|
+
if (typeof endpoint !== "string") return;
|
|
8542
|
+
validatePageElementSelector(endpoint, "page_drag from");
|
|
8543
|
+
if (endpoint.startsWith("@e")) {
|
|
8544
|
+
const entry = resolveSnapshotRef(session, endpoint);
|
|
8545
|
+
const objectId = await resolveBackendNodeObjectId(session, entry.backendNodeId);
|
|
8546
|
+
await session.client.sendCommand("Runtime.callFunctionOn", {
|
|
8547
|
+
objectId,
|
|
8548
|
+
functionDeclaration: `function() {
|
|
8549
|
+
const el = this.nodeType === Node.TEXT_NODE ? this.parentElement : this;
|
|
8550
|
+
if (!el) return false;
|
|
8551
|
+
el.scrollIntoView({ block: 'center', inline: 'center', behavior: 'instant' });
|
|
8552
|
+
return true;
|
|
8553
|
+
}`,
|
|
8554
|
+
returnByValue: true
|
|
8555
|
+
});
|
|
8556
|
+
return;
|
|
8557
|
+
}
|
|
8558
|
+
const result = await session.client.sendCommand("Runtime.evaluate", {
|
|
8559
|
+
expression: `(() => {
|
|
8560
|
+
const selector = ${JSON.stringify(endpoint)};
|
|
8561
|
+
const el = document.querySelector(selector);
|
|
8562
|
+
if (!el) return { error: 'Element not found: ' + selector };
|
|
8563
|
+
el.scrollIntoView({ block: 'center', inline: 'center', behavior: 'instant' });
|
|
8564
|
+
return { scrolled: true };
|
|
8565
|
+
})()`,
|
|
8566
|
+
returnByValue: true
|
|
8567
|
+
});
|
|
8568
|
+
const value = result.result?.value;
|
|
8569
|
+
if (value?.error) throw new Error(`page_drag: ${value.error}`);
|
|
8570
|
+
}
|
|
8571
|
+
function unwrapDragElementCoords(value, endpoint) {
|
|
8572
|
+
if (!value) throw new Error(`page_drag: cannot resolve element coordinates for "${endpoint}"`);
|
|
8573
|
+
if (value.error) throw new Error(`page_drag: ${value.error}: "${endpoint}"`);
|
|
8574
|
+
return value;
|
|
8575
|
+
}
|
|
8576
|
+
async function resolveDragEndpoint(session, endpoint) {
|
|
8577
|
+
if (typeof endpoint !== "string") {
|
|
8578
|
+
return { requested: endpoint, point: endpoint };
|
|
8579
|
+
}
|
|
8580
|
+
validatePageElementSelector(endpoint, "page_drag endpoint");
|
|
8581
|
+
let coords;
|
|
8582
|
+
if (endpoint.startsWith("@e")) {
|
|
8583
|
+
const entry = resolveSnapshotRef(session, endpoint);
|
|
8584
|
+
const objectId = await resolveBackendNodeObjectId(session, entry.backendNodeId);
|
|
8585
|
+
const result = await session.client.sendCommand("Runtime.callFunctionOn", {
|
|
8586
|
+
objectId,
|
|
8587
|
+
functionDeclaration: GET_DRAG_COORDS_FROM_NODE_SRC,
|
|
8588
|
+
returnByValue: true
|
|
8589
|
+
});
|
|
8590
|
+
coords = unwrapDragElementCoords(result.result?.value, endpoint);
|
|
8591
|
+
} else {
|
|
8592
|
+
const result = await session.client.sendCommand("Runtime.evaluate", {
|
|
8593
|
+
expression: `(() => {
|
|
8594
|
+
const selector = ${JSON.stringify(endpoint)};
|
|
8595
|
+
const el = document.querySelector(selector);
|
|
8596
|
+
if (!el) return { error: 'Element not found: ' + selector };
|
|
8597
|
+
return (${GET_DRAG_COORDS_FROM_NODE_SRC}).call(el);
|
|
8598
|
+
})()`,
|
|
8599
|
+
returnByValue: true
|
|
8600
|
+
});
|
|
8601
|
+
coords = unwrapDragElementCoords(result.result?.value, endpoint);
|
|
8602
|
+
}
|
|
8603
|
+
return {
|
|
8604
|
+
requested: endpoint,
|
|
8605
|
+
point: { x: coords.x, y: coords.y },
|
|
8606
|
+
tag: coords.tag,
|
|
8607
|
+
text: coords.text
|
|
8608
|
+
};
|
|
8609
|
+
}
|
|
8610
|
+
async function cdpPageDrag(session, data) {
|
|
8611
|
+
const input = resolvePageDragInput(data);
|
|
8612
|
+
const visibilityResult = input.dragMode === "auto" ? await session.client.sendCommand("Runtime.evaluate", {
|
|
8613
|
+
expression: "document.visibilityState",
|
|
8614
|
+
returnByValue: true
|
|
8615
|
+
}) : void 0;
|
|
8616
|
+
const touchEmulationEnabled = input.dragMode === "auto" || input.dragMode === "dom" ? await prefersTouchInput(session) : input.dragMode === "touch";
|
|
8617
|
+
const policy = resolvePageDragDispatchPolicy(
|
|
8618
|
+
input.dragMode,
|
|
8619
|
+
visibilityResult?.result?.value,
|
|
8620
|
+
touchEmulationEnabled
|
|
8621
|
+
);
|
|
8622
|
+
await scrollDragSourceIntoView(session, input.from);
|
|
8623
|
+
const from = await resolveDragEndpoint(session, input.from);
|
|
8624
|
+
const to = await resolveDragEndpoint(session, input.to);
|
|
8625
|
+
if (Math.hypot(to.point.x - from.point.x, to.point.y - from.point.y) < 1) {
|
|
8626
|
+
throw new Error("page_drag: resolved endpoints must be at least 1 viewport pixel apart");
|
|
8627
|
+
}
|
|
8628
|
+
if (policy.dispatchMode === "dom") {
|
|
8629
|
+
const path15 = buildPageDragPath(from.point, to.point, input.durationMs);
|
|
8630
|
+
const result = await session.client.sendCommand("Runtime.evaluate", {
|
|
8631
|
+
expression: buildPageDragDomExpression(from.point, path15.points, policy.pointerType),
|
|
8632
|
+
returnByValue: true
|
|
8633
|
+
});
|
|
8634
|
+
if (result.exceptionDetails) {
|
|
8635
|
+
throw new Error(
|
|
8636
|
+
result.exceptionDetails.exception?.description ?? result.exceptionDetails.text ?? "page_drag: DOM simulation failed"
|
|
8637
|
+
);
|
|
8638
|
+
}
|
|
8639
|
+
const { steps: steps2 } = unwrapPageDragDomResult(
|
|
8640
|
+
result.result?.value
|
|
8641
|
+
);
|
|
8642
|
+
return {
|
|
8643
|
+
dragged: true,
|
|
8644
|
+
from,
|
|
8645
|
+
to,
|
|
8646
|
+
dispatchMode: "dom",
|
|
8647
|
+
pointerType: policy.pointerType,
|
|
8648
|
+
durationMs: input.durationMs,
|
|
8649
|
+
steps: steps2,
|
|
8650
|
+
...policy.fallbackReason ? { fallbackReason: policy.fallbackReason } : {}
|
|
8651
|
+
};
|
|
8652
|
+
}
|
|
8653
|
+
const { steps } = await dispatchCdpDrag(
|
|
8654
|
+
session,
|
|
8655
|
+
from.point,
|
|
8656
|
+
to.point,
|
|
8657
|
+
policy.pointerType,
|
|
8658
|
+
input.durationMs
|
|
8659
|
+
);
|
|
8660
|
+
return {
|
|
8661
|
+
dragged: true,
|
|
8662
|
+
from,
|
|
8663
|
+
to,
|
|
8664
|
+
dispatchMode: "trusted-input",
|
|
8665
|
+
pointerType: policy.pointerType,
|
|
8666
|
+
durationMs: input.durationMs,
|
|
8667
|
+
steps
|
|
8668
|
+
};
|
|
8669
|
+
}
|
|
8254
8670
|
async function cdpPageType(session, data) {
|
|
8255
8671
|
const { selector, text, clearFirst = true } = data;
|
|
8256
8672
|
if (!selector || typeof selector !== "string") {
|
|
@@ -8571,6 +8987,7 @@ async function cdpPageHover(session, data) {
|
|
|
8571
8987
|
}
|
|
8572
8988
|
registerActions([
|
|
8573
8989
|
{ name: "page_click", session: "page", handler: cdpPageClick },
|
|
8990
|
+
{ name: "page_drag", session: "page", handler: cdpPageDrag },
|
|
8574
8991
|
{ name: "page_type", session: "page", handler: cdpPageType },
|
|
8575
8992
|
{ name: "page_hover", session: "page", handler: cdpPageHover },
|
|
8576
8993
|
{ name: "page_scroll", session: "page", handler: cdpPageScroll },
|
|
@@ -9373,6 +9790,12 @@ function acquireLaunchLock(browserId) {
|
|
|
9373
9790
|
};
|
|
9374
9791
|
}
|
|
9375
9792
|
async function launchManagedBrowser(input, deps) {
|
|
9793
|
+
if (input.reuse !== void 0 && input.reuse !== "never" && input.reuse !== "prefer" && input.reuse !== "require") {
|
|
9794
|
+
throw new Error('reuse must be "never", "prefer", or "require"');
|
|
9795
|
+
}
|
|
9796
|
+
if (input.reuse === "prefer" || input.reuse === "require") {
|
|
9797
|
+
throw new Error("reuse is currently supported only by the AgentBay browser provider");
|
|
9798
|
+
}
|
|
9376
9799
|
if (input.imageId !== void 0) {
|
|
9377
9800
|
throw new Error("imageId is only supported by the AgentBay browser provider");
|
|
9378
9801
|
}
|
|
@@ -9477,7 +9900,11 @@ async function launchManagedBrowser(input, deps) {
|
|
|
9477
9900
|
recordWritten = true;
|
|
9478
9901
|
await waitForBrowserRegistration(browserId, hostPid);
|
|
9479
9902
|
deps.log(`managed browser launched: ${browserId}, chrome=${chromePid}, host=${hostPid}`);
|
|
9480
|
-
return
|
|
9903
|
+
return {
|
|
9904
|
+
...browserSummary(record2, true),
|
|
9905
|
+
created: true,
|
|
9906
|
+
reused: false
|
|
9907
|
+
};
|
|
9481
9908
|
} catch (error48) {
|
|
9482
9909
|
session?.close();
|
|
9483
9910
|
if (hostPid) await stopProcess(hostPid, `--browser-id ${browserId}`);
|
|
@@ -9513,6 +9940,127 @@ import { spawn as spawn4 } from "node:child_process";
|
|
|
9513
9940
|
import { homedir as homedir2 } from "node:os";
|
|
9514
9941
|
import { dirname as dirname3, join as join4, parse, resolve } from "node:path";
|
|
9515
9942
|
import { fileURLToPath } from "node:url";
|
|
9943
|
+
|
|
9944
|
+
// src/agentbay-lifecycle.ts
|
|
9945
|
+
var MIN_AGENTBAY_IDLE_RELEASE_MINUTES = 3;
|
|
9946
|
+
var DEFAULT_AGENTBAY_IDLE_RELEASE_MINUTES = 30;
|
|
9947
|
+
var DEFAULT_AGENTBAY_MAX_RUNTIME_MINUTES = 120;
|
|
9948
|
+
var DEFAULT_AGENTBAY_LIFECYCLE_CHECK_INTERVAL_MS = 6e4;
|
|
9949
|
+
function positiveIntegerMinutes(value, name, fallback) {
|
|
9950
|
+
if (value === void 0 || !value.trim()) return fallback;
|
|
9951
|
+
const minutes = Number(value);
|
|
9952
|
+
if (!Number.isInteger(minutes) || minutes <= 0) {
|
|
9953
|
+
throw new Error(`${name} must be a positive integer in minutes`);
|
|
9954
|
+
}
|
|
9955
|
+
return minutes;
|
|
9956
|
+
}
|
|
9957
|
+
function resolveAgentBayLifecycleConfig(env = process.env) {
|
|
9958
|
+
const idleReleaseTimeout = positiveIntegerMinutes(
|
|
9959
|
+
env.MEARL_AGENTBAY_IDLE_RELEASE_MINUTES,
|
|
9960
|
+
"MEARL_AGENTBAY_IDLE_RELEASE_MINUTES",
|
|
9961
|
+
DEFAULT_AGENTBAY_IDLE_RELEASE_MINUTES
|
|
9962
|
+
);
|
|
9963
|
+
const maxRuntime = positiveIntegerMinutes(
|
|
9964
|
+
env.MEARL_AGENTBAY_MAX_RUNTIME_MINUTES,
|
|
9965
|
+
"MEARL_AGENTBAY_MAX_RUNTIME_MINUTES",
|
|
9966
|
+
DEFAULT_AGENTBAY_MAX_RUNTIME_MINUTES
|
|
9967
|
+
);
|
|
9968
|
+
if (idleReleaseTimeout < MIN_AGENTBAY_IDLE_RELEASE_MINUTES) {
|
|
9969
|
+
throw new Error(
|
|
9970
|
+
`MEARL_AGENTBAY_IDLE_RELEASE_MINUTES must be at least ${MIN_AGENTBAY_IDLE_RELEASE_MINUTES}`
|
|
9971
|
+
);
|
|
9972
|
+
}
|
|
9973
|
+
if (maxRuntime < idleReleaseTimeout) {
|
|
9974
|
+
throw new Error(
|
|
9975
|
+
"MEARL_AGENTBAY_MAX_RUNTIME_MINUTES must be greater than or equal to MEARL_AGENTBAY_IDLE_RELEASE_MINUTES"
|
|
9976
|
+
);
|
|
9977
|
+
}
|
|
9978
|
+
return { idleReleaseTimeout, maxRuntime };
|
|
9979
|
+
}
|
|
9980
|
+
var AgentBaySessionWatchdog = class {
|
|
9981
|
+
idleTimeoutMs;
|
|
9982
|
+
checkIntervalMs;
|
|
9983
|
+
now;
|
|
9984
|
+
keepAlive;
|
|
9985
|
+
close;
|
|
9986
|
+
log;
|
|
9987
|
+
activeRequests = 0;
|
|
9988
|
+
lastActivityAt;
|
|
9989
|
+
timer = null;
|
|
9990
|
+
checking = false;
|
|
9991
|
+
closing = false;
|
|
9992
|
+
stopped = false;
|
|
9993
|
+
constructor(options) {
|
|
9994
|
+
if (!Number.isFinite(options.idleTimeoutMs) || options.idleTimeoutMs <= 0) {
|
|
9995
|
+
throw new Error("AgentBay idle timeout must be positive");
|
|
9996
|
+
}
|
|
9997
|
+
this.idleTimeoutMs = options.idleTimeoutMs;
|
|
9998
|
+
this.checkIntervalMs = options.checkIntervalMs ?? DEFAULT_AGENTBAY_LIFECYCLE_CHECK_INTERVAL_MS;
|
|
9999
|
+
if (!Number.isFinite(this.checkIntervalMs) || this.checkIntervalMs <= 0) {
|
|
10000
|
+
throw new Error("AgentBay lifecycle check interval must be positive");
|
|
10001
|
+
}
|
|
10002
|
+
this.now = options.now ?? Date.now;
|
|
10003
|
+
this.keepAlive = options.keepAlive;
|
|
10004
|
+
this.close = options.close;
|
|
10005
|
+
this.log = options.log;
|
|
10006
|
+
this.lastActivityAt = this.now();
|
|
10007
|
+
}
|
|
10008
|
+
start() {
|
|
10009
|
+
if (this.timer || this.stopped) return;
|
|
10010
|
+
this.timer = setInterval(() => void this.checkNow(), this.checkIntervalMs);
|
|
10011
|
+
this.timer.unref();
|
|
10012
|
+
}
|
|
10013
|
+
stop() {
|
|
10014
|
+
this.stopped = true;
|
|
10015
|
+
if (this.timer) {
|
|
10016
|
+
clearInterval(this.timer);
|
|
10017
|
+
this.timer = null;
|
|
10018
|
+
}
|
|
10019
|
+
}
|
|
10020
|
+
beginActivity() {
|
|
10021
|
+
if (this.stopped || this.closing) return false;
|
|
10022
|
+
this.activeRequests += 1;
|
|
10023
|
+
this.lastActivityAt = this.now();
|
|
10024
|
+
return true;
|
|
10025
|
+
}
|
|
10026
|
+
endActivity() {
|
|
10027
|
+
if (this.activeRequests > 0) this.activeRequests -= 1;
|
|
10028
|
+
this.lastActivityAt = this.now();
|
|
10029
|
+
}
|
|
10030
|
+
async checkNow() {
|
|
10031
|
+
if (this.stopped || this.checking || this.closing) return;
|
|
10032
|
+
this.checking = true;
|
|
10033
|
+
try {
|
|
10034
|
+
const idleForMs = this.now() - this.lastActivityAt;
|
|
10035
|
+
if (this.activeRequests > 0 || idleForMs < this.idleTimeoutMs) {
|
|
10036
|
+
try {
|
|
10037
|
+
await this.keepAlive();
|
|
10038
|
+
} catch (error48) {
|
|
10039
|
+
this.log(
|
|
10040
|
+
"AgentBay keepalive failed:",
|
|
10041
|
+
error48 instanceof Error ? error48.message : String(error48)
|
|
10042
|
+
);
|
|
10043
|
+
}
|
|
10044
|
+
return;
|
|
10045
|
+
}
|
|
10046
|
+
this.closing = true;
|
|
10047
|
+
this.log(`AgentBay browser reached the idle timeout after ${idleForMs}ms; closing`);
|
|
10048
|
+
try {
|
|
10049
|
+
await this.close();
|
|
10050
|
+
} catch (error48) {
|
|
10051
|
+
this.closing = false;
|
|
10052
|
+
this.log(
|
|
10053
|
+
"AgentBay idle close failed:",
|
|
10054
|
+
error48 instanceof Error ? error48.message : String(error48)
|
|
10055
|
+
);
|
|
10056
|
+
}
|
|
10057
|
+
} finally {
|
|
10058
|
+
this.checking = false;
|
|
10059
|
+
}
|
|
10060
|
+
}
|
|
10061
|
+
};
|
|
10062
|
+
|
|
10063
|
+
// src/agentbay-browser.ts
|
|
9516
10064
|
var AGENTBAY_BROWSERS_SUBDIR = "agentbay-browsers";
|
|
9517
10065
|
var DEFAULT_AGENTBAY_IMAGE_ID = "imgc-0aae4rgl8kvagdv1j";
|
|
9518
10066
|
var sdkPromise = null;
|
|
@@ -9589,6 +10137,24 @@ function validateAgentBayInput(input) {
|
|
|
9589
10137
|
throw new Error("TDBank login parameters are only supported by the local browser provider");
|
|
9590
10138
|
}
|
|
9591
10139
|
}
|
|
10140
|
+
function resolveReuseMode(value) {
|
|
10141
|
+
if (value === void 0) return "never";
|
|
10142
|
+
if (value === "never" || value === "prefer" || value === "require") return value;
|
|
10143
|
+
throw new Error('reuse must be "never", "prefer", or "require"');
|
|
10144
|
+
}
|
|
10145
|
+
function validateReusableAgentBayConfiguration(input, record2) {
|
|
10146
|
+
if (input.imageId !== void 0 && resolveAgentBayImageId(input.imageId) !== record2.imageId) {
|
|
10147
|
+
throw new Error(`Cannot change imageId while reusing AgentBay browser: ${record2.browserId}`);
|
|
10148
|
+
}
|
|
10149
|
+
if (input.persistent !== void 0 && input.persistent !== (record2.persistent === true)) {
|
|
10150
|
+
throw new Error(`Cannot change persistent while reusing AgentBay browser: ${record2.browserId}`);
|
|
10151
|
+
}
|
|
10152
|
+
if (input.userAgentMode !== void 0 && record2.userAgentMode !== void 0 && input.userAgentMode !== record2.userAgentMode) {
|
|
10153
|
+
throw new Error(
|
|
10154
|
+
`Cannot change userAgentMode while reusing AgentBay browser: ${record2.browserId}`
|
|
10155
|
+
);
|
|
10156
|
+
}
|
|
10157
|
+
}
|
|
9592
10158
|
function resolveAgentBayImageId(value) {
|
|
9593
10159
|
if (value === void 0) return DEFAULT_AGENTBAY_IMAGE_ID;
|
|
9594
10160
|
const imageId = typeof value === "string" ? value.trim() : "";
|
|
@@ -9647,11 +10213,14 @@ function listRecords2() {
|
|
|
9647
10213
|
}
|
|
9648
10214
|
return records;
|
|
9649
10215
|
}
|
|
9650
|
-
function
|
|
10216
|
+
function findRecordWithStatus(selector) {
|
|
9651
10217
|
const normalized = selector.trim().toLowerCase();
|
|
9652
10218
|
return listRecords2().find(
|
|
9653
10219
|
({ record: record2 }) => record2.browserId.toLowerCase() === normalized || record2.name.toLowerCase() === normalized
|
|
9654
|
-
)
|
|
10220
|
+
) ?? null;
|
|
10221
|
+
}
|
|
10222
|
+
function findRecord(selector) {
|
|
10223
|
+
return findRecordWithStatus(selector)?.record ?? null;
|
|
9655
10224
|
}
|
|
9656
10225
|
function hasAgentBayBrowser(selector) {
|
|
9657
10226
|
return findRecord(selector) !== null;
|
|
@@ -9723,13 +10292,9 @@ async function keepAgentBaySessionAlive(sessionId) {
|
|
|
9723
10292
|
const result = await session.keepAlive();
|
|
9724
10293
|
if (result.success === false) throw new Error(result.errorMessage || "AgentBay keepalive failed");
|
|
9725
10294
|
}
|
|
9726
|
-
async function
|
|
9727
|
-
const initialized = await session.browser.initializeAsync(
|
|
9728
|
-
options.userAgent ? { userAgent: options.userAgent } : {}
|
|
9729
|
-
);
|
|
9730
|
-
if (!initialized) throw new Error("AgentBay browser initialization failed");
|
|
10295
|
+
async function configureAgentBayBrowserSession(session, input, cookies, connectCdp = (url2) => CdpClient.connect(url2)) {
|
|
9731
10296
|
const endpoint = await session.browser.getEndpointUrl();
|
|
9732
|
-
const cdp = await
|
|
10297
|
+
const cdp = await connectCdp(endpoint);
|
|
9733
10298
|
try {
|
|
9734
10299
|
await cdp.sendCommand("Browser.getVersion", {}, 1e4);
|
|
9735
10300
|
const copiedCookieCount = await cdpImportCookiesWithClient(cdp, cookies);
|
|
@@ -9741,32 +10306,76 @@ async function initializeAgentBayBrowserSession(session, input, cookies, options
|
|
|
9741
10306
|
cdp.close();
|
|
9742
10307
|
}
|
|
9743
10308
|
}
|
|
10309
|
+
async function initializeAgentBayBrowserSession(session, input, cookies, options = {}) {
|
|
10310
|
+
const initialized = await session.browser.initializeAsync(
|
|
10311
|
+
options.userAgent ? { userAgent: options.userAgent } : {}
|
|
10312
|
+
);
|
|
10313
|
+
if (!initialized) throw new Error("AgentBay browser initialization failed");
|
|
10314
|
+
return configureAgentBayBrowserSession(session, input, cookies, options.connectCdp);
|
|
10315
|
+
}
|
|
10316
|
+
async function reuseAgentBayBrowserSession(session, input, cookies, options = {}) {
|
|
10317
|
+
return configureAgentBayBrowserSession(session, input, cookies, options.connectCdp);
|
|
10318
|
+
}
|
|
9744
10319
|
async function launchAgentBayBrowser(input, deps) {
|
|
9745
10320
|
const name = validateName(input.name);
|
|
9746
10321
|
const browserId = agentBayBrowserId(name);
|
|
9747
10322
|
validateAgentBayInput(input);
|
|
10323
|
+
const reuseMode = resolveReuseMode(input.reuse);
|
|
9748
10324
|
const imageId = resolveAgentBayImageId(input.imageId);
|
|
9749
10325
|
const persistent = input.persistent === true;
|
|
9750
10326
|
const userAgentMode = resolveUserAgentMode(input.userAgentMode);
|
|
9751
10327
|
const copyCookieDomains = input.copyCookieDomains === void 0 ? [] : normalizeCookieDomains(input.copyCookieDomains);
|
|
9752
10328
|
const apiKey = (deps.resolveApiKey ?? resolveAgentBayApiKey)();
|
|
10329
|
+
const lifecycle = resolveAgentBayLifecycleConfig();
|
|
9753
10330
|
const releaseLock = acquireLaunchLock(browserId);
|
|
9754
10331
|
const persistBrowserRecord = deps.persistBrowserRecord ?? ((record2) => writeRecord(keyedConfigFile(AGENTBAY_BROWSERS_SUBDIR, record2.browserId), record2));
|
|
10332
|
+
const findBrowserRecord = deps.findBrowserRecord ?? findRecordWithStatus;
|
|
9755
10333
|
let session = null;
|
|
9756
10334
|
let context;
|
|
9757
10335
|
let hostPid = 0;
|
|
9758
10336
|
let recordWritten = false;
|
|
9759
10337
|
try {
|
|
9760
|
-
|
|
10338
|
+
const existing = findBrowserRecord(browserId);
|
|
10339
|
+
if (existing) {
|
|
10340
|
+
if (reuseMode === "never") {
|
|
10341
|
+
throw new Error(`AgentBay browser already exists: ${browserId}`);
|
|
10342
|
+
}
|
|
10343
|
+
if (!existing.running) {
|
|
10344
|
+
throw new Error(`AgentBay browser is not running and cannot be reused: ${browserId}`);
|
|
10345
|
+
}
|
|
10346
|
+
validateReusableAgentBayConfiguration(input, existing.record);
|
|
10347
|
+
const cookies2 = copyCookieDomains.length > 0 ? await deps.exportCookies(copyCookieDomains) : [];
|
|
10348
|
+
const agentBay2 = await (deps.createAgentBayClient ?? createClient)(apiKey);
|
|
10349
|
+
const existingSession = await requireSessionFromClient(agentBay2, existing.record.sessionId);
|
|
10350
|
+
const copiedCookieCount2 = await reuseAgentBayBrowserSession(existingSession, input, cookies2, {
|
|
10351
|
+
connectCdp: deps.connectCdp
|
|
10352
|
+
});
|
|
10353
|
+
const record3 = {
|
|
10354
|
+
...existing.record,
|
|
10355
|
+
...copyCookieDomains.length > 0 ? { copiedCookies: { domains: copyCookieDomains, count: copiedCookieCount2 } } : {}
|
|
10356
|
+
};
|
|
10357
|
+
if (copyCookieDomains.length > 0) persistBrowserRecord(record3);
|
|
10358
|
+
deps.log(`AgentBay browser reused: ${browserId}, host=${record3.pid}`);
|
|
10359
|
+
return {
|
|
10360
|
+
...browserSummary2(record3, true),
|
|
10361
|
+
created: false,
|
|
10362
|
+
reused: true
|
|
10363
|
+
};
|
|
10364
|
+
}
|
|
10365
|
+
if (reuseMode === "require") {
|
|
10366
|
+
throw new Error(`AgentBay browser is not running and reuse=require: ${browserId}`);
|
|
10367
|
+
}
|
|
9761
10368
|
const [cookies, userAgent] = await Promise.all([
|
|
9762
10369
|
copyCookieDomains.length > 0 ? deps.exportCookies(copyCookieDomains) : Promise.resolve([]),
|
|
9763
10370
|
userAgentMode === "desktop" ? deps.getBrowserUserAgent().then(normalizeDesktopUserAgent) : Promise.resolve(void 0)
|
|
9764
10371
|
]);
|
|
9765
10372
|
const agentBay = await (deps.createAgentBayClient ?? createClient)(apiKey);
|
|
9766
10373
|
context = persistent ? await resolvePersistentContext(agentBay, browserId) : void 0;
|
|
10374
|
+
const { LifecyclePolicy } = await loadSdk();
|
|
9767
10375
|
const created = await agentBay.create({
|
|
9768
10376
|
imageId,
|
|
9769
10377
|
framework: "mearl",
|
|
10378
|
+
lifecyclePolicy: new LifecyclePolicy(lifecycle),
|
|
9770
10379
|
...context ? {
|
|
9771
10380
|
browserContext: {
|
|
9772
10381
|
contextId: context.id,
|
|
@@ -9780,7 +10389,8 @@ async function launchAgentBayBrowser(input, deps) {
|
|
|
9780
10389
|
}
|
|
9781
10390
|
session = created.session;
|
|
9782
10391
|
const copiedCookieCount = await initializeAgentBayBrowserSession(session, input, cookies, {
|
|
9783
|
-
userAgent
|
|
10392
|
+
userAgent,
|
|
10393
|
+
connectCdp: deps.connectCdp
|
|
9784
10394
|
});
|
|
9785
10395
|
const host = spawn4(
|
|
9786
10396
|
process.execPath,
|
|
@@ -9792,7 +10402,9 @@ async function launchAgentBayBrowser(input, deps) {
|
|
|
9792
10402
|
"--browser-id",
|
|
9793
10403
|
browserId,
|
|
9794
10404
|
"--browser-name",
|
|
9795
|
-
name
|
|
10405
|
+
name,
|
|
10406
|
+
"--agentbay-idle-release-minutes",
|
|
10407
|
+
String(lifecycle.idleReleaseTimeout)
|
|
9796
10408
|
],
|
|
9797
10409
|
{
|
|
9798
10410
|
detached: true,
|
|
@@ -9813,6 +10425,7 @@ async function launchAgentBayBrowser(input, deps) {
|
|
|
9813
10425
|
name,
|
|
9814
10426
|
sessionId: session.sessionId,
|
|
9815
10427
|
imageId,
|
|
10428
|
+
userAgentMode,
|
|
9816
10429
|
persistent,
|
|
9817
10430
|
...context ? { contextId: context.id, contextName: context.name } : {},
|
|
9818
10431
|
createdAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
@@ -9822,7 +10435,11 @@ async function launchAgentBayBrowser(input, deps) {
|
|
|
9822
10435
|
recordWritten = true;
|
|
9823
10436
|
await waitForBrowserRegistration(browserId, hostPid);
|
|
9824
10437
|
deps.log(`AgentBay browser launched: ${browserId}, host=${hostPid}`);
|
|
9825
|
-
return
|
|
10438
|
+
return {
|
|
10439
|
+
...browserSummary2(record2, true),
|
|
10440
|
+
created: true,
|
|
10441
|
+
reused: false
|
|
10442
|
+
};
|
|
9826
10443
|
} catch (error48) {
|
|
9827
10444
|
if (hostPid) await stopProcess(hostPid, `--browser-id ${browserId}`);
|
|
9828
10445
|
if (recordWritten) {
|
|
@@ -9846,6 +10463,7 @@ async function launchAgentBayBrowser(input, deps) {
|
|
|
9846
10463
|
name,
|
|
9847
10464
|
sessionId: session.sessionId,
|
|
9848
10465
|
imageId,
|
|
10466
|
+
userAgentMode,
|
|
9849
10467
|
persistent,
|
|
9850
10468
|
...context ? { contextId: context.id, contextName: context.name } : {},
|
|
9851
10469
|
createdAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
@@ -9940,31 +10558,38 @@ async function releaseAgentBayBrowserResources(record2, agentBay, options = {})
|
|
|
9940
10558
|
};
|
|
9941
10559
|
}
|
|
9942
10560
|
async function closeAgentBayBrowser(selector, options = {}) {
|
|
9943
|
-
|
|
10561
|
+
let record2 = findRecord(selector);
|
|
9944
10562
|
if (!record2) throw new Error(`AgentBay browser not found: ${selector}`);
|
|
9945
10563
|
const agentBay = await createClient(resolveAgentBayApiKey());
|
|
9946
|
-
const
|
|
9947
|
-
|
|
9948
|
-
|
|
9949
|
-
|
|
9950
|
-
|
|
9951
|
-
|
|
9952
|
-
|
|
9953
|
-
|
|
9954
|
-
|
|
9955
|
-
|
|
9956
|
-
|
|
9957
|
-
|
|
9958
|
-
|
|
9959
|
-
|
|
10564
|
+
const releaseLock = acquireLaunchLock(record2.browserId);
|
|
10565
|
+
try {
|
|
10566
|
+
record2 = findRecord(record2.browserId);
|
|
10567
|
+
if (!record2) throw new Error(`AgentBay browser not found: ${selector}`);
|
|
10568
|
+
const released = await releaseAgentBayBrowserResources(record2, agentBay, {
|
|
10569
|
+
...options,
|
|
10570
|
+
persistRecord: (updatedRecord) => writeRecord(
|
|
10571
|
+
keyedConfigFile(AGENTBAY_BROWSERS_SUBDIR, updatedRecord.browserId),
|
|
10572
|
+
updatedRecord
|
|
10573
|
+
)
|
|
10574
|
+
});
|
|
10575
|
+
const closingOwnHost = record2.pid === process.pid;
|
|
10576
|
+
if (!closingOwnHost) await stopProcess(record2.pid, `--browser-id ${record2.browserId}`);
|
|
10577
|
+
removeRecord(keyedConfigFile(AGENTBAY_BROWSERS_SUBDIR, record2.browserId));
|
|
10578
|
+
removeRecord(keyedConfigFile("browsers", record2.browserId));
|
|
10579
|
+
if (closingOwnHost) {
|
|
10580
|
+
const timer = setTimeout(() => process.kill(process.pid, "SIGTERM"), 250);
|
|
10581
|
+
timer.unref();
|
|
10582
|
+
}
|
|
10583
|
+
return {
|
|
10584
|
+
browserId: record2.browserId,
|
|
10585
|
+
provider: "agentbay",
|
|
10586
|
+
closed: true,
|
|
10587
|
+
...released,
|
|
10588
|
+
...record2.persistent === true ? { profileDeleted: options.deleteProfile === true } : { profileDeleted: true }
|
|
10589
|
+
};
|
|
10590
|
+
} finally {
|
|
10591
|
+
releaseLock();
|
|
9960
10592
|
}
|
|
9961
|
-
return {
|
|
9962
|
-
browserId: record2.browserId,
|
|
9963
|
-
provider: "agentbay",
|
|
9964
|
-
closed: true,
|
|
9965
|
-
...released,
|
|
9966
|
-
...record2.persistent === true ? { profileDeleted: options.deleteProfile === true } : { profileDeleted: true }
|
|
9967
|
-
};
|
|
9968
10593
|
}
|
|
9969
10594
|
|
|
9970
10595
|
// src/browser-registry.ts
|
|
@@ -10163,7 +10788,7 @@ import os2 from "node:os";
|
|
|
10163
10788
|
import { randomUUID as randomUUID4 } from "node:crypto";
|
|
10164
10789
|
|
|
10165
10790
|
// src/version.ts
|
|
10166
|
-
var NATIVE_HOST_VERSION = true ? "2.
|
|
10791
|
+
var NATIVE_HOST_VERSION = true ? "2.7.1" : "unknown";
|
|
10167
10792
|
|
|
10168
10793
|
// src/analytics.ts
|
|
10169
10794
|
var MEASUREMENT_ID = "G-W84LJDZ6PH";
|
|
@@ -30153,6 +30778,7 @@ var AgentSkillManager = class {
|
|
|
30153
30778
|
"--agent",
|
|
30154
30779
|
providerId,
|
|
30155
30780
|
scope === "global" ? "--global" : "--project",
|
|
30781
|
+
"--full-depth",
|
|
30156
30782
|
"--yes"
|
|
30157
30783
|
];
|
|
30158
30784
|
const output = await this.run(executable, args, cwd);
|
|
@@ -30229,7 +30855,7 @@ function createExtensionRequestMessage(id, action, data, trustedInternal = false
|
|
|
30229
30855
|
id,
|
|
30230
30856
|
action,
|
|
30231
30857
|
data,
|
|
30232
|
-
controlSource,
|
|
30858
|
+
controlSource: toControlSourceWireValue(controlSource),
|
|
30233
30859
|
...trustedInternal ? { trustedInternal: true } : {}
|
|
30234
30860
|
};
|
|
30235
30861
|
}
|
|
@@ -30654,9 +31280,22 @@ function positivePort(value, fallback) {
|
|
|
30654
31280
|
}
|
|
30655
31281
|
return port;
|
|
30656
31282
|
}
|
|
31283
|
+
function agentBayIdleReleaseMinutes(value) {
|
|
31284
|
+
if (value === void 0) return void 0;
|
|
31285
|
+
const minutes = Number(value);
|
|
31286
|
+
if (!Number.isInteger(minutes) || minutes < MIN_AGENTBAY_IDLE_RELEASE_MINUTES) {
|
|
31287
|
+
throw new Error(
|
|
31288
|
+
`Invalid --agentbay-idle-release-minutes: ${value}. Expected an integer of at least ${MIN_AGENTBAY_IDLE_RELEASE_MINUTES}.`
|
|
31289
|
+
);
|
|
31290
|
+
}
|
|
31291
|
+
return minutes;
|
|
31292
|
+
}
|
|
30657
31293
|
var CDP_PORT = positivePort(cliOption("--cdp-port"), 9222);
|
|
30658
31294
|
var CDP_USER_DATA_DIR = cliOption("--cdp-user-data-dir");
|
|
30659
31295
|
var AGENTBAY_SESSION_ID = cliOption("--agentbay-session-id");
|
|
31296
|
+
var AGENTBAY_IDLE_RELEASE_MINUTES = agentBayIdleReleaseMinutes(
|
|
31297
|
+
cliOption("--agentbay-idle-release-minutes")
|
|
31298
|
+
);
|
|
30660
31299
|
var CDP_BROWSER_ID = cliOption("--browser-id") ?? "cdp";
|
|
30661
31300
|
var CDP_BROWSER_NAME = cliOption("--browser-name") ?? "CDP";
|
|
30662
31301
|
if (cliArg === "--init" || cliArg === "--uninstall") {
|
|
@@ -30684,6 +31323,8 @@ Options:
|
|
|
30684
31323
|
--cdp-port <port> CDP port for daemon mode (default 9222)
|
|
30685
31324
|
--cdp-user-data-dir <dir> Read DevToolsActivePort from this Chrome profile
|
|
30686
31325
|
--agentbay-session-id <id> Connect to an AgentBay browser session over CDP
|
|
31326
|
+
--agentbay-idle-release-minutes <minutes>
|
|
31327
|
+
Close an idle AgentBay browser (minimum 3 minutes)
|
|
30687
31328
|
--browser-id <id> Registry id for this CDP browser
|
|
30688
31329
|
--browser-name <name> Human-readable registry name
|
|
30689
31330
|
--version, -v Show version
|
|
@@ -30818,6 +31459,7 @@ var socketServer = null;
|
|
|
30818
31459
|
var legacyServer = null;
|
|
30819
31460
|
var inventoryExitTimer = null;
|
|
30820
31461
|
var cdpRegistrationTimer = null;
|
|
31462
|
+
var agentBaySessionWatchdog = null;
|
|
30821
31463
|
function scheduleInventoryExit() {
|
|
30822
31464
|
if (inventoryExitTimer) clearTimeout(inventoryExitTimer);
|
|
30823
31465
|
inventoryExitTimer = setTimeout(() => cleanupAndExit(0), INVENTORY_IDLE_EXIT_MS);
|
|
@@ -31085,7 +31727,7 @@ function toTimeoutMs(action, data) {
|
|
|
31085
31727
|
}
|
|
31086
31728
|
return DEFAULT_TIMEOUT_MS;
|
|
31087
31729
|
}
|
|
31088
|
-
async function
|
|
31730
|
+
async function handleSocketRequest(socket, line) {
|
|
31089
31731
|
let request;
|
|
31090
31732
|
try {
|
|
31091
31733
|
request = JSON.parse(line);
|
|
@@ -31096,7 +31738,7 @@ async function handleSocketLine(socket, line) {
|
|
|
31096
31738
|
return;
|
|
31097
31739
|
}
|
|
31098
31740
|
const { id = "", action, data = {}, version: clientVersion } = request;
|
|
31099
|
-
const controlSource = request.controlSource
|
|
31741
|
+
const controlSource = normalizeControlSource(request.controlSource);
|
|
31100
31742
|
if (action === "start_cloud_connector") {
|
|
31101
31743
|
const serverUrl = data?.serverUrl;
|
|
31102
31744
|
if (!serverUrl || typeof serverUrl !== "string") {
|
|
@@ -31386,6 +32028,33 @@ async function handleSocketLine(socket, line) {
|
|
|
31386
32028
|
socket.write(JSON.stringify({ id, success: false, error: message }) + "\n");
|
|
31387
32029
|
}
|
|
31388
32030
|
}
|
|
32031
|
+
async function handleSocketLine(socket, line) {
|
|
32032
|
+
const watchdog = agentBaySessionWatchdog;
|
|
32033
|
+
if (!watchdog) {
|
|
32034
|
+
await handleSocketRequest(socket, line);
|
|
32035
|
+
return;
|
|
32036
|
+
}
|
|
32037
|
+
if (!watchdog.beginActivity()) {
|
|
32038
|
+
let id = "";
|
|
32039
|
+
try {
|
|
32040
|
+
id = JSON.parse(line)?.id ?? "";
|
|
32041
|
+
} catch {
|
|
32042
|
+
}
|
|
32043
|
+
socket.write(
|
|
32044
|
+
JSON.stringify({
|
|
32045
|
+
id,
|
|
32046
|
+
success: false,
|
|
32047
|
+
error: "AgentBay browser is closing after reaching its idle timeout"
|
|
32048
|
+
}) + "\n"
|
|
32049
|
+
);
|
|
32050
|
+
return;
|
|
32051
|
+
}
|
|
32052
|
+
try {
|
|
32053
|
+
await handleSocketRequest(socket, line);
|
|
32054
|
+
} finally {
|
|
32055
|
+
watchdog.endActivity();
|
|
32056
|
+
}
|
|
32057
|
+
}
|
|
31389
32058
|
async function handleViaCdp(action, data, _timeoutMs, controlSource) {
|
|
31390
32059
|
if (action === "set_rule" || action === "get_rules") {
|
|
31391
32060
|
throw new Error(
|
|
@@ -31544,6 +32213,8 @@ function cleanupAndExit(code) {
|
|
|
31544
32213
|
clearInterval(cdpRegistrationTimer);
|
|
31545
32214
|
cdpRegistrationTimer = null;
|
|
31546
32215
|
}
|
|
32216
|
+
agentBaySessionWatchdog?.stop();
|
|
32217
|
+
agentBaySessionWatchdog = null;
|
|
31547
32218
|
cdpSession?.close();
|
|
31548
32219
|
agentChatManager?.dispose();
|
|
31549
32220
|
agentChatManager = null;
|
|
@@ -31630,15 +32301,17 @@ async function main() {
|
|
|
31630
32301
|
await monitorDaemonBrowser();
|
|
31631
32302
|
}
|
|
31632
32303
|
if (AGENTBAY_SESSION_ID) {
|
|
31633
|
-
const
|
|
31634
|
-
|
|
31635
|
-
|
|
31636
|
-
|
|
31637
|
-
|
|
31638
|
-
);
|
|
31639
|
-
}
|
|
31640
|
-
|
|
31641
|
-
|
|
32304
|
+
const idleReleaseMinutes = AGENTBAY_IDLE_RELEASE_MINUTES ?? resolveAgentBayLifecycleConfig().idleReleaseTimeout;
|
|
32305
|
+
agentBaySessionWatchdog = new AgentBaySessionWatchdog({
|
|
32306
|
+
idleTimeoutMs: idleReleaseMinutes * 6e4,
|
|
32307
|
+
keepAlive: () => keepAgentBaySessionAlive(AGENTBAY_SESSION_ID),
|
|
32308
|
+
close: async () => {
|
|
32309
|
+
await closeAgentBayBrowser(CDP_BROWSER_ID);
|
|
32310
|
+
},
|
|
32311
|
+
log
|
|
32312
|
+
});
|
|
32313
|
+
agentBaySessionWatchdog.start();
|
|
32314
|
+
log(`AgentBay idle close enabled for ${AGENTBAY_SESSION_ID}: ${idleReleaseMinutes} minutes`);
|
|
31642
32315
|
}
|
|
31643
32316
|
process.on("SIGINT", () => cleanupAndExit(0));
|
|
31644
32317
|
process.on("SIGTERM", () => cleanupAndExit(0));
|
|
@@ -1,8 +1,9 @@
|
|
|
1
|
+
import { type ControlSource, type ControlSourceWireValue } from '@mearl/browser-core';
|
|
1
2
|
export declare function isInternalExtensionAction(action: unknown): boolean;
|
|
2
|
-
export declare function createExtensionRequestMessage(id: string, action: string, data: any, trustedInternal?: boolean, controlSource?:
|
|
3
|
+
export declare function createExtensionRequestMessage(id: string, action: string, data: any, trustedInternal?: boolean, controlSource?: ControlSource): {
|
|
3
4
|
id: string;
|
|
4
5
|
action: string;
|
|
5
6
|
data: any;
|
|
6
|
-
controlSource:
|
|
7
|
+
controlSource: ControlSourceWireValue;
|
|
7
8
|
trustedInternal?: true;
|
|
8
9
|
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@mearl/native-host",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.7.1",
|
|
4
4
|
"description": "Native Messaging Host for Mearl — bridges Chrome Extension and local socket server",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/install.js",
|
|
@@ -37,9 +37,9 @@
|
|
|
37
37
|
"@types/ws": "^8.18.1",
|
|
38
38
|
"esbuild": "^0.25.2",
|
|
39
39
|
"typescript": "^5.8.3",
|
|
40
|
-
"@mearl/browser-core": "2.
|
|
41
|
-
"@mearl/
|
|
42
|
-
"@mearl/
|
|
40
|
+
"@mearl/browser-core": "2.7.1",
|
|
41
|
+
"@mearl/daemon-core": "2.7.1",
|
|
42
|
+
"@mearl/setup": "2.7.1"
|
|
43
43
|
},
|
|
44
44
|
"scripts": {
|
|
45
45
|
"build": "rm -rf dist/ && node build.mjs",
|