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.
Files changed (95) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +45 -0
  3. package/extensions/answer.ts +601 -0
  4. package/extensions/branch-term.ts +405 -0
  5. package/extensions/btw.ts +444 -0
  6. package/extensions/ghostty.ts +301 -0
  7. package/extensions/git-diff-stats.ts +277 -0
  8. package/extensions/git-pr-status.ts +286 -0
  9. package/extensions/insights.ts +2367 -0
  10. package/extensions/interlude.ts +144 -0
  11. package/extensions/loop.ts +529 -0
  12. package/extensions/memory.ts +1889 -0
  13. package/extensions/notify.ts +161 -0
  14. package/extensions/openai-fast.ts +227 -0
  15. package/extensions/openai-verbosity.ts +223 -0
  16. package/extensions/review.ts +4347 -0
  17. package/extensions/sandbox/index.ts +2578 -0
  18. package/extensions/telegram/README.md +81 -0
  19. package/extensions/telegram/daemon.mjs +1942 -0
  20. package/extensions/telegram/index.ts +1073 -0
  21. package/extensions/telegram/message-text.d.mts +1 -0
  22. package/extensions/telegram/message-text.mjs +16 -0
  23. package/extensions/usage/anthropic.ts +198 -0
  24. package/extensions/usage/github-copilot.ts +204 -0
  25. package/extensions/usage/google-gemini-cli.ts +232 -0
  26. package/extensions/usage/index.ts +2310 -0
  27. package/extensions/usage/minimax.ts +208 -0
  28. package/extensions/usage/openai-codex.ts +180 -0
  29. package/extensions/usage/openrouter.ts +168 -0
  30. package/extensions/usage/providers.ts +26 -0
  31. package/extensions/usage/shared.ts +156 -0
  32. package/extensions/usage/types.ts +53 -0
  33. package/extensions/usage/zai.ts +186 -0
  34. package/extensions/websearch/README.md +68 -0
  35. package/extensions/websearch/browser/chromium.ts +330 -0
  36. package/extensions/websearch/browser/discovery.ts +75 -0
  37. package/extensions/websearch/browser/firefox.ts +150 -0
  38. package/extensions/websearch/browser/sqlite.ts +38 -0
  39. package/extensions/websearch/config.ts +76 -0
  40. package/extensions/websearch/index.ts +312 -0
  41. package/extensions/websearch/normalize.ts +155 -0
  42. package/extensions/websearch/providers/anthropic.pi.ts +139 -0
  43. package/extensions/websearch/providers/gemini.browser.ts +185 -0
  44. package/extensions/websearch/providers/gemini.pi.ts +108 -0
  45. package/extensions/websearch/providers/openai-codex.browser.ts +77 -0
  46. package/extensions/websearch/providers/openai-codex.pi.ts +49 -0
  47. package/extensions/websearch/providers/openai-codex.shared.ts +123 -0
  48. package/extensions/websearch/providers/pi-model.shared.ts +86 -0
  49. package/extensions/websearch/providers/search-prompt.shared.ts +17 -0
  50. package/extensions/websearch/providers/shared.ts +76 -0
  51. package/extensions/websearch/types.ts +52 -0
  52. package/extensions/worktree.ts +2223 -0
  53. package/package.json +76 -0
  54. package/skills/browser-tools/SKILL.md +253 -0
  55. package/skills/browser-tools/scripts/browser-content.js +100 -0
  56. package/skills/browser-tools/scripts/browser-cookies.js +33 -0
  57. package/skills/browser-tools/scripts/browser-dismiss-cookies.js +455 -0
  58. package/skills/browser-tools/scripts/browser-eval.js +75 -0
  59. package/skills/browser-tools/scripts/browser-logs-tail.js +89 -0
  60. package/skills/browser-tools/scripts/browser-nav.js +28 -0
  61. package/skills/browser-tools/scripts/browser-net-summary.js +115 -0
  62. package/skills/browser-tools/scripts/browser-pick.js +133 -0
  63. package/skills/browser-tools/scripts/browser-screenshot.js +18 -0
  64. package/skills/browser-tools/scripts/browser-start.js +281 -0
  65. package/skills/browser-tools/scripts/browser-watch.js +321 -0
  66. package/skills/browser-tools/scripts/utils.js +60 -0
  67. package/skills/git-clean-history/SKILL.md +45 -0
  68. package/skills/git-commit/SKILL.md +55 -0
  69. package/skills/homeassistant-ops/SKILL.md +184 -0
  70. package/skills/homeassistant-ops/references/api.md +64 -0
  71. package/skills/homeassistant-ops/references/id_conventions.md +71 -0
  72. package/skills/homeassistant-ops/references/playbook.md +150 -0
  73. package/skills/homeassistant-ops/scripts/ha_ops.js +2942 -0
  74. package/skills/openscad/SKILL.md +149 -0
  75. package/skills/openscad/examples/parametric_box.scad +92 -0
  76. package/skills/openscad/examples/phone_stand.scad +95 -0
  77. package/skills/openscad/scripts/common.sh +49 -0
  78. package/skills/openscad/scripts/export-stl.sh +56 -0
  79. package/skills/openscad/scripts/extract-params.sh +147 -0
  80. package/skills/openscad/scripts/multi-preview.sh +68 -0
  81. package/skills/openscad/scripts/preview.sh +74 -0
  82. package/skills/openscad/scripts/render-with-params.sh +91 -0
  83. package/skills/openscad/scripts/validate.sh +46 -0
  84. package/skills/oracle/SKILL.md +64 -0
  85. package/skills/oracle/scripts/oracle +249 -0
  86. package/skills/oracle/scripts/oracle-bundle +227 -0
  87. package/skills/sentry/SKILL.md +198 -0
  88. package/skills/sentry/lib/auth.js +99 -0
  89. package/skills/sentry/scripts/fetch-event.js +325 -0
  90. package/skills/sentry/scripts/fetch-issue.js +368 -0
  91. package/skills/sentry/scripts/list-issues.js +245 -0
  92. package/skills/sentry/scripts/search-events.js +291 -0
  93. package/skills/sentry/scripts/search-logs.js +234 -0
  94. package/skills/update-changelog/SKILL.md +135 -0
  95. 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();