@mearl/native-host 2.0.0
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 +99 -0
- package/dist/agent/codex-provider.d.ts +45 -0
- package/dist/agent/event-transport.d.ts +3 -0
- package/dist/agent/instructions.d.ts +1 -0
- package/dist/agent/manager.d.ts +44 -0
- package/dist/agent/process-env.d.ts +6 -0
- package/dist/agent/provider.d.ts +53 -0
- package/dist/agent/qoder-provider.d.ts +49 -0
- package/dist/agent/skill-manager.d.ts +28 -0
- package/dist/agent/workspace-directory.d.ts +4 -0
- package/dist/analytics.d.ts +1 -0
- package/dist/browser-inventory.d.ts +33 -0
- package/dist/browser-registry.d.ts +78 -0
- package/dist/cdp-backend/action-registry.d.ts +35 -0
- package/dist/cdp-backend/browser-handlers.d.ts +7 -0
- package/dist/cdp-backend/buffers.d.ts +46 -0
- package/dist/cdp-backend/cdp-session.d.ts +60 -0
- package/dist/cdp-backend/cdp-utils.d.ts +66 -0
- package/dist/cdp-backend/client.d.ts +47 -0
- package/dist/cdp-backend/composite-handlers.d.ts +19 -0
- package/dist/cdp-backend/controlled-indicator.d.ts +17 -0
- package/dist/cdp-backend/cookie-transfer.d.ts +25 -0
- package/dist/cdp-backend/discover.d.ts +28 -0
- package/dist/cdp-backend/domain-manager.d.ts +4 -0
- package/dist/cdp-backend/handlers.d.ts +29 -0
- package/dist/cdp-backend/http-handlers.d.ts +7 -0
- package/dist/cdp-backend/interaction-handlers.d.ts +16 -0
- package/dist/cdp-backend/mock-handlers.d.ts +6 -0
- package/dist/cdp-backend/navigation-observer.d.ts +7 -0
- package/dist/cdp-backend/page-observer.d.ts +5 -0
- package/dist/cdp-backend/recording-handlers.d.ts +9 -0
- package/dist/cdp-backend/request-handlers.d.ts +7 -0
- package/dist/cdp-backend/snapshot-handlers.d.ts +6 -0
- package/dist/cdp-backend/tab-handlers.d.ts +7 -0
- package/dist/cdp-backend/tdbank-handlers.d.ts +13 -0
- package/dist/cdp-backend/timezone-handlers.d.ts +3 -0
- package/dist/cloud-connector.d.ts +56 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +26945 -0
- package/dist/install.d.ts +36 -0
- package/dist/install.js +220 -0
- package/dist/managed-browser.d.ts +85 -0
- package/package.json +44 -0
package/README.md
ADDED
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
# @mearl/native-host
|
|
2
|
+
|
|
3
|
+
Mearl 的 Native Messaging Host —— 桥接 Chrome 扩展与本地 socket 服务。
|
|
4
|
+
|
|
5
|
+
浏览器扩展通过 `chrome.runtime.connectNative()` 拉起本进程;进程对内通过 CDP(Chrome DevTools Protocol)与浏览器交互,对外通过 **Unix Socket** 暴露给 `@mearl/client` 等本地调用方,并可作为云端链路的本地落点。
|
|
6
|
+
|
|
7
|
+
## 安装与注册
|
|
8
|
+
|
|
9
|
+
注册 Native Messaging Host manifest(让浏览器能够拉起本进程):
|
|
10
|
+
|
|
11
|
+
```bash
|
|
12
|
+
# 同时注册 Skills 与 MCP 两种集成模式(默认)
|
|
13
|
+
mearl-native-host --init
|
|
14
|
+
|
|
15
|
+
# 仅注册某一种模式
|
|
16
|
+
mearl-native-host --init --mode skills
|
|
17
|
+
mearl-native-host --init --mode mcp
|
|
18
|
+
|
|
19
|
+
# 指定自定义扩展 ID(开发版)
|
|
20
|
+
mearl-native-host --init --extension-id <your-extension-id>
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
> Skills 与 MCP 两个 host 运行同一份二进制、共享同一个 Unix socket,因此可同时启用。
|
|
24
|
+
|
|
25
|
+
也可在代码中调用安装入口:
|
|
26
|
+
|
|
27
|
+
```typescript
|
|
28
|
+
import { installNativeHost } from '@mearl/native-host';
|
|
29
|
+
|
|
30
|
+
await installNativeHost(); // 默认注册两种模式
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
运行时入口(被浏览器拉起、或手动启动)通过子路径导出:
|
|
34
|
+
|
|
35
|
+
```typescript
|
|
36
|
+
import '@mearl/native-host/runtime';
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
## CLI 选项
|
|
40
|
+
|
|
41
|
+
```bash
|
|
42
|
+
mearl-native-host [options]
|
|
43
|
+
|
|
44
|
+
--init [--mode <skills|mcp|both>] [--extension-id <id>] 注册 host manifest
|
|
45
|
+
--daemon 独立 CDP daemon
|
|
46
|
+
--cdp-port <port> 指定 CDP 端口
|
|
47
|
+
--cdp-user-data-dir <dir> 指定 Chrome Profile
|
|
48
|
+
--browser-id <id> 指定浏览器注册 ID
|
|
49
|
+
--browser-name <name> 指定浏览器显示名称
|
|
50
|
+
--version, -v 查看版本
|
|
51
|
+
--help, -h 查看帮助
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
## 架构
|
|
55
|
+
|
|
56
|
+
```
|
|
57
|
+
Chrome Extension
|
|
58
|
+
↓ (Native Messaging)
|
|
59
|
+
@mearl/native-host ← CDP → Chrome
|
|
60
|
+
↓ (Unix Socket)
|
|
61
|
+
@mearl/client / @mearl/cloud-connector
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
`browser_launch` 会为每个托管 Chrome 启动一个带独立 CDP 配置的 native-host 子进程,使多个 headless/Profile/TDBank 账号实例可以同时被现有浏览器注册表寻址。设计细节见 [托管浏览器与 TDBank 多账号设计](../../docs/managed-browsers.md)。
|
|
65
|
+
|
|
66
|
+
## 侧边栏 Agent Chat
|
|
67
|
+
|
|
68
|
+
扩展侧边栏通过 Native Messaging 调用 native-host,再按用户选择启动持久的 Codex app-server 或 Qoder ACP 进程。两种 Provider 都支持流式消息、思考与工具进度、中断和审批,并复用各自 CLI 已保存的本地登录态。
|
|
69
|
+
|
|
70
|
+
需要先安装并登录支持 `app-server` 的 Codex CLI。若事件中的模型错误提示需要更新版本,应先升级本机 Codex CLI。native-host 会依次从安装时的 Node 可执行文件目录、常见全局安装目录和 `PATH` 查找 `codex`;自定义路径可通过环境变量指定:
|
|
71
|
+
|
|
72
|
+
```bash
|
|
73
|
+
MEARL_CODEX_PATH=/absolute/path/to/codex
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
Qoder 使用官方 ACP 接口,需要先安装 Qoder CLI 并完成登录:
|
|
77
|
+
|
|
78
|
+
```bash
|
|
79
|
+
# macOS / Linux
|
|
80
|
+
curl -fsSL https://qoder.com/install | bash
|
|
81
|
+
qodercli
|
|
82
|
+
# 进入交互界面后输入 /login
|
|
83
|
+
|
|
84
|
+
# 可选:指定非标准安装位置
|
|
85
|
+
MEARL_QODER_PATH=/absolute/path/to/qodercli
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
侧边栏中未选择本地目录时,Codex 对话使用只读沙箱且不允许提权,Qoder 对话通过首条提示词约束为只读模式;选择后才使用该目录作为可写工作区。`MEARL_AGENT_CWD` 仅用于 Codex 只读对话的默认浏览目录,不会开启写权限;Qoder 未选目录时从用户主目录开始浏览。Agent 接入由统一 Provider 接口承载,后续可新增 Claude 等实现而无需修改扩展与 native-host 之间的 Chat 协议。只安装 ChatGPT 等桌面聊天客户端不能替代 Provider CLI;侧边栏会在 CLI 缺失时展示安装与登录指引。
|
|
89
|
+
|
|
90
|
+
## 构建
|
|
91
|
+
|
|
92
|
+
```bash
|
|
93
|
+
pnpm build # esbuild 打包到 dist/(内联 browser-core / daemon-core)
|
|
94
|
+
pnpm typecheck # 仅类型检查
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
## License
|
|
98
|
+
|
|
99
|
+
ISC
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import type { AgentChatImageInput, AgentChatProviderDescriptor, AgentInstalledSkill } from '@mearl/browser-core';
|
|
2
|
+
import type { AgentProvider, AgentProviderEvent, AgentProviderListSessionsOptions, AgentProviderListSessionsResult, AgentProviderResumeResult, AgentProviderSessionOptions } from './provider.js';
|
|
3
|
+
export declare function codexThreadUrl(providerSessionId: string): string;
|
|
4
|
+
export declare function externalOpenCommand(url: string, platform?: NodeJS.Platform): {
|
|
5
|
+
command: string;
|
|
6
|
+
args: string[];
|
|
7
|
+
};
|
|
8
|
+
export declare class CodexAgentProvider implements AgentProvider {
|
|
9
|
+
private readonly log;
|
|
10
|
+
private readonly clientVersion;
|
|
11
|
+
readonly id = "codex";
|
|
12
|
+
private process;
|
|
13
|
+
private initialized;
|
|
14
|
+
private startPromise;
|
|
15
|
+
private nextRequestId;
|
|
16
|
+
private pendingRequests;
|
|
17
|
+
private listeners;
|
|
18
|
+
private knownThreadIds;
|
|
19
|
+
constructor(log: (...args: any[]) => void, clientVersion: string);
|
|
20
|
+
getDescriptor(): Promise<AgentChatProviderDescriptor>;
|
|
21
|
+
onEvent(listener: (event: AgentProviderEvent) => void): () => void;
|
|
22
|
+
listSessions(options: AgentProviderListSessionsOptions): Promise<AgentProviderListSessionsResult>;
|
|
23
|
+
listSkills(cwd?: string): Promise<AgentInstalledSkill[]>;
|
|
24
|
+
startSession(options: AgentProviderSessionOptions): Promise<string>;
|
|
25
|
+
resumeSession(providerSessionId: string, options: AgentProviderSessionOptions): Promise<AgentProviderResumeResult>;
|
|
26
|
+
sendMessage(providerSessionId: string, message: string, images?: AgentChatImageInput[]): Promise<{
|
|
27
|
+
turnId: string;
|
|
28
|
+
}>;
|
|
29
|
+
interrupt(providerSessionId: string, turnId: string): Promise<void>;
|
|
30
|
+
closeSession(providerSessionId: string): Promise<void>;
|
|
31
|
+
openSession(providerSessionId: string): Promise<void>;
|
|
32
|
+
respondToRequest(requestId: string | number, result: unknown): void;
|
|
33
|
+
dispose(): void;
|
|
34
|
+
private ensureStarted;
|
|
35
|
+
private start;
|
|
36
|
+
private handleLine;
|
|
37
|
+
private request;
|
|
38
|
+
private write;
|
|
39
|
+
private handleExit;
|
|
40
|
+
private rejectPending;
|
|
41
|
+
private emit;
|
|
42
|
+
private extractThreadId;
|
|
43
|
+
private probeExecutable;
|
|
44
|
+
private resolveExecutable;
|
|
45
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function createBrowserDeveloperInstructions(tabId: number): string;
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import type { AgentChatEvent, AgentChatCloseOptions, AgentChatEventsOptions, AgentChatEventsResult, AgentChatInterruptOptions, AgentChatListSessionsOptions, AgentChatListSessionsResult, AgentChatOpenOptions, AgentChatRefreshResult, AgentChatRespondOptions, AgentChatSendOptions, AgentChatSession, AgentChatStartOptions, AgentChatStatus, AgentInstalledSkill, AgentSkillReadResult } from '@mearl/browser-core';
|
|
2
|
+
import type { AgentProvider } from './provider.js';
|
|
3
|
+
export declare class AgentChatManager {
|
|
4
|
+
private readonly emit;
|
|
5
|
+
private readonly providers;
|
|
6
|
+
private readonly sessions;
|
|
7
|
+
private readonly sessionByProviderThread;
|
|
8
|
+
private readonly pendingRequests;
|
|
9
|
+
private readonly completedTurns;
|
|
10
|
+
private readonly recentEvents;
|
|
11
|
+
private readonly recentEventBytes;
|
|
12
|
+
private readonly unsubscribe;
|
|
13
|
+
constructor(providers: AgentProvider[], emit: (event: AgentChatEvent) => void);
|
|
14
|
+
getStatus(): Promise<AgentChatStatus>;
|
|
15
|
+
listSkills(options: {
|
|
16
|
+
providerId?: unknown;
|
|
17
|
+
cwd?: unknown;
|
|
18
|
+
}): Promise<AgentInstalledSkill[]>;
|
|
19
|
+
readSkill(options: {
|
|
20
|
+
providerId?: unknown;
|
|
21
|
+
path?: unknown;
|
|
22
|
+
cwd?: unknown;
|
|
23
|
+
}): Promise<AgentSkillReadResult>;
|
|
24
|
+
listSessions(options: AgentChatListSessionsOptions): Promise<AgentChatListSessionsResult>;
|
|
25
|
+
startSession(options: AgentChatStartOptions): Promise<AgentChatSession>;
|
|
26
|
+
sendMessage(options: AgentChatSendOptions): Promise<{
|
|
27
|
+
turnId: string;
|
|
28
|
+
}>;
|
|
29
|
+
interrupt(options: AgentChatInterruptOptions): Promise<void>;
|
|
30
|
+
openSession(options: AgentChatOpenOptions): Promise<void>;
|
|
31
|
+
closeSession(options: AgentChatCloseOptions): Promise<void>;
|
|
32
|
+
getEvents(options: AgentChatEventsOptions): AgentChatEventsResult;
|
|
33
|
+
refreshSession(options: AgentChatEventsOptions): Promise<AgentChatRefreshResult>;
|
|
34
|
+
respondToRequest(options: AgentChatRespondOptions): void;
|
|
35
|
+
dispose(): void;
|
|
36
|
+
private handleProviderEvent;
|
|
37
|
+
private emitEvent;
|
|
38
|
+
private recordEvent;
|
|
39
|
+
private getProvider;
|
|
40
|
+
private getSession;
|
|
41
|
+
private providerThreadKey;
|
|
42
|
+
private providerRequestKey;
|
|
43
|
+
private sessionTurnKey;
|
|
44
|
+
}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
export interface AgentSpawnCommand {
|
|
2
|
+
command: string;
|
|
3
|
+
args: string[];
|
|
4
|
+
}
|
|
5
|
+
export declare function agentSpawnCommand(executable: string, args: string[], platform?: NodeJS.Platform, commandInterpreter?: string | undefined): AgentSpawnCommand;
|
|
6
|
+
export declare function createAgentChildEnv(sourceEnv?: NodeJS.ProcessEnv, nodeExecutable?: string): NodeJS.ProcessEnv;
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import type { AgentChatEventKind, AgentChatImageInput, AgentChatProviderDescriptor, AgentInstalledSkill } from '@mearl/browser-core';
|
|
2
|
+
export interface AgentProviderEvent {
|
|
3
|
+
providerSessionId?: string;
|
|
4
|
+
kind: AgentChatEventKind;
|
|
5
|
+
method: string;
|
|
6
|
+
params: unknown;
|
|
7
|
+
timestamp?: number;
|
|
8
|
+
requestId?: string | number;
|
|
9
|
+
}
|
|
10
|
+
export interface AgentProviderSessionOptions {
|
|
11
|
+
cwd?: string;
|
|
12
|
+
developerInstructions?: string;
|
|
13
|
+
ephemeral?: boolean;
|
|
14
|
+
}
|
|
15
|
+
export interface AgentProviderSessionSummary {
|
|
16
|
+
providerSessionId: string;
|
|
17
|
+
title?: string;
|
|
18
|
+
cwd?: string;
|
|
19
|
+
updatedAt?: number;
|
|
20
|
+
ephemeral?: boolean;
|
|
21
|
+
}
|
|
22
|
+
export interface AgentProviderListSessionsOptions {
|
|
23
|
+
cwd?: string;
|
|
24
|
+
cursor?: string;
|
|
25
|
+
limit: number;
|
|
26
|
+
}
|
|
27
|
+
export interface AgentProviderListSessionsResult {
|
|
28
|
+
sessions: AgentProviderSessionSummary[];
|
|
29
|
+
nextCursor?: string;
|
|
30
|
+
}
|
|
31
|
+
export interface AgentProviderResumeResult {
|
|
32
|
+
providerSessionId: string;
|
|
33
|
+
history?: AgentProviderEvent[];
|
|
34
|
+
historyComplete?: boolean;
|
|
35
|
+
}
|
|
36
|
+
export interface AgentProvider {
|
|
37
|
+
readonly id: string;
|
|
38
|
+
getDescriptor(): Promise<AgentChatProviderDescriptor>;
|
|
39
|
+
onEvent(listener: (event: AgentProviderEvent) => void): () => void;
|
|
40
|
+
listSessions?(options: AgentProviderListSessionsOptions): Promise<AgentProviderListSessionsResult>;
|
|
41
|
+
listSkills?(cwd?: string): Promise<AgentInstalledSkill[]>;
|
|
42
|
+
startSession(options: AgentProviderSessionOptions): Promise<string>;
|
|
43
|
+
resumeSession(providerSessionId: string, options: AgentProviderSessionOptions): Promise<AgentProviderResumeResult>;
|
|
44
|
+
validateMessageInput?(images: AgentChatImageInput[]): Promise<void>;
|
|
45
|
+
sendMessage(providerSessionId: string, message: string, images?: AgentChatImageInput[]): Promise<{
|
|
46
|
+
turnId: string;
|
|
47
|
+
}>;
|
|
48
|
+
interrupt(providerSessionId: string, turnId: string): Promise<void>;
|
|
49
|
+
closeSession(providerSessionId: string): Promise<void>;
|
|
50
|
+
openSession?(providerSessionId: string): Promise<void>;
|
|
51
|
+
respondToRequest(requestId: string | number, result: unknown): void;
|
|
52
|
+
dispose(): void;
|
|
53
|
+
}
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import type { AgentChatImageInput, AgentChatProviderDescriptor, AgentInstalledSkill } from '@mearl/browser-core';
|
|
2
|
+
import type { AgentProvider, AgentProviderEvent, AgentProviderListSessionsOptions, AgentProviderListSessionsResult, AgentProviderResumeResult, AgentProviderSessionOptions } from './provider.js';
|
|
3
|
+
export declare function parseQoderSkillsList(output: string, cwd: string, home?: string): AgentInstalledSkill[];
|
|
4
|
+
export declare function qoderInstallCommand(platform?: NodeJS.Platform): string;
|
|
5
|
+
export declare class QoderAgentProvider implements AgentProvider {
|
|
6
|
+
private readonly log;
|
|
7
|
+
private readonly clientVersion;
|
|
8
|
+
readonly id = "qoder";
|
|
9
|
+
private process;
|
|
10
|
+
private connection;
|
|
11
|
+
private context;
|
|
12
|
+
private initializeResponse;
|
|
13
|
+
private startPromise;
|
|
14
|
+
private listeners;
|
|
15
|
+
private sessions;
|
|
16
|
+
private pendingPermissions;
|
|
17
|
+
private historyCaptures;
|
|
18
|
+
private nextTurnId;
|
|
19
|
+
constructor(log: (...args: any[]) => void, clientVersion: string);
|
|
20
|
+
getDescriptor(): Promise<AgentChatProviderDescriptor>;
|
|
21
|
+
onEvent(listener: (event: AgentProviderEvent) => void): () => void;
|
|
22
|
+
listSessions(options: AgentProviderListSessionsOptions): Promise<AgentProviderListSessionsResult>;
|
|
23
|
+
listSkills(cwd?: string): Promise<AgentInstalledSkill[]>;
|
|
24
|
+
startSession(options: AgentProviderSessionOptions): Promise<string>;
|
|
25
|
+
resumeSession(providerSessionId: string, options: AgentProviderSessionOptions): Promise<AgentProviderResumeResult>;
|
|
26
|
+
validateMessageInput(images: AgentChatImageInput[]): Promise<void>;
|
|
27
|
+
sendMessage(providerSessionId: string, message: string, images?: AgentChatImageInput[]): Promise<{
|
|
28
|
+
turnId: string;
|
|
29
|
+
}>;
|
|
30
|
+
interrupt(providerSessionId: string, _turnId: string): Promise<void>;
|
|
31
|
+
closeSession(providerSessionId: string): Promise<void>;
|
|
32
|
+
respondToRequest(requestId: string | number, result: unknown): void;
|
|
33
|
+
dispose(): void;
|
|
34
|
+
private ensureStarted;
|
|
35
|
+
private start;
|
|
36
|
+
private runPrompt;
|
|
37
|
+
private handleSessionUpdate;
|
|
38
|
+
private emitToolUpdate;
|
|
39
|
+
private handlePermissionRequest;
|
|
40
|
+
private cancelPermissions;
|
|
41
|
+
private handleExit;
|
|
42
|
+
private getContext;
|
|
43
|
+
private assertImageInputSupported;
|
|
44
|
+
private withTimeout;
|
|
45
|
+
private probeExecutable;
|
|
46
|
+
private runSkillsList;
|
|
47
|
+
private resolveExecutable;
|
|
48
|
+
private emit;
|
|
49
|
+
}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import type { AgentSkillCheckUpdatesResult, AgentSkillCliStatus, AgentSkillInstallOptions, AgentSkillMutationOptions, AgentSkillMutationResult, AgentSkillScope, AgentSkillUpdate } from '@mearl/browser-core';
|
|
2
|
+
interface SkillCliOutput {
|
|
3
|
+
stdout: string;
|
|
4
|
+
stderr: string;
|
|
5
|
+
}
|
|
6
|
+
export type SkillCliRunner = (executable: string, args: string[], cwd?: string) => Promise<SkillCliOutput>;
|
|
7
|
+
export interface AgentSkillManagerOptions {
|
|
8
|
+
resolveExecutable?: () => string | null;
|
|
9
|
+
run?: SkillCliRunner;
|
|
10
|
+
}
|
|
11
|
+
export declare function resolveAliSkillsExecutable(): string | null;
|
|
12
|
+
export declare const runSkillCli: SkillCliRunner;
|
|
13
|
+
export declare function parseSkillUpdates(stdout: string, scope: AgentSkillScope): AgentSkillUpdate[];
|
|
14
|
+
export declare class AgentSkillManager {
|
|
15
|
+
private readonly executableResolver;
|
|
16
|
+
private readonly run;
|
|
17
|
+
constructor(options?: AgentSkillManagerOptions);
|
|
18
|
+
getStatus(): Promise<AgentSkillCliStatus>;
|
|
19
|
+
install(data: AgentSkillInstallOptions): Promise<AgentSkillMutationResult>;
|
|
20
|
+
checkUpdates(data: {
|
|
21
|
+
providerId?: unknown;
|
|
22
|
+
cwd?: unknown;
|
|
23
|
+
}): Promise<AgentSkillCheckUpdatesResult>;
|
|
24
|
+
update(data: AgentSkillMutationOptions): Promise<AgentSkillMutationResult>;
|
|
25
|
+
uninstall(data: AgentSkillMutationOptions): Promise<AgentSkillMutationResult>;
|
|
26
|
+
private mutationContext;
|
|
27
|
+
}
|
|
28
|
+
export {};
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
export type DirectoryPickerRunner = (command: string, args: string[]) => Promise<string | null>;
|
|
2
|
+
export declare const runDirectoryPickerCommand: DirectoryPickerRunner;
|
|
3
|
+
export declare function pickWorkspaceDirectory(platform?: NodeJS.Platform, run?: DirectoryPickerRunner): Promise<string | null>;
|
|
4
|
+
export declare function resolveWorkspaceDirectory(directory: unknown): string;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function trackAction(action: string, params?: Record<string, string | number | boolean>): void;
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { type BrowserRecord } from './browser-registry.js';
|
|
2
|
+
import { type ManagedBrowserSummary } from './managed-browser.js';
|
|
3
|
+
export type BrowserStatus = 'connected' | 'running_disconnected' | 'stopped';
|
|
4
|
+
interface BrowserInventoryBase {
|
|
5
|
+
browserId: string;
|
|
6
|
+
name: string;
|
|
7
|
+
status: BrowserStatus;
|
|
8
|
+
extensionVersion?: string;
|
|
9
|
+
nativeHostVersion?: string;
|
|
10
|
+
transport?: 'extension' | 'cdp';
|
|
11
|
+
}
|
|
12
|
+
export interface RegularBrowserInventoryItem extends BrowserInventoryBase {
|
|
13
|
+
type: 'regular';
|
|
14
|
+
status: 'connected';
|
|
15
|
+
}
|
|
16
|
+
export interface ManagedBrowserInventoryItem extends BrowserInventoryBase {
|
|
17
|
+
type: 'managed';
|
|
18
|
+
headless: boolean;
|
|
19
|
+
persistent: boolean;
|
|
20
|
+
cdpPort: number;
|
|
21
|
+
createdAt: string;
|
|
22
|
+
account?: ManagedBrowserSummary['account'];
|
|
23
|
+
copiedCookies?: ManagedBrowserSummary['copiedCookies'];
|
|
24
|
+
}
|
|
25
|
+
export type BrowserInventoryItem = RegularBrowserInventoryItem | ManagedBrowserInventoryItem;
|
|
26
|
+
export interface BrowserInventoryResult {
|
|
27
|
+
count: number;
|
|
28
|
+
connectedCount: number;
|
|
29
|
+
browsers: BrowserInventoryItem[];
|
|
30
|
+
}
|
|
31
|
+
export declare function mergeBrowserInventory(connected: BrowserRecord[], managed: ManagedBrowserSummary[]): BrowserInventoryResult;
|
|
32
|
+
export declare function listBrowserInventory(): BrowserInventoryResult;
|
|
33
|
+
export {};
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Browser registry — advertises this native-host's connection in a shared,
|
|
3
|
+
* on-disk registry so clients can discover and address multiple browsers at
|
|
4
|
+
* once (multiple Chrome profiles / Chromium-based browsers, each with the
|
|
5
|
+
* extension, each launching its own native-host).
|
|
6
|
+
*
|
|
7
|
+
* Why a registry instead of the old single well-known socket
|
|
8
|
+
* ----------------------------------------------------------
|
|
9
|
+
* Historically every native-host fought over one fixed socket; the last one to
|
|
10
|
+
* start kicked the others off, so at most one browser was reachable and the two
|
|
11
|
+
* flapped on every reconnect. Here each native-host listens on its own unique
|
|
12
|
+
* per-process socket and publishes a record; the client reads the registry and
|
|
13
|
+
* connects to the socket of whichever browser it wants (default: most-recently
|
|
14
|
+
* focused). No contention, N browsers reachable simultaneously.
|
|
15
|
+
*
|
|
16
|
+
* Ownership
|
|
17
|
+
* ---------
|
|
18
|
+
* The record is keyed by the browser's stable installation id, so a browser
|
|
19
|
+
* that reconnects (extension reload → new native-host pid → new socket) simply
|
|
20
|
+
* overwrites its own record. To avoid a briefly-overlapping predecessor wiping
|
|
21
|
+
* the successor's record during a reload, every mutation first checks that the
|
|
22
|
+
* on-disk record still belongs to this process (or is stale) before writing.
|
|
23
|
+
*/
|
|
24
|
+
import { type DaemonRecord } from '@mearl/daemon-core';
|
|
25
|
+
export interface BrowserRecord extends DaemonRecord {
|
|
26
|
+
/** native-host pid — used for liveness pruning. */
|
|
27
|
+
pid: number;
|
|
28
|
+
/** Stable per-installation id reported by the extension (or 'cdp' for daemon mode). */
|
|
29
|
+
browserId: string;
|
|
30
|
+
/** Absolute path of the per-process socket this native-host listens on. */
|
|
31
|
+
socketPath: string;
|
|
32
|
+
/** Human-readable browser name, e.g. "Chrome", "Edge". */
|
|
33
|
+
browserName: string;
|
|
34
|
+
extensionVersion?: string;
|
|
35
|
+
nativeHostVersion: string;
|
|
36
|
+
transport: 'extension' | 'cdp';
|
|
37
|
+
/** Epoch ms when this browser window last gained focus; drives default target selection. */
|
|
38
|
+
lastFocusedAt?: number;
|
|
39
|
+
startedAt: string;
|
|
40
|
+
/** Epoch ms of the last record write; latest-wins tiebreak. */
|
|
41
|
+
updatedAt: number;
|
|
42
|
+
}
|
|
43
|
+
export declare function initBrowserRegistry(deps: {
|
|
44
|
+
log: (...args: any[]) => void;
|
|
45
|
+
}): void;
|
|
46
|
+
/**
|
|
47
|
+
* Publish (or refresh) this native-host's registry record. Called once the
|
|
48
|
+
* browser identity is known — from the extension handshake, or immediately with
|
|
49
|
+
* a synthetic identity in CDP daemon mode.
|
|
50
|
+
*/
|
|
51
|
+
export declare function registerBrowser(info: {
|
|
52
|
+
browserId: string;
|
|
53
|
+
socketPath: string;
|
|
54
|
+
browserName: string;
|
|
55
|
+
nativeHostVersion: string;
|
|
56
|
+
transport: 'extension' | 'cdp';
|
|
57
|
+
extensionVersion?: string;
|
|
58
|
+
/** Initial focus rank. Managed headless browsers use 0 so they do not steal the default target. */
|
|
59
|
+
lastFocusedAt?: number;
|
|
60
|
+
}): void;
|
|
61
|
+
/**
|
|
62
|
+
* Mark this browser as most-recently focused, so it becomes the default target.
|
|
63
|
+
* Focus can change in rapid bursts (fast window switching), so writes are
|
|
64
|
+
* throttled: the first change in an interval writes immediately, further changes
|
|
65
|
+
* schedule a single trailing write rather than one write each.
|
|
66
|
+
*/
|
|
67
|
+
export declare function touchFocus(): void;
|
|
68
|
+
/** Remove our record on shutdown — but only if it is still ours. */
|
|
69
|
+
export declare function unregisterBrowser(): void;
|
|
70
|
+
/**
|
|
71
|
+
* Reap registry records left behind by native-hosts that exited without
|
|
72
|
+
* cleanup (crashes). Live records are kept; a dead record is kept until it is
|
|
73
|
+
* older than STALE_RECORD_TTL_MS so a quickly-reconnecting browser can still
|
|
74
|
+
* inherit its own last-focus time. Meant to run once at startup.
|
|
75
|
+
*/
|
|
76
|
+
export declare function pruneDeadBrowsers(): void;
|
|
77
|
+
/** Live browser connections, ordered by the same focus rank used for default routing. */
|
|
78
|
+
export declare function listRegisteredBrowsers(): BrowserRecord[];
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* CDP action registry.
|
|
3
|
+
*
|
|
4
|
+
* Each handler sub-module registers its actions here. The registry replaces
|
|
5
|
+
* the giant switch/case in handleViaCdp with a data-driven lookup.
|
|
6
|
+
*
|
|
7
|
+
* Two session levels:
|
|
8
|
+
* - `browser`: handler receives CdpSession (for tab management, tdbank, etc.)
|
|
9
|
+
* - `page`: handler receives PageSession (for most page-level actions)
|
|
10
|
+
*/
|
|
11
|
+
import type { CdpSession, PageSession } from './cdp-session.js';
|
|
12
|
+
export interface BrowserAction {
|
|
13
|
+
name: string;
|
|
14
|
+
session: 'browser';
|
|
15
|
+
handler: (session: CdpSession, data: any) => Promise<any>;
|
|
16
|
+
}
|
|
17
|
+
export interface PageAction {
|
|
18
|
+
name: string;
|
|
19
|
+
session: 'page';
|
|
20
|
+
handler: (session: PageSession, data: any) => Promise<any>;
|
|
21
|
+
}
|
|
22
|
+
export type CdpAction = BrowserAction | PageAction;
|
|
23
|
+
/** Register one or more CDP actions. Called by each handler sub-module. */
|
|
24
|
+
export declare function registerActions(actions: CdpAction[]): void;
|
|
25
|
+
/** Look up a registered action by name. Returns undefined if not found. */
|
|
26
|
+
export declare function getAction(name: string): CdpAction | undefined;
|
|
27
|
+
/** Get all registered action names (useful for diagnostics). */
|
|
28
|
+
export declare function getRegisteredActionNames(): string[];
|
|
29
|
+
/**
|
|
30
|
+
* Dispatch an action by name.
|
|
31
|
+
*
|
|
32
|
+
* Resolves the appropriate session level and calls the handler.
|
|
33
|
+
* Throws if the action is unknown or CDP is not available.
|
|
34
|
+
*/
|
|
35
|
+
export declare function dispatchCdpAction(cdpSession: CdpSession, action: string, data: any): Promise<any>;
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* CDP browser-level handlers: screenshot, selected element, user info.
|
|
3
|
+
*/
|
|
4
|
+
import type { PageSession } from './cdp-session.js';
|
|
5
|
+
export declare function cdpPageScreenshot(session: PageSession, data: any): Promise<any>;
|
|
6
|
+
export declare function cdpPageSelectedElement(session: PageSession, _data: any): Promise<any>;
|
|
7
|
+
export declare function cdpGetUserInfo(session: PageSession, data: any): Promise<any>;
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* In-memory circular buffers for CDP network requests, console logs, and
|
|
3
|
+
* RUM/tracking events. A single CdpSession subscribes to CDP events and
|
|
4
|
+
* fills these buffers; callers read from them without CDP interaction.
|
|
5
|
+
*/
|
|
6
|
+
import type { MtopRequest, RumEvent, ConsoleLog } from '@mearl/browser-core';
|
|
7
|
+
import type { CdpClient, CdpChildSession } from './client.js';
|
|
8
|
+
interface PartialRequest {
|
|
9
|
+
id: number;
|
|
10
|
+
requestId: string;
|
|
11
|
+
timestamp: string;
|
|
12
|
+
timestampMs: number;
|
|
13
|
+
method: string;
|
|
14
|
+
url: string;
|
|
15
|
+
apiName: string;
|
|
16
|
+
headers: Array<{
|
|
17
|
+
name: string;
|
|
18
|
+
value: string;
|
|
19
|
+
}>;
|
|
20
|
+
postData: any;
|
|
21
|
+
queryString: Array<{
|
|
22
|
+
name: string;
|
|
23
|
+
value: string;
|
|
24
|
+
}>;
|
|
25
|
+
status: number;
|
|
26
|
+
statusText: string;
|
|
27
|
+
responseHeaders: Array<{
|
|
28
|
+
name: string;
|
|
29
|
+
value: string;
|
|
30
|
+
}>;
|
|
31
|
+
responseBody: any;
|
|
32
|
+
isProxy: boolean;
|
|
33
|
+
proxyRequestUrl: string | null;
|
|
34
|
+
proxyData: any;
|
|
35
|
+
startTs: number;
|
|
36
|
+
}
|
|
37
|
+
export interface CdpBuffers {
|
|
38
|
+
requests: MtopRequest[];
|
|
39
|
+
logs: ConsoleLog[];
|
|
40
|
+
events: RumEvent[];
|
|
41
|
+
pending: Map<string, PartialRequest>;
|
|
42
|
+
requestCounter: number;
|
|
43
|
+
}
|
|
44
|
+
export declare function createBuffers(): CdpBuffers;
|
|
45
|
+
export declare function setupBufferListeners(client: CdpClient | CdpChildSession, buffers: CdpBuffers): void;
|
|
46
|
+
export {};
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* CDP session manager — maintains browser-level and page-level CDP connections.
|
|
3
|
+
*
|
|
4
|
+
* - Browser client: connected to the browser's top-level WebSocket endpoint.
|
|
5
|
+
* Used for Target.* commands (list/open/close tabs).
|
|
6
|
+
* - Page sessions: per-target connections used for page interaction, screenshots,
|
|
7
|
+
* and buffered network/log/event collection.
|
|
8
|
+
*/
|
|
9
|
+
import { CdpClient, CdpChildSession } from './client.js';
|
|
10
|
+
import { type CdpBuffers } from './buffers.js';
|
|
11
|
+
export interface PageSession {
|
|
12
|
+
client: CdpClient | CdpChildSession;
|
|
13
|
+
buffers: CdpBuffers;
|
|
14
|
+
targetId: string;
|
|
15
|
+
url: string;
|
|
16
|
+
}
|
|
17
|
+
export declare class CdpSession {
|
|
18
|
+
readonly port: number;
|
|
19
|
+
readonly userDataDir?: string;
|
|
20
|
+
private browserClient;
|
|
21
|
+
private pageSessions;
|
|
22
|
+
/** Virtual tabId counter → CDP targetId */
|
|
23
|
+
private tabIdMap;
|
|
24
|
+
private nextTabId;
|
|
25
|
+
constructor(options?: number | {
|
|
26
|
+
port?: number;
|
|
27
|
+
userDataDir?: string;
|
|
28
|
+
});
|
|
29
|
+
getBrowserClient(): Promise<CdpClient>;
|
|
30
|
+
/**
|
|
31
|
+
* Get (or create) a page session.
|
|
32
|
+
*
|
|
33
|
+
* @param virtualTabId Numeric tabId returned by `tab_open` (optional).
|
|
34
|
+
* If omitted, connects to the first available page target.
|
|
35
|
+
*/
|
|
36
|
+
getPageSession(virtualTabId?: number): Promise<PageSession>;
|
|
37
|
+
/** Register a newly opened target as a virtual tabId and return the numeric ID. */
|
|
38
|
+
registerTarget(targetId: string): Promise<number>;
|
|
39
|
+
/** Return the existing virtual tabId for a target, registering it when first discovered. */
|
|
40
|
+
ensureTargetRegistered(targetId: string): Promise<number>;
|
|
41
|
+
private createPageSession;
|
|
42
|
+
private findTargetById;
|
|
43
|
+
private findDefaultPageTarget;
|
|
44
|
+
/** Effective port: from DevToolsActivePort file if present, else this.port */
|
|
45
|
+
private effectivePort;
|
|
46
|
+
/**
|
|
47
|
+
* Resolve the browser-level WebSocket URL.
|
|
48
|
+
* Priority: DevToolsActivePort file → HTTP /json/version
|
|
49
|
+
*/
|
|
50
|
+
private resolveBrowserWsUrl;
|
|
51
|
+
/**
|
|
52
|
+
* Build a page-level WebSocket URL from a target ID.
|
|
53
|
+
* Used when HTTP /json is unavailable.
|
|
54
|
+
*/
|
|
55
|
+
private pageWsUrl;
|
|
56
|
+
private findTargetByIdViaCdp;
|
|
57
|
+
private findDefaultPageTargetViaCdp;
|
|
58
|
+
private fetchVersionInfo;
|
|
59
|
+
close(): void;
|
|
60
|
+
}
|