@flrande/browserctl 0.1.0-dev.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.
Files changed (91) hide show
  1. package/LICENSE +21 -0
  2. package/README-CN.md +66 -0
  3. package/README.md +66 -0
  4. package/apps/browserctl/src/commands/a11y-snapshot.ts +20 -0
  5. package/apps/browserctl/src/commands/act.ts +20 -0
  6. package/apps/browserctl/src/commands/common.test.ts +87 -0
  7. package/apps/browserctl/src/commands/common.ts +191 -0
  8. package/apps/browserctl/src/commands/console-list.ts +20 -0
  9. package/apps/browserctl/src/commands/cookie-clear.ts +18 -0
  10. package/apps/browserctl/src/commands/cookie-get.ts +18 -0
  11. package/apps/browserctl/src/commands/cookie-set.ts +22 -0
  12. package/apps/browserctl/src/commands/dialog-arm.ts +20 -0
  13. package/apps/browserctl/src/commands/dom-query-all.ts +18 -0
  14. package/apps/browserctl/src/commands/dom-query.ts +18 -0
  15. package/apps/browserctl/src/commands/download-trigger.ts +22 -0
  16. package/apps/browserctl/src/commands/download-wait.test.ts +67 -0
  17. package/apps/browserctl/src/commands/download-wait.ts +27 -0
  18. package/apps/browserctl/src/commands/element-screenshot.ts +20 -0
  19. package/apps/browserctl/src/commands/frame-list.ts +16 -0
  20. package/apps/browserctl/src/commands/frame-snapshot.ts +18 -0
  21. package/apps/browserctl/src/commands/network-wait-for.ts +100 -0
  22. package/apps/browserctl/src/commands/profile-list.ts +16 -0
  23. package/apps/browserctl/src/commands/profile-use.ts +18 -0
  24. package/apps/browserctl/src/commands/response-body.ts +24 -0
  25. package/apps/browserctl/src/commands/screenshot.ts +16 -0
  26. package/apps/browserctl/src/commands/snapshot.ts +16 -0
  27. package/apps/browserctl/src/commands/status.ts +10 -0
  28. package/apps/browserctl/src/commands/storage-get.ts +20 -0
  29. package/apps/browserctl/src/commands/storage-set.ts +22 -0
  30. package/apps/browserctl/src/commands/tab-close.ts +20 -0
  31. package/apps/browserctl/src/commands/tab-focus.ts +20 -0
  32. package/apps/browserctl/src/commands/tab-open.ts +19 -0
  33. package/apps/browserctl/src/commands/tabs.ts +13 -0
  34. package/apps/browserctl/src/commands/upload-arm.ts +26 -0
  35. package/apps/browserctl/src/daemon-client.test.ts +253 -0
  36. package/apps/browserctl/src/daemon-client.ts +632 -0
  37. package/apps/browserctl/src/e2e.test.ts +99 -0
  38. package/apps/browserctl/src/main.test.ts +215 -0
  39. package/apps/browserctl/src/main.ts +372 -0
  40. package/apps/browserctl/src/smoke.test.ts +16 -0
  41. package/apps/browserctl/src/smoke.ts +5 -0
  42. package/apps/browserd/src/bootstrap.ts +432 -0
  43. package/apps/browserd/src/chrome-relay-extension-bridge.test.ts +275 -0
  44. package/apps/browserd/src/chrome-relay-extension-bridge.ts +506 -0
  45. package/apps/browserd/src/container.ts +1531 -0
  46. package/apps/browserd/src/main.test.ts +864 -0
  47. package/apps/browserd/src/main.ts +7 -0
  48. package/bin/browserctl.cjs +21 -0
  49. package/bin/browserd.cjs +21 -0
  50. package/extensions/chrome-relay/README-CN.md +38 -0
  51. package/extensions/chrome-relay/README.md +38 -0
  52. package/extensions/chrome-relay/background.js +1687 -0
  53. package/extensions/chrome-relay/manifest.json +15 -0
  54. package/extensions/chrome-relay/popup.html +369 -0
  55. package/extensions/chrome-relay/popup.js +972 -0
  56. package/package.json +51 -0
  57. package/packages/core/src/bootstrap.test.ts +10 -0
  58. package/packages/core/src/driver-registry.test.ts +45 -0
  59. package/packages/core/src/driver-registry.ts +22 -0
  60. package/packages/core/src/driver.ts +47 -0
  61. package/packages/core/src/index.ts +5 -0
  62. package/packages/core/src/ref-cache.test.ts +61 -0
  63. package/packages/core/src/ref-cache.ts +28 -0
  64. package/packages/core/src/session-store.test.ts +49 -0
  65. package/packages/core/src/session-store.ts +33 -0
  66. package/packages/core/src/types.ts +9 -0
  67. package/packages/driver-chrome-relay/src/chrome-relay-driver.test.ts +634 -0
  68. package/packages/driver-chrome-relay/src/chrome-relay-driver.ts +2206 -0
  69. package/packages/driver-chrome-relay/src/chrome-relay-extension-runtime.test.ts +264 -0
  70. package/packages/driver-chrome-relay/src/chrome-relay-extension-runtime.ts +521 -0
  71. package/packages/driver-chrome-relay/src/index.ts +26 -0
  72. package/packages/driver-managed/src/index.ts +22 -0
  73. package/packages/driver-managed/src/managed-driver.test.ts +59 -0
  74. package/packages/driver-managed/src/managed-driver.ts +125 -0
  75. package/packages/driver-managed/src/managed-local-driver.test.ts +506 -0
  76. package/packages/driver-managed/src/managed-local-driver.ts +2021 -0
  77. package/packages/driver-remote-cdp/src/index.ts +19 -0
  78. package/packages/driver-remote-cdp/src/remote-cdp-driver.test.ts +617 -0
  79. package/packages/driver-remote-cdp/src/remote-cdp-driver.ts +2042 -0
  80. package/packages/protocol/src/envelope.test.ts +25 -0
  81. package/packages/protocol/src/envelope.ts +31 -0
  82. package/packages/protocol/src/errors.test.ts +17 -0
  83. package/packages/protocol/src/errors.ts +11 -0
  84. package/packages/protocol/src/index.ts +3 -0
  85. package/packages/protocol/src/tools.ts +3 -0
  86. package/packages/transport-mcp-stdio/src/index.ts +3 -0
  87. package/packages/transport-mcp-stdio/src/sdk-server.ts +139 -0
  88. package/packages/transport-mcp-stdio/src/server.test.ts +281 -0
  89. package/packages/transport-mcp-stdio/src/server.ts +183 -0
  90. package/packages/transport-mcp-stdio/src/tool-map.ts +67 -0
  91. package/scripts/smoke.ps1 +127 -0
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 browserctl contributors
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README-CN.md ADDED
@@ -0,0 +1,66 @@
1
+ # browserctl
2
+
3
+ [English](README.md) | 简体中文
4
+
5
+ 最快路径:通过 relay 扩展直接控制你已打开的 Edge/Chrome。
6
+
7
+ ## 扩展模式最简上手(推荐)
8
+
9
+ 前置条件:
10
+
11
+ - Node.js 22+
12
+ - npm 或 pnpm
13
+ - PowerShell 7 (`pwsh`)
14
+ - Edge 或 Chrome
15
+
16
+ 1. 安装已发布包:
17
+
18
+ ```powershell
19
+ npm install --global @flrande/browserctl
20
+ # 或:pnpm add --global @flrande/browserctl
21
+ ```
22
+
23
+ 2. 用扩展 relay 模式启动 daemon:
24
+
25
+ ```powershell
26
+ $env:BROWSERD_DEFAULT_DRIVER = "chrome-relay"
27
+ $env:BROWSERD_CHROME_RELAY_MODE = "extension"
28
+ $env:BROWSERD_CHROME_RELAY_URL = "http://127.0.0.1:9223"
29
+ $env:BROWSERD_CHROME_RELAY_EXTENSION_TOKEN = "relay-secret"
30
+ browserctl daemon-stop --json
31
+ browserctl daemon-start --json
32
+ ```
33
+
34
+ 3. 加载扩展:
35
+
36
+ 1. 打开 `edge://extensions` 或 `chrome://extensions`。
37
+ 2. 开启开发者模式。
38
+ 3. 选择“加载已解压的扩展程序”,目录为 `extensions/chrome-relay`。
39
+ 4. 在扩展弹窗中设置:
40
+ - `Bridge URL`: `ws://127.0.0.1:9223/bridge`
41
+ - `Token`: `relay-secret`
42
+ 5. 点击 `Save`,再点击 `Reconnect`。
43
+
44
+ 4. 连接验证与首个命令:
45
+
46
+ ```powershell
47
+ browserctl status --json --session relay --profile chrome-relay
48
+ browserctl tab-open --json --session relay --profile chrome-relay https://example.com
49
+ ```
50
+
51
+ ## 30 秒排查
52
+
53
+ ```powershell
54
+ browserctl daemon-status --json
55
+ Invoke-WebRequest -UseBasicParsing -Uri "http://127.0.0.1:9223/browserctl/relay/status" -TimeoutSec 3
56
+ ```
57
+
58
+ 若 `connected` 为 `false`,重新打开扩展弹窗并点击 `Reconnect`。
59
+
60
+ ## 完整文档
61
+
62
+ - [Full User Guide (English)](docs/user-guide.md)
63
+ - [完整使用文档(中文)](docs/user-guide-cn.md)
64
+ - [Chrome Relay Extension README (English)](extensions/chrome-relay/README.md)
65
+ - [Chrome Relay 扩展文档(中文)](extensions/chrome-relay/README-CN.md)
66
+
package/README.md ADDED
@@ -0,0 +1,66 @@
1
+ # browserctl
2
+
3
+ English | [简体中文](README-CN.md)
4
+
5
+ Fastest path: control your current Edge/Chrome through the relay extension.
6
+
7
+ ## Extension Quick Start (Recommended)
8
+
9
+ Prerequisites:
10
+
11
+ - Node.js 22+
12
+ - npm or pnpm
13
+ - PowerShell 7 (`pwsh`)
14
+ - Edge or Chrome
15
+
16
+ 1. Install the released package:
17
+
18
+ ```powershell
19
+ npm install --global @flrande/browserctl
20
+ # or: pnpm add --global @flrande/browserctl
21
+ ```
22
+
23
+ 2. Start daemon in extension relay mode:
24
+
25
+ ```powershell
26
+ $env:BROWSERD_DEFAULT_DRIVER = "chrome-relay"
27
+ $env:BROWSERD_CHROME_RELAY_MODE = "extension"
28
+ $env:BROWSERD_CHROME_RELAY_URL = "http://127.0.0.1:9223"
29
+ $env:BROWSERD_CHROME_RELAY_EXTENSION_TOKEN = "relay-secret"
30
+ browserctl daemon-stop --json
31
+ browserctl daemon-start --json
32
+ ```
33
+
34
+ 3. Load extension:
35
+
36
+ 1. Open `edge://extensions` or `chrome://extensions`.
37
+ 2. Enable Developer mode.
38
+ 3. Load unpacked extension from `extensions/chrome-relay`.
39
+ 4. In extension popup, set:
40
+ - `Bridge URL`: `ws://127.0.0.1:9223/bridge`
41
+ - `Token`: `relay-secret`
42
+ 5. Click `Save` then `Reconnect`.
43
+
44
+ 4. Verify and run first command:
45
+
46
+ ```powershell
47
+ browserctl status --json --session relay --profile chrome-relay
48
+ browserctl tab-open --json --session relay --profile chrome-relay https://example.com
49
+ ```
50
+
51
+ ## 30-Second Troubleshooting
52
+
53
+ ```powershell
54
+ browserctl daemon-status --json
55
+ Invoke-WebRequest -UseBasicParsing -Uri "http://127.0.0.1:9223/browserctl/relay/status" -TimeoutSec 3
56
+ ```
57
+
58
+ If `connected` is `false`, reopen extension popup and click `Reconnect`.
59
+
60
+ ## Full Docs
61
+
62
+ - [Full User Guide (English)](docs/user-guide.md)
63
+ - [完整使用文档(中文)](docs/user-guide-cn.md)
64
+ - [Chrome Relay Extension README (English)](extensions/chrome-relay/README.md)
65
+ - [Chrome Relay 扩展文档(中文)](extensions/chrome-relay/README-CN.md)
66
+
@@ -0,0 +1,20 @@
1
+ import { callDaemonTool } from "../daemon-client";
2
+ import { buildToolArguments, parseCommandContext, requirePositionalArg } from "./common";
3
+
4
+ export type A11ySnapshotCommandResult = Record<string, unknown>;
5
+
6
+ export async function runA11ySnapshotCommand(args: string[]): Promise<A11ySnapshotCommandResult> {
7
+ const context = parseCommandContext(args);
8
+ const targetId = requirePositionalArg(context, 0, "targetId");
9
+ const selector = context.positional[1];
10
+
11
+ return await callDaemonTool<A11ySnapshotCommandResult>(
12
+ "browser.a11y.snapshot",
13
+ buildToolArguments(context, {
14
+ targetId,
15
+ ...(typeof selector === "string" && selector.trim().length > 0
16
+ ? { selector: selector.trim() }
17
+ : {})
18
+ })
19
+ );
20
+ }
@@ -0,0 +1,20 @@
1
+ import { callDaemonTool } from "../daemon-client";
2
+ import { buildToolArguments, parseCommandContext, requirePositionalArg } from "./common";
3
+
4
+ export type ActCommandResult = Record<string, unknown>;
5
+
6
+ export async function runActCommand(args: string[]): Promise<ActCommandResult> {
7
+ const context = parseCommandContext(args);
8
+ const actionType = requirePositionalArg(context, 0, "actionType");
9
+ const targetId = requirePositionalArg(context, 1, "targetId");
10
+
11
+ return await callDaemonTool<ActCommandResult>(
12
+ "browser.act",
13
+ buildToolArguments(context, {
14
+ targetId,
15
+ action: {
16
+ type: actionType
17
+ }
18
+ })
19
+ );
20
+ }
@@ -0,0 +1,87 @@
1
+ import { afterEach, describe, expect, it } from "vitest";
2
+
3
+ import {
4
+ DAEMON_STARTUP_ARGUMENT,
5
+ buildToolArguments,
6
+ parseCommandContext
7
+ } from "./common";
8
+
9
+ const ORIGINAL_AUTH_TOKEN = process.env.BROWSERCTL_AUTH_TOKEN;
10
+ const ORIGINAL_BROWSER = process.env.BROWSERCTL_BROWSER;
11
+
12
+ afterEach(() => {
13
+ if (ORIGINAL_AUTH_TOKEN === undefined) {
14
+ delete process.env.BROWSERCTL_AUTH_TOKEN;
15
+ } else {
16
+ process.env.BROWSERCTL_AUTH_TOKEN = ORIGINAL_AUTH_TOKEN;
17
+ }
18
+
19
+ if (ORIGINAL_BROWSER === undefined) {
20
+ delete process.env.BROWSERCTL_BROWSER;
21
+ return;
22
+ }
23
+
24
+ process.env.BROWSERCTL_BROWSER = ORIGINAL_BROWSER;
25
+ });
26
+
27
+ describe("parseCommandContext", () => {
28
+ it("uses BROWSERCTL_AUTH_TOKEN by default", () => {
29
+ process.env.BROWSERCTL_AUTH_TOKEN = "env-token";
30
+
31
+ const context = parseCommandContext(["target:1"]);
32
+
33
+ expect(context.authToken).toBe("env-token");
34
+ expect(context.positional).toEqual(["target:1"]);
35
+ });
36
+
37
+ it("allows --token to override the environment token", () => {
38
+ process.env.BROWSERCTL_AUTH_TOKEN = "env-token";
39
+
40
+ const context = parseCommandContext(["--token", "cli-token", "target:1"]);
41
+
42
+ expect(context.authToken).toBe("cli-token");
43
+ expect(context.positional).toEqual(["target:1"]);
44
+ });
45
+
46
+ it("throws when --token is missing a value", () => {
47
+ expect(() => parseCommandContext(["--token"])).toThrow("Missing value for --token.");
48
+ });
49
+
50
+ it("supports --browser edge preset and forwards daemon startup metadata", () => {
51
+ const context = parseCommandContext(["--browser", "edge", "target:1"]);
52
+
53
+ expect(context.daemonStartup).toEqual({
54
+ managedLocal: {
55
+ browserName: "chromium",
56
+ channel: "msedge"
57
+ }
58
+ });
59
+ expect(buildToolArguments(context)).toMatchObject({
60
+ [DAEMON_STARTUP_ARGUMENT]: {
61
+ managedLocal: {
62
+ browserName: "chromium",
63
+ channel: "msedge"
64
+ }
65
+ }
66
+ });
67
+ });
68
+
69
+ it("supports BROWSERCTL_BROWSER env preset", () => {
70
+ process.env.BROWSERCTL_BROWSER = "edge";
71
+
72
+ const context = parseCommandContext(["target:1"]);
73
+
74
+ expect(context.daemonStartup).toEqual({
75
+ managedLocal: {
76
+ browserName: "chromium",
77
+ channel: "msedge"
78
+ }
79
+ });
80
+ });
81
+
82
+ it("throws on unsupported --browser values", () => {
83
+ expect(() => parseCommandContext(["--browser", "opera"])).toThrow(
84
+ "Invalid value for --browser: opera. Supported values: chromium, chrome, edge."
85
+ );
86
+ });
87
+ });
@@ -0,0 +1,191 @@
1
+ export type ManagedLocalBrowserPreset = "chromium" | "chrome" | "edge";
2
+
3
+ export type DaemonStartupConfig = {
4
+ managedLocal: {
5
+ browserName: "chromium";
6
+ channel?: string;
7
+ };
8
+ };
9
+
10
+ export type CommandContext = {
11
+ sessionId: string;
12
+ profile?: string;
13
+ authToken?: string;
14
+ daemonStartup?: DaemonStartupConfig;
15
+ positional: string[];
16
+ };
17
+
18
+ export const DAEMON_STARTUP_ARGUMENT = "__browserctlDaemonStartup";
19
+
20
+ const DEFAULT_SESSION_ID = "cli:local";
21
+ const SUPPORTED_BROWSER_PRESETS = new Set<ManagedLocalBrowserPreset>([
22
+ "chromium",
23
+ "chrome",
24
+ "edge"
25
+ ]);
26
+
27
+ function resolveAuthToken(value: string | undefined): string | undefined {
28
+ if (value === undefined) {
29
+ return undefined;
30
+ }
31
+
32
+ const trimmedValue = value.trim();
33
+ return trimmedValue.length > 0 ? trimmedValue : undefined;
34
+ }
35
+
36
+ function requireValue(args: string[], index: number, option: string): string {
37
+ const value = args[index + 1];
38
+ if (value === undefined || value.startsWith("--")) {
39
+ throw new Error(`Missing value for ${option}.`);
40
+ }
41
+
42
+ return value;
43
+ }
44
+
45
+ function parseBrowserPreset(
46
+ value: string | undefined,
47
+ optionName: string
48
+ ): ManagedLocalBrowserPreset | undefined {
49
+ if (value === undefined) {
50
+ return undefined;
51
+ }
52
+
53
+ const normalizedValue = value.trim().toLowerCase();
54
+ if (normalizedValue.length === 0) {
55
+ return undefined;
56
+ }
57
+
58
+ if (SUPPORTED_BROWSER_PRESETS.has(normalizedValue as ManagedLocalBrowserPreset)) {
59
+ return normalizedValue as ManagedLocalBrowserPreset;
60
+ }
61
+
62
+ throw new Error(
63
+ `Invalid value for ${optionName}: ${value}. Supported values: chromium, chrome, edge.`
64
+ );
65
+ }
66
+
67
+ function resolveDaemonStartupConfig(
68
+ browserPreset: ManagedLocalBrowserPreset | undefined
69
+ ): DaemonStartupConfig | undefined {
70
+ if (browserPreset === undefined) {
71
+ return undefined;
72
+ }
73
+
74
+ if (browserPreset === "edge") {
75
+ return {
76
+ managedLocal: {
77
+ browserName: "chromium",
78
+ channel: "msedge"
79
+ }
80
+ };
81
+ }
82
+
83
+ if (browserPreset === "chrome") {
84
+ return {
85
+ managedLocal: {
86
+ browserName: "chromium",
87
+ channel: "chrome"
88
+ }
89
+ };
90
+ }
91
+
92
+ return {
93
+ managedLocal: {
94
+ browserName: "chromium"
95
+ }
96
+ };
97
+ }
98
+
99
+ export function parseCommandContext(args: string[]): CommandContext {
100
+ const positional: string[] = [];
101
+ let sessionId = DEFAULT_SESSION_ID;
102
+ let profile: string | undefined;
103
+ let authToken = resolveAuthToken(process.env.BROWSERCTL_AUTH_TOKEN);
104
+ let browserPreset = parseBrowserPreset(process.env.BROWSERCTL_BROWSER, "BROWSERCTL_BROWSER");
105
+
106
+ for (let index = 0; index < args.length; index += 1) {
107
+ const token = args[index];
108
+
109
+ if (token === "--session") {
110
+ sessionId = requireValue(args, index, "--session");
111
+ index += 1;
112
+ continue;
113
+ }
114
+
115
+ if (token === "--profile") {
116
+ profile = requireValue(args, index, "--profile");
117
+ index += 1;
118
+ continue;
119
+ }
120
+
121
+ if (token === "--token") {
122
+ authToken = resolveAuthToken(requireValue(args, index, "--token"));
123
+ if (authToken === undefined) {
124
+ throw new Error("Missing value for --token.");
125
+ }
126
+
127
+ index += 1;
128
+ continue;
129
+ }
130
+
131
+ if (token === "--browser") {
132
+ browserPreset = parseBrowserPreset(requireValue(args, index, "--browser"), "--browser");
133
+ index += 1;
134
+ continue;
135
+ }
136
+
137
+ if (token === "--") {
138
+ positional.push(...args.slice(index + 1));
139
+ break;
140
+ }
141
+
142
+ positional.push(token);
143
+ }
144
+
145
+ return {
146
+ sessionId,
147
+ profile,
148
+ authToken,
149
+ daemonStartup: resolveDaemonStartupConfig(browserPreset),
150
+ positional
151
+ };
152
+ }
153
+
154
+ export function buildToolArguments(
155
+ context: CommandContext,
156
+ args: Record<string, unknown> = {}
157
+ ): Record<string, unknown> {
158
+ const baseArguments: Record<string, unknown> = {
159
+ sessionId: context.sessionId
160
+ };
161
+
162
+ if (context.profile !== undefined) {
163
+ baseArguments.profile = context.profile;
164
+ }
165
+
166
+ if (context.authToken !== undefined) {
167
+ baseArguments.authToken = context.authToken;
168
+ }
169
+
170
+ if (context.daemonStartup !== undefined) {
171
+ baseArguments[DAEMON_STARTUP_ARGUMENT] = context.daemonStartup;
172
+ }
173
+
174
+ return {
175
+ ...baseArguments,
176
+ ...args
177
+ };
178
+ }
179
+
180
+ export function requirePositionalArg(
181
+ context: CommandContext,
182
+ index: number,
183
+ name: string
184
+ ): string {
185
+ const value = context.positional[index];
186
+ if (value === undefined) {
187
+ throw new Error(`Missing required argument: ${name}.`);
188
+ }
189
+
190
+ return value;
191
+ }
@@ -0,0 +1,20 @@
1
+ import { callDaemonTool } from "../daemon-client";
2
+ import { buildToolArguments, parseCommandContext, requirePositionalArg } from "./common";
3
+
4
+ export type ConsoleListCommandResult = {
5
+ driver: string;
6
+ targetId: string;
7
+ entries: unknown[];
8
+ };
9
+
10
+ export async function runConsoleListCommand(args: string[]): Promise<ConsoleListCommandResult> {
11
+ const context = parseCommandContext(args);
12
+ const targetId = requirePositionalArg(context, 0, "targetId");
13
+
14
+ return await callDaemonTool<ConsoleListCommandResult>(
15
+ "browser.console.list",
16
+ buildToolArguments(context, {
17
+ targetId
18
+ })
19
+ );
20
+ }
@@ -0,0 +1,18 @@
1
+ import { callDaemonTool } from "../daemon-client";
2
+ import { buildToolArguments, parseCommandContext, requirePositionalArg } from "./common";
3
+
4
+ export type CookieClearCommandResult = Record<string, unknown>;
5
+
6
+ export async function runCookieClearCommand(args: string[]): Promise<CookieClearCommandResult> {
7
+ const context = parseCommandContext(args);
8
+ const targetId = requirePositionalArg(context, 0, "targetId");
9
+ const name = context.positional[1];
10
+
11
+ return await callDaemonTool<CookieClearCommandResult>(
12
+ "browser.cookie.clear",
13
+ buildToolArguments(context, {
14
+ targetId,
15
+ ...(typeof name === "string" && name.trim().length > 0 ? { name: name.trim() } : {})
16
+ })
17
+ );
18
+ }
@@ -0,0 +1,18 @@
1
+ import { callDaemonTool } from "../daemon-client";
2
+ import { buildToolArguments, parseCommandContext, requirePositionalArg } from "./common";
3
+
4
+ export type CookieGetCommandResult = Record<string, unknown>;
5
+
6
+ export async function runCookieGetCommand(args: string[]): Promise<CookieGetCommandResult> {
7
+ const context = parseCommandContext(args);
8
+ const targetId = requirePositionalArg(context, 0, "targetId");
9
+ const name = context.positional[1];
10
+
11
+ return await callDaemonTool<CookieGetCommandResult>(
12
+ "browser.cookie.get",
13
+ buildToolArguments(context, {
14
+ targetId,
15
+ ...(typeof name === "string" && name.trim().length > 0 ? { name: name.trim() } : {})
16
+ })
17
+ );
18
+ }
@@ -0,0 +1,22 @@
1
+ import { callDaemonTool } from "../daemon-client";
2
+ import { buildToolArguments, parseCommandContext, requirePositionalArg } from "./common";
3
+
4
+ export type CookieSetCommandResult = Record<string, unknown>;
5
+
6
+ export async function runCookieSetCommand(args: string[]): Promise<CookieSetCommandResult> {
7
+ const context = parseCommandContext(args);
8
+ const targetId = requirePositionalArg(context, 0, "targetId");
9
+ const name = requirePositionalArg(context, 1, "name");
10
+ const value = requirePositionalArg(context, 2, "value");
11
+ const url = context.positional[3];
12
+
13
+ return await callDaemonTool<CookieSetCommandResult>(
14
+ "browser.cookie.set",
15
+ buildToolArguments(context, {
16
+ targetId,
17
+ name,
18
+ value,
19
+ ...(typeof url === "string" && url.trim().length > 0 ? { url: url.trim() } : {})
20
+ })
21
+ );
22
+ }
@@ -0,0 +1,20 @@
1
+ import { callDaemonTool } from "../daemon-client";
2
+ import { buildToolArguments, parseCommandContext, requirePositionalArg } from "./common";
3
+
4
+ export type DialogArmCommandResult = {
5
+ driver: string;
6
+ targetId: string;
7
+ armed: boolean;
8
+ };
9
+
10
+ export async function runDialogArmCommand(args: string[]): Promise<DialogArmCommandResult> {
11
+ const context = parseCommandContext(args);
12
+ const targetId = requirePositionalArg(context, 0, "targetId");
13
+
14
+ return await callDaemonTool<DialogArmCommandResult>(
15
+ "browser.dialog.arm",
16
+ buildToolArguments(context, {
17
+ targetId
18
+ })
19
+ );
20
+ }
@@ -0,0 +1,18 @@
1
+ import { callDaemonTool } from "../daemon-client";
2
+ import { buildToolArguments, parseCommandContext, requirePositionalArg } from "./common";
3
+
4
+ export type DomQueryAllCommandResult = Record<string, unknown>;
5
+
6
+ export async function runDomQueryAllCommand(args: string[]): Promise<DomQueryAllCommandResult> {
7
+ const context = parseCommandContext(args);
8
+ const targetId = requirePositionalArg(context, 0, "targetId");
9
+ const selector = requirePositionalArg(context, 1, "selector");
10
+
11
+ return await callDaemonTool<DomQueryAllCommandResult>(
12
+ "browser.dom.queryAll",
13
+ buildToolArguments(context, {
14
+ targetId,
15
+ selector
16
+ })
17
+ );
18
+ }
@@ -0,0 +1,18 @@
1
+ import { callDaemonTool } from "../daemon-client";
2
+ import { buildToolArguments, parseCommandContext, requirePositionalArg } from "./common";
3
+
4
+ export type DomQueryCommandResult = Record<string, unknown>;
5
+
6
+ export async function runDomQueryCommand(args: string[]): Promise<DomQueryCommandResult> {
7
+ const context = parseCommandContext(args);
8
+ const targetId = requirePositionalArg(context, 0, "targetId");
9
+ const selector = requirePositionalArg(context, 1, "selector");
10
+
11
+ return await callDaemonTool<DomQueryCommandResult>(
12
+ "browser.dom.query",
13
+ buildToolArguments(context, {
14
+ targetId,
15
+ selector
16
+ })
17
+ );
18
+ }
@@ -0,0 +1,22 @@
1
+ import { callDaemonTool } from "../daemon-client";
2
+ import { buildToolArguments, parseCommandContext, requirePositionalArg } from "./common";
3
+
4
+ export type DownloadTriggerCommandResult = {
5
+ driver: string;
6
+ targetId: string;
7
+ triggered: boolean;
8
+ };
9
+
10
+ export async function runDownloadTriggerCommand(
11
+ args: string[]
12
+ ): Promise<DownloadTriggerCommandResult> {
13
+ const context = parseCommandContext(args);
14
+ const targetId = requirePositionalArg(context, 0, "targetId");
15
+
16
+ return await callDaemonTool<DownloadTriggerCommandResult>(
17
+ "browser.download.trigger",
18
+ buildToolArguments(context, {
19
+ targetId
20
+ })
21
+ );
22
+ }