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