tau-all-agent 0.1.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/LICENSE +21 -0
- package/README.md +45 -0
- package/extensions/answer.ts +601 -0
- package/extensions/branch-term.ts +405 -0
- package/extensions/btw.ts +444 -0
- package/extensions/ghostty.ts +301 -0
- package/extensions/git-diff-stats.ts +277 -0
- package/extensions/git-pr-status.ts +286 -0
- package/extensions/insights.ts +2367 -0
- package/extensions/interlude.ts +144 -0
- package/extensions/loop.ts +529 -0
- package/extensions/memory.ts +1889 -0
- package/extensions/notify.ts +161 -0
- package/extensions/openai-fast.ts +227 -0
- package/extensions/openai-verbosity.ts +223 -0
- package/extensions/review.ts +4347 -0
- package/extensions/sandbox/index.ts +2578 -0
- package/extensions/telegram/README.md +81 -0
- package/extensions/telegram/daemon.mjs +1942 -0
- package/extensions/telegram/index.ts +1073 -0
- package/extensions/telegram/message-text.d.mts +1 -0
- package/extensions/telegram/message-text.mjs +16 -0
- package/extensions/usage/anthropic.ts +198 -0
- package/extensions/usage/github-copilot.ts +204 -0
- package/extensions/usage/google-gemini-cli.ts +232 -0
- package/extensions/usage/index.ts +2310 -0
- package/extensions/usage/minimax.ts +208 -0
- package/extensions/usage/openai-codex.ts +180 -0
- package/extensions/usage/openrouter.ts +168 -0
- package/extensions/usage/providers.ts +26 -0
- package/extensions/usage/shared.ts +156 -0
- package/extensions/usage/types.ts +53 -0
- package/extensions/usage/zai.ts +186 -0
- package/extensions/websearch/README.md +68 -0
- package/extensions/websearch/browser/chromium.ts +330 -0
- package/extensions/websearch/browser/discovery.ts +75 -0
- package/extensions/websearch/browser/firefox.ts +150 -0
- package/extensions/websearch/browser/sqlite.ts +38 -0
- package/extensions/websearch/config.ts +76 -0
- package/extensions/websearch/index.ts +312 -0
- package/extensions/websearch/normalize.ts +155 -0
- package/extensions/websearch/providers/anthropic.pi.ts +139 -0
- package/extensions/websearch/providers/gemini.browser.ts +185 -0
- package/extensions/websearch/providers/gemini.pi.ts +108 -0
- package/extensions/websearch/providers/openai-codex.browser.ts +77 -0
- package/extensions/websearch/providers/openai-codex.pi.ts +49 -0
- package/extensions/websearch/providers/openai-codex.shared.ts +123 -0
- package/extensions/websearch/providers/pi-model.shared.ts +86 -0
- package/extensions/websearch/providers/search-prompt.shared.ts +17 -0
- package/extensions/websearch/providers/shared.ts +76 -0
- package/extensions/websearch/types.ts +52 -0
- package/extensions/worktree.ts +2223 -0
- package/package.json +76 -0
- package/skills/browser-tools/SKILL.md +253 -0
- package/skills/browser-tools/scripts/browser-content.js +100 -0
- package/skills/browser-tools/scripts/browser-cookies.js +33 -0
- package/skills/browser-tools/scripts/browser-dismiss-cookies.js +455 -0
- package/skills/browser-tools/scripts/browser-eval.js +75 -0
- package/skills/browser-tools/scripts/browser-logs-tail.js +89 -0
- package/skills/browser-tools/scripts/browser-nav.js +28 -0
- package/skills/browser-tools/scripts/browser-net-summary.js +115 -0
- package/skills/browser-tools/scripts/browser-pick.js +133 -0
- package/skills/browser-tools/scripts/browser-screenshot.js +18 -0
- package/skills/browser-tools/scripts/browser-start.js +281 -0
- package/skills/browser-tools/scripts/browser-watch.js +321 -0
- package/skills/browser-tools/scripts/utils.js +60 -0
- package/skills/git-clean-history/SKILL.md +45 -0
- package/skills/git-commit/SKILL.md +55 -0
- package/skills/homeassistant-ops/SKILL.md +184 -0
- package/skills/homeassistant-ops/references/api.md +64 -0
- package/skills/homeassistant-ops/references/id_conventions.md +71 -0
- package/skills/homeassistant-ops/references/playbook.md +150 -0
- package/skills/homeassistant-ops/scripts/ha_ops.js +2942 -0
- package/skills/openscad/SKILL.md +149 -0
- package/skills/openscad/examples/parametric_box.scad +92 -0
- package/skills/openscad/examples/phone_stand.scad +95 -0
- package/skills/openscad/scripts/common.sh +49 -0
- package/skills/openscad/scripts/export-stl.sh +56 -0
- package/skills/openscad/scripts/extract-params.sh +147 -0
- package/skills/openscad/scripts/multi-preview.sh +68 -0
- package/skills/openscad/scripts/preview.sh +74 -0
- package/skills/openscad/scripts/render-with-params.sh +91 -0
- package/skills/openscad/scripts/validate.sh +46 -0
- package/skills/oracle/SKILL.md +64 -0
- package/skills/oracle/scripts/oracle +249 -0
- package/skills/oracle/scripts/oracle-bundle +227 -0
- package/skills/sentry/SKILL.md +198 -0
- package/skills/sentry/lib/auth.js +99 -0
- package/skills/sentry/scripts/fetch-event.js +325 -0
- package/skills/sentry/scripts/fetch-issue.js +368 -0
- package/skills/sentry/scripts/list-issues.js +245 -0
- package/skills/sentry/scripts/search-events.js +291 -0
- package/skills/sentry/scripts/search-logs.js +234 -0
- package/skills/update-changelog/SKILL.md +135 -0
- package/skills/web-design/SKILL.md +117 -0
|
@@ -0,0 +1,455 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Dismiss common cookie consent dialogs.
|
|
5
|
+
*
|
|
6
|
+
* Usage:
|
|
7
|
+
* browser-dismiss-cookies.js # accept cookies
|
|
8
|
+
* browser-dismiss-cookies.js --reject # reject cookies (where possible)
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
import { setTimeout as sleep } from "node:timers/promises";
|
|
12
|
+
|
|
13
|
+
import { connectBrowser, getActivePage } from "./utils.js";
|
|
14
|
+
|
|
15
|
+
const reject = process.argv.includes("--reject");
|
|
16
|
+
const mode = reject ? "reject" : "accept";
|
|
17
|
+
const acceptCookies = !reject;
|
|
18
|
+
|
|
19
|
+
const DEBUG = process.env.DEBUG === "1";
|
|
20
|
+
const log = DEBUG ? (...args) => console.error("[debug]", ...args) : () => {};
|
|
21
|
+
|
|
22
|
+
// Mostly ported from mitsuhiko/agent-stuff web-browser skill.
|
|
23
|
+
const COOKIE_DISMISS_FN = (acceptCookies) => {
|
|
24
|
+
const clicked = [];
|
|
25
|
+
|
|
26
|
+
const isVisible = (el) => {
|
|
27
|
+
if (!el) return false;
|
|
28
|
+
const style = getComputedStyle(el);
|
|
29
|
+
return (
|
|
30
|
+
style.display !== "none" &&
|
|
31
|
+
style.visibility !== "hidden" &&
|
|
32
|
+
style.opacity !== "0" &&
|
|
33
|
+
(el.offsetParent !== null || style.position === "fixed" || style.position === "sticky")
|
|
34
|
+
);
|
|
35
|
+
};
|
|
36
|
+
|
|
37
|
+
const tryClick = (selector, description) => {
|
|
38
|
+
const el = typeof selector === "string" ? document.querySelector(selector) : selector;
|
|
39
|
+
if (isVisible(el)) {
|
|
40
|
+
el.click();
|
|
41
|
+
clicked.push(description || selector);
|
|
42
|
+
return true;
|
|
43
|
+
}
|
|
44
|
+
return false;
|
|
45
|
+
};
|
|
46
|
+
|
|
47
|
+
const findButtonByText = (patterns, container = document) => {
|
|
48
|
+
const buttons = Array.from(
|
|
49
|
+
container.querySelectorAll(
|
|
50
|
+
'button, [role="button"], a.button, input[type="submit"], input[type="button"]',
|
|
51
|
+
),
|
|
52
|
+
);
|
|
53
|
+
|
|
54
|
+
// Sort patterns by length descending to match more specific patterns first.
|
|
55
|
+
const sortedPatterns = [...patterns].sort((a, b) => b.length - a.length);
|
|
56
|
+
|
|
57
|
+
for (const pattern of sortedPatterns) {
|
|
58
|
+
for (const btn of buttons) {
|
|
59
|
+
const text = (btn.textContent || btn.value || "").trim().toLowerCase();
|
|
60
|
+
if (text.length > 100) continue;
|
|
61
|
+
if (!isVisible(btn)) continue;
|
|
62
|
+
if (typeof pattern === "string" ? text.includes(pattern) : pattern.test(text)) {
|
|
63
|
+
return btn;
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
return null;
|
|
68
|
+
};
|
|
69
|
+
|
|
70
|
+
const acceptPatterns = [
|
|
71
|
+
"accept all",
|
|
72
|
+
"accept cookies",
|
|
73
|
+
"allow all",
|
|
74
|
+
"allow cookies",
|
|
75
|
+
"i agree",
|
|
76
|
+
"i accept",
|
|
77
|
+
"yes, i agree",
|
|
78
|
+
"agree and continue",
|
|
79
|
+
"alle akzeptieren",
|
|
80
|
+
"akzeptieren",
|
|
81
|
+
"alle zulassen",
|
|
82
|
+
"zustimmen",
|
|
83
|
+
"annehmen",
|
|
84
|
+
"einverstanden",
|
|
85
|
+
"accepter tout",
|
|
86
|
+
"tout accepter",
|
|
87
|
+
"j'accepte",
|
|
88
|
+
"accepter et continuer",
|
|
89
|
+
"accepter",
|
|
90
|
+
"accetta tutti",
|
|
91
|
+
"accetta",
|
|
92
|
+
"accetto",
|
|
93
|
+
"aceptar todo",
|
|
94
|
+
"aceptar",
|
|
95
|
+
"acepto",
|
|
96
|
+
"aceitar tudo",
|
|
97
|
+
"aceitar",
|
|
98
|
+
"continue",
|
|
99
|
+
"agree",
|
|
100
|
+
];
|
|
101
|
+
|
|
102
|
+
const rejectPatterns = [
|
|
103
|
+
"reject all",
|
|
104
|
+
"decline all",
|
|
105
|
+
"deny all",
|
|
106
|
+
"refuse all",
|
|
107
|
+
"i do not agree",
|
|
108
|
+
"i disagree",
|
|
109
|
+
"no thanks",
|
|
110
|
+
"alle ablehnen",
|
|
111
|
+
"ablehnen",
|
|
112
|
+
"nicht zustimmen",
|
|
113
|
+
"refuser tout",
|
|
114
|
+
"tout refuser",
|
|
115
|
+
"refuser",
|
|
116
|
+
"rifiuta tutti",
|
|
117
|
+
"rifiuta",
|
|
118
|
+
"rechazar todo",
|
|
119
|
+
"rechazar",
|
|
120
|
+
"rejeitar tudo",
|
|
121
|
+
"rejeitar",
|
|
122
|
+
"only necessary",
|
|
123
|
+
"necessary only",
|
|
124
|
+
"nur notwendige",
|
|
125
|
+
"essential only",
|
|
126
|
+
"nur essentielle",
|
|
127
|
+
];
|
|
128
|
+
|
|
129
|
+
const patterns = acceptCookies ? acceptPatterns : rejectPatterns;
|
|
130
|
+
|
|
131
|
+
// OneTrust
|
|
132
|
+
if (document.querySelector("#onetrust-banner-sdk")) {
|
|
133
|
+
const selector = acceptCookies
|
|
134
|
+
? "#onetrust-accept-btn-handler"
|
|
135
|
+
: "#onetrust-reject-all-handler";
|
|
136
|
+
if (tryClick(selector, "OneTrust")) return clicked;
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
// Google
|
|
140
|
+
if (
|
|
141
|
+
document.querySelector("[data-consent-dialog]") ||
|
|
142
|
+
document.querySelector('form[action*="consent.google"]') ||
|
|
143
|
+
document.querySelector("#CXQnmb")
|
|
144
|
+
) {
|
|
145
|
+
const selector = acceptCookies ? "#L2AGLb" : "#W0wltc";
|
|
146
|
+
if (tryClick(selector, "Google Consent")) return clicked;
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
// YouTube
|
|
150
|
+
if (document.querySelector("ytd-consent-bump-v2-lightbox")) {
|
|
151
|
+
const btn = Array.from(document.querySelectorAll("ytd-consent-bump-v2-lightbox button")).find(
|
|
152
|
+
(b) =>
|
|
153
|
+
acceptCookies
|
|
154
|
+
? b.textContent.includes("Accept all") || b.ariaLabel?.includes("Accept")
|
|
155
|
+
: b.textContent.includes("Reject all") || b.ariaLabel?.includes("Reject"),
|
|
156
|
+
);
|
|
157
|
+
if (btn) {
|
|
158
|
+
btn.click();
|
|
159
|
+
clicked.push("YouTube");
|
|
160
|
+
return clicked;
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
// Cookiebot
|
|
165
|
+
if (document.querySelector("#CybotCookiebotDialog")) {
|
|
166
|
+
const selector = acceptCookies
|
|
167
|
+
? "#CybotCookiebotDialogBodyLevelButtonLevelOptinAllowAll, #CybotCookiebotDialogBodyButtonAccept"
|
|
168
|
+
: "#CybotCookiebotDialogBodyButtonDecline, #CybotCookiebotDialogBodyLevelButtonLevelOptinDeclineAll";
|
|
169
|
+
if (tryClick(selector, "Cookiebot")) return clicked;
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
// Didomi
|
|
173
|
+
if (document.querySelector("#didomi-host") || window.Didomi) {
|
|
174
|
+
const selector = acceptCookies
|
|
175
|
+
? "#didomi-notice-agree-button"
|
|
176
|
+
: '#didomi-notice-disagree-button, [data-testid="disagree-button"]';
|
|
177
|
+
if (tryClick(selector, "Didomi")) return clicked;
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
// Quantcast
|
|
181
|
+
if (document.querySelector(".qc-cmp2-container")) {
|
|
182
|
+
const selector = acceptCookies
|
|
183
|
+
? '.qc-cmp2-summary-buttons button[mode="primary"], .qc-cmp2-button[data-testid="accept-all"]'
|
|
184
|
+
: '.qc-cmp2-summary-buttons button[mode="secondary"], .qc-cmp2-button[data-testid="reject-all"]';
|
|
185
|
+
if (tryClick(selector, "Quantcast")) return clicked;
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
// Usercentrics (shadow DOM)
|
|
189
|
+
const ucRoot = document.querySelector("#usercentrics-root");
|
|
190
|
+
if (ucRoot && ucRoot.shadowRoot) {
|
|
191
|
+
const shadow = ucRoot.shadowRoot;
|
|
192
|
+
const btn = acceptCookies
|
|
193
|
+
? shadow.querySelector('[data-testid="uc-accept-all-button"]')
|
|
194
|
+
: shadow.querySelector('[data-testid="uc-deny-all-button"]');
|
|
195
|
+
if (btn) {
|
|
196
|
+
btn.click();
|
|
197
|
+
clicked.push("Usercentrics");
|
|
198
|
+
return clicked;
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
// TrustArc
|
|
203
|
+
if (
|
|
204
|
+
document.querySelector("#truste-consent-track") ||
|
|
205
|
+
document.querySelector(".trustarc-banner")
|
|
206
|
+
) {
|
|
207
|
+
const selector = acceptCookies
|
|
208
|
+
? "#truste-consent-button, .trustarc-agree-btn"
|
|
209
|
+
: ".trustarc-decline-btn";
|
|
210
|
+
if (tryClick(selector, "TrustArc")) return clicked;
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
// Klaro
|
|
214
|
+
if (document.querySelector(".klaro")) {
|
|
215
|
+
const selector = acceptCookies
|
|
216
|
+
? ".klaro .cm-btn-accept-all, .klaro .cm-btn-success"
|
|
217
|
+
: ".klaro .cm-btn-decline";
|
|
218
|
+
if (tryClick(selector, "Klaro")) return clicked;
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
// BBC
|
|
222
|
+
if (document.querySelector("#bbccookies, .bbccookies-banner")) {
|
|
223
|
+
if (acceptCookies && tryClick("#bbccookies-continue-button", "BBC")) return clicked;
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
// Amazon
|
|
227
|
+
if (document.querySelector("#sp-cc") || document.querySelector("#sp-cc-accept")) {
|
|
228
|
+
const selector = acceptCookies ? "#sp-cc-accept" : "#sp-cc-rejectall-link, #sp-cc-decline";
|
|
229
|
+
if (tryClick(selector, "Amazon")) return clicked;
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
// CookieYes
|
|
233
|
+
if (
|
|
234
|
+
document.querySelector("#cookie-law-info-bar") ||
|
|
235
|
+
document.querySelector(".cky-consent-container")
|
|
236
|
+
) {
|
|
237
|
+
const selector = acceptCookies
|
|
238
|
+
? "#cookie_action_close_header, .cky-btn-accept"
|
|
239
|
+
: ".cky-btn-reject";
|
|
240
|
+
if (tryClick(selector, "CookieYes")) return clicked;
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
// Generic containers
|
|
244
|
+
const consentContainers = [
|
|
245
|
+
"[class*='cookie-banner']",
|
|
246
|
+
"[class*='cookie-consent']",
|
|
247
|
+
"[class*='cookie-notice']",
|
|
248
|
+
"[class*='cookieBanner']",
|
|
249
|
+
"[class*='cookieConsent']",
|
|
250
|
+
"[class*='cookieNotice']",
|
|
251
|
+
"[id*='cookie-banner']",
|
|
252
|
+
"[id*='cookie-consent']",
|
|
253
|
+
"[id*='cookie-notice']",
|
|
254
|
+
"[class*='consent-banner']",
|
|
255
|
+
"[class*='consent-modal']",
|
|
256
|
+
"[class*='consent-dialog']",
|
|
257
|
+
"[class*='gdpr']",
|
|
258
|
+
"[id*='gdpr']",
|
|
259
|
+
"[class*='privacy-banner']",
|
|
260
|
+
"[class*='privacy-notice']",
|
|
261
|
+
"[role='dialog'][aria-label*='cookie' i]",
|
|
262
|
+
"[role='dialog'][aria-label*='consent' i]",
|
|
263
|
+
];
|
|
264
|
+
|
|
265
|
+
for (const containerSel of consentContainers) {
|
|
266
|
+
const containers = document.querySelectorAll(containerSel);
|
|
267
|
+
for (const container of containers) {
|
|
268
|
+
if (!isVisible(container)) continue;
|
|
269
|
+
if (container.tagName === "HTML" || container.tagName === "BODY") continue;
|
|
270
|
+
const btn = findButtonByText(patterns, container);
|
|
271
|
+
if (btn) {
|
|
272
|
+
btn.click();
|
|
273
|
+
clicked.push(`Generic (${containerSel})`);
|
|
274
|
+
return clicked;
|
|
275
|
+
}
|
|
276
|
+
}
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
// Text-based last resort
|
|
280
|
+
const allContainers = document.querySelectorAll(
|
|
281
|
+
"div, section, aside, [class*='modal'], [class*='dialog'], [role='dialog']",
|
|
282
|
+
);
|
|
283
|
+
for (const container of allContainers) {
|
|
284
|
+
if (!isVisible(container)) continue;
|
|
285
|
+
const text = container.textContent?.toLowerCase() || "";
|
|
286
|
+
if (text.includes("cookie") && text.length > 100 && text.length < 3000) {
|
|
287
|
+
const btn = findButtonByText(patterns, container);
|
|
288
|
+
if (btn && isVisible(btn)) {
|
|
289
|
+
btn.click();
|
|
290
|
+
clicked.push("Generic (text-based)");
|
|
291
|
+
return clicked;
|
|
292
|
+
}
|
|
293
|
+
}
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
// Final fallback: scan for visible buttons when page mentions cookies
|
|
297
|
+
if (document.body.textContent?.toLowerCase().includes("cookie")) {
|
|
298
|
+
const exactPatterns = acceptCookies
|
|
299
|
+
? ["accept all", "accept cookies", "allow all", "i agree", "alle akzeptieren"]
|
|
300
|
+
: ["reject all", "decline all", "reject optional", "alle ablehnen"];
|
|
301
|
+
|
|
302
|
+
const singleWordPatterns = acceptCookies ? ["accept", "agree"] : ["reject", "decline"];
|
|
303
|
+
|
|
304
|
+
const buttons = document.querySelectorAll("button, [role='button']");
|
|
305
|
+
for (const btn of buttons) {
|
|
306
|
+
if (!isVisible(btn)) continue;
|
|
307
|
+
const text = (btn.textContent || "").trim().toLowerCase();
|
|
308
|
+
if (exactPatterns.some((p) => text.includes(p))) {
|
|
309
|
+
btn.click();
|
|
310
|
+
clicked.push("Generic (exact match)");
|
|
311
|
+
return clicked;
|
|
312
|
+
}
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
for (const btn of buttons) {
|
|
316
|
+
if (!isVisible(btn)) continue;
|
|
317
|
+
const text = (btn.textContent || "").trim().toLowerCase();
|
|
318
|
+
if (singleWordPatterns.some((p) => text === p)) {
|
|
319
|
+
btn.click();
|
|
320
|
+
clicked.push("Generic (single word)");
|
|
321
|
+
return clicked;
|
|
322
|
+
}
|
|
323
|
+
}
|
|
324
|
+
}
|
|
325
|
+
|
|
326
|
+
return clicked;
|
|
327
|
+
};
|
|
328
|
+
|
|
329
|
+
const IFRAME_DISMISS_FN = (acceptCookies) => {
|
|
330
|
+
const clicked = [];
|
|
331
|
+
|
|
332
|
+
const isVisible = (el) => {
|
|
333
|
+
if (!el) return false;
|
|
334
|
+
const style = getComputedStyle(el);
|
|
335
|
+
return (
|
|
336
|
+
style.display !== "none" &&
|
|
337
|
+
style.visibility !== "hidden" &&
|
|
338
|
+
style.opacity !== "0" &&
|
|
339
|
+
(el.offsetParent !== null || style.position === "fixed" || style.position === "sticky")
|
|
340
|
+
);
|
|
341
|
+
};
|
|
342
|
+
|
|
343
|
+
const rejectIndicators = [
|
|
344
|
+
"do not",
|
|
345
|
+
"don't",
|
|
346
|
+
"nicht",
|
|
347
|
+
"no ",
|
|
348
|
+
"refuse",
|
|
349
|
+
"reject",
|
|
350
|
+
"decline",
|
|
351
|
+
"deny",
|
|
352
|
+
"disagree",
|
|
353
|
+
"ablehnen",
|
|
354
|
+
"refuser",
|
|
355
|
+
"rifiuta",
|
|
356
|
+
"rechazar",
|
|
357
|
+
"manage",
|
|
358
|
+
"settings",
|
|
359
|
+
"options",
|
|
360
|
+
"customize",
|
|
361
|
+
];
|
|
362
|
+
|
|
363
|
+
const acceptIndicators = [
|
|
364
|
+
"accept",
|
|
365
|
+
"agree",
|
|
366
|
+
"allow",
|
|
367
|
+
"yes",
|
|
368
|
+
"ok",
|
|
369
|
+
"got it",
|
|
370
|
+
"continue",
|
|
371
|
+
"akzeptieren",
|
|
372
|
+
"zustimmen",
|
|
373
|
+
"accepter",
|
|
374
|
+
"accetta",
|
|
375
|
+
"aceptar",
|
|
376
|
+
];
|
|
377
|
+
|
|
378
|
+
const isRejectButton = (text) => rejectIndicators.some((p) => text.includes(p));
|
|
379
|
+
const isAcceptButton = (text) =>
|
|
380
|
+
acceptIndicators.some((p) => text.includes(p)) && !isRejectButton(text);
|
|
381
|
+
|
|
382
|
+
const buttons = document.querySelectorAll("button, [role='button']");
|
|
383
|
+
for (const btn of buttons) {
|
|
384
|
+
const text = (btn.textContent || "").trim().toLowerCase();
|
|
385
|
+
if (!isVisible(btn)) continue;
|
|
386
|
+
const shouldClick = acceptCookies ? isAcceptButton(text) : isRejectButton(text);
|
|
387
|
+
if (shouldClick) {
|
|
388
|
+
btn.click();
|
|
389
|
+
clicked.push("iframe: " + text.slice(0, 30));
|
|
390
|
+
return clicked;
|
|
391
|
+
}
|
|
392
|
+
}
|
|
393
|
+
|
|
394
|
+
const spBtn = acceptCookies
|
|
395
|
+
? document.querySelector('[title="Accept All"], [title="Accept"], [aria-label*="Accept"]')
|
|
396
|
+
: document.querySelector('[title="Reject All"], [title="Reject"], [aria-label*="Reject"]');
|
|
397
|
+
|
|
398
|
+
if (spBtn) {
|
|
399
|
+
spBtn.click();
|
|
400
|
+
clicked.push("Sourcepoint iframe");
|
|
401
|
+
return clicked;
|
|
402
|
+
}
|
|
403
|
+
|
|
404
|
+
return clicked;
|
|
405
|
+
};
|
|
406
|
+
|
|
407
|
+
const looksLikeConsentFrame = (url) => {
|
|
408
|
+
if (!url) return false;
|
|
409
|
+
return (
|
|
410
|
+
url.includes("sp_message") ||
|
|
411
|
+
url.includes("consent") ||
|
|
412
|
+
url.includes("privacy") ||
|
|
413
|
+
url.includes("cmp") ||
|
|
414
|
+
url.includes("sourcepoint") ||
|
|
415
|
+
url.includes("cookie") ||
|
|
416
|
+
url.includes("privacy-mgmt")
|
|
417
|
+
);
|
|
418
|
+
};
|
|
419
|
+
|
|
420
|
+
const browser = await connectBrowser();
|
|
421
|
+
const page = await getActivePage(browser);
|
|
422
|
+
|
|
423
|
+
// Give dialogs a moment to appear after navigation.
|
|
424
|
+
await sleep(500);
|
|
425
|
+
|
|
426
|
+
log("trying main page...");
|
|
427
|
+
let result = await page.evaluate(COOKIE_DISMISS_FN, acceptCookies);
|
|
428
|
+
|
|
429
|
+
if (!Array.isArray(result)) result = [];
|
|
430
|
+
|
|
431
|
+
if (result.length === 0) {
|
|
432
|
+
log("trying frames...");
|
|
433
|
+
for (const frame of page.frames()) {
|
|
434
|
+
const url = frame.url();
|
|
435
|
+
if (!looksLikeConsentFrame(url)) continue;
|
|
436
|
+
try {
|
|
437
|
+
const r = await frame.evaluate(IFRAME_DISMISS_FN, acceptCookies);
|
|
438
|
+
if (Array.isArray(r) && r.length > 0) {
|
|
439
|
+
result = r;
|
|
440
|
+
break;
|
|
441
|
+
}
|
|
442
|
+
} catch (e) {
|
|
443
|
+
// Cross-origin frames often fail; ignore.
|
|
444
|
+
log("frame evaluate failed:", url, e?.message);
|
|
445
|
+
}
|
|
446
|
+
}
|
|
447
|
+
}
|
|
448
|
+
|
|
449
|
+
if (result.length > 0) {
|
|
450
|
+
console.log(`✓ Dismissed cookie dialog (${mode}): ${result.join(", ")}`);
|
|
451
|
+
} else {
|
|
452
|
+
console.log(`○ No cookie dialog found to ${mode}`);
|
|
453
|
+
}
|
|
454
|
+
|
|
455
|
+
await browser.disconnect();
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
import fs from "node:fs";
|
|
4
|
+
|
|
5
|
+
import { connectBrowser, getActivePage, printResult } from "./utils.js";
|
|
6
|
+
|
|
7
|
+
const args = process.argv.slice(2);
|
|
8
|
+
|
|
9
|
+
const usage = () => {
|
|
10
|
+
console.log("Usage: browser-eval.js <code>");
|
|
11
|
+
console.log(" browser-eval.js --file <path>");
|
|
12
|
+
console.log(" browser-eval.js --stdin");
|
|
13
|
+
console.log("\nExamples:");
|
|
14
|
+
console.log(' browser-eval.js "document.title"');
|
|
15
|
+
console.log(
|
|
16
|
+
" browser-eval.js \"const el = document.querySelector('textarea'); return el?.value\"",
|
|
17
|
+
);
|
|
18
|
+
console.log(" browser-eval.js --file ./snippet.js");
|
|
19
|
+
console.log(" printf 'return document.title\\n' | browser-eval.js --stdin");
|
|
20
|
+
};
|
|
21
|
+
|
|
22
|
+
const readCode = () => {
|
|
23
|
+
if (args[0] === "--stdin") {
|
|
24
|
+
return { kind: "stdin", source: fs.readFileSync(0, "utf8") };
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
if (args[0] === "--file") {
|
|
28
|
+
const filePath = args[1];
|
|
29
|
+
if (!filePath) {
|
|
30
|
+
usage();
|
|
31
|
+
process.exit(1);
|
|
32
|
+
}
|
|
33
|
+
return { kind: "file", source: fs.readFileSync(filePath, "utf8") };
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
const fileArg = args.find((arg) => arg.startsWith("--file="));
|
|
37
|
+
if (fileArg) {
|
|
38
|
+
return {
|
|
39
|
+
kind: "file",
|
|
40
|
+
source: fs.readFileSync(fileArg.slice("--file=".length), "utf8"),
|
|
41
|
+
};
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
return { kind: "argv", source: args.join(" ") };
|
|
45
|
+
};
|
|
46
|
+
|
|
47
|
+
const normalizeCode = ({ kind, source }) => {
|
|
48
|
+
const trimmed = source.trim();
|
|
49
|
+
return kind === "argv" ? trimmed.replace(/\\!/g, "!") : trimmed;
|
|
50
|
+
};
|
|
51
|
+
|
|
52
|
+
const code = normalizeCode(readCode());
|
|
53
|
+
if (!code) {
|
|
54
|
+
usage();
|
|
55
|
+
process.exit(1);
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
const browser = await connectBrowser();
|
|
59
|
+
const page = await getActivePage(browser);
|
|
60
|
+
|
|
61
|
+
const result = await page.evaluate(async (source) => {
|
|
62
|
+
const AsyncFunction = (async () => {}).constructor;
|
|
63
|
+
|
|
64
|
+
try {
|
|
65
|
+
return await new AsyncFunction(`return (${source})`)();
|
|
66
|
+
} catch (error) {
|
|
67
|
+
if (error?.name !== "SyntaxError") throw error;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
return new AsyncFunction(source)();
|
|
71
|
+
}, code);
|
|
72
|
+
|
|
73
|
+
printResult(result);
|
|
74
|
+
|
|
75
|
+
await browser.disconnect();
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Tail browser-watch JSONL logs.
|
|
5
|
+
*
|
|
6
|
+
* Usage:
|
|
7
|
+
* browser-logs-tail.js # dump latest log and exit
|
|
8
|
+
* browser-logs-tail.js --follow # keep following
|
|
9
|
+
* browser-logs-tail.js --file <path> # explicit file
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
import { existsSync, readdirSync, readFileSync, statSync, watch } from "node:fs";
|
|
13
|
+
import { tmpdir } from "node:os";
|
|
14
|
+
import { join } from "node:path";
|
|
15
|
+
|
|
16
|
+
const DEFAULT_TMP = process.platform === "win32" ? tmpdir() : "/tmp";
|
|
17
|
+
|
|
18
|
+
const LOG_ROOT =
|
|
19
|
+
process.env.BROWSER_TOOLS_LOG_ROOT || join(DEFAULT_TMP, "agent-browser-tools", "logs");
|
|
20
|
+
|
|
21
|
+
function statSafe(path) {
|
|
22
|
+
try {
|
|
23
|
+
return statSync(path);
|
|
24
|
+
} catch {
|
|
25
|
+
return null;
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
function findLatestFile() {
|
|
30
|
+
if (!existsSync(LOG_ROOT)) return null;
|
|
31
|
+
|
|
32
|
+
const dirs = readdirSync(LOG_ROOT)
|
|
33
|
+
.filter((name) => /^\d{4}-\d{2}-\d{2}$/.test(name))
|
|
34
|
+
.map((name) => join(LOG_ROOT, name))
|
|
35
|
+
.filter((p) => statSafe(p)?.isDirectory())
|
|
36
|
+
.sort();
|
|
37
|
+
|
|
38
|
+
if (dirs.length === 0) return null;
|
|
39
|
+
|
|
40
|
+
const latestDir = dirs[dirs.length - 1];
|
|
41
|
+
const files = readdirSync(latestDir)
|
|
42
|
+
.filter((name) => name.endsWith(".jsonl"))
|
|
43
|
+
.map((name) => join(latestDir, name))
|
|
44
|
+
.map((p) => ({ path: p, mtime: statSafe(p)?.mtimeMs || 0 }))
|
|
45
|
+
.sort((a, b) => b.mtime - a.mtime);
|
|
46
|
+
|
|
47
|
+
return files[0]?.path || null;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
const args = process.argv.slice(2);
|
|
51
|
+
const follow = args.includes("--follow");
|
|
52
|
+
|
|
53
|
+
let filePath = null;
|
|
54
|
+
const fileIdx = args.indexOf("--file");
|
|
55
|
+
if (fileIdx !== -1) filePath = args[fileIdx + 1];
|
|
56
|
+
if (!filePath) filePath = findLatestFile();
|
|
57
|
+
|
|
58
|
+
if (!filePath) {
|
|
59
|
+
console.error("✗ No log file found");
|
|
60
|
+
process.exit(1);
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
let offset = 0;
|
|
64
|
+
|
|
65
|
+
function readAll() {
|
|
66
|
+
if (!existsSync(filePath)) return;
|
|
67
|
+
const data = readFileSync(filePath, "utf8");
|
|
68
|
+
if (data.length > 0) process.stdout.write(data);
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
function readNew() {
|
|
72
|
+
if (!existsSync(filePath)) return;
|
|
73
|
+
const data = readFileSync(filePath, "utf8");
|
|
74
|
+
if (data.length <= offset) return;
|
|
75
|
+
const chunk = data.slice(offset);
|
|
76
|
+
offset = data.length;
|
|
77
|
+
process.stdout.write(chunk);
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
try {
|
|
81
|
+
readAll();
|
|
82
|
+
if (!follow) process.exit(0);
|
|
83
|
+
offset = statSafe(filePath)?.size || 0;
|
|
84
|
+
watch(filePath, { persistent: true }, () => readNew());
|
|
85
|
+
console.log(`✓ tailing ${filePath}`);
|
|
86
|
+
} catch (e) {
|
|
87
|
+
console.error("✗ tail failed:", e.message);
|
|
88
|
+
process.exit(1);
|
|
89
|
+
}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
import { connectBrowser, getActivePage } from "./utils.js";
|
|
4
|
+
|
|
5
|
+
const url = process.argv[2];
|
|
6
|
+
const newTab = process.argv[3] === "--new";
|
|
7
|
+
|
|
8
|
+
if (!url) {
|
|
9
|
+
console.log("Usage: browser-nav.js <url> [--new]");
|
|
10
|
+
console.log("\nExamples:");
|
|
11
|
+
console.log(" browser-nav.js https://example.com # Navigate current tab");
|
|
12
|
+
console.log(" browser-nav.js https://example.com --new # Open in new tab");
|
|
13
|
+
process.exit(1);
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
const browser = await connectBrowser();
|
|
17
|
+
|
|
18
|
+
if (newTab) {
|
|
19
|
+
const page = await browser.newPage();
|
|
20
|
+
await page.goto(url, { waitUntil: "domcontentloaded" });
|
|
21
|
+
console.log("✓ Opened:", url);
|
|
22
|
+
} else {
|
|
23
|
+
const page = await getActivePage(browser);
|
|
24
|
+
await page.goto(url, { waitUntil: "domcontentloaded" });
|
|
25
|
+
console.log("✓ Navigated to:", url);
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
await browser.disconnect();
|