browser-use 0.6.1 → 0.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 +24 -18
- package/dist/actor/element.js +24 -3
- package/dist/actor/mouse.js +21 -3
- package/dist/actor/page.js +33 -11
- package/dist/agent/gif.js +28 -3
- package/dist/agent/message-manager/service.js +2 -22
- package/dist/agent/message-manager/utils.js +15 -2
- package/dist/agent/message-manager/views.d.ts +7 -7
- package/dist/agent/message-manager/views.js +1 -0
- package/dist/agent/prompts.d.ts +3 -0
- package/dist/agent/prompts.js +22 -12
- package/dist/agent/service.d.ts +9 -1
- package/dist/agent/service.js +204 -79
- package/dist/agent/system_prompt.md +12 -11
- package/dist/agent/system_prompt_anthropic_flash.md +6 -5
- package/dist/agent/system_prompt_no_thinking.md +12 -11
- package/dist/agent/views.d.ts +2 -0
- package/dist/agent/views.js +48 -36
- package/dist/browser/extensions.js +20 -10
- package/dist/browser/profile.d.ts +4 -0
- package/dist/browser/profile.js +107 -4
- package/dist/browser/session.d.ts +28 -1
- package/dist/browser/session.js +1436 -528
- package/dist/browser/watchdogs/default-action-watchdog.js +32 -3
- package/dist/browser/watchdogs/downloads-watchdog.d.ts +4 -0
- package/dist/browser/watchdogs/downloads-watchdog.js +105 -9
- package/dist/browser/watchdogs/har-recording-watchdog.d.ts +1 -0
- package/dist/browser/watchdogs/har-recording-watchdog.js +54 -2
- package/dist/browser/watchdogs/permissions-watchdog.d.ts +5 -0
- package/dist/browser/watchdogs/permissions-watchdog.js +106 -3
- package/dist/browser/watchdogs/recording-watchdog.d.ts +2 -0
- package/dist/browser/watchdogs/recording-watchdog.js +54 -2
- package/dist/browser/watchdogs/security-watchdog.d.ts +1 -0
- package/dist/browser/watchdogs/security-watchdog.js +47 -7
- package/dist/browser/watchdogs/storage-state-watchdog.d.ts +6 -0
- package/dist/browser/watchdogs/storage-state-watchdog.js +206 -14
- package/dist/cli.d.ts +13 -2
- package/dist/cli.js +190 -9
- package/dist/code-use/namespace.js +52 -7
- package/dist/code-use/notebook-export.js +18 -2
- package/dist/code-use/service.js +1 -0
- package/dist/config.js +26 -4
- package/dist/controller/action-timeout.d.ts +9 -0
- package/dist/controller/action-timeout.js +95 -0
- package/dist/controller/registry/service.d.ts +1 -0
- package/dist/controller/registry/service.js +28 -1
- package/dist/controller/service.d.ts +2 -1
- package/dist/controller/service.js +494 -329
- package/dist/entrypoint.d.ts +1 -0
- package/dist/entrypoint.js +27 -0
- package/dist/filesystem/file-system.js +38 -8
- package/dist/integrations/gmail/service.js +30 -6
- package/dist/llm/browser-use/chat.js +2 -2
- package/dist/llm/codex/auth.d.ts +118 -0
- package/dist/llm/codex/auth.js +599 -0
- package/dist/llm/codex/chat.d.ts +70 -0
- package/dist/llm/codex/chat.js +392 -0
- package/dist/llm/codex/index.d.ts +2 -0
- package/dist/llm/codex/index.js +2 -0
- package/dist/llm/google/chat.js +18 -1
- package/dist/logging-config.js +22 -11
- package/dist/mcp/client.d.ts +1 -0
- package/dist/mcp/client.js +12 -10
- package/dist/mcp/redaction.d.ts +3 -0
- package/dist/mcp/redaction.js +132 -0
- package/dist/mcp/server.d.ts +2 -0
- package/dist/mcp/server.js +64 -22
- package/dist/screenshots/service.js +25 -2
- package/dist/skill-cli/direct.d.ts +4 -1
- package/dist/skill-cli/direct.js +263 -66
- package/dist/skill-cli/server.d.ts +1 -0
- package/dist/skill-cli/server.js +115 -25
- package/dist/skill-cli/tunnel.d.ts +1 -0
- package/dist/skill-cli/tunnel.js +16 -4
- package/dist/sync/auth.js +22 -9
- package/dist/telemetry/service.js +21 -2
- package/dist/telemetry/views.js +31 -8
- package/dist/tokens/custom-pricing.js +2 -2
- package/dist/tokens/openrouter-pricing.d.ts +11 -0
- package/dist/tokens/openrouter-pricing.js +102 -0
- package/dist/tokens/service.js +20 -16
- package/dist/utils.d.ts +3 -1
- package/dist/utils.js +3 -1
- package/package.json +68 -27
package/dist/skill-cli/direct.js
CHANGED
|
@@ -6,7 +6,45 @@ import net from 'node:net';
|
|
|
6
6
|
import { spawn } from 'node:child_process';
|
|
7
7
|
import { BrowserSession, systemChrome } from '../browser/session.js';
|
|
8
8
|
import { CloudBrowserClient } from '../browser/cloud/cloud.js';
|
|
9
|
-
|
|
9
|
+
import { isMainModule } from '../entrypoint.js';
|
|
10
|
+
const getSafeUserSegment = () => {
|
|
11
|
+
if (typeof process.getuid === 'function') {
|
|
12
|
+
return String(process.getuid());
|
|
13
|
+
}
|
|
14
|
+
try {
|
|
15
|
+
const username = os.userInfo().username;
|
|
16
|
+
return username.replace(/[^a-zA-Z0-9_.-]/g, '_') || 'user';
|
|
17
|
+
}
|
|
18
|
+
catch {
|
|
19
|
+
return 'user';
|
|
20
|
+
}
|
|
21
|
+
};
|
|
22
|
+
const getDefaultDirectStateDir = () => {
|
|
23
|
+
const runtimeDir = process.env.XDG_RUNTIME_DIR?.trim();
|
|
24
|
+
if (runtimeDir && path.isAbsolute(runtimeDir)) {
|
|
25
|
+
return path.join(runtimeDir, 'browser-use');
|
|
26
|
+
}
|
|
27
|
+
const homeDir = os.homedir();
|
|
28
|
+
if (homeDir) {
|
|
29
|
+
if (process.platform === 'darwin') {
|
|
30
|
+
return path.join(homeDir, 'Library', 'Application Support', 'browser-use');
|
|
31
|
+
}
|
|
32
|
+
if (process.platform === 'win32') {
|
|
33
|
+
const localAppData = process.env.LOCALAPPDATA?.trim();
|
|
34
|
+
const baseDir = localAppData && path.isAbsolute(localAppData)
|
|
35
|
+
? localAppData
|
|
36
|
+
: path.join(homeDir, 'AppData', 'Local');
|
|
37
|
+
return path.join(baseDir, 'browser-use');
|
|
38
|
+
}
|
|
39
|
+
const stateHome = process.env.XDG_STATE_HOME?.trim();
|
|
40
|
+
const baseDir = stateHome && path.isAbsolute(stateHome)
|
|
41
|
+
? stateHome
|
|
42
|
+
: path.join(homeDir, '.local', 'state');
|
|
43
|
+
return path.join(baseDir, 'browser-use');
|
|
44
|
+
}
|
|
45
|
+
return path.join(os.tmpdir(), `browser-use-${getSafeUserSegment()}`);
|
|
46
|
+
};
|
|
47
|
+
export const DIRECT_STATE_FILE = path.join(getDefaultDirectStateDir(), 'direct-state.json');
|
|
10
48
|
const normalizeCookieDomain = (value) => String(value ?? '')
|
|
11
49
|
.trim()
|
|
12
50
|
.replace(/^\./, '')
|
|
@@ -23,6 +61,9 @@ const parseCookieHostname = (url) => {
|
|
|
23
61
|
return '';
|
|
24
62
|
}
|
|
25
63
|
};
|
|
64
|
+
const validateDirectPageAfterAction = async (session, page) => {
|
|
65
|
+
await session.validate_page_after_action?.(page);
|
|
66
|
+
};
|
|
26
67
|
const parseCookieUrl = (url) => {
|
|
27
68
|
const value = String(url ?? '').trim();
|
|
28
69
|
if (!value) {
|
|
@@ -54,9 +95,7 @@ const cookieMatchesUrl = (cookie, url) => {
|
|
|
54
95
|
if (!hostname || !domain) {
|
|
55
96
|
return false;
|
|
56
97
|
}
|
|
57
|
-
if (!(hostname === domain ||
|
|
58
|
-
hostname.endsWith(`.${domain}`) ||
|
|
59
|
-
domain.endsWith(`.${hostname}`))) {
|
|
98
|
+
if (!(hostname === domain || hostname.endsWith(`.${domain}`))) {
|
|
60
99
|
return false;
|
|
61
100
|
}
|
|
62
101
|
if (!cookiePathMatches(cookie.path, parsedUrl?.pathname || '/')) {
|
|
@@ -82,6 +121,33 @@ const normalizeSameSite = (value) => {
|
|
|
82
121
|
}
|
|
83
122
|
return undefined;
|
|
84
123
|
};
|
|
124
|
+
const getDirectCookieDenialReason = (session, cookie) => {
|
|
125
|
+
const checker = session._get_cookie_access_denial_reason;
|
|
126
|
+
if (typeof checker !== 'function') {
|
|
127
|
+
return null;
|
|
128
|
+
}
|
|
129
|
+
return checker.call(session, cookie);
|
|
130
|
+
};
|
|
131
|
+
const filterDirectAllowedCookies = (session, cookies) => cookies.filter((cookie) => !getDirectCookieDenialReason(session, cookie));
|
|
132
|
+
const partitionDirectAllowedCookies = (session, cookies) => {
|
|
133
|
+
const allowedCookies = [];
|
|
134
|
+
const blockedCookies = [];
|
|
135
|
+
for (const cookie of cookies) {
|
|
136
|
+
if (getDirectCookieDenialReason(session, cookie)) {
|
|
137
|
+
blockedCookies.push(cookie);
|
|
138
|
+
}
|
|
139
|
+
else {
|
|
140
|
+
allowedCookies.push(cookie);
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
return { allowedCookies, blockedCookies };
|
|
144
|
+
};
|
|
145
|
+
const assertDirectCookieUrlAllowed = (session, url) => {
|
|
146
|
+
const denialReason = getDirectCookieDenialReason(session, { url });
|
|
147
|
+
if (denialReason) {
|
|
148
|
+
throw new Error(`Cookie URL blocked by domain policy: ${denialReason}`);
|
|
149
|
+
}
|
|
150
|
+
};
|
|
85
151
|
const DEFAULT_STDOUT = process.stdout;
|
|
86
152
|
const DEFAULT_STDERR = process.stderr;
|
|
87
153
|
const writeLine = (stream, message) => {
|
|
@@ -99,15 +165,55 @@ export const load_direct_state = (state_file = DIRECT_STATE_FILE) => {
|
|
|
99
165
|
}
|
|
100
166
|
};
|
|
101
167
|
export const save_direct_state = (state, state_file = DIRECT_STATE_FILE) => {
|
|
102
|
-
fs.
|
|
168
|
+
fs.mkdirSync(path.dirname(state_file), { recursive: true, mode: 0o700 });
|
|
169
|
+
if (process.platform !== 'win32' &&
|
|
170
|
+
path.resolve(state_file) === path.resolve(DIRECT_STATE_FILE)) {
|
|
171
|
+
fs.chmodSync(path.dirname(state_file), 0o700);
|
|
172
|
+
}
|
|
173
|
+
fs.writeFileSync(state_file, JSON.stringify(state, null, 2), {
|
|
174
|
+
encoding: 'utf8',
|
|
175
|
+
mode: 0o600,
|
|
176
|
+
});
|
|
177
|
+
if (process.platform !== 'win32') {
|
|
178
|
+
fs.chmodSync(state_file, 0o600);
|
|
179
|
+
}
|
|
103
180
|
};
|
|
104
181
|
export const clear_direct_state = (state_file = DIRECT_STATE_FILE) => {
|
|
105
182
|
fs.rmSync(state_file, { force: true });
|
|
106
183
|
};
|
|
184
|
+
const writePrivateFile = (filePath, contents) => {
|
|
185
|
+
fs.writeFileSync(filePath, contents, { encoding: 'utf8', mode: 0o600 });
|
|
186
|
+
if (process.platform !== 'win32') {
|
|
187
|
+
fs.chmodSync(filePath, 0o600);
|
|
188
|
+
}
|
|
189
|
+
};
|
|
190
|
+
const writePrivateBufferFile = (filePath, contents) => {
|
|
191
|
+
fs.writeFileSync(filePath, contents, { mode: 0o600 });
|
|
192
|
+
if (process.platform !== 'win32') {
|
|
193
|
+
fs.chmodSync(filePath, 0o600);
|
|
194
|
+
}
|
|
195
|
+
};
|
|
196
|
+
const isOwnedDirectUserDataDir = (userDataDir) => {
|
|
197
|
+
if (!userDataDir || !fs.existsSync(userDataDir)) {
|
|
198
|
+
return false;
|
|
199
|
+
}
|
|
200
|
+
try {
|
|
201
|
+
const resolvedDir = fs.realpathSync(userDataDir);
|
|
202
|
+
const resolvedTmp = fs.realpathSync(os.tmpdir());
|
|
203
|
+
return (path.dirname(resolvedDir) === resolvedTmp &&
|
|
204
|
+
path.basename(resolvedDir).startsWith('browser-use-direct-'));
|
|
205
|
+
}
|
|
206
|
+
catch {
|
|
207
|
+
return false;
|
|
208
|
+
}
|
|
209
|
+
};
|
|
107
210
|
const cleanupOwnedDirectUserDataDir = (state) => {
|
|
108
211
|
if (!state.owns_user_data_dir || !state.user_data_dir) {
|
|
109
212
|
return;
|
|
110
213
|
}
|
|
214
|
+
if (!isOwnedDirectUserDataDir(state.user_data_dir)) {
|
|
215
|
+
return;
|
|
216
|
+
}
|
|
111
217
|
try {
|
|
112
218
|
fs.rmSync(state.user_data_dir, { recursive: true, force: true });
|
|
113
219
|
}
|
|
@@ -301,40 +407,46 @@ const readDirectNodeData = async (session, node, kind) => {
|
|
|
301
407
|
if (!page?.evaluate) {
|
|
302
408
|
throw new Error('No active page available');
|
|
303
409
|
}
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
410
|
+
await validateDirectPageAfterAction(session, page);
|
|
411
|
+
try {
|
|
412
|
+
return await page.evaluate(({ xpath, dataKind }) => {
|
|
413
|
+
const element = document.evaluate(xpath, document, null, XPathResult.FIRST_ORDERED_NODE_TYPE, null).singleNodeValue;
|
|
414
|
+
if (!element) {
|
|
415
|
+
return null;
|
|
416
|
+
}
|
|
417
|
+
if (dataKind === 'text') {
|
|
418
|
+
return element.textContent?.trim() ?? '';
|
|
419
|
+
}
|
|
420
|
+
if (dataKind === 'value') {
|
|
421
|
+
return 'value' in element
|
|
422
|
+
? String(element.value ?? '')
|
|
423
|
+
: null;
|
|
424
|
+
}
|
|
425
|
+
if (dataKind === 'attributes') {
|
|
426
|
+
return Object.fromEntries(Array.from(element.attributes).map((attribute) => [
|
|
427
|
+
attribute.name,
|
|
428
|
+
attribute.value,
|
|
429
|
+
]));
|
|
430
|
+
}
|
|
431
|
+
if (dataKind === 'bbox') {
|
|
432
|
+
const rect = element.getBoundingClientRect();
|
|
433
|
+
return {
|
|
434
|
+
x: rect.x,
|
|
435
|
+
y: rect.y,
|
|
436
|
+
width: rect.width,
|
|
437
|
+
height: rect.height,
|
|
438
|
+
top: rect.top,
|
|
439
|
+
right: rect.right,
|
|
440
|
+
bottom: rect.bottom,
|
|
441
|
+
left: rect.left,
|
|
442
|
+
};
|
|
443
|
+
}
|
|
307
444
|
return null;
|
|
308
|
-
}
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
return 'value' in element
|
|
314
|
-
? String(element.value ?? '')
|
|
315
|
-
: null;
|
|
316
|
-
}
|
|
317
|
-
if (dataKind === 'attributes') {
|
|
318
|
-
return Object.fromEntries(Array.from(element.attributes).map((attribute) => [
|
|
319
|
-
attribute.name,
|
|
320
|
-
attribute.value,
|
|
321
|
-
]));
|
|
322
|
-
}
|
|
323
|
-
if (dataKind === 'bbox') {
|
|
324
|
-
const rect = element.getBoundingClientRect();
|
|
325
|
-
return {
|
|
326
|
-
x: rect.x,
|
|
327
|
-
y: rect.y,
|
|
328
|
-
width: rect.width,
|
|
329
|
-
height: rect.height,
|
|
330
|
-
top: rect.top,
|
|
331
|
-
right: rect.right,
|
|
332
|
-
bottom: rect.bottom,
|
|
333
|
-
left: rect.left,
|
|
334
|
-
};
|
|
335
|
-
}
|
|
336
|
-
return null;
|
|
337
|
-
}, { xpath: node.xpath, dataKind: kind });
|
|
445
|
+
}, { xpath: node.xpath, dataKind: kind });
|
|
446
|
+
}
|
|
447
|
+
finally {
|
|
448
|
+
await validateDirectPageAfterAction(session, page);
|
|
449
|
+
}
|
|
338
450
|
};
|
|
339
451
|
const takeDirectOptionValue = (args, index, option) => {
|
|
340
452
|
const next = args[index + 1]?.trim();
|
|
@@ -648,7 +760,7 @@ export const run_direct_command = async (argv, options = {}) => {
|
|
|
648
760
|
}
|
|
649
761
|
const bytes = Buffer.from(screenshot, 'base64');
|
|
650
762
|
if (outputPath) {
|
|
651
|
-
|
|
763
|
+
writePrivateBufferFile(outputPath, bytes);
|
|
652
764
|
writeLine(environment.stdout, `Screenshot saved to ${outputPath} (${bytes.length} bytes)`);
|
|
653
765
|
}
|
|
654
766
|
else {
|
|
@@ -737,7 +849,12 @@ export const run_direct_command = async (argv, options = {}) => {
|
|
|
737
849
|
if (!page?.waitForFunction) {
|
|
738
850
|
throw new Error('No active page available for wait text');
|
|
739
851
|
}
|
|
740
|
-
|
|
852
|
+
try {
|
|
853
|
+
await page.waitForFunction((needle) => document.body?.innerText?.includes(needle) ?? false, text, { timeout: 5000 });
|
|
854
|
+
}
|
|
855
|
+
finally {
|
|
856
|
+
await validateDirectPageAfterAction(session, page);
|
|
857
|
+
}
|
|
741
858
|
writeLine(environment.stdout, `Waited for text "${text}"`);
|
|
742
859
|
}
|
|
743
860
|
else {
|
|
@@ -750,7 +867,13 @@ export const run_direct_command = async (argv, options = {}) => {
|
|
|
750
867
|
if (!locator?.hover) {
|
|
751
868
|
throw new Error('Hover is not available for this element');
|
|
752
869
|
}
|
|
753
|
-
await
|
|
870
|
+
const page = await session.get_current_page?.();
|
|
871
|
+
try {
|
|
872
|
+
await locator.hover({ timeout: 5000 });
|
|
873
|
+
}
|
|
874
|
+
finally {
|
|
875
|
+
await validateDirectPageAfterAction(session, page);
|
|
876
|
+
}
|
|
754
877
|
writeLine(environment.stdout, `Hovered element [${index}]`);
|
|
755
878
|
}
|
|
756
879
|
else if (command === 'dblclick') {
|
|
@@ -759,7 +882,13 @@ export const run_direct_command = async (argv, options = {}) => {
|
|
|
759
882
|
if (!locator?.dblclick) {
|
|
760
883
|
throw new Error('Double-click is not available for this element');
|
|
761
884
|
}
|
|
762
|
-
await
|
|
885
|
+
const page = await session.get_current_page?.();
|
|
886
|
+
try {
|
|
887
|
+
await locator.dblclick({ timeout: 5000 });
|
|
888
|
+
}
|
|
889
|
+
finally {
|
|
890
|
+
await validateDirectPageAfterAction(session, page);
|
|
891
|
+
}
|
|
763
892
|
writeLine(environment.stdout, `Double-clicked element [${index}]`);
|
|
764
893
|
}
|
|
765
894
|
else if (command === 'rightclick') {
|
|
@@ -768,7 +897,13 @@ export const run_direct_command = async (argv, options = {}) => {
|
|
|
768
897
|
if (!locator?.click) {
|
|
769
898
|
throw new Error('Right-click is not available for this element');
|
|
770
899
|
}
|
|
771
|
-
await
|
|
900
|
+
const page = await session.get_current_page?.();
|
|
901
|
+
try {
|
|
902
|
+
await locator.click({ button: 'right', timeout: 5000 });
|
|
903
|
+
}
|
|
904
|
+
finally {
|
|
905
|
+
await validateDirectPageAfterAction(session, page);
|
|
906
|
+
}
|
|
772
907
|
writeLine(environment.stdout, `Right-clicked element [${index}]`);
|
|
773
908
|
}
|
|
774
909
|
else if (command === 'cookies') {
|
|
@@ -776,10 +911,14 @@ export const run_direct_command = async (argv, options = {}) => {
|
|
|
776
911
|
if (cookieCommand === 'get') {
|
|
777
912
|
const parsed = parseDirectCookieOptions(args.slice(2));
|
|
778
913
|
const url = parsed.url ?? parsed.positional[0] ?? null;
|
|
914
|
+
if (url) {
|
|
915
|
+
assertDirectCookieUrlAllowed(session, url);
|
|
916
|
+
}
|
|
779
917
|
const allCookies = (await session.get_cookies?.()) ?? [];
|
|
918
|
+
const allowedCookies = filterDirectAllowedCookies(session, allCookies);
|
|
780
919
|
const cookies = url
|
|
781
|
-
?
|
|
782
|
-
:
|
|
920
|
+
? allowedCookies.filter((cookie) => cookieMatchesUrl(cookie, url))
|
|
921
|
+
: allowedCookies;
|
|
783
922
|
writeLine(environment.stdout, JSON.stringify({ cookies, count: cookies.length }, null, 2));
|
|
784
923
|
}
|
|
785
924
|
else if (cookieCommand === 'set') {
|
|
@@ -815,6 +954,10 @@ export const run_direct_command = async (argv, options = {}) => {
|
|
|
815
954
|
else {
|
|
816
955
|
throw new Error('Provide cookie url/domain or open a page first');
|
|
817
956
|
}
|
|
957
|
+
const denialReason = getDirectCookieDenialReason(session, cookie);
|
|
958
|
+
if (denialReason) {
|
|
959
|
+
throw new Error(`Cookie target blocked by domain policy: ${denialReason}`);
|
|
960
|
+
}
|
|
818
961
|
await session.browser_context.addCookies([cookie]);
|
|
819
962
|
writeLine(environment.stdout, `Set cookie ${name}`);
|
|
820
963
|
}
|
|
@@ -825,16 +968,41 @@ export const run_direct_command = async (argv, options = {}) => {
|
|
|
825
968
|
const parsed = parseDirectCookieOptions(args.slice(2));
|
|
826
969
|
const url = parsed.url ?? parsed.positional[0] ?? null;
|
|
827
970
|
if (!url) {
|
|
828
|
-
|
|
829
|
-
|
|
971
|
+
if (typeof session.get_cookies !== 'function') {
|
|
972
|
+
await session.browser_context.clearCookies();
|
|
973
|
+
writeLine(environment.stdout, 'Cleared cookies');
|
|
974
|
+
}
|
|
975
|
+
else {
|
|
976
|
+
const allCookies = (await session.get_cookies?.({ include_blocked: true })) ?? [];
|
|
977
|
+
const { allowedCookies, blockedCookies } = partitionDirectAllowedCookies(session, allCookies);
|
|
978
|
+
if (allowedCookies.length === 0 && blockedCookies.length > 0) {
|
|
979
|
+
writeLine(environment.stdout, 'Cleared 0 cookies');
|
|
980
|
+
}
|
|
981
|
+
else {
|
|
982
|
+
const addCookies = session.browser_context.addCookies;
|
|
983
|
+
if (blockedCookies.length > 0 && !addCookies) {
|
|
984
|
+
throw new Error('Browser context does not support preserving blocked cookies');
|
|
985
|
+
}
|
|
986
|
+
await session.browser_context.clearCookies();
|
|
987
|
+
if (blockedCookies.length > 0) {
|
|
988
|
+
await addCookies(blockedCookies);
|
|
989
|
+
}
|
|
990
|
+
writeLine(environment.stdout, `Cleared ${allowedCookies.length} cookies`);
|
|
991
|
+
}
|
|
992
|
+
}
|
|
830
993
|
}
|
|
831
994
|
else {
|
|
832
|
-
|
|
995
|
+
assertDirectCookieUrlAllowed(session, url);
|
|
996
|
+
const allCookies = (await session.get_cookies?.({ include_blocked: true })) ?? [];
|
|
833
997
|
const remaining = allCookies.filter((cookie) => !cookieMatchesUrl(cookie, url));
|
|
834
998
|
const removedCount = allCookies.length - remaining.length;
|
|
999
|
+
const addCookies = session.browser_context.addCookies;
|
|
1000
|
+
if (remaining.length > 0 && !addCookies) {
|
|
1001
|
+
throw new Error('Browser context does not support preserving non-matching cookies');
|
|
1002
|
+
}
|
|
835
1003
|
await session.browser_context.clearCookies();
|
|
836
|
-
if (remaining.length > 0
|
|
837
|
-
await
|
|
1004
|
+
if (remaining.length > 0) {
|
|
1005
|
+
await addCookies(remaining);
|
|
838
1006
|
}
|
|
839
1007
|
writeLine(environment.stdout, `Cleared ${removedCount} cookies matching ${url}`);
|
|
840
1008
|
}
|
|
@@ -846,12 +1014,16 @@ export const run_direct_command = async (argv, options = {}) => {
|
|
|
846
1014
|
}
|
|
847
1015
|
const parsed = parseDirectCookieOptions(args.slice(3));
|
|
848
1016
|
const url = parsed.url ?? parsed.positional[0] ?? null;
|
|
1017
|
+
if (url) {
|
|
1018
|
+
assertDirectCookieUrlAllowed(session, url);
|
|
1019
|
+
}
|
|
849
1020
|
const allCookies = (await session.get_cookies?.()) ?? [];
|
|
1021
|
+
const allowedCookies = filterDirectAllowedCookies(session, allCookies);
|
|
850
1022
|
const cookies = url
|
|
851
|
-
?
|
|
852
|
-
:
|
|
1023
|
+
? allowedCookies.filter((cookie) => cookieMatchesUrl(cookie, url))
|
|
1024
|
+
: allowedCookies;
|
|
853
1025
|
const outputPath = path.resolve(file);
|
|
854
|
-
|
|
1026
|
+
writePrivateFile(outputPath, JSON.stringify(cookies, null, 2));
|
|
855
1027
|
writeLine(environment.stdout, `Exported ${cookies.length} cookies to ${outputPath}`);
|
|
856
1028
|
}
|
|
857
1029
|
else if (cookieCommand === 'import') {
|
|
@@ -868,8 +1040,11 @@ export const run_direct_command = async (argv, options = {}) => {
|
|
|
868
1040
|
if (!Array.isArray(cookies)) {
|
|
869
1041
|
throw new Error('Cookie import file must contain a JSON array');
|
|
870
1042
|
}
|
|
871
|
-
|
|
872
|
-
|
|
1043
|
+
const allowedCookies = filterDirectAllowedCookies(session, cookies);
|
|
1044
|
+
if (allowedCookies.length > 0) {
|
|
1045
|
+
await session.browser_context.addCookies(allowedCookies);
|
|
1046
|
+
}
|
|
1047
|
+
writeLine(environment.stdout, `Imported ${allowedCookies.length} cookies from ${inputPath}`);
|
|
873
1048
|
}
|
|
874
1049
|
else {
|
|
875
1050
|
throw new Error('Usage: cookies get [url|--url <url>] | cookies set <name> <value> | cookies clear [--url <url>] | cookies export <file> [--url <url>] | cookies import <file>');
|
|
@@ -882,7 +1057,13 @@ export const run_direct_command = async (argv, options = {}) => {
|
|
|
882
1057
|
if (!page?.title) {
|
|
883
1058
|
throw new Error('No active page available for get title');
|
|
884
1059
|
}
|
|
885
|
-
|
|
1060
|
+
await validateDirectPageAfterAction(session, page);
|
|
1061
|
+
try {
|
|
1062
|
+
writeLine(environment.stdout, await page.title());
|
|
1063
|
+
}
|
|
1064
|
+
finally {
|
|
1065
|
+
await validateDirectPageAfterAction(session, page);
|
|
1066
|
+
}
|
|
886
1067
|
}
|
|
887
1068
|
else if (subcommand === 'html') {
|
|
888
1069
|
const selector = args.slice(2).join(' ').trim();
|
|
@@ -894,10 +1075,18 @@ export const run_direct_command = async (argv, options = {}) => {
|
|
|
894
1075
|
if (!page?.evaluate) {
|
|
895
1076
|
throw new Error('No active page available for get html');
|
|
896
1077
|
}
|
|
897
|
-
|
|
898
|
-
|
|
899
|
-
|
|
900
|
-
|
|
1078
|
+
await validateDirectPageAfterAction(session, page);
|
|
1079
|
+
const html = await (async () => {
|
|
1080
|
+
try {
|
|
1081
|
+
return await page.evaluate((targetSelector) => {
|
|
1082
|
+
const element = document.querySelector(targetSelector);
|
|
1083
|
+
return element ? element.outerHTML : null;
|
|
1084
|
+
}, selector);
|
|
1085
|
+
}
|
|
1086
|
+
finally {
|
|
1087
|
+
await validateDirectPageAfterAction(session, page);
|
|
1088
|
+
}
|
|
1089
|
+
})();
|
|
901
1090
|
if (typeof html !== 'string' || html.length === 0) {
|
|
902
1091
|
throw new Error(`No element found for selector: ${selector}`);
|
|
903
1092
|
}
|
|
@@ -939,10 +1128,18 @@ export const run_direct_command = async (argv, options = {}) => {
|
|
|
939
1128
|
if (!page?.evaluate) {
|
|
940
1129
|
throw new Error('No active page available for html');
|
|
941
1130
|
}
|
|
942
|
-
|
|
943
|
-
|
|
944
|
-
|
|
945
|
-
|
|
1131
|
+
await validateDirectPageAfterAction(session, page);
|
|
1132
|
+
const html = await (async () => {
|
|
1133
|
+
try {
|
|
1134
|
+
return await page.evaluate((targetSelector) => {
|
|
1135
|
+
const element = document.querySelector(targetSelector);
|
|
1136
|
+
return element ? element.outerHTML : null;
|
|
1137
|
+
}, selector);
|
|
1138
|
+
}
|
|
1139
|
+
finally {
|
|
1140
|
+
await validateDirectPageAfterAction(session, page);
|
|
1141
|
+
}
|
|
1142
|
+
})();
|
|
946
1143
|
if (typeof html !== 'string' || html.length === 0) {
|
|
947
1144
|
throw new Error(`No element found for selector: ${selector}`);
|
|
948
1145
|
}
|
|
@@ -974,11 +1171,11 @@ export const run_direct_command = async (argv, options = {}) => {
|
|
|
974
1171
|
};
|
|
975
1172
|
export const main = async (argv = process.argv.slice(2)) => {
|
|
976
1173
|
const exitCode = await run_direct_command(argv);
|
|
977
|
-
if (import.meta.url
|
|
1174
|
+
if (isMainModule(import.meta.url)) {
|
|
978
1175
|
process.exit(exitCode);
|
|
979
1176
|
}
|
|
980
1177
|
return exitCode;
|
|
981
1178
|
};
|
|
982
|
-
if (import.meta.url
|
|
1179
|
+
if (isMainModule(import.meta.url)) {
|
|
983
1180
|
void main();
|
|
984
1181
|
}
|
|
@@ -7,6 +7,7 @@ export declare class SkillCliServer {
|
|
|
7
7
|
readonly registry: SessionRegistry;
|
|
8
8
|
constructor(options?: SkillCliServerOptions);
|
|
9
9
|
private _require_node_by_index;
|
|
10
|
+
private _run_with_page_validation;
|
|
10
11
|
private _read_node_data;
|
|
11
12
|
private _handle_browser_action;
|
|
12
13
|
handle_request(request: Request | string): Promise<Response>;
|