tau-coding-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 +42 -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/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 +72 -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/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
package/package.json
ADDED
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "tau-coding-agent",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "Coding-focused Tau distribution for Pi",
|
|
5
|
+
"keywords": [
|
|
6
|
+
"pi-extension",
|
|
7
|
+
"pi-package",
|
|
8
|
+
"pi-skill"
|
|
9
|
+
],
|
|
10
|
+
"license": "MIT",
|
|
11
|
+
"repository": {
|
|
12
|
+
"type": "git",
|
|
13
|
+
"url": "https://github.com/goncalossilva/tau.git",
|
|
14
|
+
"directory": "packages/tau-coding-agent"
|
|
15
|
+
},
|
|
16
|
+
"type": "module",
|
|
17
|
+
"dependencies": {
|
|
18
|
+
"@anthropic-ai/sandbox-runtime": "^0.0.49",
|
|
19
|
+
"@mozilla/readability": "^0.6.0",
|
|
20
|
+
"jsdom": "^29.0.2",
|
|
21
|
+
"puppeteer-core": "^24.40.0",
|
|
22
|
+
"turndown": "^7.2.4",
|
|
23
|
+
"turndown-plugin-gfm": "^1.0.2"
|
|
24
|
+
},
|
|
25
|
+
"peerDependencies": {
|
|
26
|
+
"@mariozechner/pi-agent-core": "*",
|
|
27
|
+
"@mariozechner/pi-ai": "*",
|
|
28
|
+
"@mariozechner/pi-coding-agent": "*",
|
|
29
|
+
"@mariozechner/pi-tui": "*",
|
|
30
|
+
"typebox": "*"
|
|
31
|
+
},
|
|
32
|
+
"engines": {
|
|
33
|
+
"node": ">=24"
|
|
34
|
+
},
|
|
35
|
+
"pi": {
|
|
36
|
+
"extensions": [
|
|
37
|
+
"extensions/answer.ts",
|
|
38
|
+
"extensions/branch-term.ts",
|
|
39
|
+
"extensions/btw.ts",
|
|
40
|
+
"extensions/ghostty.ts",
|
|
41
|
+
"extensions/git-diff-stats.ts",
|
|
42
|
+
"extensions/git-pr-status.ts",
|
|
43
|
+
"extensions/insights.ts",
|
|
44
|
+
"extensions/interlude.ts",
|
|
45
|
+
"extensions/loop.ts",
|
|
46
|
+
"extensions/memory.ts",
|
|
47
|
+
"extensions/notify.ts",
|
|
48
|
+
"extensions/openai-fast.ts",
|
|
49
|
+
"extensions/openai-verbosity.ts",
|
|
50
|
+
"extensions/review.ts",
|
|
51
|
+
"extensions/sandbox/index.ts",
|
|
52
|
+
"extensions/usage/index.ts",
|
|
53
|
+
"extensions/websearch/index.ts",
|
|
54
|
+
"extensions/worktree.ts"
|
|
55
|
+
],
|
|
56
|
+
"skills": [
|
|
57
|
+
"skills/browser-tools",
|
|
58
|
+
"skills/git-clean-history",
|
|
59
|
+
"skills/git-commit",
|
|
60
|
+
"skills/oracle",
|
|
61
|
+
"skills/sentry",
|
|
62
|
+
"skills/update-changelog",
|
|
63
|
+
"skills/web-design"
|
|
64
|
+
]
|
|
65
|
+
},
|
|
66
|
+
"files": [
|
|
67
|
+
"extensions",
|
|
68
|
+
"skills",
|
|
69
|
+
"README.md",
|
|
70
|
+
"LICENSE"
|
|
71
|
+
]
|
|
72
|
+
}
|
|
@@ -0,0 +1,253 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: browser-tools
|
|
3
|
+
description: Interactive browser automation via Chrome DevTools Protocol. Use when you need to interact with web pages, test frontends, or when user interaction with a visible browser is required.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Browser Tools
|
|
7
|
+
|
|
8
|
+
Chrome DevTools Protocol tools for agent-assisted web automation. These tools connect to a Chromium-based browser (Chromium/Chrome) running on `:9222` with remote debugging enabled.
|
|
9
|
+
|
|
10
|
+
## Requirements
|
|
11
|
+
|
|
12
|
+
Chromium or Google Chrome with remote debugging support. The scripts auto-detect common installs; set `BROWSER_TOOLS_BROWSER` or `BROWSER_TOOLS_EXECUTABLE` if auto-detection picks the wrong browser.
|
|
13
|
+
|
|
14
|
+
## Setup
|
|
15
|
+
|
|
16
|
+
When hacking on this skill from a source checkout:
|
|
17
|
+
|
|
18
|
+
```bash
|
|
19
|
+
npm install
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
## Tools
|
|
23
|
+
|
|
24
|
+
Important: Always run commands from this skill directory.
|
|
25
|
+
|
|
26
|
+
### Start Chromium / Chrome
|
|
27
|
+
|
|
28
|
+
```bash
|
|
29
|
+
"./scripts/browser-start.js" # Dedicated tool profile
|
|
30
|
+
"./scripts/browser-start.js" --profile # Seed from your browser profile (cookies, logins)
|
|
31
|
+
"./scripts/browser-start.js" --watch # Start background JSONL logging
|
|
32
|
+
"./scripts/browser-start.js" --browser chromium
|
|
33
|
+
"./scripts/browser-start.js" --browser chrome
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
Launch a browser with remote debugging on `:9222`. Use `--profile` to preserve your authentication state. If a browser is already running on `:9222`, it is reused; launch options like `--browser`, `--executable`, and `--profile` only affect new browser instances.
|
|
37
|
+
|
|
38
|
+
If the auto-detection picks the wrong browser, set:
|
|
39
|
+
|
|
40
|
+
- `BROWSER_TOOLS_BROWSER=chromium` (or `chrome`)
|
|
41
|
+
- `BROWSER_TOOLS_EXECUTABLE=/absolute/path/to/browser`
|
|
42
|
+
- `BROWSER_TOOLS_PROFILE_SRC=/absolute/path/to/profile/dir` (optional; useful with `--executable --profile`)
|
|
43
|
+
|
|
44
|
+
### Navigate
|
|
45
|
+
|
|
46
|
+
```bash
|
|
47
|
+
"./scripts/browser-nav.js" https://example.com
|
|
48
|
+
"./scripts/browser-nav.js" https://example.com --new
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
Navigate to URLs. Use `--new` flag to open in a new tab instead of reusing current tab.
|
|
52
|
+
|
|
53
|
+
### Evaluate JavaScript
|
|
54
|
+
|
|
55
|
+
```bash
|
|
56
|
+
"./scripts/browser-eval.js" 'document.title'
|
|
57
|
+
"./scripts/browser-eval.js" 'document.querySelectorAll("a").length'
|
|
58
|
+
"./scripts/browser-eval.js" 'const el = document.querySelector("textarea"); return el?.value'
|
|
59
|
+
"./scripts/browser-eval.js" --file ./snippet.js
|
|
60
|
+
printf 'return document.title\n' | "./scripts/browser-eval.js" --stdin
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
Execute JavaScript in the active tab. Code runs in async context. Expressions and statement bodies are both supported. Use `return` when passing statements or multi-line code.
|
|
64
|
+
|
|
65
|
+
### Screenshot
|
|
66
|
+
|
|
67
|
+
```bash
|
|
68
|
+
"./scripts/browser-screenshot.js"
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
Capture current viewport and return temporary file path. Use this to visually inspect page state or verify UI changes.
|
|
72
|
+
|
|
73
|
+
### Pick Elements
|
|
74
|
+
|
|
75
|
+
```bash
|
|
76
|
+
"./scripts/browser-pick.js" "Click the submit button"
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
Use this when the user wants to select specific DOM elements on the page. This launches an interactive picker: click elements to select them, Cmd/Ctrl+Click for multi-select, Enter to finish.
|
|
80
|
+
|
|
81
|
+
### Dismiss cookie banners
|
|
82
|
+
|
|
83
|
+
```bash
|
|
84
|
+
"./scripts/browser-dismiss-cookies.js" # Accept cookies
|
|
85
|
+
"./scripts/browser-dismiss-cookies.js" --reject # Reject (where possible)
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
Run after navigation if cookie dialogs interfere with interaction.
|
|
89
|
+
|
|
90
|
+
### Cookies
|
|
91
|
+
|
|
92
|
+
```bash
|
|
93
|
+
"./scripts/browser-cookies.js"
|
|
94
|
+
"./scripts/browser-cookies.js" --format=netscape > cookies.txt
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
Display all cookies for the current tab including domain, path, httpOnly, and secure flags.
|
|
98
|
+
|
|
99
|
+
The `--format=netscape` option outputs cookies in Netscape format for use with curl/wget (`curl -b cookies.txt`).
|
|
100
|
+
|
|
101
|
+
### Extract Page Content
|
|
102
|
+
|
|
103
|
+
```bash
|
|
104
|
+
"./scripts/browser-content.js" https://example.com
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
Navigate to a URL and extract readable content as markdown. Uses Mozilla Readability for article extraction and Turndown for HTML-to-markdown conversion.
|
|
108
|
+
|
|
109
|
+
### Background logging (console + errors + network)
|
|
110
|
+
|
|
111
|
+
Start the watcher:
|
|
112
|
+
|
|
113
|
+
```bash
|
|
114
|
+
"./scripts/browser-watch.js"
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
Or launch the browser with logging enabled:
|
|
118
|
+
|
|
119
|
+
```bash
|
|
120
|
+
"./scripts/browser-start.js" --watch
|
|
121
|
+
```
|
|
122
|
+
|
|
123
|
+
Logs are written as JSONL to a temp directory by default:
|
|
124
|
+
|
|
125
|
+
- Default: `/tmp/agent-browser-tools/logs/YYYY-MM-DD/<targetId>.jsonl`
|
|
126
|
+
- Override: `BROWSER_TOOLS_LOG_ROOT=/some/dir`
|
|
127
|
+
|
|
128
|
+
Tail the most recent log:
|
|
129
|
+
|
|
130
|
+
```bash
|
|
131
|
+
"./scripts/browser-logs-tail.js" # dump and exit
|
|
132
|
+
"./scripts/browser-logs-tail.js" --follow # follow
|
|
133
|
+
```
|
|
134
|
+
|
|
135
|
+
Summarize network responses (status codes, failures):
|
|
136
|
+
|
|
137
|
+
```bash
|
|
138
|
+
"./scripts/browser-net-summary.js"
|
|
139
|
+
"./scripts/browser-net-summary.js" --file /path/to/log.jsonl
|
|
140
|
+
```
|
|
141
|
+
|
|
142
|
+
### When to Use
|
|
143
|
+
|
|
144
|
+
- Testing frontend code in a real browser
|
|
145
|
+
- Interacting with pages that require JavaScript
|
|
146
|
+
- When user needs to visually see or interact with a page
|
|
147
|
+
- Debugging authentication or session issues
|
|
148
|
+
- Scraping dynamic content that requires JS execution
|
|
149
|
+
|
|
150
|
+
## Efficiency Guide
|
|
151
|
+
|
|
152
|
+
### DOM Inspection Over Screenshots
|
|
153
|
+
|
|
154
|
+
**Don't** lead with screenshots to inspect page state. **Do** prefer parsing the DOM directly first, and use screenshots when you need visual/layout verification:
|
|
155
|
+
|
|
156
|
+
```javascript
|
|
157
|
+
// Get page structure
|
|
158
|
+
document.body.innerHTML.slice(0, 5000);
|
|
159
|
+
|
|
160
|
+
// Find interactive elements
|
|
161
|
+
Array.from(document.querySelectorAll('button, input, [role="button"]')).map((e) => ({
|
|
162
|
+
id: e.id,
|
|
163
|
+
text: e.textContent.trim(),
|
|
164
|
+
class: e.className,
|
|
165
|
+
}));
|
|
166
|
+
```
|
|
167
|
+
|
|
168
|
+
### Complex Scripts in Single Calls
|
|
169
|
+
|
|
170
|
+
Use one eval call for multi-step workflows. `browser-eval.js` supports statement bodies, `return`, and `await`:
|
|
171
|
+
|
|
172
|
+
```javascript
|
|
173
|
+
const data = document.querySelector("#target")?.textContent;
|
|
174
|
+
const buttons = document.querySelectorAll("button");
|
|
175
|
+
|
|
176
|
+
buttons[0]?.click();
|
|
177
|
+
|
|
178
|
+
return {
|
|
179
|
+
data,
|
|
180
|
+
buttonCount: buttons.length,
|
|
181
|
+
};
|
|
182
|
+
```
|
|
183
|
+
|
|
184
|
+
### Batch Interactions
|
|
185
|
+
|
|
186
|
+
**Don't** make separate calls for each click. **Do** batch them:
|
|
187
|
+
|
|
188
|
+
```javascript
|
|
189
|
+
const actions = ["btn1", "btn2", "btn3"];
|
|
190
|
+
actions.forEach((id) => document.getElementById(id)?.click());
|
|
191
|
+
return "Done";
|
|
192
|
+
```
|
|
193
|
+
|
|
194
|
+
### Typing/Input Sequences
|
|
195
|
+
|
|
196
|
+
For normal form inputs, set the value and dispatch events in one call:
|
|
197
|
+
|
|
198
|
+
```javascript
|
|
199
|
+
const input = document.querySelector('input[name="email"]');
|
|
200
|
+
if (!input) return "Input not found";
|
|
201
|
+
|
|
202
|
+
input.value = "user@example.com";
|
|
203
|
+
input.dispatchEvent(new Event("input", { bubbles: true }));
|
|
204
|
+
input.dispatchEvent(new Event("change", { bubbles: true }));
|
|
205
|
+
|
|
206
|
+
document.querySelector('button[type="submit"]')?.click();
|
|
207
|
+
return "Submitted";
|
|
208
|
+
```
|
|
209
|
+
|
|
210
|
+
### Reading App/Game State
|
|
211
|
+
|
|
212
|
+
Extract structured state in one call:
|
|
213
|
+
|
|
214
|
+
```javascript
|
|
215
|
+
const state = {
|
|
216
|
+
score: document.querySelector(".score")?.textContent,
|
|
217
|
+
status: document.querySelector(".status")?.className,
|
|
218
|
+
items: Array.from(document.querySelectorAll(".item")).map((el) => ({
|
|
219
|
+
text: el.textContent,
|
|
220
|
+
active: el.classList.contains("active"),
|
|
221
|
+
})),
|
|
222
|
+
};
|
|
223
|
+
return state;
|
|
224
|
+
```
|
|
225
|
+
|
|
226
|
+
### Waiting for Updates
|
|
227
|
+
|
|
228
|
+
If DOM updates after actions, wait inside the same eval call:
|
|
229
|
+
|
|
230
|
+
```javascript
|
|
231
|
+
document.querySelector("#submit")?.click();
|
|
232
|
+
await new Promise((resolve) => setTimeout(resolve, 500));
|
|
233
|
+
|
|
234
|
+
return {
|
|
235
|
+
status: document.querySelector(".status")?.textContent,
|
|
236
|
+
};
|
|
237
|
+
```
|
|
238
|
+
|
|
239
|
+
### Investigate Before Interacting
|
|
240
|
+
|
|
241
|
+
Always start by understanding the page structure:
|
|
242
|
+
|
|
243
|
+
```javascript
|
|
244
|
+
return {
|
|
245
|
+
title: document.title,
|
|
246
|
+
forms: document.forms.length,
|
|
247
|
+
buttons: document.querySelectorAll("button").length,
|
|
248
|
+
inputs: document.querySelectorAll("input").length,
|
|
249
|
+
mainContent: document.body.innerHTML.slice(0, 3000),
|
|
250
|
+
};
|
|
251
|
+
```
|
|
252
|
+
|
|
253
|
+
Then target specific elements based on what you find.
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
import { Readability } from "@mozilla/readability";
|
|
4
|
+
import { JSDOM } from "jsdom";
|
|
5
|
+
import TurndownService from "turndown";
|
|
6
|
+
import { gfm } from "turndown-plugin-gfm";
|
|
7
|
+
import { connectBrowser, getActivePage } from "./utils.js";
|
|
8
|
+
|
|
9
|
+
// Global timeout - exit if script takes too long
|
|
10
|
+
const TIMEOUT = 30000;
|
|
11
|
+
setTimeout(() => {
|
|
12
|
+
console.error("✗ Timeout after 30s");
|
|
13
|
+
process.exit(1);
|
|
14
|
+
}, TIMEOUT).unref();
|
|
15
|
+
|
|
16
|
+
const url = process.argv[2];
|
|
17
|
+
|
|
18
|
+
if (!url) {
|
|
19
|
+
console.log("Usage: browser-content.js <url>");
|
|
20
|
+
console.log("\nExtracts readable content from a URL as markdown.");
|
|
21
|
+
console.log("\nExamples:");
|
|
22
|
+
console.log(" browser-content.js https://example.com");
|
|
23
|
+
console.log(" browser-content.js https://en.wikipedia.org/wiki/Rust_(programming_language)");
|
|
24
|
+
process.exit(1);
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
const browser = await connectBrowser();
|
|
28
|
+
const page = await getActivePage(browser);
|
|
29
|
+
|
|
30
|
+
await Promise.race([
|
|
31
|
+
page.goto(url, { waitUntil: "networkidle2" }),
|
|
32
|
+
new Promise((r) => setTimeout(r, 10000)),
|
|
33
|
+
]).catch(() => {});
|
|
34
|
+
|
|
35
|
+
// Get HTML via CDP (works even with TrustedScriptURL restrictions)
|
|
36
|
+
const client = await page.createCDPSession();
|
|
37
|
+
const { root } = await client.send("DOM.getDocument", {
|
|
38
|
+
depth: -1,
|
|
39
|
+
pierce: true,
|
|
40
|
+
});
|
|
41
|
+
const { outerHTML } = await client.send("DOM.getOuterHTML", {
|
|
42
|
+
nodeId: root.nodeId,
|
|
43
|
+
});
|
|
44
|
+
await client.detach();
|
|
45
|
+
|
|
46
|
+
const finalUrl = page.url();
|
|
47
|
+
|
|
48
|
+
// Extract with Readability
|
|
49
|
+
const doc = new JSDOM(outerHTML, { url: finalUrl });
|
|
50
|
+
const reader = new Readability(doc.window.document);
|
|
51
|
+
const article = reader.parse();
|
|
52
|
+
|
|
53
|
+
// Convert to markdown
|
|
54
|
+
function htmlToMarkdown(html) {
|
|
55
|
+
const turndown = new TurndownService({
|
|
56
|
+
headingStyle: "atx",
|
|
57
|
+
codeBlockStyle: "fenced",
|
|
58
|
+
});
|
|
59
|
+
turndown.use(gfm);
|
|
60
|
+
turndown.addRule("removeEmptyLinks", {
|
|
61
|
+
filter: (node) => node.nodeName === "A" && !node.textContent?.trim(),
|
|
62
|
+
replacement: () => "",
|
|
63
|
+
});
|
|
64
|
+
return turndown
|
|
65
|
+
.turndown(html)
|
|
66
|
+
.replace(/\[\\?\[\s*\\?\]\]\([^)]*\)/g, "")
|
|
67
|
+
.replace(/ +/g, " ")
|
|
68
|
+
.replace(/\s+,/g, ",")
|
|
69
|
+
.replace(/\s+\./g, ".")
|
|
70
|
+
.replace(/\n{3,}/g, "\n\n")
|
|
71
|
+
.trim();
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
let content;
|
|
75
|
+
if (article && article.content) {
|
|
76
|
+
content = htmlToMarkdown(article.content);
|
|
77
|
+
} else {
|
|
78
|
+
// Fallback
|
|
79
|
+
const fallbackDoc = new JSDOM(outerHTML, { url: finalUrl });
|
|
80
|
+
const fallbackBody = fallbackDoc.window.document;
|
|
81
|
+
fallbackBody
|
|
82
|
+
.querySelectorAll("script, style, noscript, nav, header, footer, aside")
|
|
83
|
+
.forEach((el) => el.remove());
|
|
84
|
+
const main =
|
|
85
|
+
fallbackBody.querySelector("main, article, [role='main'], .content, #content") ||
|
|
86
|
+
fallbackBody.body;
|
|
87
|
+
const fallbackHtml = main?.innerHTML || "";
|
|
88
|
+
if (fallbackHtml.trim().length > 100) {
|
|
89
|
+
content = htmlToMarkdown(fallbackHtml);
|
|
90
|
+
} else {
|
|
91
|
+
content = "(Could not extract content)";
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
console.log(`URL: ${finalUrl}`);
|
|
96
|
+
if (article?.title) console.log(`Title: ${article.title}`);
|
|
97
|
+
console.log("");
|
|
98
|
+
console.log(content);
|
|
99
|
+
|
|
100
|
+
process.exit(0);
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
import { connectBrowser, getActivePage } from "./utils.js";
|
|
4
|
+
|
|
5
|
+
const args = process.argv.slice(2);
|
|
6
|
+
const format = args.includes("--format=netscape") ? "netscape" : "human";
|
|
7
|
+
|
|
8
|
+
const browser = await connectBrowser();
|
|
9
|
+
const page = await getActivePage(browser);
|
|
10
|
+
|
|
11
|
+
const cookies = await page.cookies();
|
|
12
|
+
|
|
13
|
+
if (format === "netscape") {
|
|
14
|
+
for (const cookie of cookies) {
|
|
15
|
+
const includeSubdomains = cookie.domain.startsWith(".") ? "TRUE" : "FALSE";
|
|
16
|
+
const secure = cookie.secure ? "TRUE" : "FALSE";
|
|
17
|
+
const expiry = cookie.expires > 0 ? Math.floor(cookie.expires) : "0";
|
|
18
|
+
console.log(
|
|
19
|
+
`${cookie.domain}\t${includeSubdomains}\t${cookie.path}\t${secure}\t${expiry}\t${cookie.name}\t${cookie.value}`,
|
|
20
|
+
);
|
|
21
|
+
}
|
|
22
|
+
} else {
|
|
23
|
+
for (const cookie of cookies) {
|
|
24
|
+
console.log(`${cookie.name}: ${cookie.value}`);
|
|
25
|
+
console.log(` domain: ${cookie.domain}`);
|
|
26
|
+
console.log(` path: ${cookie.path}`);
|
|
27
|
+
console.log(` httpOnly: ${cookie.httpOnly}`);
|
|
28
|
+
console.log(` secure: ${cookie.secure}`);
|
|
29
|
+
console.log("");
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
await browser.disconnect();
|