@flrande/browserctl 0.5.0 → 0.6.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/dist/client.d.ts +34 -0
- package/dist/client.js +138 -0
- package/dist/commandRegistry.d.ts +16 -0
- package/dist/commandRegistry.js +21 -0
- package/dist/help.d.ts +4 -0
- package/dist/help.js +24 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.js +23 -0
- package/dist/runCli.d.ts +5 -0
- package/dist/runCli.js +170 -0
- package/package.json +32 -57
- package/INSTALL-CN.md +0 -92
- package/INSTALL.md +0 -92
- package/LICENSE +0 -21
- package/README-CN.md +0 -69
- package/README.md +0 -69
- package/apps/browserctl/src/commands/a11y-snapshot.ts +0 -20
- package/apps/browserctl/src/commands/act.test.ts +0 -71
- package/apps/browserctl/src/commands/act.ts +0 -64
- package/apps/browserctl/src/commands/command-wrappers.test.ts +0 -688
- package/apps/browserctl/src/commands/common.test.ts +0 -87
- package/apps/browserctl/src/commands/common.ts +0 -191
- package/apps/browserctl/src/commands/console-list.test.ts +0 -102
- package/apps/browserctl/src/commands/console-list.ts +0 -108
- package/apps/browserctl/src/commands/cookie-clear.ts +0 -18
- package/apps/browserctl/src/commands/cookie-get.ts +0 -18
- package/apps/browserctl/src/commands/cookie-set.ts +0 -22
- package/apps/browserctl/src/commands/dialog-arm.ts +0 -20
- package/apps/browserctl/src/commands/dom-query-all.ts +0 -18
- package/apps/browserctl/src/commands/dom-query.ts +0 -18
- package/apps/browserctl/src/commands/download-trigger.ts +0 -22
- package/apps/browserctl/src/commands/download-wait.test.ts +0 -67
- package/apps/browserctl/src/commands/download-wait.ts +0 -27
- package/apps/browserctl/src/commands/element-screenshot.ts +0 -20
- package/apps/browserctl/src/commands/frame-list.ts +0 -16
- package/apps/browserctl/src/commands/frame-snapshot.ts +0 -18
- package/apps/browserctl/src/commands/har-export.test.ts +0 -112
- package/apps/browserctl/src/commands/har-export.ts +0 -120
- package/apps/browserctl/src/commands/memory-delete.ts +0 -20
- package/apps/browserctl/src/commands/memory-inspect.ts +0 -20
- package/apps/browserctl/src/commands/memory-list.ts +0 -90
- package/apps/browserctl/src/commands/memory-mode-set.ts +0 -29
- package/apps/browserctl/src/commands/memory-purge.ts +0 -16
- package/apps/browserctl/src/commands/memory-resolve.ts +0 -56
- package/apps/browserctl/src/commands/memory-status.ts +0 -16
- package/apps/browserctl/src/commands/memory-ttl-set.ts +0 -28
- package/apps/browserctl/src/commands/memory-upsert.ts +0 -142
- package/apps/browserctl/src/commands/network-list.test.ts +0 -110
- package/apps/browserctl/src/commands/network-list.ts +0 -112
- package/apps/browserctl/src/commands/network-wait-for.test.ts +0 -90
- package/apps/browserctl/src/commands/network-wait-for.ts +0 -100
- package/apps/browserctl/src/commands/profile-list.ts +0 -16
- package/apps/browserctl/src/commands/profile-use.ts +0 -18
- package/apps/browserctl/src/commands/response-body.ts +0 -24
- package/apps/browserctl/src/commands/screenshot.ts +0 -16
- package/apps/browserctl/src/commands/session-drop.test.ts +0 -36
- package/apps/browserctl/src/commands/session-drop.ts +0 -16
- package/apps/browserctl/src/commands/session-list.test.ts +0 -81
- package/apps/browserctl/src/commands/session-list.ts +0 -70
- package/apps/browserctl/src/commands/snapshot.ts +0 -16
- package/apps/browserctl/src/commands/status.ts +0 -10
- package/apps/browserctl/src/commands/storage-get.ts +0 -20
- package/apps/browserctl/src/commands/storage-set.ts +0 -22
- package/apps/browserctl/src/commands/tab-close.ts +0 -20
- package/apps/browserctl/src/commands/tab-focus.ts +0 -20
- package/apps/browserctl/src/commands/tab-open.ts +0 -19
- package/apps/browserctl/src/commands/tabs.ts +0 -13
- package/apps/browserctl/src/commands/trace-get.test.ts +0 -61
- package/apps/browserctl/src/commands/trace-get.ts +0 -62
- package/apps/browserctl/src/commands/upload-arm.ts +0 -26
- package/apps/browserctl/src/commands/wait-element.test.ts +0 -80
- package/apps/browserctl/src/commands/wait-element.ts +0 -76
- package/apps/browserctl/src/commands/wait-text.test.ts +0 -110
- package/apps/browserctl/src/commands/wait-text.ts +0 -93
- package/apps/browserctl/src/commands/wait-url.test.ts +0 -80
- package/apps/browserctl/src/commands/wait-url.ts +0 -76
- package/apps/browserctl/src/daemon-client.test.ts +0 -253
- package/apps/browserctl/src/daemon-client.ts +0 -632
- package/apps/browserctl/src/e2e.test.ts +0 -103
- package/apps/browserctl/src/main.dispatch.test.ts +0 -461
- package/apps/browserctl/src/main.test.ts +0 -334
- package/apps/browserctl/src/main.ts +0 -957
- package/apps/browserctl/src/smoke.e2e.test.ts +0 -97
- package/apps/browserctl/src/test-port.ts +0 -26
- package/apps/browserd/src/bootstrap.ts +0 -432
- package/apps/browserd/src/chrome-relay-extension-bridge.test.ts +0 -250
- package/apps/browserd/src/chrome-relay-extension-bridge.ts +0 -506
- package/apps/browserd/src/container.ts +0 -3088
- package/apps/browserd/src/main.test.ts +0 -1436
- package/apps/browserd/src/main.ts +0 -7
- package/apps/browserd/src/test-port.ts +0 -26
- package/apps/browserd/src/tool-matrix.test.ts +0 -887
- package/bin/browserctl.cjs +0 -21
- package/bin/browserd.cjs +0 -21
- package/extensions/chrome-relay/README-CN.md +0 -39
- package/extensions/chrome-relay/README.md +0 -39
- package/extensions/chrome-relay/background.js +0 -1687
- package/extensions/chrome-relay/manifest.json +0 -15
- package/extensions/chrome-relay/popup.html +0 -369
- package/extensions/chrome-relay/popup.js +0 -972
- package/packages/core/src/bootstrap.test.ts +0 -10
- package/packages/core/src/driver-registry.test.ts +0 -45
- package/packages/core/src/driver-registry.ts +0 -22
- package/packages/core/src/driver.ts +0 -47
- package/packages/core/src/index.ts +0 -6
- package/packages/core/src/navigation-memory.test.ts +0 -259
- package/packages/core/src/navigation-memory.ts +0 -360
- package/packages/core/src/ref-cache.test.ts +0 -61
- package/packages/core/src/ref-cache.ts +0 -28
- package/packages/core/src/session-store.test.ts +0 -82
- package/packages/core/src/session-store.ts +0 -138
- package/packages/core/src/types.ts +0 -9
- package/packages/driver-chrome-relay/src/chrome-relay-driver.test.ts +0 -744
- package/packages/driver-chrome-relay/src/chrome-relay-driver.ts +0 -2429
- package/packages/driver-chrome-relay/src/chrome-relay-extension-runtime.test.ts +0 -264
- package/packages/driver-chrome-relay/src/chrome-relay-extension-runtime.ts +0 -521
- package/packages/driver-chrome-relay/src/index.ts +0 -26
- package/packages/driver-managed/src/index.ts +0 -22
- package/packages/driver-managed/src/managed-driver.test.ts +0 -183
- package/packages/driver-managed/src/managed-driver.ts +0 -341
- package/packages/driver-managed/src/managed-local-driver.test.ts +0 -608
- package/packages/driver-managed/src/managed-local-driver.ts +0 -2243
- package/packages/driver-remote-cdp/src/index.ts +0 -19
- package/packages/driver-remote-cdp/src/remote-cdp-driver.test.ts +0 -727
- package/packages/driver-remote-cdp/src/remote-cdp-driver.ts +0 -2264
- package/packages/protocol/src/envelope.test.ts +0 -25
- package/packages/protocol/src/envelope.ts +0 -31
- package/packages/protocol/src/errors.test.ts +0 -17
- package/packages/protocol/src/errors.ts +0 -11
- package/packages/protocol/src/index.ts +0 -3
- package/packages/protocol/src/tools.ts +0 -3
- package/packages/transport-mcp-stdio/src/index.ts +0 -3
- package/packages/transport-mcp-stdio/src/sdk-server.ts +0 -139
- package/packages/transport-mcp-stdio/src/server.test.ts +0 -281
- package/packages/transport-mcp-stdio/src/server.ts +0 -183
- package/packages/transport-mcp-stdio/src/tool-map.ts +0 -84
|
@@ -1,87 +0,0 @@
|
|
|
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
|
-
});
|
|
@@ -1,191 +0,0 @@
|
|
|
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
|
-
}
|
|
@@ -1,102 +0,0 @@
|
|
|
1
|
-
import { afterEach, describe, expect, it, vi } from "vitest";
|
|
2
|
-
|
|
3
|
-
const { callDaemonToolMock } = vi.hoisted(() => ({
|
|
4
|
-
callDaemonToolMock: vi.fn()
|
|
5
|
-
}));
|
|
6
|
-
|
|
7
|
-
vi.mock("../daemon-client", () => ({
|
|
8
|
-
callDaemonTool: callDaemonToolMock
|
|
9
|
-
}));
|
|
10
|
-
|
|
11
|
-
import { runConsoleListCommand } from "./console-list";
|
|
12
|
-
|
|
13
|
-
afterEach(() => {
|
|
14
|
-
callDaemonToolMock.mockReset();
|
|
15
|
-
});
|
|
16
|
-
|
|
17
|
-
describe("runConsoleListCommand", () => {
|
|
18
|
-
it("forwards filter options", async () => {
|
|
19
|
-
callDaemonToolMock.mockResolvedValue({ ok: true });
|
|
20
|
-
|
|
21
|
-
await runConsoleListCommand([
|
|
22
|
-
"target:1",
|
|
23
|
-
"--type",
|
|
24
|
-
"error",
|
|
25
|
-
"--contains",
|
|
26
|
-
"timeout",
|
|
27
|
-
"--since",
|
|
28
|
-
"2026-01-01T00:00:00.000Z",
|
|
29
|
-
"--limit",
|
|
30
|
-
"20"
|
|
31
|
-
]);
|
|
32
|
-
|
|
33
|
-
expect(callDaemonToolMock).toHaveBeenCalledTimes(1);
|
|
34
|
-
expect(callDaemonToolMock).toHaveBeenCalledWith("browser.console.list", {
|
|
35
|
-
sessionId: "cli:local",
|
|
36
|
-
targetId: "target:1",
|
|
37
|
-
type: "error",
|
|
38
|
-
contains: "timeout",
|
|
39
|
-
since: "2026-01-01T00:00:00.000Z",
|
|
40
|
-
limit: 20
|
|
41
|
-
});
|
|
42
|
-
});
|
|
43
|
-
|
|
44
|
-
it.each([
|
|
45
|
-
{
|
|
46
|
-
label: "--type",
|
|
47
|
-
args: ["target:1", "--type"],
|
|
48
|
-
message: "Missing value for --type."
|
|
49
|
-
},
|
|
50
|
-
{
|
|
51
|
-
label: "--contains",
|
|
52
|
-
args: ["target:1", "--contains"],
|
|
53
|
-
message: "Missing value for --contains."
|
|
54
|
-
},
|
|
55
|
-
{
|
|
56
|
-
label: "--since",
|
|
57
|
-
args: ["target:1", "--since"],
|
|
58
|
-
message: "Missing value for --since."
|
|
59
|
-
},
|
|
60
|
-
{
|
|
61
|
-
label: "--limit",
|
|
62
|
-
args: ["target:1", "--limit"],
|
|
63
|
-
message: "Missing value for --limit."
|
|
64
|
-
}
|
|
65
|
-
])("throws when $label is missing a value", async ({ args, message }) => {
|
|
66
|
-
await expect(runConsoleListCommand(args)).rejects.toThrow(message);
|
|
67
|
-
expect(callDaemonToolMock).not.toHaveBeenCalled();
|
|
68
|
-
});
|
|
69
|
-
|
|
70
|
-
it.each([
|
|
71
|
-
{
|
|
72
|
-
label: "empty --type",
|
|
73
|
-
args: ["target:1", "--type", " "],
|
|
74
|
-
message: "--type must be a non-empty string."
|
|
75
|
-
},
|
|
76
|
-
{
|
|
77
|
-
label: "empty --contains",
|
|
78
|
-
args: ["target:1", "--contains", " "],
|
|
79
|
-
message: "--contains must be a non-empty string."
|
|
80
|
-
},
|
|
81
|
-
{
|
|
82
|
-
label: "invalid --limit",
|
|
83
|
-
args: ["target:1", "--limit", "0"],
|
|
84
|
-
message: "--limit must be a positive integer."
|
|
85
|
-
},
|
|
86
|
-
{
|
|
87
|
-
label: "non-strict --limit text",
|
|
88
|
-
args: ["target:1", "--limit", "20rows"],
|
|
89
|
-
message: "--limit must be a positive integer."
|
|
90
|
-
}
|
|
91
|
-
])("throws on invalid option value: $label", async ({ args, message }) => {
|
|
92
|
-
await expect(runConsoleListCommand(args)).rejects.toThrow(message);
|
|
93
|
-
expect(callDaemonToolMock).not.toHaveBeenCalled();
|
|
94
|
-
});
|
|
95
|
-
|
|
96
|
-
it("throws on unknown option token", async () => {
|
|
97
|
-
await expect(runConsoleListCommand(["target:1", "--unknown"]))
|
|
98
|
-
.rejects.toThrow("Unknown console-list option: --unknown");
|
|
99
|
-
|
|
100
|
-
expect(callDaemonToolMock).not.toHaveBeenCalled();
|
|
101
|
-
});
|
|
102
|
-
});
|
|
@@ -1,108 +0,0 @@
|
|
|
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
|
-
function parsePositiveInteger(value: string, optionName: string): number {
|
|
11
|
-
const normalized = value.trim();
|
|
12
|
-
if (!/^[1-9]\d*$/.test(normalized)) {
|
|
13
|
-
throw new Error(`${optionName} must be a positive integer.`);
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
return Number.parseInt(normalized, 10);
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
function parseConsoleOptions(tokens: string[]): {
|
|
20
|
-
type?: string;
|
|
21
|
-
contains?: string;
|
|
22
|
-
since?: string;
|
|
23
|
-
limit?: number;
|
|
24
|
-
} {
|
|
25
|
-
let type: string | undefined;
|
|
26
|
-
let contains: string | undefined;
|
|
27
|
-
let since: string | undefined;
|
|
28
|
-
let limit: number | undefined;
|
|
29
|
-
|
|
30
|
-
for (let index = 0; index < tokens.length; index += 1) {
|
|
31
|
-
const token = tokens[index];
|
|
32
|
-
|
|
33
|
-
if (token === "--type") {
|
|
34
|
-
const value = tokens[index + 1];
|
|
35
|
-
if (value === undefined) {
|
|
36
|
-
throw new Error("Missing value for --type.");
|
|
37
|
-
}
|
|
38
|
-
const trimmed = value.trim();
|
|
39
|
-
if (trimmed.length === 0) {
|
|
40
|
-
throw new Error("--type must be a non-empty string.");
|
|
41
|
-
}
|
|
42
|
-
type = trimmed;
|
|
43
|
-
index += 1;
|
|
44
|
-
continue;
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
if (token === "--contains") {
|
|
48
|
-
const value = tokens[index + 1];
|
|
49
|
-
if (value === undefined) {
|
|
50
|
-
throw new Error("Missing value for --contains.");
|
|
51
|
-
}
|
|
52
|
-
const trimmed = value.trim();
|
|
53
|
-
if (trimmed.length === 0) {
|
|
54
|
-
throw new Error("--contains must be a non-empty string.");
|
|
55
|
-
}
|
|
56
|
-
contains = trimmed;
|
|
57
|
-
index += 1;
|
|
58
|
-
continue;
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
if (token === "--since") {
|
|
62
|
-
const value = tokens[index + 1];
|
|
63
|
-
if (value === undefined) {
|
|
64
|
-
throw new Error("Missing value for --since.");
|
|
65
|
-
}
|
|
66
|
-
const trimmed = value.trim();
|
|
67
|
-
if (trimmed.length === 0) {
|
|
68
|
-
throw new Error("--since must be a non-empty ISO timestamp.");
|
|
69
|
-
}
|
|
70
|
-
since = trimmed;
|
|
71
|
-
index += 1;
|
|
72
|
-
continue;
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
-
if (token === "--limit") {
|
|
76
|
-
const value = tokens[index + 1];
|
|
77
|
-
if (value === undefined) {
|
|
78
|
-
throw new Error("Missing value for --limit.");
|
|
79
|
-
}
|
|
80
|
-
limit = parsePositiveInteger(value, "--limit");
|
|
81
|
-
index += 1;
|
|
82
|
-
continue;
|
|
83
|
-
}
|
|
84
|
-
|
|
85
|
-
throw new Error(`Unknown console-list option: ${token}`);
|
|
86
|
-
}
|
|
87
|
-
|
|
88
|
-
return {
|
|
89
|
-
...(type !== undefined ? { type } : {}),
|
|
90
|
-
...(contains !== undefined ? { contains } : {}),
|
|
91
|
-
...(since !== undefined ? { since } : {}),
|
|
92
|
-
...(limit !== undefined ? { limit } : {})
|
|
93
|
-
};
|
|
94
|
-
}
|
|
95
|
-
|
|
96
|
-
export async function runConsoleListCommand(args: string[]): Promise<ConsoleListCommandResult> {
|
|
97
|
-
const context = parseCommandContext(args);
|
|
98
|
-
const targetId = requirePositionalArg(context, 0, "targetId");
|
|
99
|
-
const options = parseConsoleOptions(context.positional.slice(1));
|
|
100
|
-
|
|
101
|
-
return await callDaemonTool<ConsoleListCommandResult>(
|
|
102
|
-
"browser.console.list",
|
|
103
|
-
buildToolArguments(context, {
|
|
104
|
-
targetId,
|
|
105
|
-
...options
|
|
106
|
-
})
|
|
107
|
-
);
|
|
108
|
-
}
|
|
@@ -1,18 +0,0 @@
|
|
|
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
|
-
}
|
|
@@ -1,18 +0,0 @@
|
|
|
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
|
-
}
|
|
@@ -1,22 +0,0 @@
|
|
|
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
|
-
}
|
|
@@ -1,20 +0,0 @@
|
|
|
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
|
-
}
|
|
@@ -1,18 +0,0 @@
|
|
|
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
|
-
}
|
|
@@ -1,18 +0,0 @@
|
|
|
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
|
-
}
|
|
@@ -1,22 +0,0 @@
|
|
|
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
|
-
}
|
|
@@ -1,67 +0,0 @@
|
|
|
1
|
-
import { afterEach, describe, expect, it, vi } from "vitest";
|
|
2
|
-
|
|
3
|
-
const { callDaemonToolMock } = vi.hoisted(() => ({
|
|
4
|
-
callDaemonToolMock: vi.fn()
|
|
5
|
-
}));
|
|
6
|
-
|
|
7
|
-
vi.mock("../daemon-client", () => ({
|
|
8
|
-
callDaemonTool: callDaemonToolMock
|
|
9
|
-
}));
|
|
10
|
-
|
|
11
|
-
import { runDownloadWaitCommand } from "./download-wait";
|
|
12
|
-
|
|
13
|
-
const ORIGINAL_AUTH_TOKEN = process.env.BROWSERCTL_AUTH_TOKEN;
|
|
14
|
-
|
|
15
|
-
afterEach(() => {
|
|
16
|
-
callDaemonToolMock.mockReset();
|
|
17
|
-
|
|
18
|
-
if (ORIGINAL_AUTH_TOKEN === undefined) {
|
|
19
|
-
delete process.env.BROWSERCTL_AUTH_TOKEN;
|
|
20
|
-
return;
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
process.env.BROWSERCTL_AUTH_TOKEN = ORIGINAL_AUTH_TOKEN;
|
|
24
|
-
});
|
|
25
|
-
|
|
26
|
-
describe("runDownloadWaitCommand", () => {
|
|
27
|
-
it("forwards optional download path when provided", async () => {
|
|
28
|
-
callDaemonToolMock.mockResolvedValue({
|
|
29
|
-
driver: "managed",
|
|
30
|
-
targetId: "target:1",
|
|
31
|
-
download: {}
|
|
32
|
-
});
|
|
33
|
-
|
|
34
|
-
await runDownloadWaitCommand(["target:1", "downloads/file.bin"]);
|
|
35
|
-
|
|
36
|
-
expect(callDaemonToolMock).toHaveBeenCalledTimes(1);
|
|
37
|
-
expect(callDaemonToolMock).toHaveBeenCalledWith(
|
|
38
|
-
"browser.download.wait",
|
|
39
|
-
expect.objectContaining({
|
|
40
|
-
sessionId: "cli:local",
|
|
41
|
-
targetId: "target:1",
|
|
42
|
-
path: "downloads/file.bin"
|
|
43
|
-
})
|
|
44
|
-
);
|
|
45
|
-
});
|
|
46
|
-
|
|
47
|
-
it("uses --token over environment token", async () => {
|
|
48
|
-
process.env.BROWSERCTL_AUTH_TOKEN = "env-token";
|
|
49
|
-
callDaemonToolMock.mockResolvedValue({
|
|
50
|
-
driver: "managed",
|
|
51
|
-
targetId: "target:1",
|
|
52
|
-
download: {}
|
|
53
|
-
});
|
|
54
|
-
|
|
55
|
-
await runDownloadWaitCommand(["--token", "cli-token", "target:1"]);
|
|
56
|
-
|
|
57
|
-
expect(callDaemonToolMock).toHaveBeenCalledTimes(1);
|
|
58
|
-
expect(callDaemonToolMock).toHaveBeenCalledWith(
|
|
59
|
-
"browser.download.wait",
|
|
60
|
-
expect.objectContaining({
|
|
61
|
-
sessionId: "cli:local",
|
|
62
|
-
targetId: "target:1",
|
|
63
|
-
authToken: "cli-token"
|
|
64
|
-
})
|
|
65
|
-
);
|
|
66
|
-
});
|
|
67
|
-
});
|