@marcfargas/skills 0.2.0 → 0.2.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +1 -1
- package/README.md +35 -12
- package/package.json +6 -2
- package/search/web-search/SKILL.md +107 -0
- package/search/web-search/content.js +86 -0
- package/search/web-search/package-lock.json +617 -0
- package/search/web-search/package.json +13 -0
- package/search/web-search/search.js +215 -0
package/LICENSE
CHANGED
package/README.md
CHANGED
|
@@ -8,6 +8,7 @@ Reusable skills for AI coding agents. Works with [pi](https://github.com/marioze
|
|
|
8
8
|
|----------|-------|-------------|
|
|
9
9
|
| ☁️ Google Cloud | [gcloud](google-cloud/gcloud/) | GCP CLI with agent safety model — hub + 7 reference files |
|
|
10
10
|
| 🚀 Release | [pre-release](release/pre-release/) | Pre-release checklist + AI-written changesets via @changesets/cli |
|
|
11
|
+
| 🔍 Search | [web-search](search/web-search/) | Web search + content extraction via [ddgs](https://github.com/deedy5/ddgs) — no API keys |
|
|
11
12
|
| 🎬 Terminal | [vhs](terminal/vhs/) | Record terminal sessions as GIF/MP4 with [VHS](https://github.com/charmbracelet/vhs) |
|
|
12
13
|
|
|
13
14
|
## Install
|
|
@@ -42,13 +43,26 @@ Copy the skill directory into your agent's skill folder:
|
|
|
42
43
|
cp -r google-cloud/gcloud ~/.claude/skills/gcloud
|
|
43
44
|
```
|
|
44
45
|
|
|
45
|
-
##
|
|
46
|
+
## How We Build Skills
|
|
46
47
|
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
3
|
|
50
|
-
|
|
51
|
-
|
|
48
|
+
### Multi-Model Review
|
|
49
|
+
|
|
50
|
+
Every skill is reviewed by **3+ models** (Claude, Gemini, GPT) before publishing — structure, agent usability, safety, and real-world scenario testing. If an agent can misinterpret an instruction, we find out before you do.
|
|
51
|
+
|
|
52
|
+
### Safety Classification
|
|
53
|
+
|
|
54
|
+
Every operation is classified: **READ** / **WRITE** / **DESTRUCTIVE** / **EXPENSIVE** / **FORBIDDEN**. Destructive and expensive operations are gated — the agent must confirm before executing, and costs are flagged upfront.
|
|
55
|
+
|
|
56
|
+
### Progressive Discovery
|
|
57
|
+
|
|
58
|
+
Skills use a **hub + spoke** architecture. The SKILL.md hub is ~140 lines — just enough to match the right skill and know what's available. Detailed per-topic reference files are loaded on demand, keeping your context window lean.
|
|
59
|
+
|
|
60
|
+
### Also
|
|
61
|
+
|
|
62
|
+
- **Agent-native** — `--format=json` everywhere, idempotent patterns, structured error handling
|
|
63
|
+
- **Portable** — no hardcoded paths, no personal config, works on any machine
|
|
64
|
+
- **Spec-compliant** — validated against the [Agent Skills specification](https://agentskills.io/specification) using [skills-ref](https://github.com/agentskills/agentskills) in CI
|
|
65
|
+
- **Continuous validation** — `agentskills validate` on every push ([validate.yml](.github/workflows/validate.yml)), [pre-release checklist](release/pre-release/) with AI-written changesets, [npm Trusted Publishing](https://docs.npmjs.com/trusted-publishers) with provenance
|
|
52
66
|
|
|
53
67
|
## Structure
|
|
54
68
|
|
|
@@ -58,19 +72,22 @@ skills/
|
|
|
58
72
|
│ └── gcloud/ # 8 files, ~1100 lines total
|
|
59
73
|
├── release/
|
|
60
74
|
│ └── pre-release/ # 1 file
|
|
75
|
+
├── search/
|
|
76
|
+
│ └── web-search/ # SKILL.md + search.js + content.js
|
|
61
77
|
├── terminal/
|
|
62
78
|
│ └── vhs/ # 1 file
|
|
63
79
|
└── README.md
|
|
64
80
|
```
|
|
65
81
|
|
|
66
|
-
## External Skills
|
|
82
|
+
## External Skills
|
|
67
83
|
|
|
68
|
-
Some skills
|
|
84
|
+
Some skills live in their own repositories — install them directly or via their npm packages:
|
|
69
85
|
|
|
70
|
-
| Skill |
|
|
71
|
-
|
|
72
|
-
|
|
|
73
|
-
|
|
|
86
|
+
| Skill | Description | Install |
|
|
87
|
+
|-------|-------------|---------|
|
|
88
|
+
| [go-easy](https://github.com/marcfargas/go-easy) | Gmail, Drive, Calendar for AI agents — `npx go-gmail`, `npx go-drive`, `npx go-calendar` | `npx skills add marcfargas/go-easy` |
|
|
89
|
+
| [holdpty](https://github.com/marcfargas/holdpty) | Detached PTY sessions — launch, attach, view, record terminal processes | `npx skills add marcfargas/holdpty` |
|
|
90
|
+
| [odoo](https://github.com/marcfargas/odoo-toolbox) | Odoo ERP integration — connect, introspect, automate | `npx skills add marcfargas/odoo-toolbox` |
|
|
74
91
|
|
|
75
92
|
## Contributing
|
|
76
93
|
|
|
@@ -101,6 +118,12 @@ agentskills to-prompt path/to/skill-a path/to/skill-b
|
|
|
101
118
|
|
|
102
119
|
CI runs `agentskills validate` on every push — see [`.github/workflows/validate.yml`](.github/workflows/validate.yml).
|
|
103
120
|
|
|
121
|
+
## Sponsor
|
|
122
|
+
|
|
123
|
+
Building high-quality, multi-model-reviewed agent skills takes serious token budget. If these skills save you time, consider sponsoring:
|
|
124
|
+
|
|
125
|
+
[](https://github.com/sponsors/marcfargas)
|
|
126
|
+
|
|
104
127
|
## License
|
|
105
128
|
|
|
106
129
|
MIT
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@marcfargas/skills",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.1",
|
|
4
4
|
"description": "Reusable AI agent skills for pi, Claude Code, Cursor, and any Agent Skills compatible agent",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"author": "Marc Fargas <marc@marcfargas.com>",
|
|
@@ -14,12 +14,15 @@
|
|
|
14
14
|
"ai-agent",
|
|
15
15
|
"skills",
|
|
16
16
|
"gcloud",
|
|
17
|
-
"
|
|
17
|
+
"pre-release",
|
|
18
|
+
"vhs",
|
|
19
|
+
"web-search"
|
|
18
20
|
],
|
|
19
21
|
"pi": {
|
|
20
22
|
"skills": [
|
|
21
23
|
"google-cloud",
|
|
22
24
|
"release",
|
|
25
|
+
"search",
|
|
23
26
|
"terminal"
|
|
24
27
|
]
|
|
25
28
|
},
|
|
@@ -35,6 +38,7 @@
|
|
|
35
38
|
"files": [
|
|
36
39
|
"google-cloud/",
|
|
37
40
|
"release/",
|
|
41
|
+
"search/",
|
|
38
42
|
"terminal/",
|
|
39
43
|
"README.md",
|
|
40
44
|
"LICENSE"
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: web-search
|
|
3
|
+
description: >-
|
|
4
|
+
Web search and content extraction using ddgs (multi-engine metasearch). No API keys required.
|
|
5
|
+
Use when: searching documentation, facts, current information, news, fetching web content.
|
|
6
|
+
Triggers: search the web, look up, find information, web search, news search, fetch page.
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
# Web Search
|
|
10
|
+
|
|
11
|
+
Web search and content extraction using [ddgs](https://github.com/deedy5/ddgs) — a multi-engine metasearch CLI.
|
|
12
|
+
No API keys, no signup, no browser required.
|
|
13
|
+
|
|
14
|
+
## Setup
|
|
15
|
+
|
|
16
|
+
Install ddgs (run once):
|
|
17
|
+
|
|
18
|
+
```bash
|
|
19
|
+
uv tool install ddgs
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
Install Node.js dependencies for content extraction (run once):
|
|
23
|
+
|
|
24
|
+
```bash
|
|
25
|
+
cd {baseDir}
|
|
26
|
+
npm install
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
## Search
|
|
30
|
+
|
|
31
|
+
```bash
|
|
32
|
+
{baseDir}/search.js "query" # Basic search (5 results)
|
|
33
|
+
{baseDir}/search.js "query" -n 10 # More results
|
|
34
|
+
{baseDir}/search.js "query" --content # Include page content as markdown
|
|
35
|
+
{baseDir}/search.js "query" -t w # Results from last week
|
|
36
|
+
{baseDir}/search.js "query" -t m # Results from last month
|
|
37
|
+
{baseDir}/search.js "query" -r es-es # Results in Spanish
|
|
38
|
+
{baseDir}/search.js "query" -b google # Use Google backend
|
|
39
|
+
{baseDir}/search.js "query" -n 3 --content # Combined options
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
### Options
|
|
43
|
+
|
|
44
|
+
- `-n <num>` — Number of results (default: 5)
|
|
45
|
+
- `--content` — Fetch and include page content as markdown
|
|
46
|
+
- `-r <region>` — Region code: `us-en`, `es-es`, `de-de`, `fr-fr`, etc. (default: none)
|
|
47
|
+
- `-t <timelimit>` — Filter by time: `d` (day), `w` (week), `m` (month), `y` (year)
|
|
48
|
+
- `-b <backend>` — Search backend: `auto`, `all`, `bing`, `brave`, `duckduckgo`, `google`, `mojeek`, `yandex`, `yahoo`, `wikipedia` (default: auto)
|
|
49
|
+
|
|
50
|
+
## News Search
|
|
51
|
+
|
|
52
|
+
```bash
|
|
53
|
+
{baseDir}/search.js --news "query" # News search
|
|
54
|
+
{baseDir}/search.js --news "query" -n 10 -t w # News from last week
|
|
55
|
+
{baseDir}/search.js --news "query" --content # News with full article content
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
News backends: `auto`, `all`, `bing`, `duckduckgo`, `yahoo`
|
|
59
|
+
|
|
60
|
+
## Extract Page Content
|
|
61
|
+
|
|
62
|
+
```bash
|
|
63
|
+
{baseDir}/content.js https://example.com/article
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
Fetches a URL and extracts readable content as markdown.
|
|
67
|
+
|
|
68
|
+
## Output Format
|
|
69
|
+
|
|
70
|
+
### Text search
|
|
71
|
+
|
|
72
|
+
```
|
|
73
|
+
--- Result 1 ---
|
|
74
|
+
Title: Page Title
|
|
75
|
+
Link: https://example.com/page
|
|
76
|
+
Snippet: Description from search results
|
|
77
|
+
Content: (if --content flag used)
|
|
78
|
+
Markdown content extracted from the page...
|
|
79
|
+
|
|
80
|
+
--- Result 2 ---
|
|
81
|
+
...
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
### News search
|
|
85
|
+
|
|
86
|
+
```
|
|
87
|
+
--- Result 1 ---
|
|
88
|
+
Title: Article Title
|
|
89
|
+
Link: https://news.example.com/article
|
|
90
|
+
Date: 2026-02-08T10:18:00+00:00
|
|
91
|
+
Source: Reuters
|
|
92
|
+
Snippet: Article summary...
|
|
93
|
+
Content: (if --content flag used)
|
|
94
|
+
Full article as markdown...
|
|
95
|
+
|
|
96
|
+
--- Result 2 ---
|
|
97
|
+
...
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
## When to Use
|
|
101
|
+
|
|
102
|
+
- Searching for documentation or API references
|
|
103
|
+
- Looking up facts or current information
|
|
104
|
+
- News search for recent events
|
|
105
|
+
- Fetching content from specific URLs
|
|
106
|
+
- Any task requiring web search without interactive browsing
|
|
107
|
+
- When no API key is available (unlike Brave Search)
|
|
@@ -0,0 +1,86 @@
|
|
|
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
|
+
|
|
8
|
+
const url = process.argv[2];
|
|
9
|
+
|
|
10
|
+
if (!url) {
|
|
11
|
+
console.log("Usage: content.js <url>");
|
|
12
|
+
console.log("\nExtracts readable content from a webpage as markdown.");
|
|
13
|
+
console.log("\nExamples:");
|
|
14
|
+
console.log(" content.js https://example.com/article");
|
|
15
|
+
console.log(" content.js https://doc.rust-lang.org/book/ch04-01-what-is-ownership.html");
|
|
16
|
+
process.exit(1);
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
function htmlToMarkdown(html) {
|
|
20
|
+
const turndown = new TurndownService({ headingStyle: "atx", codeBlockStyle: "fenced" });
|
|
21
|
+
turndown.use(gfm);
|
|
22
|
+
turndown.addRule("removeEmptyLinks", {
|
|
23
|
+
filter: (node) => node.nodeName === "A" && !node.textContent?.trim(),
|
|
24
|
+
replacement: () => "",
|
|
25
|
+
});
|
|
26
|
+
return turndown
|
|
27
|
+
.turndown(html)
|
|
28
|
+
.replace(/\[\\?\[\s*\\?\]\]\([^)]*\)/g, "")
|
|
29
|
+
.replace(/ +/g, " ")
|
|
30
|
+
.replace(/\s+,/g, ",")
|
|
31
|
+
.replace(/\s+\./g, ".")
|
|
32
|
+
.replace(/\n{3,}/g, "\n\n")
|
|
33
|
+
.trim();
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
try {
|
|
37
|
+
const response = await fetch(url, {
|
|
38
|
+
headers: {
|
|
39
|
+
"User-Agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36",
|
|
40
|
+
"Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8",
|
|
41
|
+
"Accept-Language": "en-US,en;q=0.9",
|
|
42
|
+
},
|
|
43
|
+
signal: AbortSignal.timeout(15000),
|
|
44
|
+
});
|
|
45
|
+
|
|
46
|
+
if (!response.ok) {
|
|
47
|
+
console.error(`HTTP ${response.status}: ${response.statusText}`);
|
|
48
|
+
process.exit(1);
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
const html = await response.text();
|
|
52
|
+
const dom = new JSDOM(html, { url });
|
|
53
|
+
const reader = new Readability(dom.window.document);
|
|
54
|
+
const article = reader.parse();
|
|
55
|
+
|
|
56
|
+
if (article && article.content) {
|
|
57
|
+
if (article.title) {
|
|
58
|
+
console.log(`# ${article.title}\n`);
|
|
59
|
+
}
|
|
60
|
+
console.log(htmlToMarkdown(article.content));
|
|
61
|
+
process.exit(0);
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
// Fallback: try to extract main content
|
|
65
|
+
const fallbackDoc = new JSDOM(html, { url });
|
|
66
|
+
const body = fallbackDoc.window.document;
|
|
67
|
+
body.querySelectorAll("script, style, noscript, nav, header, footer, aside").forEach((el) => el.remove());
|
|
68
|
+
|
|
69
|
+
const title = body.querySelector("title")?.textContent?.trim();
|
|
70
|
+
const main = body.querySelector("main, article, [role='main'], .content, #content") || body.body;
|
|
71
|
+
|
|
72
|
+
if (title) {
|
|
73
|
+
console.log(`# ${title}\n`);
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
const text = main?.innerHTML || "";
|
|
77
|
+
if (text.trim().length > 100) {
|
|
78
|
+
console.log(htmlToMarkdown(text));
|
|
79
|
+
} else {
|
|
80
|
+
console.error("Could not extract readable content from this page.");
|
|
81
|
+
process.exit(1);
|
|
82
|
+
}
|
|
83
|
+
} catch (e) {
|
|
84
|
+
console.error(`Error: ${e.message}`);
|
|
85
|
+
process.exit(1);
|
|
86
|
+
}
|
|
@@ -0,0 +1,617 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "web-search",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"lockfileVersion": 3,
|
|
5
|
+
"requires": true,
|
|
6
|
+
"packages": {
|
|
7
|
+
"": {
|
|
8
|
+
"name": "web-search",
|
|
9
|
+
"version": "1.0.0",
|
|
10
|
+
"license": "MIT",
|
|
11
|
+
"dependencies": {
|
|
12
|
+
"@mozilla/readability": "^0.6.0",
|
|
13
|
+
"jsdom": "^27.0.1",
|
|
14
|
+
"turndown": "^7.2.2",
|
|
15
|
+
"turndown-plugin-gfm": "^1.0.2"
|
|
16
|
+
}
|
|
17
|
+
},
|
|
18
|
+
"node_modules/@acemir/cssom": {
|
|
19
|
+
"version": "0.9.31",
|
|
20
|
+
"resolved": "https://registry.npmjs.org/@acemir/cssom/-/cssom-0.9.31.tgz",
|
|
21
|
+
"integrity": "sha512-ZnR3GSaH+/vJ0YlHau21FjfLYjMpYVIzTD8M8vIEQvIGxeOXyXdzCI140rrCY862p/C/BbzWsjc1dgnM9mkoTA==",
|
|
22
|
+
"license": "MIT"
|
|
23
|
+
},
|
|
24
|
+
"node_modules/@asamuzakjp/css-color": {
|
|
25
|
+
"version": "4.1.2",
|
|
26
|
+
"resolved": "https://registry.npmjs.org/@asamuzakjp/css-color/-/css-color-4.1.2.tgz",
|
|
27
|
+
"integrity": "sha512-NfBUvBaYgKIuq6E/RBLY1m0IohzNHAYyaJGuTK79Z23uNwmz2jl1mPsC5ZxCCxylinKhT1Amn5oNTlx1wN8cQg==",
|
|
28
|
+
"license": "MIT",
|
|
29
|
+
"dependencies": {
|
|
30
|
+
"@csstools/css-calc": "^3.0.0",
|
|
31
|
+
"@csstools/css-color-parser": "^4.0.1",
|
|
32
|
+
"@csstools/css-parser-algorithms": "^4.0.0",
|
|
33
|
+
"@csstools/css-tokenizer": "^4.0.0",
|
|
34
|
+
"lru-cache": "^11.2.5"
|
|
35
|
+
}
|
|
36
|
+
},
|
|
37
|
+
"node_modules/@asamuzakjp/dom-selector": {
|
|
38
|
+
"version": "6.7.8",
|
|
39
|
+
"resolved": "https://registry.npmjs.org/@asamuzakjp/dom-selector/-/dom-selector-6.7.8.tgz",
|
|
40
|
+
"integrity": "sha512-stisC1nULNc9oH5lakAj8MH88ZxeGxzyWNDfbdCxvJSJIvDsHNZqYvscGTgy/ysgXWLJPt6K/4t0/GjvtKcFJQ==",
|
|
41
|
+
"license": "MIT",
|
|
42
|
+
"dependencies": {
|
|
43
|
+
"@asamuzakjp/nwsapi": "^2.3.9",
|
|
44
|
+
"bidi-js": "^1.0.3",
|
|
45
|
+
"css-tree": "^3.1.0",
|
|
46
|
+
"is-potential-custom-element-name": "^1.0.1",
|
|
47
|
+
"lru-cache": "^11.2.5"
|
|
48
|
+
}
|
|
49
|
+
},
|
|
50
|
+
"node_modules/@asamuzakjp/nwsapi": {
|
|
51
|
+
"version": "2.3.9",
|
|
52
|
+
"resolved": "https://registry.npmjs.org/@asamuzakjp/nwsapi/-/nwsapi-2.3.9.tgz",
|
|
53
|
+
"integrity": "sha512-n8GuYSrI9bF7FFZ/SjhwevlHc8xaVlb/7HmHelnc/PZXBD2ZR49NnN9sMMuDdEGPeeRQ5d0hqlSlEpgCX3Wl0Q==",
|
|
54
|
+
"license": "MIT"
|
|
55
|
+
},
|
|
56
|
+
"node_modules/@csstools/color-helpers": {
|
|
57
|
+
"version": "6.0.1",
|
|
58
|
+
"resolved": "https://registry.npmjs.org/@csstools/color-helpers/-/color-helpers-6.0.1.tgz",
|
|
59
|
+
"integrity": "sha512-NmXRccUJMk2AWA5A7e5a//3bCIMyOu2hAtdRYrhPPHjDxINuCwX1w6rnIZ4xjLcp0ayv6h8Pc3X0eJUGiAAXHQ==",
|
|
60
|
+
"funding": [
|
|
61
|
+
{
|
|
62
|
+
"type": "github",
|
|
63
|
+
"url": "https://github.com/sponsors/csstools"
|
|
64
|
+
},
|
|
65
|
+
{
|
|
66
|
+
"type": "opencollective",
|
|
67
|
+
"url": "https://opencollective.com/csstools"
|
|
68
|
+
}
|
|
69
|
+
],
|
|
70
|
+
"license": "MIT-0",
|
|
71
|
+
"engines": {
|
|
72
|
+
"node": ">=20.19.0"
|
|
73
|
+
}
|
|
74
|
+
},
|
|
75
|
+
"node_modules/@csstools/css-calc": {
|
|
76
|
+
"version": "3.0.0",
|
|
77
|
+
"resolved": "https://registry.npmjs.org/@csstools/css-calc/-/css-calc-3.0.0.tgz",
|
|
78
|
+
"integrity": "sha512-q4d82GTl8BIlh/dTnVsWmxnbWJeb3kiU8eUH71UxlxnS+WIaALmtzTL8gR15PkYOexMQYVk0CO4qIG93C1IvPA==",
|
|
79
|
+
"funding": [
|
|
80
|
+
{
|
|
81
|
+
"type": "github",
|
|
82
|
+
"url": "https://github.com/sponsors/csstools"
|
|
83
|
+
},
|
|
84
|
+
{
|
|
85
|
+
"type": "opencollective",
|
|
86
|
+
"url": "https://opencollective.com/csstools"
|
|
87
|
+
}
|
|
88
|
+
],
|
|
89
|
+
"license": "MIT",
|
|
90
|
+
"engines": {
|
|
91
|
+
"node": ">=20.19.0"
|
|
92
|
+
},
|
|
93
|
+
"peerDependencies": {
|
|
94
|
+
"@csstools/css-parser-algorithms": "^4.0.0",
|
|
95
|
+
"@csstools/css-tokenizer": "^4.0.0"
|
|
96
|
+
}
|
|
97
|
+
},
|
|
98
|
+
"node_modules/@csstools/css-color-parser": {
|
|
99
|
+
"version": "4.0.1",
|
|
100
|
+
"resolved": "https://registry.npmjs.org/@csstools/css-color-parser/-/css-color-parser-4.0.1.tgz",
|
|
101
|
+
"integrity": "sha512-vYwO15eRBEkeF6xjAno/KQ61HacNhfQuuU/eGwH67DplL0zD5ZixUa563phQvUelA07yDczIXdtmYojCphKJcw==",
|
|
102
|
+
"funding": [
|
|
103
|
+
{
|
|
104
|
+
"type": "github",
|
|
105
|
+
"url": "https://github.com/sponsors/csstools"
|
|
106
|
+
},
|
|
107
|
+
{
|
|
108
|
+
"type": "opencollective",
|
|
109
|
+
"url": "https://opencollective.com/csstools"
|
|
110
|
+
}
|
|
111
|
+
],
|
|
112
|
+
"license": "MIT",
|
|
113
|
+
"dependencies": {
|
|
114
|
+
"@csstools/color-helpers": "^6.0.1",
|
|
115
|
+
"@csstools/css-calc": "^3.0.0"
|
|
116
|
+
},
|
|
117
|
+
"engines": {
|
|
118
|
+
"node": ">=20.19.0"
|
|
119
|
+
},
|
|
120
|
+
"peerDependencies": {
|
|
121
|
+
"@csstools/css-parser-algorithms": "^4.0.0",
|
|
122
|
+
"@csstools/css-tokenizer": "^4.0.0"
|
|
123
|
+
}
|
|
124
|
+
},
|
|
125
|
+
"node_modules/@csstools/css-parser-algorithms": {
|
|
126
|
+
"version": "4.0.0",
|
|
127
|
+
"resolved": "https://registry.npmjs.org/@csstools/css-parser-algorithms/-/css-parser-algorithms-4.0.0.tgz",
|
|
128
|
+
"integrity": "sha512-+B87qS7fIG3L5h3qwJ/IFbjoVoOe/bpOdh9hAjXbvx0o8ImEmUsGXN0inFOnk2ChCFgqkkGFQ+TpM5rbhkKe4w==",
|
|
129
|
+
"funding": [
|
|
130
|
+
{
|
|
131
|
+
"type": "github",
|
|
132
|
+
"url": "https://github.com/sponsors/csstools"
|
|
133
|
+
},
|
|
134
|
+
{
|
|
135
|
+
"type": "opencollective",
|
|
136
|
+
"url": "https://opencollective.com/csstools"
|
|
137
|
+
}
|
|
138
|
+
],
|
|
139
|
+
"license": "MIT",
|
|
140
|
+
"peer": true,
|
|
141
|
+
"engines": {
|
|
142
|
+
"node": ">=20.19.0"
|
|
143
|
+
},
|
|
144
|
+
"peerDependencies": {
|
|
145
|
+
"@csstools/css-tokenizer": "^4.0.0"
|
|
146
|
+
}
|
|
147
|
+
},
|
|
148
|
+
"node_modules/@csstools/css-syntax-patches-for-csstree": {
|
|
149
|
+
"version": "1.0.26",
|
|
150
|
+
"resolved": "https://registry.npmjs.org/@csstools/css-syntax-patches-for-csstree/-/css-syntax-patches-for-csstree-1.0.26.tgz",
|
|
151
|
+
"integrity": "sha512-6boXK0KkzT5u5xOgF6TKB+CLq9SOpEGmkZw0g5n9/7yg85wab3UzSxB8TxhLJ31L4SGJ6BCFRw/iftTha1CJXA==",
|
|
152
|
+
"funding": [
|
|
153
|
+
{
|
|
154
|
+
"type": "github",
|
|
155
|
+
"url": "https://github.com/sponsors/csstools"
|
|
156
|
+
},
|
|
157
|
+
{
|
|
158
|
+
"type": "opencollective",
|
|
159
|
+
"url": "https://opencollective.com/csstools"
|
|
160
|
+
}
|
|
161
|
+
],
|
|
162
|
+
"license": "MIT-0"
|
|
163
|
+
},
|
|
164
|
+
"node_modules/@csstools/css-tokenizer": {
|
|
165
|
+
"version": "4.0.0",
|
|
166
|
+
"resolved": "https://registry.npmjs.org/@csstools/css-tokenizer/-/css-tokenizer-4.0.0.tgz",
|
|
167
|
+
"integrity": "sha512-QxULHAm7cNu72w97JUNCBFODFaXpbDg+dP8b/oWFAZ2MTRppA3U00Y2L1HqaS4J6yBqxwa/Y3nMBaxVKbB/NsA==",
|
|
168
|
+
"funding": [
|
|
169
|
+
{
|
|
170
|
+
"type": "github",
|
|
171
|
+
"url": "https://github.com/sponsors/csstools"
|
|
172
|
+
},
|
|
173
|
+
{
|
|
174
|
+
"type": "opencollective",
|
|
175
|
+
"url": "https://opencollective.com/csstools"
|
|
176
|
+
}
|
|
177
|
+
],
|
|
178
|
+
"license": "MIT",
|
|
179
|
+
"peer": true,
|
|
180
|
+
"engines": {
|
|
181
|
+
"node": ">=20.19.0"
|
|
182
|
+
}
|
|
183
|
+
},
|
|
184
|
+
"node_modules/@exodus/bytes": {
|
|
185
|
+
"version": "1.12.0",
|
|
186
|
+
"resolved": "https://registry.npmjs.org/@exodus/bytes/-/bytes-1.12.0.tgz",
|
|
187
|
+
"integrity": "sha512-BuCOHA/EJdPN0qQ5MdgAiJSt9fYDHbghlgrj33gRdy/Yp1/FMCDhU6vJfcKrLC0TPWGSrfH3vYXBQWmFHxlddw==",
|
|
188
|
+
"license": "MIT",
|
|
189
|
+
"engines": {
|
|
190
|
+
"node": "^20.19.0 || ^22.12.0 || >=24.0.0"
|
|
191
|
+
},
|
|
192
|
+
"peerDependencies": {
|
|
193
|
+
"@noble/hashes": "^1.8.0 || ^2.0.0"
|
|
194
|
+
},
|
|
195
|
+
"peerDependenciesMeta": {
|
|
196
|
+
"@noble/hashes": {
|
|
197
|
+
"optional": true
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
},
|
|
201
|
+
"node_modules/@mixmark-io/domino": {
|
|
202
|
+
"version": "2.2.0",
|
|
203
|
+
"resolved": "https://registry.npmjs.org/@mixmark-io/domino/-/domino-2.2.0.tgz",
|
|
204
|
+
"integrity": "sha512-Y28PR25bHXUg88kCV7nivXrP2Nj2RueZ3/l/jdx6J9f8J4nsEGcgX0Qe6lt7Pa+J79+kPiJU3LguR6O/6zrLOw==",
|
|
205
|
+
"license": "BSD-2-Clause"
|
|
206
|
+
},
|
|
207
|
+
"node_modules/@mozilla/readability": {
|
|
208
|
+
"version": "0.6.0",
|
|
209
|
+
"resolved": "https://registry.npmjs.org/@mozilla/readability/-/readability-0.6.0.tgz",
|
|
210
|
+
"integrity": "sha512-juG5VWh4qAivzTAeMzvY9xs9HY5rAcr2E4I7tiSSCokRFi7XIZCAu92ZkSTsIj1OPceCifL3cpfteP3pDT9/QQ==",
|
|
211
|
+
"license": "Apache-2.0",
|
|
212
|
+
"engines": {
|
|
213
|
+
"node": ">=14.0.0"
|
|
214
|
+
}
|
|
215
|
+
},
|
|
216
|
+
"node_modules/agent-base": {
|
|
217
|
+
"version": "7.1.4",
|
|
218
|
+
"resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.4.tgz",
|
|
219
|
+
"integrity": "sha512-MnA+YT8fwfJPgBx3m60MNqakm30XOkyIoH1y6huTQvC0PwZG7ki8NacLBcrPbNoo8vEZy7Jpuk7+jMO+CUovTQ==",
|
|
220
|
+
"license": "MIT",
|
|
221
|
+
"engines": {
|
|
222
|
+
"node": ">= 14"
|
|
223
|
+
}
|
|
224
|
+
},
|
|
225
|
+
"node_modules/bidi-js": {
|
|
226
|
+
"version": "1.0.3",
|
|
227
|
+
"resolved": "https://registry.npmjs.org/bidi-js/-/bidi-js-1.0.3.tgz",
|
|
228
|
+
"integrity": "sha512-RKshQI1R3YQ+n9YJz2QQ147P66ELpa1FQEg20Dk8oW9t2KgLbpDLLp9aGZ7y8WHSshDknG0bknqGw5/tyCs5tw==",
|
|
229
|
+
"license": "MIT",
|
|
230
|
+
"dependencies": {
|
|
231
|
+
"require-from-string": "^2.0.2"
|
|
232
|
+
}
|
|
233
|
+
},
|
|
234
|
+
"node_modules/css-tree": {
|
|
235
|
+
"version": "3.1.0",
|
|
236
|
+
"resolved": "https://registry.npmjs.org/css-tree/-/css-tree-3.1.0.tgz",
|
|
237
|
+
"integrity": "sha512-0eW44TGN5SQXU1mWSkKwFstI/22X2bG1nYzZTYMAWjylYURhse752YgbE4Cx46AC+bAvI+/dYTPRk1LqSUnu6w==",
|
|
238
|
+
"license": "MIT",
|
|
239
|
+
"dependencies": {
|
|
240
|
+
"mdn-data": "2.12.2",
|
|
241
|
+
"source-map-js": "^1.0.1"
|
|
242
|
+
},
|
|
243
|
+
"engines": {
|
|
244
|
+
"node": "^10 || ^12.20.0 || ^14.13.0 || >=15.0.0"
|
|
245
|
+
}
|
|
246
|
+
},
|
|
247
|
+
"node_modules/cssstyle": {
|
|
248
|
+
"version": "5.3.7",
|
|
249
|
+
"resolved": "https://registry.npmjs.org/cssstyle/-/cssstyle-5.3.7.tgz",
|
|
250
|
+
"integrity": "sha512-7D2EPVltRrsTkhpQmksIu+LxeWAIEk6wRDMJ1qljlv+CKHJM+cJLlfhWIzNA44eAsHXSNe3+vO6DW1yCYx8SuQ==",
|
|
251
|
+
"license": "MIT",
|
|
252
|
+
"dependencies": {
|
|
253
|
+
"@asamuzakjp/css-color": "^4.1.1",
|
|
254
|
+
"@csstools/css-syntax-patches-for-csstree": "^1.0.21",
|
|
255
|
+
"css-tree": "^3.1.0",
|
|
256
|
+
"lru-cache": "^11.2.4"
|
|
257
|
+
},
|
|
258
|
+
"engines": {
|
|
259
|
+
"node": ">=20"
|
|
260
|
+
}
|
|
261
|
+
},
|
|
262
|
+
"node_modules/data-urls": {
|
|
263
|
+
"version": "6.0.1",
|
|
264
|
+
"resolved": "https://registry.npmjs.org/data-urls/-/data-urls-6.0.1.tgz",
|
|
265
|
+
"integrity": "sha512-euIQENZg6x8mj3fO6o9+fOW8MimUI4PpD/fZBhJfeioZVy9TUpM4UY7KjQNVZFlqwJ0UdzRDzkycB997HEq1BQ==",
|
|
266
|
+
"license": "MIT",
|
|
267
|
+
"dependencies": {
|
|
268
|
+
"whatwg-mimetype": "^5.0.0",
|
|
269
|
+
"whatwg-url": "^15.1.0"
|
|
270
|
+
},
|
|
271
|
+
"engines": {
|
|
272
|
+
"node": ">=20"
|
|
273
|
+
}
|
|
274
|
+
},
|
|
275
|
+
"node_modules/data-urls/node_modules/whatwg-mimetype": {
|
|
276
|
+
"version": "5.0.0",
|
|
277
|
+
"resolved": "https://registry.npmjs.org/whatwg-mimetype/-/whatwg-mimetype-5.0.0.tgz",
|
|
278
|
+
"integrity": "sha512-sXcNcHOC51uPGF0P/D4NVtrkjSU2fNsm9iog4ZvZJsL3rjoDAzXZhkm2MWt1y+PUdggKAYVoMAIYcs78wJ51Cw==",
|
|
279
|
+
"license": "MIT",
|
|
280
|
+
"engines": {
|
|
281
|
+
"node": ">=20"
|
|
282
|
+
}
|
|
283
|
+
},
|
|
284
|
+
"node_modules/debug": {
|
|
285
|
+
"version": "4.4.3",
|
|
286
|
+
"resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz",
|
|
287
|
+
"integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==",
|
|
288
|
+
"license": "MIT",
|
|
289
|
+
"dependencies": {
|
|
290
|
+
"ms": "^2.1.3"
|
|
291
|
+
},
|
|
292
|
+
"engines": {
|
|
293
|
+
"node": ">=6.0"
|
|
294
|
+
},
|
|
295
|
+
"peerDependenciesMeta": {
|
|
296
|
+
"supports-color": {
|
|
297
|
+
"optional": true
|
|
298
|
+
}
|
|
299
|
+
}
|
|
300
|
+
},
|
|
301
|
+
"node_modules/decimal.js": {
|
|
302
|
+
"version": "10.6.0",
|
|
303
|
+
"resolved": "https://registry.npmjs.org/decimal.js/-/decimal.js-10.6.0.tgz",
|
|
304
|
+
"integrity": "sha512-YpgQiITW3JXGntzdUmyUR1V812Hn8T1YVXhCu+wO3OpS4eU9l4YdD3qjyiKdV6mvV29zapkMeD390UVEf2lkUg==",
|
|
305
|
+
"license": "MIT"
|
|
306
|
+
},
|
|
307
|
+
"node_modules/entities": {
|
|
308
|
+
"version": "6.0.1",
|
|
309
|
+
"resolved": "https://registry.npmjs.org/entities/-/entities-6.0.1.tgz",
|
|
310
|
+
"integrity": "sha512-aN97NXWF6AWBTahfVOIrB/NShkzi5H7F9r1s9mD3cDj4Ko5f2qhhVoYMibXF7GlLveb/D2ioWay8lxI97Ven3g==",
|
|
311
|
+
"license": "BSD-2-Clause",
|
|
312
|
+
"engines": {
|
|
313
|
+
"node": ">=0.12"
|
|
314
|
+
},
|
|
315
|
+
"funding": {
|
|
316
|
+
"url": "https://github.com/fb55/entities?sponsor=1"
|
|
317
|
+
}
|
|
318
|
+
},
|
|
319
|
+
"node_modules/html-encoding-sniffer": {
|
|
320
|
+
"version": "6.0.0",
|
|
321
|
+
"resolved": "https://registry.npmjs.org/html-encoding-sniffer/-/html-encoding-sniffer-6.0.0.tgz",
|
|
322
|
+
"integrity": "sha512-CV9TW3Y3f8/wT0BRFc1/KAVQ3TUHiXmaAb6VW9vtiMFf7SLoMd1PdAc4W3KFOFETBJUb90KatHqlsZMWV+R9Gg==",
|
|
323
|
+
"license": "MIT",
|
|
324
|
+
"dependencies": {
|
|
325
|
+
"@exodus/bytes": "^1.6.0"
|
|
326
|
+
},
|
|
327
|
+
"engines": {
|
|
328
|
+
"node": "^20.19.0 || ^22.12.0 || >=24.0.0"
|
|
329
|
+
}
|
|
330
|
+
},
|
|
331
|
+
"node_modules/http-proxy-agent": {
|
|
332
|
+
"version": "7.0.2",
|
|
333
|
+
"resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-7.0.2.tgz",
|
|
334
|
+
"integrity": "sha512-T1gkAiYYDWYx3V5Bmyu7HcfcvL7mUrTWiM6yOfa3PIphViJ/gFPbvidQ+veqSOHci/PxBcDabeUNCzpOODJZig==",
|
|
335
|
+
"license": "MIT",
|
|
336
|
+
"dependencies": {
|
|
337
|
+
"agent-base": "^7.1.0",
|
|
338
|
+
"debug": "^4.3.4"
|
|
339
|
+
},
|
|
340
|
+
"engines": {
|
|
341
|
+
"node": ">= 14"
|
|
342
|
+
}
|
|
343
|
+
},
|
|
344
|
+
"node_modules/https-proxy-agent": {
|
|
345
|
+
"version": "7.0.6",
|
|
346
|
+
"resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-7.0.6.tgz",
|
|
347
|
+
"integrity": "sha512-vK9P5/iUfdl95AI+JVyUuIcVtd4ofvtrOr3HNtM2yxC9bnMbEdp3x01OhQNnjb8IJYi38VlTE3mBXwcfvywuSw==",
|
|
348
|
+
"license": "MIT",
|
|
349
|
+
"dependencies": {
|
|
350
|
+
"agent-base": "^7.1.2",
|
|
351
|
+
"debug": "4"
|
|
352
|
+
},
|
|
353
|
+
"engines": {
|
|
354
|
+
"node": ">= 14"
|
|
355
|
+
}
|
|
356
|
+
},
|
|
357
|
+
"node_modules/is-potential-custom-element-name": {
|
|
358
|
+
"version": "1.0.1",
|
|
359
|
+
"resolved": "https://registry.npmjs.org/is-potential-custom-element-name/-/is-potential-custom-element-name-1.0.1.tgz",
|
|
360
|
+
"integrity": "sha512-bCYeRA2rVibKZd+s2625gGnGF/t7DSqDs4dP7CrLA1m7jKWz6pps0LpYLJN8Q64HtmPKJ1hrN3nzPNKFEKOUiQ==",
|
|
361
|
+
"license": "MIT"
|
|
362
|
+
},
|
|
363
|
+
"node_modules/jsdom": {
|
|
364
|
+
"version": "27.4.0",
|
|
365
|
+
"resolved": "https://registry.npmjs.org/jsdom/-/jsdom-27.4.0.tgz",
|
|
366
|
+
"integrity": "sha512-mjzqwWRD9Y1J1KUi7W97Gja1bwOOM5Ug0EZ6UDK3xS7j7mndrkwozHtSblfomlzyB4NepioNt+B2sOSzczVgtQ==",
|
|
367
|
+
"license": "MIT",
|
|
368
|
+
"dependencies": {
|
|
369
|
+
"@acemir/cssom": "^0.9.28",
|
|
370
|
+
"@asamuzakjp/dom-selector": "^6.7.6",
|
|
371
|
+
"@exodus/bytes": "^1.6.0",
|
|
372
|
+
"cssstyle": "^5.3.4",
|
|
373
|
+
"data-urls": "^6.0.0",
|
|
374
|
+
"decimal.js": "^10.6.0",
|
|
375
|
+
"html-encoding-sniffer": "^6.0.0",
|
|
376
|
+
"http-proxy-agent": "^7.0.2",
|
|
377
|
+
"https-proxy-agent": "^7.0.6",
|
|
378
|
+
"is-potential-custom-element-name": "^1.0.1",
|
|
379
|
+
"parse5": "^8.0.0",
|
|
380
|
+
"saxes": "^6.0.0",
|
|
381
|
+
"symbol-tree": "^3.2.4",
|
|
382
|
+
"tough-cookie": "^6.0.0",
|
|
383
|
+
"w3c-xmlserializer": "^5.0.0",
|
|
384
|
+
"webidl-conversions": "^8.0.0",
|
|
385
|
+
"whatwg-mimetype": "^4.0.0",
|
|
386
|
+
"whatwg-url": "^15.1.0",
|
|
387
|
+
"ws": "^8.18.3",
|
|
388
|
+
"xml-name-validator": "^5.0.0"
|
|
389
|
+
},
|
|
390
|
+
"engines": {
|
|
391
|
+
"node": "^20.19.0 || ^22.12.0 || >=24.0.0"
|
|
392
|
+
},
|
|
393
|
+
"peerDependencies": {
|
|
394
|
+
"canvas": "^3.0.0"
|
|
395
|
+
},
|
|
396
|
+
"peerDependenciesMeta": {
|
|
397
|
+
"canvas": {
|
|
398
|
+
"optional": true
|
|
399
|
+
}
|
|
400
|
+
}
|
|
401
|
+
},
|
|
402
|
+
"node_modules/lru-cache": {
|
|
403
|
+
"version": "11.2.5",
|
|
404
|
+
"resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-11.2.5.tgz",
|
|
405
|
+
"integrity": "sha512-vFrFJkWtJvJnD5hg+hJvVE8Lh/TcMzKnTgCWmtBipwI5yLX/iX+5UB2tfuyODF5E7k9xEzMdYgGqaSb1c0c5Yw==",
|
|
406
|
+
"license": "BlueOak-1.0.0",
|
|
407
|
+
"engines": {
|
|
408
|
+
"node": "20 || >=22"
|
|
409
|
+
}
|
|
410
|
+
},
|
|
411
|
+
"node_modules/mdn-data": {
|
|
412
|
+
"version": "2.12.2",
|
|
413
|
+
"resolved": "https://registry.npmjs.org/mdn-data/-/mdn-data-2.12.2.tgz",
|
|
414
|
+
"integrity": "sha512-IEn+pegP1aManZuckezWCO+XZQDplx1366JoVhTpMpBB1sPey/SbveZQUosKiKiGYjg1wH4pMlNgXbCiYgihQA==",
|
|
415
|
+
"license": "CC0-1.0"
|
|
416
|
+
},
|
|
417
|
+
"node_modules/ms": {
|
|
418
|
+
"version": "2.1.3",
|
|
419
|
+
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz",
|
|
420
|
+
"integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==",
|
|
421
|
+
"license": "MIT"
|
|
422
|
+
},
|
|
423
|
+
"node_modules/parse5": {
|
|
424
|
+
"version": "8.0.0",
|
|
425
|
+
"resolved": "https://registry.npmjs.org/parse5/-/parse5-8.0.0.tgz",
|
|
426
|
+
"integrity": "sha512-9m4m5GSgXjL4AjumKzq1Fgfp3Z8rsvjRNbnkVwfu2ImRqE5D0LnY2QfDen18FSY9C573YU5XxSapdHZTZ2WolA==",
|
|
427
|
+
"license": "MIT",
|
|
428
|
+
"dependencies": {
|
|
429
|
+
"entities": "^6.0.0"
|
|
430
|
+
},
|
|
431
|
+
"funding": {
|
|
432
|
+
"url": "https://github.com/inikulin/parse5?sponsor=1"
|
|
433
|
+
}
|
|
434
|
+
},
|
|
435
|
+
"node_modules/punycode": {
|
|
436
|
+
"version": "2.3.1",
|
|
437
|
+
"resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz",
|
|
438
|
+
"integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==",
|
|
439
|
+
"license": "MIT",
|
|
440
|
+
"engines": {
|
|
441
|
+
"node": ">=6"
|
|
442
|
+
}
|
|
443
|
+
},
|
|
444
|
+
"node_modules/require-from-string": {
|
|
445
|
+
"version": "2.0.2",
|
|
446
|
+
"resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz",
|
|
447
|
+
"integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==",
|
|
448
|
+
"license": "MIT",
|
|
449
|
+
"engines": {
|
|
450
|
+
"node": ">=0.10.0"
|
|
451
|
+
}
|
|
452
|
+
},
|
|
453
|
+
"node_modules/saxes": {
|
|
454
|
+
"version": "6.0.0",
|
|
455
|
+
"resolved": "https://registry.npmjs.org/saxes/-/saxes-6.0.0.tgz",
|
|
456
|
+
"integrity": "sha512-xAg7SOnEhrm5zI3puOOKyy1OMcMlIJZYNJY7xLBwSze0UjhPLnWfj2GF2EpT0jmzaJKIWKHLsaSSajf35bcYnA==",
|
|
457
|
+
"license": "ISC",
|
|
458
|
+
"dependencies": {
|
|
459
|
+
"xmlchars": "^2.2.0"
|
|
460
|
+
},
|
|
461
|
+
"engines": {
|
|
462
|
+
"node": ">=v12.22.7"
|
|
463
|
+
}
|
|
464
|
+
},
|
|
465
|
+
"node_modules/source-map-js": {
|
|
466
|
+
"version": "1.2.1",
|
|
467
|
+
"resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz",
|
|
468
|
+
"integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==",
|
|
469
|
+
"license": "BSD-3-Clause",
|
|
470
|
+
"engines": {
|
|
471
|
+
"node": ">=0.10.0"
|
|
472
|
+
}
|
|
473
|
+
},
|
|
474
|
+
"node_modules/symbol-tree": {
|
|
475
|
+
"version": "3.2.4",
|
|
476
|
+
"resolved": "https://registry.npmjs.org/symbol-tree/-/symbol-tree-3.2.4.tgz",
|
|
477
|
+
"integrity": "sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw==",
|
|
478
|
+
"license": "MIT"
|
|
479
|
+
},
|
|
480
|
+
"node_modules/tldts": {
|
|
481
|
+
"version": "7.0.23",
|
|
482
|
+
"resolved": "https://registry.npmjs.org/tldts/-/tldts-7.0.23.tgz",
|
|
483
|
+
"integrity": "sha512-ASdhgQIBSay0R/eXggAkQ53G4nTJqTXqC2kbaBbdDwM7SkjyZyO0OaaN1/FH7U/yCeqOHDwFO5j8+Os/IS1dXw==",
|
|
484
|
+
"license": "MIT",
|
|
485
|
+
"dependencies": {
|
|
486
|
+
"tldts-core": "^7.0.23"
|
|
487
|
+
},
|
|
488
|
+
"bin": {
|
|
489
|
+
"tldts": "bin/cli.js"
|
|
490
|
+
}
|
|
491
|
+
},
|
|
492
|
+
"node_modules/tldts-core": {
|
|
493
|
+
"version": "7.0.23",
|
|
494
|
+
"resolved": "https://registry.npmjs.org/tldts-core/-/tldts-core-7.0.23.tgz",
|
|
495
|
+
"integrity": "sha512-0g9vrtDQLrNIiCj22HSe9d4mLVG3g5ph5DZ8zCKBr4OtrspmNB6ss7hVyzArAeE88ceZocIEGkyW1Ime7fxPtQ==",
|
|
496
|
+
"license": "MIT"
|
|
497
|
+
},
|
|
498
|
+
"node_modules/tough-cookie": {
|
|
499
|
+
"version": "6.0.0",
|
|
500
|
+
"resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-6.0.0.tgz",
|
|
501
|
+
"integrity": "sha512-kXuRi1mtaKMrsLUxz3sQYvVl37B0Ns6MzfrtV5DvJceE9bPyspOqk9xxv7XbZWcfLWbFmm997vl83qUWVJA64w==",
|
|
502
|
+
"license": "BSD-3-Clause",
|
|
503
|
+
"dependencies": {
|
|
504
|
+
"tldts": "^7.0.5"
|
|
505
|
+
},
|
|
506
|
+
"engines": {
|
|
507
|
+
"node": ">=16"
|
|
508
|
+
}
|
|
509
|
+
},
|
|
510
|
+
"node_modules/tr46": {
|
|
511
|
+
"version": "6.0.0",
|
|
512
|
+
"resolved": "https://registry.npmjs.org/tr46/-/tr46-6.0.0.tgz",
|
|
513
|
+
"integrity": "sha512-bLVMLPtstlZ4iMQHpFHTR7GAGj2jxi8Dg0s2h2MafAE4uSWF98FC/3MomU51iQAMf8/qDUbKWf5GxuvvVcXEhw==",
|
|
514
|
+
"license": "MIT",
|
|
515
|
+
"dependencies": {
|
|
516
|
+
"punycode": "^2.3.1"
|
|
517
|
+
},
|
|
518
|
+
"engines": {
|
|
519
|
+
"node": ">=20"
|
|
520
|
+
}
|
|
521
|
+
},
|
|
522
|
+
"node_modules/turndown": {
|
|
523
|
+
"version": "7.2.2",
|
|
524
|
+
"resolved": "https://registry.npmjs.org/turndown/-/turndown-7.2.2.tgz",
|
|
525
|
+
"integrity": "sha512-1F7db8BiExOKxjSMU2b7if62D/XOyQyZbPKq/nUwopfgnHlqXHqQ0lvfUTeUIr1lZJzOPFn43dODyMSIfvWRKQ==",
|
|
526
|
+
"license": "MIT",
|
|
527
|
+
"dependencies": {
|
|
528
|
+
"@mixmark-io/domino": "^2.2.0"
|
|
529
|
+
}
|
|
530
|
+
},
|
|
531
|
+
"node_modules/turndown-plugin-gfm": {
|
|
532
|
+
"version": "1.0.2",
|
|
533
|
+
"resolved": "https://registry.npmjs.org/turndown-plugin-gfm/-/turndown-plugin-gfm-1.0.2.tgz",
|
|
534
|
+
"integrity": "sha512-vwz9tfvF7XN/jE0dGoBei3FXWuvll78ohzCZQuOb+ZjWrs3a0XhQVomJEb2Qh4VHTPNRO4GPZh0V7VRbiWwkRg==",
|
|
535
|
+
"license": "MIT"
|
|
536
|
+
},
|
|
537
|
+
"node_modules/w3c-xmlserializer": {
|
|
538
|
+
"version": "5.0.0",
|
|
539
|
+
"resolved": "https://registry.npmjs.org/w3c-xmlserializer/-/w3c-xmlserializer-5.0.0.tgz",
|
|
540
|
+
"integrity": "sha512-o8qghlI8NZHU1lLPrpi2+Uq7abh4GGPpYANlalzWxyWteJOCsr/P+oPBA49TOLu5FTZO4d3F9MnWJfiMo4BkmA==",
|
|
541
|
+
"license": "MIT",
|
|
542
|
+
"dependencies": {
|
|
543
|
+
"xml-name-validator": "^5.0.0"
|
|
544
|
+
},
|
|
545
|
+
"engines": {
|
|
546
|
+
"node": ">=18"
|
|
547
|
+
}
|
|
548
|
+
},
|
|
549
|
+
"node_modules/webidl-conversions": {
|
|
550
|
+
"version": "8.0.1",
|
|
551
|
+
"resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-8.0.1.tgz",
|
|
552
|
+
"integrity": "sha512-BMhLD/Sw+GbJC21C/UgyaZX41nPt8bUTg+jWyDeg7e7YN4xOM05YPSIXceACnXVtqyEw/LMClUQMtMZ+PGGpqQ==",
|
|
553
|
+
"license": "BSD-2-Clause",
|
|
554
|
+
"engines": {
|
|
555
|
+
"node": ">=20"
|
|
556
|
+
}
|
|
557
|
+
},
|
|
558
|
+
"node_modules/whatwg-mimetype": {
|
|
559
|
+
"version": "4.0.0",
|
|
560
|
+
"resolved": "https://registry.npmjs.org/whatwg-mimetype/-/whatwg-mimetype-4.0.0.tgz",
|
|
561
|
+
"integrity": "sha512-QaKxh0eNIi2mE9p2vEdzfagOKHCcj1pJ56EEHGQOVxp8r9/iszLUUV7v89x9O1p/T+NlTM5W7jW6+cz4Fq1YVg==",
|
|
562
|
+
"license": "MIT",
|
|
563
|
+
"engines": {
|
|
564
|
+
"node": ">=18"
|
|
565
|
+
}
|
|
566
|
+
},
|
|
567
|
+
"node_modules/whatwg-url": {
|
|
568
|
+
"version": "15.1.0",
|
|
569
|
+
"resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-15.1.0.tgz",
|
|
570
|
+
"integrity": "sha512-2ytDk0kiEj/yu90JOAp44PVPUkO9+jVhyf+SybKlRHSDlvOOZhdPIrr7xTH64l4WixO2cP+wQIcgujkGBPPz6g==",
|
|
571
|
+
"license": "MIT",
|
|
572
|
+
"dependencies": {
|
|
573
|
+
"tr46": "^6.0.0",
|
|
574
|
+
"webidl-conversions": "^8.0.0"
|
|
575
|
+
},
|
|
576
|
+
"engines": {
|
|
577
|
+
"node": ">=20"
|
|
578
|
+
}
|
|
579
|
+
},
|
|
580
|
+
"node_modules/ws": {
|
|
581
|
+
"version": "8.19.0",
|
|
582
|
+
"resolved": "https://registry.npmjs.org/ws/-/ws-8.19.0.tgz",
|
|
583
|
+
"integrity": "sha512-blAT2mjOEIi0ZzruJfIhb3nps74PRWTCz1IjglWEEpQl5XS/UNama6u2/rjFkDDouqr4L67ry+1aGIALViWjDg==",
|
|
584
|
+
"license": "MIT",
|
|
585
|
+
"engines": {
|
|
586
|
+
"node": ">=10.0.0"
|
|
587
|
+
},
|
|
588
|
+
"peerDependencies": {
|
|
589
|
+
"bufferutil": "^4.0.1",
|
|
590
|
+
"utf-8-validate": ">=5.0.2"
|
|
591
|
+
},
|
|
592
|
+
"peerDependenciesMeta": {
|
|
593
|
+
"bufferutil": {
|
|
594
|
+
"optional": true
|
|
595
|
+
},
|
|
596
|
+
"utf-8-validate": {
|
|
597
|
+
"optional": true
|
|
598
|
+
}
|
|
599
|
+
}
|
|
600
|
+
},
|
|
601
|
+
"node_modules/xml-name-validator": {
|
|
602
|
+
"version": "5.0.0",
|
|
603
|
+
"resolved": "https://registry.npmjs.org/xml-name-validator/-/xml-name-validator-5.0.0.tgz",
|
|
604
|
+
"integrity": "sha512-EvGK8EJ3DhaHfbRlETOWAS5pO9MZITeauHKJyb8wyajUfQUenkIg2MvLDTZ4T/TgIcm3HU0TFBgWWboAZ30UHg==",
|
|
605
|
+
"license": "Apache-2.0",
|
|
606
|
+
"engines": {
|
|
607
|
+
"node": ">=18"
|
|
608
|
+
}
|
|
609
|
+
},
|
|
610
|
+
"node_modules/xmlchars": {
|
|
611
|
+
"version": "2.2.0",
|
|
612
|
+
"resolved": "https://registry.npmjs.org/xmlchars/-/xmlchars-2.2.0.tgz",
|
|
613
|
+
"integrity": "sha512-JZnDKK8B0RCDw84FNdDAIpZK+JuJw+s7Lz8nksI7SIuU3UXJJslUthsi+uWBUYOwPFwW7W7PRLRfUKpxjtjFCw==",
|
|
614
|
+
"license": "MIT"
|
|
615
|
+
}
|
|
616
|
+
}
|
|
617
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "web-search",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"type": "module",
|
|
5
|
+
"description": "Web search via ddgs with optional content extraction",
|
|
6
|
+
"license": "MIT",
|
|
7
|
+
"dependencies": {
|
|
8
|
+
"@mozilla/readability": "^0.6.0",
|
|
9
|
+
"jsdom": "^27.0.1",
|
|
10
|
+
"turndown": "^7.2.2",
|
|
11
|
+
"turndown-plugin-gfm": "^1.0.2"
|
|
12
|
+
}
|
|
13
|
+
}
|
|
@@ -0,0 +1,215 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
import { execFileSync } from "child_process";
|
|
4
|
+
import { readFileSync, unlinkSync } from "fs";
|
|
5
|
+
import { join } from "path";
|
|
6
|
+
import { tmpdir } from "os";
|
|
7
|
+
import { randomBytes } from "crypto";
|
|
8
|
+
import { Readability } from "@mozilla/readability";
|
|
9
|
+
import { JSDOM } from "jsdom";
|
|
10
|
+
import TurndownService from "turndown";
|
|
11
|
+
import { gfm } from "turndown-plugin-gfm";
|
|
12
|
+
|
|
13
|
+
const args = process.argv.slice(2);
|
|
14
|
+
|
|
15
|
+
// Parse --content flag
|
|
16
|
+
const contentIndex = args.indexOf("--content");
|
|
17
|
+
const fetchContent = contentIndex !== -1;
|
|
18
|
+
if (fetchContent) args.splice(contentIndex, 1);
|
|
19
|
+
|
|
20
|
+
// Parse --news flag
|
|
21
|
+
const newsIndex = args.indexOf("--news");
|
|
22
|
+
const isNews = newsIndex !== -1;
|
|
23
|
+
if (isNews) args.splice(newsIndex, 1);
|
|
24
|
+
|
|
25
|
+
// Parse -n <num>
|
|
26
|
+
let numResults = 5;
|
|
27
|
+
const nIndex = args.indexOf("-n");
|
|
28
|
+
if (nIndex !== -1 && args[nIndex + 1]) {
|
|
29
|
+
numResults = parseInt(args[nIndex + 1], 10);
|
|
30
|
+
args.splice(nIndex, 2);
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
// Parse -r <region>
|
|
34
|
+
let region = null;
|
|
35
|
+
const rIndex = args.indexOf("-r");
|
|
36
|
+
if (rIndex !== -1 && args[rIndex + 1]) {
|
|
37
|
+
region = args[rIndex + 1];
|
|
38
|
+
args.splice(rIndex, 2);
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
// Parse -t <timelimit>
|
|
42
|
+
let timelimit = null;
|
|
43
|
+
const tIndex = args.indexOf("-t");
|
|
44
|
+
if (tIndex !== -1 && args[tIndex + 1]) {
|
|
45
|
+
timelimit = args[tIndex + 1];
|
|
46
|
+
args.splice(tIndex, 2);
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
// Parse -b <backend>
|
|
50
|
+
let backend = null;
|
|
51
|
+
const bIndex = args.indexOf("-b");
|
|
52
|
+
if (bIndex !== -1 && args[bIndex + 1]) {
|
|
53
|
+
backend = args[bIndex + 1];
|
|
54
|
+
args.splice(bIndex, 2);
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
const query = args.join(" ");
|
|
58
|
+
|
|
59
|
+
if (!query) {
|
|
60
|
+
console.log("Usage: search.js [--news] <query> [-n <num>] [--content] [-r <region>] [-t <timelimit>] [-b <backend>]");
|
|
61
|
+
console.log("\nOptions:");
|
|
62
|
+
console.log(" --news News search instead of web search");
|
|
63
|
+
console.log(" -n <num> Number of results (default: 5)");
|
|
64
|
+
console.log(" --content Fetch readable content as markdown");
|
|
65
|
+
console.log(" -r <region> Region: us-en, es-es, de-de, fr-fr, etc.");
|
|
66
|
+
console.log(" -t <timelimit> Time filter: d (day), w (week), m (month), y (year)");
|
|
67
|
+
console.log(" -b <backend> Backend: auto, all, bing, brave, duckduckgo, google, etc.");
|
|
68
|
+
console.log("\nExamples:");
|
|
69
|
+
console.log(' search.js "javascript async await"');
|
|
70
|
+
console.log(' search.js "rust programming" -n 10');
|
|
71
|
+
console.log(' search.js "climate change" --content');
|
|
72
|
+
console.log(' search.js --news "AI agents" -t w');
|
|
73
|
+
process.exit(1);
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
function htmlToMarkdown(html) {
|
|
77
|
+
const turndown = new TurndownService({ headingStyle: "atx", codeBlockStyle: "fenced" });
|
|
78
|
+
turndown.use(gfm);
|
|
79
|
+
turndown.addRule("removeEmptyLinks", {
|
|
80
|
+
filter: (node) => node.nodeName === "A" && !node.textContent?.trim(),
|
|
81
|
+
replacement: () => "",
|
|
82
|
+
});
|
|
83
|
+
return turndown
|
|
84
|
+
.turndown(html)
|
|
85
|
+
.replace(/\[\\?\[\s*\\?\]\]\([^)]*\)/g, "")
|
|
86
|
+
.replace(/ +/g, " ")
|
|
87
|
+
.replace(/\s+,/g, ",")
|
|
88
|
+
.replace(/\s+\./g, ".")
|
|
89
|
+
.replace(/\n{3,}/g, "\n\n")
|
|
90
|
+
.trim();
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
async function fetchPageContent(url) {
|
|
94
|
+
try {
|
|
95
|
+
const response = await fetch(url, {
|
|
96
|
+
headers: {
|
|
97
|
+
"User-Agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36",
|
|
98
|
+
"Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8",
|
|
99
|
+
},
|
|
100
|
+
signal: AbortSignal.timeout(10000),
|
|
101
|
+
});
|
|
102
|
+
|
|
103
|
+
if (!response.ok) {
|
|
104
|
+
return `(HTTP ${response.status})`;
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
const html = await response.text();
|
|
108
|
+
const dom = new JSDOM(html, { url });
|
|
109
|
+
const reader = new Readability(dom.window.document);
|
|
110
|
+
const article = reader.parse();
|
|
111
|
+
|
|
112
|
+
if (article && article.content) {
|
|
113
|
+
return htmlToMarkdown(article.content).substring(0, 5000);
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
// Fallback: try to get main content
|
|
117
|
+
const fallbackDoc = new JSDOM(html, { url });
|
|
118
|
+
const body = fallbackDoc.window.document;
|
|
119
|
+
body.querySelectorAll("script, style, noscript, nav, header, footer, aside").forEach((el) => el.remove());
|
|
120
|
+
const main = body.querySelector("main, article, [role='main'], .content, #content") || body.body;
|
|
121
|
+
const text = main?.textContent || "";
|
|
122
|
+
|
|
123
|
+
if (text.trim().length > 100) {
|
|
124
|
+
return text.trim().substring(0, 5000);
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
return "(Could not extract content)";
|
|
128
|
+
} catch (e) {
|
|
129
|
+
return `(Error: ${e.message})`;
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
function runDdgs(query, numResults, isNews, region, timelimit, backend) {
|
|
134
|
+
const tmpFile = join(tmpdir(), `ddgs-${randomBytes(6).toString("hex")}.json`);
|
|
135
|
+
const subcommand = isNews ? "news" : "text";
|
|
136
|
+
|
|
137
|
+
const ddgsArgs = [subcommand, "-q", query, "-m", String(numResults), "-o", tmpFile];
|
|
138
|
+
|
|
139
|
+
if (region) {
|
|
140
|
+
ddgsArgs.push("-r", region);
|
|
141
|
+
}
|
|
142
|
+
if (timelimit) {
|
|
143
|
+
ddgsArgs.push("-t", timelimit);
|
|
144
|
+
}
|
|
145
|
+
if (backend) {
|
|
146
|
+
ddgsArgs.push("-b", backend);
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
try {
|
|
150
|
+
execFileSync("ddgs", ddgsArgs, {
|
|
151
|
+
stdio: ["pipe", "pipe", "pipe"],
|
|
152
|
+
timeout: 30000,
|
|
153
|
+
});
|
|
154
|
+
} catch (e) {
|
|
155
|
+
// ddgs may write output to file even if it exits with error
|
|
156
|
+
// (e.g. "Aborted!" after printing results)
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
try {
|
|
160
|
+
const raw = readFileSync(tmpFile, "utf-8");
|
|
161
|
+
unlinkSync(tmpFile);
|
|
162
|
+
return JSON.parse(raw);
|
|
163
|
+
} catch {
|
|
164
|
+
try {
|
|
165
|
+
unlinkSync(tmpFile);
|
|
166
|
+
} catch {}
|
|
167
|
+
throw new Error("ddgs produced no results. Is ddgs installed? Run: uv tool install ddgs");
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
// Main
|
|
172
|
+
try {
|
|
173
|
+
const rawResults = runDdgs(query, numResults, isNews, region, timelimit, backend);
|
|
174
|
+
|
|
175
|
+
if (!rawResults || rawResults.length === 0) {
|
|
176
|
+
console.error("No results found.");
|
|
177
|
+
process.exit(0);
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
// Normalize results to a common shape
|
|
181
|
+
const results = rawResults.map((r) => ({
|
|
182
|
+
title: r.title || "",
|
|
183
|
+
link: r.href || r.url || "",
|
|
184
|
+
snippet: r.body || "",
|
|
185
|
+
date: r.date || "",
|
|
186
|
+
source: r.source || "",
|
|
187
|
+
}));
|
|
188
|
+
|
|
189
|
+
if (fetchContent) {
|
|
190
|
+
for (const result of results) {
|
|
191
|
+
result.content = await fetchPageContent(result.link);
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
for (let i = 0; i < results.length; i++) {
|
|
196
|
+
const r = results[i];
|
|
197
|
+
console.log(`--- Result ${i + 1} ---`);
|
|
198
|
+
console.log(`Title: ${r.title}`);
|
|
199
|
+
console.log(`Link: ${r.link}`);
|
|
200
|
+
if (r.date) {
|
|
201
|
+
console.log(`Date: ${r.date}`);
|
|
202
|
+
}
|
|
203
|
+
if (r.source) {
|
|
204
|
+
console.log(`Source: ${r.source}`);
|
|
205
|
+
}
|
|
206
|
+
console.log(`Snippet: ${r.snippet}`);
|
|
207
|
+
if (r.content) {
|
|
208
|
+
console.log(`Content:\n${r.content}`);
|
|
209
|
+
}
|
|
210
|
+
console.log("");
|
|
211
|
+
}
|
|
212
|
+
} catch (e) {
|
|
213
|
+
console.error(`Error: ${e.message}`);
|
|
214
|
+
process.exit(1);
|
|
215
|
+
}
|