webscraping-ai-cli 1.0.0 → 1.0.2
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/.claude-plugin/marketplace.json +19 -0
- package/CHANGELOG.md +14 -1
- package/README.md +16 -1
- package/dist/cli.js +10 -10
- package/dist/cli.js.map +1 -1
- package/package.json +7 -3
- package/plugins/webscraping-ai/.claude-plugin/plugin.json +13 -0
- package/plugins/webscraping-ai/skills/webscraping-ai/SKILL.md +9 -0
- package/plugins/webscraping-ai/.claude-plugin/marketplace.json +0 -17
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "https://json.schemastore.org/claude-code-plugin-marketplace.json",
|
|
3
|
+
"name": "webscraping-ai",
|
|
4
|
+
"description": "Official WebScraping.AI plugins for Claude Code — scrape and AI-extract data from any URL.",
|
|
5
|
+
"owner": {
|
|
6
|
+
"name": "WebScraping.AI",
|
|
7
|
+
"url": "https://webscraping.ai"
|
|
8
|
+
},
|
|
9
|
+
"plugins": [
|
|
10
|
+
{
|
|
11
|
+
"name": "webscraping-ai",
|
|
12
|
+
"source": "./plugins/webscraping-ai",
|
|
13
|
+
"description": "Fetch HTML, text, selected elements, or AI-extracted data from any URL — including JS-rendered pages — via the WebScraping.AI CLI.",
|
|
14
|
+
"version": "1.0.0",
|
|
15
|
+
"category": "web-scraping",
|
|
16
|
+
"keywords": ["web-scraping", "html", "text-extraction", "ai-extraction", "scraping", "cli"]
|
|
17
|
+
}
|
|
18
|
+
]
|
|
19
|
+
}
|
package/CHANGELOG.md
CHANGED
|
@@ -5,7 +5,20 @@ All notable changes to `webscraping-ai-cli` are documented in this file.
|
|
|
5
5
|
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
|
|
6
6
|
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
7
7
|
|
|
8
|
-
##
|
|
8
|
+
## 1.0.2 — 2026-07-17
|
|
9
|
+
|
|
10
|
+
### Changed
|
|
11
|
+
|
|
12
|
+
- Documentation: expanded README — API docs, signup/dashboard links, badges, and links to the other official clients; package metadata homepage now points to https://webscraping.ai where it previously pointed at GitHub.
|
|
13
|
+
|
|
14
|
+
## [1.0.1] — 2026-06-21
|
|
15
|
+
|
|
16
|
+
### Fixed
|
|
17
|
+
|
|
18
|
+
- `auth set` now enforces `0600` permissions on an existing credential file, not only when creating it.
|
|
19
|
+
- `selected` and `selected-multiple` no longer require a selector; omitting it returns whole-page HTML, matching the API.
|
|
20
|
+
|
|
21
|
+
## [1.0.0] — 2026-05-19
|
|
9
22
|
|
|
10
23
|
Initial release.
|
|
11
24
|
|
package/README.md
CHANGED
|
@@ -1,6 +1,9 @@
|
|
|
1
1
|
# webscraping-ai-cli
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
[](https://www.npmjs.com/package/webscraping-ai-cli)
|
|
4
|
+
[](https://github.com/webscraping-ai/webscraping-ai-cli/actions/workflows/ci.yml)
|
|
5
|
+
|
|
6
|
+
Official command-line client for the [WebScraping.AI](https://webscraping.ai) API. Fetch HTML, plain text, AI-extracted data, or single CSS selections from any URL — with JavaScript rendering, residential / stealth proxies, and per-country geolocation. See the [API documentation](https://webscraping.ai/docs) for the full parameter reference.
|
|
4
7
|
|
|
5
8
|
Ships with an **AI agent skill** that you can install into Claude Code, Cursor, Windsurf, Kiro, OpenCode, Gemini CLI, GitHub Copilot, Augment, or Factory — your coding agent learns when and how to use the CLI for live page fetches.
|
|
6
9
|
|
|
@@ -16,6 +19,10 @@ The package exposes two executables: `webscraping-ai` (primary) and `wsai` (shor
|
|
|
16
19
|
|
|
17
20
|
## Authenticate
|
|
18
21
|
|
|
22
|
+
[Sign up](https://webscraping.ai/auth/sign_up) to get an API key — the free
|
|
23
|
+
trial includes 2,000 credits, no credit card required. Your key lives in the
|
|
24
|
+
[dashboard](https://webscraping.ai/dashboard).
|
|
25
|
+
|
|
19
26
|
Pick one:
|
|
20
27
|
|
|
21
28
|
```bash
|
|
@@ -115,6 +122,14 @@ cat urls.txt | webscraping-ai extract - \
|
|
|
115
122
|
webscraping-ai html https://example.com --headers @./headers.json
|
|
116
123
|
```
|
|
117
124
|
|
|
125
|
+
## Links
|
|
126
|
+
|
|
127
|
+
- [WebScraping.AI](https://webscraping.ai) — features, pricing, signup
|
|
128
|
+
- [API documentation](https://webscraping.ai/docs)
|
|
129
|
+
- [Dashboard](https://webscraping.ai/dashboard) — API key, usage, request builder
|
|
130
|
+
- Other official clients: [Python](https://github.com/webscraping-ai/webscraping-ai-python) · [JavaScript](https://github.com/webscraping-ai/webscraping-ai-js) · [Ruby](https://github.com/webscraping-ai/webscraping-ai-ruby) · [PHP](https://github.com/webscraping-ai/webscraping-ai-php) · [Go](https://github.com/webscraping-ai/webscraping-ai-go) · [Java](https://github.com/webscraping-ai/webscraping-ai-java) · [.NET](https://github.com/webscraping-ai/webscraping-ai-dotnet) · [MCP server](https://github.com/webscraping-ai/webscraping-ai-mcp-server) · [n8n node](https://github.com/webscraping-ai/webscraping-ai-n8n)
|
|
131
|
+
- Support: [support@webscraping.ai](mailto:support@webscraping.ai)
|
|
132
|
+
|
|
118
133
|
## License
|
|
119
134
|
|
|
120
135
|
[MIT](./LICENSE) — © WebScraping.AI
|
package/dist/cli.js
CHANGED
|
@@ -39,6 +39,7 @@ async function writePersistedConfig(config) {
|
|
|
39
39
|
const path = configPath();
|
|
40
40
|
await fs.mkdir(dirname(path), { recursive: true });
|
|
41
41
|
await fs.writeFile(path, JSON.stringify(config, null, 2) + "\n", { mode: 384 });
|
|
42
|
+
await fs.chmod(path, 384);
|
|
42
43
|
}
|
|
43
44
|
async function clearPersistedConfig() {
|
|
44
45
|
try {
|
|
@@ -158,15 +159,15 @@ function buildTextOptions(url, flags, resolved, extras = {}) {
|
|
|
158
159
|
return out;
|
|
159
160
|
}
|
|
160
161
|
function buildSelectedOptions(url, selector, flags, resolved) {
|
|
161
|
-
const out = { url,
|
|
162
|
+
const out = { url, ...buildCommonOptions(flags, resolved) };
|
|
163
|
+
if (selector !== void 0) out.selector = selector;
|
|
162
164
|
if (flags.format) out.format = flags.format;
|
|
163
165
|
return out;
|
|
164
166
|
}
|
|
165
167
|
function buildSelectedMultipleOptions(url, selectors, flags, resolved) {
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
return { url, selectors, ...buildCommonOptions(flags, resolved) };
|
|
168
|
+
const out = { url, ...buildCommonOptions(flags, resolved) };
|
|
169
|
+
if (selectors.length > 0) out.selectors = selectors;
|
|
170
|
+
return out;
|
|
170
171
|
}
|
|
171
172
|
function buildQuestionOptions(url, question, flags, resolved) {
|
|
172
173
|
const out = { url, question, ...buildCommonOptions(flags, resolved) };
|
|
@@ -352,10 +353,12 @@ function htmlCommand() {
|
|
|
352
353
|
// src/commands/selected.ts
|
|
353
354
|
import { Command as Command6, Option as Option4 } from "commander";
|
|
354
355
|
function selectedCommand() {
|
|
355
|
-
const cmd = new Command6("selected").description("Fetch HTML of one CSS-selected page area").argument("<url>", "page URL (or `-` to read URLs from stdin)").
|
|
356
|
+
const cmd = new Command6("selected").description("Fetch HTML of one CSS-selected page area").argument("<url>", "page URL (or `-` to read URLs from stdin)").option(
|
|
357
|
+
"-s, --selector <css>",
|
|
358
|
+
"CSS selector for the target element(s); omit for whole-page HTML"
|
|
359
|
+
).addOption(new Option4("--format <fmt>", "response wrapping").choices(["text", "json"]));
|
|
356
360
|
addCommonScrapeOptions(cmd);
|
|
357
361
|
cmd.action(async (url, opts) => {
|
|
358
|
-
if (!opts.selector) throw new Error("--selector is required");
|
|
359
362
|
const apiKey = await resolveApiKey({ flag: opts.apiKey });
|
|
360
363
|
const headers = await parseHeaders(opts.headers);
|
|
361
364
|
const client = createClient({ apiKey, requestTimeoutMs: opts.timeout });
|
|
@@ -380,9 +383,6 @@ function selectedMultipleCommand() {
|
|
|
380
383
|
addCommonScrapeOptions(cmd);
|
|
381
384
|
cmd.action(async (url, opts) => {
|
|
382
385
|
const selectors = opts.selector ?? [];
|
|
383
|
-
if (selectors.length === 0) {
|
|
384
|
-
throw new Error("at least one --selector is required");
|
|
385
|
-
}
|
|
386
386
|
const apiKey = await resolveApiKey({ flag: opts.apiKey });
|
|
387
387
|
const headers = await parseHeaders(opts.headers);
|
|
388
388
|
const client = createClient({ apiKey, requestTimeoutMs: opts.timeout });
|
package/dist/cli.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/cli.ts","../src/commands/account.ts","../src/lib/config.ts","../src/lib/output.ts","../src/lib/sdk.ts","../src/commands/ask.ts","../src/lib/buildRequest.ts","../src/lib/options.ts","../src/lib/stdin.ts","../src/commands/auth.ts","../src/commands/extract.ts","../src/commands/html.ts","../src/commands/selected.ts","../src/commands/selectedMultiple.ts","../src/commands/setupSkill.ts","../src/lib/skill.ts","../src/commands/text.ts","../src/lib/errors.ts"],"sourcesContent":["/**\n * `webscraping-ai` / `wsai` CLI entry point.\n *\n * Wires every subcommand into a single commander root. Errors thrown from\n * subcommand actions land in the top-level `.catch`, which maps SDK\n * exception classes to stable exit codes (see `lib/errors.ts`).\n */\n\nimport { Command } from 'commander';\nimport { readFileSync } from 'node:fs';\nimport { dirname, resolve } from 'node:path';\nimport { fileURLToPath } from 'node:url';\n\nimport { accountCommand } from './commands/account.js';\nimport { askCommand } from './commands/ask.js';\nimport { authCommand } from './commands/auth.js';\nimport { extractCommand } from './commands/extract.js';\nimport { htmlCommand } from './commands/html.js';\nimport { selectedCommand } from './commands/selected.js';\nimport { selectedMultipleCommand } from './commands/selectedMultiple.js';\nimport { setupCommand } from './commands/setupSkill.js';\nimport { textCommand } from './commands/text.js';\nimport { exitCodeFor, formatError } from './lib/errors.js';\n\nfunction packageVersion(): string {\n // Read package.json at runtime so we don't need a code-gen step. The dist\n // bundle sits at `dist/cli.js`, so package.json is one directory up.\n const here = dirname(fileURLToPath(import.meta.url));\n const pkgPath = resolve(here, '..', 'package.json');\n const raw = JSON.parse(readFileSync(pkgPath, 'utf8')) as { version: string };\n return raw.version;\n}\n\nexport function buildProgram(): Command {\n const program = new Command();\n program\n .name('webscraping-ai')\n .description(\n 'Official CLI for the WebScraping.AI API. Fetch pages, ask AI, extract structured data.\\n' +\n 'Set $WEBSCRAPING_AI_API_KEY or run `webscraping-ai auth set <key>` first.',\n )\n .version(packageVersion(), '-v, --version', 'output the version number')\n .showHelpAfterError();\n\n program.addCommand(htmlCommand());\n program.addCommand(textCommand());\n program.addCommand(selectedCommand());\n program.addCommand(selectedMultipleCommand());\n program.addCommand(askCommand());\n program.addCommand(extractCommand());\n program.addCommand(accountCommand());\n program.addCommand(authCommand());\n program.addCommand(setupCommand());\n\n return program;\n}\n\nasync function main(argv: string[]): Promise<void> {\n const program = buildProgram();\n try {\n await program.parseAsync(argv);\n } catch (err) {\n process.stderr.write(`Error: ${formatError(err)}\\n`);\n process.exit(exitCodeFor(err));\n }\n}\n\n// Only execute when run as a script. When imported (e.g. from tests),\n// `buildProgram` is the public surface and `main` stays dormant.\nconst entryPath = process.argv[1] ? fileURLToPath(import.meta.url) : '';\nif (entryPath && process.argv[1] === entryPath) {\n main(process.argv).catch((err: unknown) => {\n process.stderr.write(`Error: ${formatError(err)}\\n`);\n process.exit(exitCodeFor(err));\n });\n}\n","import { Command } from 'commander';\n\nimport { resolveApiKey } from '../lib/config.js';\nimport { emit } from '../lib/output.js';\nimport { createClient } from '../lib/sdk.js';\n\ninterface AccountFlags {\n apiKey?: string;\n output?: string;\n pretty?: boolean;\n}\n\nexport function accountCommand(): Command {\n return new Command('account')\n .description('Show account quota and usage information')\n .option('-k, --api-key <key>', 'API key (overrides $WEBSCRAPING_AI_API_KEY and config file)')\n .option('-o, --output <file>', 'write result to FILE instead of stdout')\n .option('--pretty', 'pretty-print JSON output (default on TTY)')\n .option('--no-pretty', 'force single-line JSON output')\n .action(async (opts: AccountFlags) => {\n const apiKey = await resolveApiKey({ flag: opts.apiKey });\n const client = createClient({ apiKey });\n const result = await client.account();\n await emit(result, opts);\n });\n}\n","/**\n * API key resolution and persisted config.\n *\n * Resolution order (first non-empty wins):\n * 1. `--api-key` flag (handled by the caller, passed in)\n * 2. `WEBSCRAPING_AI_API_KEY` env var\n * 3. `~/.config/webscraping-ai/config.json` (or `$XDG_CONFIG_HOME/...`)\n *\n * The config file is plain JSON with `{ \"apiKey\": \"...\" }`. We chmod it to\n * 0600 so it's not world-readable.\n */\n\nimport { promises as fs } from 'node:fs';\nimport { homedir } from 'node:os';\nimport { dirname, join } from 'node:path';\n\nconst CONFIG_FILENAME = 'config.json';\n\nexport function configDir(): string {\n const xdg = process.env.XDG_CONFIG_HOME;\n if (xdg && xdg.trim() !== '') return join(xdg, 'webscraping-ai');\n return join(homedir(), '.config', 'webscraping-ai');\n}\n\nexport function configPath(): string {\n return join(configDir(), CONFIG_FILENAME);\n}\n\nexport interface PersistedConfig {\n apiKey?: string;\n}\n\nexport async function readPersistedConfig(): Promise<PersistedConfig> {\n try {\n const text = await fs.readFile(configPath(), 'utf8');\n const parsed = JSON.parse(text) as unknown;\n if (parsed && typeof parsed === 'object' && !Array.isArray(parsed)) {\n return parsed as PersistedConfig;\n }\n return {};\n } catch (err) {\n if ((err as NodeJS.ErrnoException).code === 'ENOENT') return {};\n throw err;\n }\n}\n\nexport async function writePersistedConfig(config: PersistedConfig): Promise<void> {\n const path = configPath();\n await fs.mkdir(dirname(path), { recursive: true });\n await fs.writeFile(path, JSON.stringify(config, null, 2) + '\\n', { mode: 0o600 });\n}\n\nexport async function clearPersistedConfig(): Promise<boolean> {\n try {\n await fs.unlink(configPath());\n return true;\n } catch (err) {\n if ((err as NodeJS.ErrnoException).code === 'ENOENT') return false;\n throw err;\n }\n}\n\nexport interface ResolveApiKeyOptions {\n /** `--api-key` flag value, if the user passed one. */\n flag?: string;\n}\n\nexport async function resolveApiKey(opts: ResolveApiKeyOptions = {}): Promise<string> {\n if (opts.flag && opts.flag.trim() !== '') return opts.flag.trim();\n\n const env = process.env.WEBSCRAPING_AI_API_KEY;\n if (env && env.trim() !== '') return env.trim();\n\n const persisted = await readPersistedConfig();\n if (persisted.apiKey && persisted.apiKey.trim() !== '') return persisted.apiKey.trim();\n\n throw new Error(\n 'No API key found. Set $WEBSCRAPING_AI_API_KEY, pass --api-key, or run `webscraping-ai auth set <key>`.',\n );\n}\n\nexport function maskApiKey(key: string): string {\n if (key.length <= 8) return '*'.repeat(key.length);\n return `${key.slice(0, 4)}${'*'.repeat(key.length - 8)}${key.slice(-4)}`;\n}\n","/**\n * Output handling for command results.\n *\n * All command handlers funnel through `emit()`. The result type drives format:\n * - strings → written verbatim\n * - objects/arrays → JSON.stringified (pretty by default; `--pretty` is the\n * default for terminals, single-line for pipes/redirects)\n *\n * `--output FILE` writes to disk instead of stdout.\n */\n\nimport { promises as fs } from 'node:fs';\n\nexport interface EmitOptions {\n output?: string;\n pretty?: boolean;\n}\n\nexport async function emit(value: unknown, opts: EmitOptions = {}): Promise<void> {\n const text = stringify(value, opts);\n\n if (opts.output && opts.output.trim() !== '') {\n await fs.writeFile(opts.output, text + (text.endsWith('\\n') ? '' : '\\n'));\n return;\n }\n\n process.stdout.write(text);\n if (!text.endsWith('\\n')) process.stdout.write('\\n');\n}\n\nexport function stringify(value: unknown, opts: EmitOptions = {}): string {\n if (typeof value === 'string') return value;\n\n const pretty = opts.pretty ?? process.stdout.isTTY ?? false;\n return JSON.stringify(value, null, pretty ? 2 : 0);\n}\n","/**\n * SDK adapter.\n *\n * Wraps the published `webscraping-ai` Node SDK. The CLI sends a\n * `from_cli=true` analytics flag on every request (mirrors `from_n8n` and\n * `from_mcp_server` used by the n8n node and MCP server). The SDK's typed\n * options don't accept arbitrary keys, so we go through a single cast point\n * here rather than scattering casts across each command.\n */\n\nimport { WebScrapingAI } from 'webscraping-ai';\nimport type {\n FieldsOptions,\n HtmlOptions,\n QuestionOptions,\n SelectedMultipleOptions,\n SelectedOptions,\n TextOptions,\n} from 'webscraping-ai';\n\nexport interface CreateClientOptions {\n apiKey: string;\n /** Per-request timeout in ms. Maps to the SDK's `timeoutMs`. */\n requestTimeoutMs?: number;\n}\n\n/**\n * Returns a `WebScrapingAI` instance with the CLI's analytics tag baked into\n * every endpoint method. The methods preserve the SDK's public signatures.\n */\nexport function createClient(options: CreateClientOptions): WebScrapingAI {\n const inner = new WebScrapingAI({\n apiKey: options.apiKey,\n timeoutMs: options.requestTimeoutMs,\n });\n\n // Wrap each endpoint method so it injects the CLI analytics tag. We splice\n // the tag in at this seam rather than at every callsite. The cast is local;\n // the SDK passes unknown query params through to the wire.\n const tag = { from_cli: true } as unknown as Record<string, never>;\n\n return new Proxy(inner, {\n get(target, prop, receiver) {\n const value = Reflect.get(target, prop, receiver);\n if (typeof value !== 'function') return value;\n switch (prop) {\n case 'html':\n return (o: HtmlOptions) => target.html({ ...o, ...tag });\n case 'text':\n return (o: TextOptions) => target.text({ ...o, ...tag });\n case 'selected':\n return (o: SelectedOptions) => target.selected({ ...o, ...tag });\n case 'selectedMultiple':\n return (o: SelectedMultipleOptions) => target.selectedMultiple({ ...o, ...tag });\n case 'question':\n return (o: QuestionOptions) => target.question({ ...o, ...tag });\n case 'fields':\n return (o: FieldsOptions) => target.fields({ ...o, ...tag });\n case 'account':\n // `account` takes no options; the SDK builds the request itself, so\n // there's no seam here for the analytics tag — accepted limitation.\n return value.bind(target);\n default:\n return typeof value === 'function' ? value.bind(target) : value;\n }\n },\n });\n}\n","import { Command, Option } from 'commander';\n\nimport { buildQuestionOptions } from '../lib/buildRequest.js';\nimport { resolveApiKey } from '../lib/config.js';\nimport { addCommonScrapeOptions, parseHeaders } from '../lib/options.js';\nimport type { CommonRawFlags } from '../lib/options.js';\nimport { emit } from '../lib/output.js';\nimport { createClient } from '../lib/sdk.js';\nimport { urlIterator } from '../lib/stdin.js';\n\ninterface AskFlags extends CommonRawFlags {\n question?: string;\n}\n\nexport function askCommand(): Command {\n const cmd = new Command('ask')\n .description('Ask an AI question about a page (wraps /ai/question)')\n .argument('<url>', 'page URL (or `-` to read URLs from stdin)')\n .requiredOption('-q, --question <text>', 'the question / instructions for the model')\n .addOption(new Option('--format <fmt>', 'response wrapping').choices(['text', 'json']));\n\n addCommonScrapeOptions(cmd);\n\n cmd.action(async (url: string, opts: AskFlags) => {\n if (!opts.question) throw new Error('--question is required');\n const apiKey = await resolveApiKey({ flag: opts.apiKey });\n const headers = await parseHeaders(opts.headers);\n const client = createClient({ apiKey, requestTimeoutMs: opts.timeout });\n\n for await (const target of urlIterator(url)) {\n const sdkOptions = buildQuestionOptions(target, opts.question, opts, { headers });\n const result = await client.question(sdkOptions);\n await emit(result, opts);\n }\n });\n\n return cmd;\n}\n","/**\n * Pure builder that maps `--flag` values into SDK option objects.\n *\n * Extracted as a separate function so unit tests can pin the mapping without\n * spinning up commander or making HTTP calls. Each command handler reads its\n * own commander options, awaits `parseHeaders` / `parseFields`, then calls\n * the matching `build*` function here.\n */\n\nimport type {\n CommonRequestOptions,\n FieldsOptions,\n HtmlOptions,\n QuestionOptions,\n SelectedMultipleOptions,\n SelectedOptions,\n TextOptions,\n} from 'webscraping-ai';\n\nimport type { CommonRawFlags } from './options.js';\n\nexport interface ResolvedCommonOptions {\n headers?: Record<string, string>;\n}\n\nexport function buildCommonOptions(\n flags: CommonRawFlags,\n resolved: ResolvedCommonOptions = {},\n): CommonRequestOptions {\n const out: CommonRequestOptions = {};\n // `flags.js` is `false` when `--no-js`, `true` when `--js`, `undefined`\n // when neither passed. Pass through only when explicit, so the API default\n // applies otherwise.\n if (flags.js !== undefined) out.js = flags.js;\n if (flags.proxy !== undefined) out.proxy = flags.proxy;\n if (flags.country !== undefined) out.country = flags.country;\n if (flags.device !== undefined) out.device = flags.device;\n if (flags.timeout !== undefined) out.timeout = flags.timeout;\n if (flags.jsTimeout !== undefined) out.js_timeout = flags.jsTimeout;\n if (flags.waitFor !== undefined) out.wait_for = flags.waitFor;\n if (flags.jsScript !== undefined) out.js_script = flags.jsScript;\n if (flags.customProxy !== undefined) out.custom_proxy = flags.customProxy;\n if (flags.errorOn404) out.error_on_404 = true;\n if (flags.errorOnRedirect) out.error_on_redirect = true;\n if (resolved.headers) out.headers = resolved.headers;\n return out;\n}\n\nexport interface HtmlExtras {\n returnScriptResult?: boolean;\n}\n\nexport function buildHtmlOptions(\n url: string,\n flags: CommonRawFlags,\n resolved: ResolvedCommonOptions,\n extras: HtmlExtras = {},\n): HtmlOptions {\n const out: HtmlOptions = { url, ...buildCommonOptions(flags, resolved) };\n if (flags.format) out.format = flags.format;\n if (extras.returnScriptResult) out.return_script_result = true;\n return out;\n}\n\nexport interface TextExtras {\n textFormat?: 'plain' | 'json' | 'xml';\n returnLinks?: boolean;\n}\n\nexport function buildTextOptions(\n url: string,\n flags: CommonRawFlags,\n resolved: ResolvedCommonOptions,\n extras: TextExtras = {},\n): TextOptions {\n const out: TextOptions = { url, ...buildCommonOptions(flags, resolved) };\n if (extras.textFormat) out.text_format = extras.textFormat;\n if (extras.returnLinks) out.return_links = true;\n return out;\n}\n\nexport function buildSelectedOptions(\n url: string,\n selector: string,\n flags: CommonRawFlags,\n resolved: ResolvedCommonOptions,\n): SelectedOptions {\n const out: SelectedOptions = { url, selector, ...buildCommonOptions(flags, resolved) };\n if (flags.format) out.format = flags.format;\n return out;\n}\n\nexport function buildSelectedMultipleOptions(\n url: string,\n selectors: readonly string[],\n flags: CommonRawFlags,\n resolved: ResolvedCommonOptions,\n): SelectedMultipleOptions {\n if (selectors.length === 0) {\n throw new Error('selected-multiple requires at least one --selector');\n }\n return { url, selectors, ...buildCommonOptions(flags, resolved) };\n}\n\nexport function buildQuestionOptions(\n url: string,\n question: string,\n flags: CommonRawFlags,\n resolved: ResolvedCommonOptions,\n): QuestionOptions {\n const out: QuestionOptions = { url, question, ...buildCommonOptions(flags, resolved) };\n if (flags.format) out.format = flags.format;\n return out;\n}\n\nexport function buildFieldsOptions(\n url: string,\n fields: Record<string, string>,\n flags: CommonRawFlags,\n resolved: ResolvedCommonOptions,\n): FieldsOptions {\n return { url, fields, ...buildCommonOptions(flags, resolved) };\n}\n","/**\n * Shared commander options and the parsers/expanders behind them.\n *\n * All scrape endpoints share the same nine \"common\" flags. We define them in\n * one place and apply via `addCommonScrapeOptions(cmd)` so the surface stays\n * consistent and any change lands everywhere at once.\n */\n\nimport { promises as fs } from 'node:fs';\nimport { Option } from 'commander';\nimport type { Command } from 'commander';\n\nexport interface CommonRawFlags {\n apiKey?: string;\n output?: string;\n format?: 'text' | 'json';\n pretty?: boolean;\n js?: boolean;\n proxy?: 'datacenter' | 'residential' | 'stealth';\n country?: string;\n device?: 'desktop' | 'mobile' | 'tablet';\n timeout?: number;\n jsTimeout?: number;\n waitFor?: string;\n headers?: string;\n jsScript?: string;\n customProxy?: string;\n errorOn404?: boolean;\n errorOnRedirect?: boolean;\n}\n\n/**\n * Add the flags every scrape-endpoint command needs. Adding here keeps the\n * surface aligned across `html`, `text`, `selected`, etc.\n */\nexport function addCommonScrapeOptions(cmd: Command): Command {\n return cmd\n .option('-k, --api-key <key>', 'API key (overrides $WEBSCRAPING_AI_API_KEY and config file)')\n .option('-o, --output <file>', 'write result to FILE instead of stdout')\n .option('--pretty', 'pretty-print JSON output (default on TTY)')\n .addOption(\n new Option('--no-pretty', 'force single-line JSON output').conflicts('pretty'),\n )\n .option('--js', 'execute JS via a headless browser (default: enabled)')\n .option('--no-js', 'disable JS execution (faster, cheaper)')\n .addOption(\n new Option('--proxy <type>', 'proxy pool').choices([\n 'datacenter',\n 'residential',\n 'stealth',\n ]),\n )\n .option('--country <code>', 'two-letter proxy country code, e.g. us, gb, de')\n .addOption(\n new Option('--device <type>', 'device emulation').choices(['desktop', 'mobile', 'tablet']),\n )\n .option('--timeout <ms>', 'page-retrieval timeout in ms (max 30000)', integerParser)\n .option('--js-timeout <ms>', 'JS rendering time in ms after load (max 20000)', integerParser)\n .option('--wait-for <selector>', 'CSS selector to wait for before returning content')\n .option(\n '--headers <json>',\n 'HTTP headers to forward to the target page; JSON object or @file.json',\n )\n .option('--js-script <code>', 'custom JS to evaluate on the target page')\n .option('--custom-proxy <url>', 'custom proxy URL: http://user:pass@host:port')\n .option('--error-on-404', 'return error on 404')\n .option('--error-on-redirect', 'return error on redirect');\n}\n\nexport function integerParser(value: string): number {\n const n = Number.parseInt(value, 10);\n if (!Number.isFinite(n)) throw new Error(`expected integer, got: ${value}`);\n return n;\n}\n\n/**\n * Parse a `--headers` argument. Accepts a JSON object literal or `@path` to\n * load JSON from a file. Returns `undefined` if the input is empty.\n */\nexport async function parseHeaders(input: string | undefined): Promise<Record<string, string> | undefined> {\n if (!input || input.trim() === '') return undefined;\n const text = input.startsWith('@') ? await fs.readFile(input.slice(1), 'utf8') : input;\n return parseJsonObject(text, 'headers');\n}\n\n/**\n * Parse a `--fields` argument for `extract`. Accepts JSON object literal or\n * `@path` to a file.\n */\nexport async function parseFields(input: string): Promise<Record<string, string>> {\n const text = input.startsWith('@') ? await fs.readFile(input.slice(1), 'utf8') : input;\n const parsed = parseJsonObject(text, 'fields');\n if (!parsed || Object.keys(parsed).length === 0) {\n throw new Error('fields must be a non-empty JSON object of { fieldName: description }');\n }\n return parsed;\n}\n\nfunction parseJsonObject(text: string, label: string): Record<string, string> {\n let parsed: unknown;\n try {\n parsed = JSON.parse(text);\n } catch (err) {\n throw new Error(`${label}: invalid JSON — ${(err as Error).message}`);\n }\n if (!parsed || typeof parsed !== 'object' || Array.isArray(parsed)) {\n throw new Error(`${label} must be a JSON object`);\n }\n const out: Record<string, string> = {};\n for (const [k, v] of Object.entries(parsed as Record<string, unknown>)) {\n if (typeof v !== 'string') throw new Error(`${label}.${k}: value must be a string`);\n out[k] = v;\n }\n return out;\n}\n","/**\n * Stdin batching helpers.\n *\n * When a URL argument is `-`, the command reads one URL per line from stdin\n * and runs the operation for each. Empty lines and lines starting with `#`\n * are skipped so users can comment URL lists.\n */\n\nimport readline from 'node:readline';\n\nexport function urlIsStdin(url: string): boolean {\n return url === '-';\n}\n\nexport async function* readUrlsFromStdin(): AsyncIterable<string> {\n const rl = readline.createInterface({ input: process.stdin, crlfDelay: Infinity });\n for await (const raw of rl) {\n const line = raw.trim();\n if (line === '' || line.startsWith('#')) continue;\n yield line;\n }\n}\n\n/**\n * Iterate URLs from either a single CLI arg or from stdin (if the arg is `-`).\n */\nexport async function* urlIterator(arg: string): AsyncIterable<string> {\n if (urlIsStdin(arg)) {\n yield* readUrlsFromStdin();\n return;\n }\n yield arg;\n}\n","import { Command } from 'commander';\n\nimport {\n clearPersistedConfig,\n configPath,\n maskApiKey,\n readPersistedConfig,\n writePersistedConfig,\n} from '../lib/config.js';\n\nexport function authCommand(): Command {\n const cmd = new Command('auth').description('Manage your stored API key');\n\n cmd\n .command('set <key>')\n .description(`Save an API key to ${configPath()}`)\n .action(async (key: string) => {\n if (!key || key.trim() === '') throw new Error('API key is empty');\n await writePersistedConfig({ apiKey: key.trim() });\n process.stdout.write(`API key saved to ${configPath()}\\n`);\n });\n\n cmd\n .command('show')\n .description('Display the currently stored API key (masked)')\n .action(async () => {\n const persisted = await readPersistedConfig();\n const env = process.env.WEBSCRAPING_AI_API_KEY;\n if (env && env.trim() !== '') {\n process.stdout.write(`Active key (env WEBSCRAPING_AI_API_KEY): ${maskApiKey(env.trim())}\\n`);\n }\n if (persisted.apiKey && persisted.apiKey.trim() !== '') {\n process.stdout.write(\n `Stored key (${configPath()}): ${maskApiKey(persisted.apiKey.trim())}\\n`,\n );\n }\n if (!env && !persisted.apiKey) {\n process.stdout.write('No API key configured. Run `webscraping-ai auth set <key>`.\\n');\n }\n });\n\n cmd\n .command('clear')\n .description('Remove the stored API key file')\n .action(async () => {\n const removed = await clearPersistedConfig();\n process.stdout.write(removed ? `Removed ${configPath()}\\n` : 'No stored API key to remove.\\n');\n });\n\n return cmd;\n}\n","import { Command } from 'commander';\n\nimport { buildFieldsOptions } from '../lib/buildRequest.js';\nimport { resolveApiKey } from '../lib/config.js';\nimport { addCommonScrapeOptions, parseFields, parseHeaders } from '../lib/options.js';\nimport type { CommonRawFlags } from '../lib/options.js';\nimport { emit } from '../lib/output.js';\nimport { createClient } from '../lib/sdk.js';\nimport { urlIterator } from '../lib/stdin.js';\n\ninterface ExtractFlags extends CommonRawFlags {\n fields?: string;\n}\n\nexport function extractCommand(): Command {\n const cmd = new Command('extract')\n .description('Extract structured fields from a page using AI (wraps /ai/fields)')\n .argument('<url>', 'page URL (or `-` to read URLs from stdin)')\n .requiredOption(\n '-f, --fields <json>',\n 'fields to extract — JSON object {name: description} or @file.json',\n );\n\n addCommonScrapeOptions(cmd);\n\n cmd.action(async (url: string, opts: ExtractFlags) => {\n if (!opts.fields) throw new Error('--fields is required');\n const apiKey = await resolveApiKey({ flag: opts.apiKey });\n const headers = await parseHeaders(opts.headers);\n const fields = await parseFields(opts.fields);\n const client = createClient({ apiKey, requestTimeoutMs: opts.timeout });\n\n for await (const target of urlIterator(url)) {\n const sdkOptions = buildFieldsOptions(target, fields, opts, { headers });\n const result = await client.fields(sdkOptions);\n await emit(result, opts);\n }\n });\n\n return cmd;\n}\n","import { Command, Option } from 'commander';\n\nimport { buildHtmlOptions } from '../lib/buildRequest.js';\nimport { resolveApiKey } from '../lib/config.js';\nimport { addCommonScrapeOptions, parseHeaders } from '../lib/options.js';\nimport type { CommonRawFlags } from '../lib/options.js';\nimport { emit } from '../lib/output.js';\nimport { createClient } from '../lib/sdk.js';\nimport { urlIterator } from '../lib/stdin.js';\n\ninterface HtmlFlags extends CommonRawFlags {\n returnScriptResult?: boolean;\n}\n\nexport function htmlCommand(): Command {\n const cmd = new Command('html')\n .description('Fetch the full HTML of a page')\n .argument('<url>', 'page URL (or `-` to read URLs from stdin)')\n .addOption(new Option('--return-script-result', 'return the result of --js-script instead of HTML'))\n .addOption(new Option('--format <fmt>', 'response wrapping').choices(['text', 'json']));\n\n addCommonScrapeOptions(cmd);\n\n cmd.action(async (url: string, opts: HtmlFlags) => {\n const apiKey = await resolveApiKey({ flag: opts.apiKey });\n const headers = await parseHeaders(opts.headers);\n const client = createClient({ apiKey, requestTimeoutMs: opts.timeout });\n\n for await (const target of urlIterator(url)) {\n const sdkOptions = buildHtmlOptions(target, opts, { headers }, {\n returnScriptResult: opts.returnScriptResult,\n });\n const result = await client.html(sdkOptions);\n await emit(result, opts);\n }\n });\n\n return cmd;\n}\n","import { Command, Option } from 'commander';\n\nimport { buildSelectedOptions } from '../lib/buildRequest.js';\nimport { resolveApiKey } from '../lib/config.js';\nimport { addCommonScrapeOptions, parseHeaders } from '../lib/options.js';\nimport type { CommonRawFlags } from '../lib/options.js';\nimport { emit } from '../lib/output.js';\nimport { createClient } from '../lib/sdk.js';\nimport { urlIterator } from '../lib/stdin.js';\n\ninterface SelectedFlags extends CommonRawFlags {\n selector?: string;\n}\n\nexport function selectedCommand(): Command {\n const cmd = new Command('selected')\n .description('Fetch HTML of one CSS-selected page area')\n .argument('<url>', 'page URL (or `-` to read URLs from stdin)')\n .requiredOption('-s, --selector <css>', 'CSS selector for the target element(s)')\n .addOption(new Option('--format <fmt>', 'response wrapping').choices(['text', 'json']));\n\n addCommonScrapeOptions(cmd);\n\n cmd.action(async (url: string, opts: SelectedFlags) => {\n if (!opts.selector) throw new Error('--selector is required');\n const apiKey = await resolveApiKey({ flag: opts.apiKey });\n const headers = await parseHeaders(opts.headers);\n const client = createClient({ apiKey, requestTimeoutMs: opts.timeout });\n\n for await (const target of urlIterator(url)) {\n const sdkOptions = buildSelectedOptions(target, opts.selector, opts, { headers });\n const result = await client.selected(sdkOptions);\n await emit(result, opts);\n }\n });\n\n return cmd;\n}\n","import { Command } from 'commander';\n\nimport { buildSelectedMultipleOptions } from '../lib/buildRequest.js';\nimport { resolveApiKey } from '../lib/config.js';\nimport { addCommonScrapeOptions, parseHeaders } from '../lib/options.js';\nimport type { CommonRawFlags } from '../lib/options.js';\nimport { emit } from '../lib/output.js';\nimport { createClient } from '../lib/sdk.js';\nimport { urlIterator } from '../lib/stdin.js';\n\ninterface SelectedMultipleFlags extends CommonRawFlags {\n selector?: string[];\n}\n\nexport function selectedMultipleCommand(): Command {\n const cmd = new Command('selected-multiple')\n .description('Fetch HTML of multiple CSS-selected page areas')\n .argument('<url>', 'page URL (or `-` to read URLs from stdin)')\n .option(\n '-s, --selector <css>',\n 'CSS selector — pass multiple times for multiple selectors',\n collectMulti,\n [] as string[],\n );\n\n addCommonScrapeOptions(cmd);\n\n cmd.action(async (url: string, opts: SelectedMultipleFlags) => {\n const selectors = opts.selector ?? [];\n if (selectors.length === 0) {\n throw new Error('at least one --selector is required');\n }\n const apiKey = await resolveApiKey({ flag: opts.apiKey });\n const headers = await parseHeaders(opts.headers);\n const client = createClient({ apiKey, requestTimeoutMs: opts.timeout });\n\n for await (const target of urlIterator(url)) {\n const sdkOptions = buildSelectedMultipleOptions(target, selectors, opts, { headers });\n const result = await client.selectedMultiple(sdkOptions);\n await emit(result, opts);\n }\n });\n\n return cmd;\n}\n\nfunction collectMulti(value: string, previous: string[]): string[] {\n return [...previous, value];\n}\n","import { Command } from 'commander';\n\nimport { EDITORS, editorById, installSkill, resolveTarget } from '../lib/skill.js';\nimport type { EditorAdapter } from '../lib/skill.js';\n\ninterface SetupSkillFlags {\n editor?: string[];\n all?: boolean;\n force?: boolean;\n}\n\nexport function setupCommand(): Command {\n const cmd = new Command('setup').description('Install the AI agent skill into your editor(s)');\n\n cmd\n .command('skill')\n .description('Install the WebScraping.AI skill into one or more AI coding editors')\n .option(\n '-e, --editor <id>',\n `editor id (repeatable). Supported: ${EDITORS.map((e) => e.id).join(', ')}`,\n collect,\n [] as string[],\n )\n .option('-a, --all', 'install into every supported editor')\n .option('-f, --force', 'overwrite existing skill files')\n .action(async (opts: SetupSkillFlags) => {\n const selected = resolveSelection(opts);\n if (selected.length === 0) {\n throw new Error(\n 'no editor selected — pass --all or --editor <id> (e.g. --editor claude-code)',\n );\n }\n\n for (const editor of selected) {\n const target = resolveTarget(editor);\n const result = await installSkill(target, { force: opts.force });\n process.stdout.write(`${editor.label}: ${result.status} → ${target.destination}\\n`);\n }\n });\n\n cmd\n .command('list', { hidden: false })\n .description('List supported editors for `setup skill`')\n .action(() => {\n for (const e of EDITORS) {\n const target = resolveTarget(e);\n process.stdout.write(`${e.id.padEnd(14)} ${e.label.padEnd(18)} ${target.destination}\\n`);\n }\n });\n\n return cmd;\n}\n\nfunction collect(value: string, previous: string[]): string[] {\n return [...previous, value];\n}\n\nfunction resolveSelection(opts: SetupSkillFlags): EditorAdapter[] {\n if (opts.all) return [...EDITORS];\n const ids = opts.editor ?? [];\n return ids.map((id) => {\n const editor = editorById(id);\n if (!editor) throw new Error(`unknown editor: ${id}`);\n return editor;\n });\n}\n","/**\n * Multi-editor skill installer.\n *\n * The canonical skill lives in this package at\n * `plugins/webscraping-ai/skills/webscraping-ai/SKILL.md`. Each supported\n * editor reads skill content from a different location and may want either\n * a single Markdown file or a whole skill directory. The `editors` registry\n * captures those differences so `setup skill` is a small driver over it.\n *\n * `mode: 'dir'` copies the entire skill directory (Claude Code-shaped\n * editors that scan a folder per skill). `mode: 'file'` writes the SKILL.md\n * contents to a single file (editors that read flat instructions, like\n * GitHub Copilot's `.github/copilot-instructions.md`).\n *\n * `kind: 'user'` writes to `$HOME`; `kind: 'project'` writes to the current\n * working directory. We prefer user-scope where the editor supports it so\n * the skill becomes globally available without polluting individual repos.\n */\n\nimport { promises as fs } from 'node:fs';\nimport { homedir } from 'node:os';\nimport { dirname, join, resolve } from 'node:path';\nimport { fileURLToPath } from 'node:url';\n\nexport type EditorMode = 'dir' | 'file';\nexport type EditorKind = 'user' | 'project';\n\nexport interface EditorAdapter {\n /** Stable identifier used by `--editor <name>`. */\n id: string;\n /** Human-readable label printed in summaries. */\n label: string;\n mode: EditorMode;\n kind: EditorKind;\n /**\n * Where the skill ends up. For `mode: 'dir'`, the skill directory copies\n * to `{root}/{path}`. For `mode: 'file'`, the SKILL.md contents write to\n * `{root}/{path}` (i.e. `path` ends in a filename).\n *\n * `root` is `$HOME` (user) or CWD (project).\n */\n path: string;\n}\n\n/**\n * Registry of supported editors. Order matters: it's also the order the\n * `setup skill` summary lists them in.\n */\nexport const EDITORS: readonly EditorAdapter[] = [\n // Claude Code: user-scope skills live under ~/.claude/skills/<name>/\n { id: 'claude-code', label: 'Claude Code', mode: 'dir', kind: 'user', path: '.claude/skills/webscraping-ai' },\n // Cursor: project-scope rule file. Cursor doesn't have a user-wide skill\n // dir, so this is the editor's \"install\" surface.\n { id: 'cursor', label: 'Cursor', mode: 'file', kind: 'project', path: '.cursor/rules/webscraping-ai.mdc' },\n // Windsurf: project-scope rules. Same shape as Cursor.\n { id: 'windsurf', label: 'Windsurf', mode: 'file', kind: 'project', path: '.windsurf/rules/webscraping-ai.md' },\n // Kiro: user-scope steering directory.\n { id: 'kiro', label: 'Kiro', mode: 'dir', kind: 'user', path: '.kiro/steering/webscraping-ai' },\n // OpenCode: project-scope skills (mirrors Claude shape).\n { id: 'opencode', label: 'OpenCode', mode: 'dir', kind: 'project', path: '.opencode/skills/webscraping-ai' },\n // Gemini CLI: user-scope extension dir with a GEMINI.md.\n { id: 'gemini', label: 'Gemini CLI', mode: 'file', kind: 'user', path: '.gemini/extensions/webscraping-ai/GEMINI.md' },\n // GitHub Copilot: project-scope custom instructions file. Single file.\n { id: 'copilot', label: 'GitHub Copilot', mode: 'file', kind: 'project', path: '.github/copilot-instructions.md' },\n // Augment: project-scope rules file.\n { id: 'augment', label: 'Augment', mode: 'file', kind: 'project', path: '.augment/rules/webscraping-ai.md' },\n // Factory: project-scope rules file.\n { id: 'factory', label: 'Factory', mode: 'file', kind: 'project', path: '.factory/rules/webscraping-ai.md' },\n] as const;\n\nexport function editorById(id: string): EditorAdapter | undefined {\n return EDITORS.find((e) => e.id === id);\n}\n\n/**\n * Where this package keeps the canonical skill. Resolved relative to the\n * compiled `dist/cli.js` so it works after `npm install -g`.\n */\nexport function bundledSkillDir(): string {\n const here = dirname(fileURLToPath(import.meta.url));\n // dist/ sits at the package root, so the plugins dir is two levels up\n // from any compiled file: dist/cli.js → package root → plugins/...\n return resolve(here, '..', 'plugins', 'webscraping-ai', 'skills', 'webscraping-ai');\n}\n\nexport interface InstallTarget {\n editor: EditorAdapter;\n /** Absolute destination path (file or directory depending on `mode`). */\n destination: string;\n}\n\nexport interface ResolveOptions {\n cwd?: string;\n home?: string;\n}\n\nexport function resolveTarget(editor: EditorAdapter, opts: ResolveOptions = {}): InstallTarget {\n const root = editor.kind === 'user' ? (opts.home ?? homedir()) : (opts.cwd ?? process.cwd());\n return { editor, destination: join(root, editor.path) };\n}\n\nexport interface InstallOptions {\n /** If true, overwrite existing files/dirs. */\n force?: boolean;\n /** Override skill source dir (for tests). */\n sourceDir?: string;\n}\n\nexport type InstallStatus = 'installed' | 'skipped-exists' | 'overwritten';\n\nexport interface InstallResult {\n target: InstallTarget;\n status: InstallStatus;\n}\n\nexport async function installSkill(\n target: InstallTarget,\n opts: InstallOptions = {},\n): Promise<InstallResult> {\n const source = opts.sourceDir ?? bundledSkillDir();\n const exists = await pathExists(target.destination);\n\n if (exists && !opts.force) {\n return { target, status: 'skipped-exists' };\n }\n\n if (target.editor.mode === 'dir') {\n if (exists) await fs.rm(target.destination, { recursive: true, force: true });\n await copyDirectory(source, target.destination);\n } else {\n const skillBody = await fs.readFile(join(source, 'SKILL.md'), 'utf8');\n await fs.mkdir(dirname(target.destination), { recursive: true });\n await fs.writeFile(target.destination, skillBody);\n }\n\n return { target, status: exists ? 'overwritten' : 'installed' };\n}\n\nasync function pathExists(path: string): Promise<boolean> {\n try {\n await fs.access(path);\n return true;\n } catch {\n return false;\n }\n}\n\nasync function copyDirectory(source: string, destination: string): Promise<void> {\n // `fs.cp` with `recursive` exists on Node 16.7+; we target Node 20.\n await fs.cp(source, destination, { recursive: true, force: true });\n}\n","import { Command, Option } from 'commander';\n\nimport { buildTextOptions } from '../lib/buildRequest.js';\nimport { resolveApiKey } from '../lib/config.js';\nimport { addCommonScrapeOptions, parseHeaders } from '../lib/options.js';\nimport type { CommonRawFlags } from '../lib/options.js';\nimport { emit } from '../lib/output.js';\nimport { createClient } from '../lib/sdk.js';\nimport { urlIterator } from '../lib/stdin.js';\n\ninterface TextFlags extends CommonRawFlags {\n textFormat?: 'plain' | 'json' | 'xml';\n returnLinks?: boolean;\n}\n\nexport function textCommand(): Command {\n const cmd = new Command('text')\n .description('Fetch the visible text of a page')\n .argument('<url>', 'page URL (or `-` to read URLs from stdin)')\n .addOption(\n new Option('--text-format <fmt>', 'text response format').choices(['plain', 'json', 'xml']),\n )\n .option('--return-links', 'include page links (text-format=json only)');\n\n addCommonScrapeOptions(cmd);\n\n cmd.action(async (url: string, opts: TextFlags) => {\n const apiKey = await resolveApiKey({ flag: opts.apiKey });\n const headers = await parseHeaders(opts.headers);\n const client = createClient({ apiKey, requestTimeoutMs: opts.timeout });\n\n for await (const target of urlIterator(url)) {\n const sdkOptions = buildTextOptions(target, opts, { headers }, {\n textFormat: opts.textFormat,\n returnLinks: opts.returnLinks,\n });\n const result = await client.text(sdkOptions);\n await emit(result, opts);\n }\n });\n\n return cmd;\n}\n","/**\n * Exit-code mapping for SDK errors.\n *\n * The SDK throws typed subclasses of `WebScrapingAIError`. Map each to a\n * stable exit code so shell scripts can branch on the failure mode without\n * parsing stderr.\n */\n\nimport {\n APIConnectionError,\n APIError,\n APITimeoutError,\n AuthenticationError,\n BadRequestError,\n GatewayTimeoutError,\n PaymentRequiredError,\n RateLimitError,\n ServerError,\n WebScrapingAIError,\n} from 'webscraping-ai';\n\nexport const EXIT_CODES = {\n ok: 0,\n generic: 1,\n usage: 2,\n badRequest: 3,\n auth: 4,\n paymentRequired: 5,\n rateLimit: 6,\n serverError: 7,\n timeout: 8,\n connection: 9,\n} as const;\n\nexport function exitCodeFor(err: unknown): number {\n if (err instanceof BadRequestError) return EXIT_CODES.badRequest;\n if (err instanceof AuthenticationError) return EXIT_CODES.auth;\n if (err instanceof PaymentRequiredError) return EXIT_CODES.paymentRequired;\n if (err instanceof RateLimitError) return EXIT_CODES.rateLimit;\n if (err instanceof ServerError) return EXIT_CODES.serverError;\n if (err instanceof GatewayTimeoutError) return EXIT_CODES.timeout;\n if (err instanceof APITimeoutError) return EXIT_CODES.timeout;\n if (err instanceof APIConnectionError) return EXIT_CODES.connection;\n if (err instanceof APIError) return EXIT_CODES.serverError;\n if (err instanceof WebScrapingAIError) return EXIT_CODES.generic;\n return EXIT_CODES.generic;\n}\n\nexport function formatError(err: unknown): string {\n if (err instanceof APIError) {\n const parts = [err.message];\n if (err.status) parts.push(`(HTTP ${err.status})`);\n return parts.join(' ');\n }\n if (err instanceof Error) return err.message;\n return String(err);\n}\n"],"mappings":";;;AAQA,SAAS,WAAAA,iBAAe;AACxB,SAAS,oBAAoB;AAC7B,SAAS,WAAAC,UAAS,WAAAC,gBAAe;AACjC,SAAS,iBAAAC,sBAAqB;;;ACX9B,SAAS,eAAe;;;ACYxB,SAAS,YAAY,UAAU;AAC/B,SAAS,eAAe;AACxB,SAAS,SAAS,YAAY;AAE9B,IAAM,kBAAkB;AAEjB,SAAS,YAAoB;AAClC,QAAM,MAAM,QAAQ,IAAI;AACxB,MAAI,OAAO,IAAI,KAAK,MAAM,GAAI,QAAO,KAAK,KAAK,gBAAgB;AAC/D,SAAO,KAAK,QAAQ,GAAG,WAAW,gBAAgB;AACpD;AAEO,SAAS,aAAqB;AACnC,SAAO,KAAK,UAAU,GAAG,eAAe;AAC1C;AAMA,eAAsB,sBAAgD;AACpE,MAAI;AACF,UAAM,OAAO,MAAM,GAAG,SAAS,WAAW,GAAG,MAAM;AACnD,UAAM,SAAS,KAAK,MAAM,IAAI;AAC9B,QAAI,UAAU,OAAO,WAAW,YAAY,CAAC,MAAM,QAAQ,MAAM,GAAG;AAClE,aAAO;AAAA,IACT;AACA,WAAO,CAAC;AAAA,EACV,SAAS,KAAK;AACZ,QAAK,IAA8B,SAAS,SAAU,QAAO,CAAC;AAC9D,UAAM;AAAA,EACR;AACF;AAEA,eAAsB,qBAAqB,QAAwC;AACjF,QAAM,OAAO,WAAW;AACxB,QAAM,GAAG,MAAM,QAAQ,IAAI,GAAG,EAAE,WAAW,KAAK,CAAC;AACjD,QAAM,GAAG,UAAU,MAAM,KAAK,UAAU,QAAQ,MAAM,CAAC,IAAI,MAAM,EAAE,MAAM,IAAM,CAAC;AAClF;AAEA,eAAsB,uBAAyC;AAC7D,MAAI;AACF,UAAM,GAAG,OAAO,WAAW,CAAC;AAC5B,WAAO;AAAA,EACT,SAAS,KAAK;AACZ,QAAK,IAA8B,SAAS,SAAU,QAAO;AAC7D,UAAM;AAAA,EACR;AACF;AAOA,eAAsB,cAAc,OAA6B,CAAC,GAAoB;AACpF,MAAI,KAAK,QAAQ,KAAK,KAAK,KAAK,MAAM,GAAI,QAAO,KAAK,KAAK,KAAK;AAEhE,QAAM,MAAM,QAAQ,IAAI;AACxB,MAAI,OAAO,IAAI,KAAK,MAAM,GAAI,QAAO,IAAI,KAAK;AAE9C,QAAM,YAAY,MAAM,oBAAoB;AAC5C,MAAI,UAAU,UAAU,UAAU,OAAO,KAAK,MAAM,GAAI,QAAO,UAAU,OAAO,KAAK;AAErF,QAAM,IAAI;AAAA,IACR;AAAA,EACF;AACF;AAEO,SAAS,WAAW,KAAqB;AAC9C,MAAI,IAAI,UAAU,EAAG,QAAO,IAAI,OAAO,IAAI,MAAM;AACjD,SAAO,GAAG,IAAI,MAAM,GAAG,CAAC,CAAC,GAAG,IAAI,OAAO,IAAI,SAAS,CAAC,CAAC,GAAG,IAAI,MAAM,EAAE,CAAC;AACxE;;;ACzEA,SAAS,YAAYC,WAAU;AAO/B,eAAsB,KAAK,OAAgB,OAAoB,CAAC,GAAkB;AAChF,QAAM,OAAO,UAAU,OAAO,IAAI;AAElC,MAAI,KAAK,UAAU,KAAK,OAAO,KAAK,MAAM,IAAI;AAC5C,UAAMA,IAAG,UAAU,KAAK,QAAQ,QAAQ,KAAK,SAAS,IAAI,IAAI,KAAK,KAAK;AACxE;AAAA,EACF;AAEA,UAAQ,OAAO,MAAM,IAAI;AACzB,MAAI,CAAC,KAAK,SAAS,IAAI,EAAG,SAAQ,OAAO,MAAM,IAAI;AACrD;AAEO,SAAS,UAAU,OAAgB,OAAoB,CAAC,GAAW;AACxE,MAAI,OAAO,UAAU,SAAU,QAAO;AAEtC,QAAM,SAAS,KAAK,UAAU,QAAQ,OAAO,SAAS;AACtD,SAAO,KAAK,UAAU,OAAO,MAAM,SAAS,IAAI,CAAC;AACnD;;;ACzBA,SAAS,qBAAqB;AAoBvB,SAAS,aAAa,SAA6C;AACxE,QAAM,QAAQ,IAAI,cAAc;AAAA,IAC9B,QAAQ,QAAQ;AAAA,IAChB,WAAW,QAAQ;AAAA,EACrB,CAAC;AAKD,QAAM,MAAM,EAAE,UAAU,KAAK;AAE7B,SAAO,IAAI,MAAM,OAAO;AAAA,IACtB,IAAI,QAAQ,MAAM,UAAU;AAC1B,YAAM,QAAQ,QAAQ,IAAI,QAAQ,MAAM,QAAQ;AAChD,UAAI,OAAO,UAAU,WAAY,QAAO;AACxC,cAAQ,MAAM;AAAA,QACZ,KAAK;AACH,iBAAO,CAAC,MAAmB,OAAO,KAAK,EAAE,GAAG,GAAG,GAAG,IAAI,CAAC;AAAA,QACzD,KAAK;AACH,iBAAO,CAAC,MAAmB,OAAO,KAAK,EAAE,GAAG,GAAG,GAAG,IAAI,CAAC;AAAA,QACzD,KAAK;AACH,iBAAO,CAAC,MAAuB,OAAO,SAAS,EAAE,GAAG,GAAG,GAAG,IAAI,CAAC;AAAA,QACjE,KAAK;AACH,iBAAO,CAAC,MAA+B,OAAO,iBAAiB,EAAE,GAAG,GAAG,GAAG,IAAI,CAAC;AAAA,QACjF,KAAK;AACH,iBAAO,CAAC,MAAuB,OAAO,SAAS,EAAE,GAAG,GAAG,GAAG,IAAI,CAAC;AAAA,QACjE,KAAK;AACH,iBAAO,CAAC,MAAqB,OAAO,OAAO,EAAE,GAAG,GAAG,GAAG,IAAI,CAAC;AAAA,QAC7D,KAAK;AAGH,iBAAO,MAAM,KAAK,MAAM;AAAA,QAC1B;AACE,iBAAO,OAAO,UAAU,aAAa,MAAM,KAAK,MAAM,IAAI;AAAA,MAC9D;AAAA,IACF;AAAA,EACF,CAAC;AACH;;;AHvDO,SAAS,iBAA0B;AACxC,SAAO,IAAI,QAAQ,SAAS,EACzB,YAAY,0CAA0C,EACtD,OAAO,uBAAuB,6DAA6D,EAC3F,OAAO,uBAAuB,wCAAwC,EACtE,OAAO,YAAY,2CAA2C,EAC9D,OAAO,eAAe,+BAA+B,EACrD,OAAO,OAAO,SAAuB;AACpC,UAAM,SAAS,MAAM,cAAc,EAAE,MAAM,KAAK,OAAO,CAAC;AACxD,UAAM,SAAS,aAAa,EAAE,OAAO,CAAC;AACtC,UAAM,SAAS,MAAM,OAAO,QAAQ;AACpC,UAAM,KAAK,QAAQ,IAAI;AAAA,EACzB,CAAC;AACL;;;AIzBA,SAAS,WAAAC,UAAS,UAAAC,eAAc;;;ACyBzB,SAAS,mBACd,OACA,WAAkC,CAAC,GACb;AACtB,QAAM,MAA4B,CAAC;AAInC,MAAI,MAAM,OAAO,OAAW,KAAI,KAAK,MAAM;AAC3C,MAAI,MAAM,UAAU,OAAW,KAAI,QAAQ,MAAM;AACjD,MAAI,MAAM,YAAY,OAAW,KAAI,UAAU,MAAM;AACrD,MAAI,MAAM,WAAW,OAAW,KAAI,SAAS,MAAM;AACnD,MAAI,MAAM,YAAY,OAAW,KAAI,UAAU,MAAM;AACrD,MAAI,MAAM,cAAc,OAAW,KAAI,aAAa,MAAM;AAC1D,MAAI,MAAM,YAAY,OAAW,KAAI,WAAW,MAAM;AACtD,MAAI,MAAM,aAAa,OAAW,KAAI,YAAY,MAAM;AACxD,MAAI,MAAM,gBAAgB,OAAW,KAAI,eAAe,MAAM;AAC9D,MAAI,MAAM,WAAY,KAAI,eAAe;AACzC,MAAI,MAAM,gBAAiB,KAAI,oBAAoB;AACnD,MAAI,SAAS,QAAS,KAAI,UAAU,SAAS;AAC7C,SAAO;AACT;AAMO,SAAS,iBACd,KACA,OACA,UACA,SAAqB,CAAC,GACT;AACb,QAAM,MAAmB,EAAE,KAAK,GAAG,mBAAmB,OAAO,QAAQ,EAAE;AACvE,MAAI,MAAM,OAAQ,KAAI,SAAS,MAAM;AACrC,MAAI,OAAO,mBAAoB,KAAI,uBAAuB;AAC1D,SAAO;AACT;AAOO,SAAS,iBACd,KACA,OACA,UACA,SAAqB,CAAC,GACT;AACb,QAAM,MAAmB,EAAE,KAAK,GAAG,mBAAmB,OAAO,QAAQ,EAAE;AACvE,MAAI,OAAO,WAAY,KAAI,cAAc,OAAO;AAChD,MAAI,OAAO,YAAa,KAAI,eAAe;AAC3C,SAAO;AACT;AAEO,SAAS,qBACd,KACA,UACA,OACA,UACiB;AACjB,QAAM,MAAuB,EAAE,KAAK,UAAU,GAAG,mBAAmB,OAAO,QAAQ,EAAE;AACrF,MAAI,MAAM,OAAQ,KAAI,SAAS,MAAM;AACrC,SAAO;AACT;AAEO,SAAS,6BACd,KACA,WACA,OACA,UACyB;AACzB,MAAI,UAAU,WAAW,GAAG;AAC1B,UAAM,IAAI,MAAM,oDAAoD;AAAA,EACtE;AACA,SAAO,EAAE,KAAK,WAAW,GAAG,mBAAmB,OAAO,QAAQ,EAAE;AAClE;AAEO,SAAS,qBACd,KACA,UACA,OACA,UACiB;AACjB,QAAM,MAAuB,EAAE,KAAK,UAAU,GAAG,mBAAmB,OAAO,QAAQ,EAAE;AACrF,MAAI,MAAM,OAAQ,KAAI,SAAS,MAAM;AACrC,SAAO;AACT;AAEO,SAAS,mBACd,KACA,QACA,OACA,UACe;AACf,SAAO,EAAE,KAAK,QAAQ,GAAG,mBAAmB,OAAO,QAAQ,EAAE;AAC/D;;;AClHA,SAAS,YAAYC,WAAU;AAC/B,SAAS,cAAc;AA0BhB,SAAS,uBAAuB,KAAuB;AAC5D,SAAO,IACJ,OAAO,uBAAuB,6DAA6D,EAC3F,OAAO,uBAAuB,wCAAwC,EACtE,OAAO,YAAY,2CAA2C,EAC9D;AAAA,IACC,IAAI,OAAO,eAAe,+BAA+B,EAAE,UAAU,QAAQ;AAAA,EAC/E,EACC,OAAO,QAAQ,sDAAsD,EACrE,OAAO,WAAW,wCAAwC,EAC1D;AAAA,IACC,IAAI,OAAO,kBAAkB,YAAY,EAAE,QAAQ;AAAA,MACjD;AAAA,MACA;AAAA,MACA;AAAA,IACF,CAAC;AAAA,EACH,EACC,OAAO,oBAAoB,gDAAgD,EAC3E;AAAA,IACC,IAAI,OAAO,mBAAmB,kBAAkB,EAAE,QAAQ,CAAC,WAAW,UAAU,QAAQ,CAAC;AAAA,EAC3F,EACC,OAAO,kBAAkB,4CAA4C,aAAa,EAClF,OAAO,qBAAqB,kDAAkD,aAAa,EAC3F,OAAO,yBAAyB,mDAAmD,EACnF;AAAA,IACC;AAAA,IACA;AAAA,EACF,EACC,OAAO,sBAAsB,0CAA0C,EACvE,OAAO,wBAAwB,8CAA8C,EAC7E,OAAO,kBAAkB,qBAAqB,EAC9C,OAAO,uBAAuB,0BAA0B;AAC7D;AAEO,SAAS,cAAc,OAAuB;AACnD,QAAM,IAAI,OAAO,SAAS,OAAO,EAAE;AACnC,MAAI,CAAC,OAAO,SAAS,CAAC,EAAG,OAAM,IAAI,MAAM,0BAA0B,KAAK,EAAE;AAC1E,SAAO;AACT;AAMA,eAAsB,aAAa,OAAwE;AACzG,MAAI,CAAC,SAAS,MAAM,KAAK,MAAM,GAAI,QAAO;AAC1C,QAAM,OAAO,MAAM,WAAW,GAAG,IAAI,MAAMA,IAAG,SAAS,MAAM,MAAM,CAAC,GAAG,MAAM,IAAI;AACjF,SAAO,gBAAgB,MAAM,SAAS;AACxC;AAMA,eAAsB,YAAY,OAAgD;AAChF,QAAM,OAAO,MAAM,WAAW,GAAG,IAAI,MAAMA,IAAG,SAAS,MAAM,MAAM,CAAC,GAAG,MAAM,IAAI;AACjF,QAAM,SAAS,gBAAgB,MAAM,QAAQ;AAC7C,MAAI,CAAC,UAAU,OAAO,KAAK,MAAM,EAAE,WAAW,GAAG;AAC/C,UAAM,IAAI,MAAM,sEAAsE;AAAA,EACxF;AACA,SAAO;AACT;AAEA,SAAS,gBAAgB,MAAc,OAAuC;AAC5E,MAAI;AACJ,MAAI;AACF,aAAS,KAAK,MAAM,IAAI;AAAA,EAC1B,SAAS,KAAK;AACZ,UAAM,IAAI,MAAM,GAAG,KAAK,yBAAqB,IAAc,OAAO,EAAE;AAAA,EACtE;AACA,MAAI,CAAC,UAAU,OAAO,WAAW,YAAY,MAAM,QAAQ,MAAM,GAAG;AAClE,UAAM,IAAI,MAAM,GAAG,KAAK,wBAAwB;AAAA,EAClD;AACA,QAAM,MAA8B,CAAC;AACrC,aAAW,CAAC,GAAG,CAAC,KAAK,OAAO,QAAQ,MAAiC,GAAG;AACtE,QAAI,OAAO,MAAM,SAAU,OAAM,IAAI,MAAM,GAAG,KAAK,IAAI,CAAC,0BAA0B;AAClF,QAAI,CAAC,IAAI;AAAA,EACX;AACA,SAAO;AACT;;;AC1GA,OAAO,cAAc;AAEd,SAAS,WAAW,KAAsB;AAC/C,SAAO,QAAQ;AACjB;AAEA,gBAAuB,oBAA2C;AAChE,QAAM,KAAK,SAAS,gBAAgB,EAAE,OAAO,QAAQ,OAAO,WAAW,SAAS,CAAC;AACjF,mBAAiB,OAAO,IAAI;AAC1B,UAAM,OAAO,IAAI,KAAK;AACtB,QAAI,SAAS,MAAM,KAAK,WAAW,GAAG,EAAG;AACzC,UAAM;AAAA,EACR;AACF;AAKA,gBAAuB,YAAY,KAAoC;AACrE,MAAI,WAAW,GAAG,GAAG;AACnB,WAAO,kBAAkB;AACzB;AAAA,EACF;AACA,QAAM;AACR;;;AHlBO,SAAS,aAAsB;AACpC,QAAM,MAAM,IAAIC,SAAQ,KAAK,EAC1B,YAAY,sDAAsD,EAClE,SAAS,SAAS,2CAA2C,EAC7D,eAAe,yBAAyB,2CAA2C,EACnF,UAAU,IAAIC,QAAO,kBAAkB,mBAAmB,EAAE,QAAQ,CAAC,QAAQ,MAAM,CAAC,CAAC;AAExF,yBAAuB,GAAG;AAE1B,MAAI,OAAO,OAAO,KAAa,SAAmB;AAChD,QAAI,CAAC,KAAK,SAAU,OAAM,IAAI,MAAM,wBAAwB;AAC5D,UAAM,SAAS,MAAM,cAAc,EAAE,MAAM,KAAK,OAAO,CAAC;AACxD,UAAM,UAAU,MAAM,aAAa,KAAK,OAAO;AAC/C,UAAM,SAAS,aAAa,EAAE,QAAQ,kBAAkB,KAAK,QAAQ,CAAC;AAEtE,qBAAiB,UAAU,YAAY,GAAG,GAAG;AAC3C,YAAM,aAAa,qBAAqB,QAAQ,KAAK,UAAU,MAAM,EAAE,QAAQ,CAAC;AAChF,YAAM,SAAS,MAAM,OAAO,SAAS,UAAU;AAC/C,YAAM,KAAK,QAAQ,IAAI;AAAA,IACzB;AAAA,EACF,CAAC;AAED,SAAO;AACT;;;AIrCA,SAAS,WAAAC,gBAAe;AAUjB,SAAS,cAAuB;AACrC,QAAM,MAAM,IAAIC,SAAQ,MAAM,EAAE,YAAY,4BAA4B;AAExE,MACG,QAAQ,WAAW,EACnB,YAAY,sBAAsB,WAAW,CAAC,EAAE,EAChD,OAAO,OAAO,QAAgB;AAC7B,QAAI,CAAC,OAAO,IAAI,KAAK,MAAM,GAAI,OAAM,IAAI,MAAM,kBAAkB;AACjE,UAAM,qBAAqB,EAAE,QAAQ,IAAI,KAAK,EAAE,CAAC;AACjD,YAAQ,OAAO,MAAM,oBAAoB,WAAW,CAAC;AAAA,CAAI;AAAA,EAC3D,CAAC;AAEH,MACG,QAAQ,MAAM,EACd,YAAY,+CAA+C,EAC3D,OAAO,YAAY;AAClB,UAAM,YAAY,MAAM,oBAAoB;AAC5C,UAAM,MAAM,QAAQ,IAAI;AACxB,QAAI,OAAO,IAAI,KAAK,MAAM,IAAI;AAC5B,cAAQ,OAAO,MAAM,4CAA4C,WAAW,IAAI,KAAK,CAAC,CAAC;AAAA,CAAI;AAAA,IAC7F;AACA,QAAI,UAAU,UAAU,UAAU,OAAO,KAAK,MAAM,IAAI;AACtD,cAAQ,OAAO;AAAA,QACb,eAAe,WAAW,CAAC,MAAM,WAAW,UAAU,OAAO,KAAK,CAAC,CAAC;AAAA;AAAA,MACtE;AAAA,IACF;AACA,QAAI,CAAC,OAAO,CAAC,UAAU,QAAQ;AAC7B,cAAQ,OAAO,MAAM,+DAA+D;AAAA,IACtF;AAAA,EACF,CAAC;AAEH,MACG,QAAQ,OAAO,EACf,YAAY,gCAAgC,EAC5C,OAAO,YAAY;AAClB,UAAM,UAAU,MAAM,qBAAqB;AAC3C,YAAQ,OAAO,MAAM,UAAU,WAAW,WAAW,CAAC;AAAA,IAAO,gCAAgC;AAAA,EAC/F,CAAC;AAEH,SAAO;AACT;;;AClDA,SAAS,WAAAC,gBAAe;AAcjB,SAAS,iBAA0B;AACxC,QAAM,MAAM,IAAIC,SAAQ,SAAS,EAC9B,YAAY,mEAAmE,EAC/E,SAAS,SAAS,2CAA2C,EAC7D;AAAA,IACC;AAAA,IACA;AAAA,EACF;AAEF,yBAAuB,GAAG;AAE1B,MAAI,OAAO,OAAO,KAAa,SAAuB;AACpD,QAAI,CAAC,KAAK,OAAQ,OAAM,IAAI,MAAM,sBAAsB;AACxD,UAAM,SAAS,MAAM,cAAc,EAAE,MAAM,KAAK,OAAO,CAAC;AACxD,UAAM,UAAU,MAAM,aAAa,KAAK,OAAO;AAC/C,UAAM,SAAS,MAAM,YAAY,KAAK,MAAM;AAC5C,UAAM,SAAS,aAAa,EAAE,QAAQ,kBAAkB,KAAK,QAAQ,CAAC;AAEtE,qBAAiB,UAAU,YAAY,GAAG,GAAG;AAC3C,YAAM,aAAa,mBAAmB,QAAQ,QAAQ,MAAM,EAAE,QAAQ,CAAC;AACvE,YAAM,SAAS,MAAM,OAAO,OAAO,UAAU;AAC7C,YAAM,KAAK,QAAQ,IAAI;AAAA,IACzB;AAAA,EACF,CAAC;AAED,SAAO;AACT;;;ACxCA,SAAS,WAAAC,UAAS,UAAAC,eAAc;AAczB,SAAS,cAAuB;AACrC,QAAM,MAAM,IAAIC,SAAQ,MAAM,EAC3B,YAAY,+BAA+B,EAC3C,SAAS,SAAS,2CAA2C,EAC7D,UAAU,IAAIC,QAAO,0BAA0B,kDAAkD,CAAC,EAClG,UAAU,IAAIA,QAAO,kBAAkB,mBAAmB,EAAE,QAAQ,CAAC,QAAQ,MAAM,CAAC,CAAC;AAExF,yBAAuB,GAAG;AAE1B,MAAI,OAAO,OAAO,KAAa,SAAoB;AACjD,UAAM,SAAS,MAAM,cAAc,EAAE,MAAM,KAAK,OAAO,CAAC;AACxD,UAAM,UAAU,MAAM,aAAa,KAAK,OAAO;AAC/C,UAAM,SAAS,aAAa,EAAE,QAAQ,kBAAkB,KAAK,QAAQ,CAAC;AAEtE,qBAAiB,UAAU,YAAY,GAAG,GAAG;AAC3C,YAAM,aAAa,iBAAiB,QAAQ,MAAM,EAAE,QAAQ,GAAG;AAAA,QAC7D,oBAAoB,KAAK;AAAA,MAC3B,CAAC;AACD,YAAM,SAAS,MAAM,OAAO,KAAK,UAAU;AAC3C,YAAM,KAAK,QAAQ,IAAI;AAAA,IACzB;AAAA,EACF,CAAC;AAED,SAAO;AACT;;;ACtCA,SAAS,WAAAC,UAAS,UAAAC,eAAc;AAczB,SAAS,kBAA2B;AACzC,QAAM,MAAM,IAAIC,SAAQ,UAAU,EAC/B,YAAY,0CAA0C,EACtD,SAAS,SAAS,2CAA2C,EAC7D,eAAe,wBAAwB,wCAAwC,EAC/E,UAAU,IAAIC,QAAO,kBAAkB,mBAAmB,EAAE,QAAQ,CAAC,QAAQ,MAAM,CAAC,CAAC;AAExF,yBAAuB,GAAG;AAE1B,MAAI,OAAO,OAAO,KAAa,SAAwB;AACrD,QAAI,CAAC,KAAK,SAAU,OAAM,IAAI,MAAM,wBAAwB;AAC5D,UAAM,SAAS,MAAM,cAAc,EAAE,MAAM,KAAK,OAAO,CAAC;AACxD,UAAM,UAAU,MAAM,aAAa,KAAK,OAAO;AAC/C,UAAM,SAAS,aAAa,EAAE,QAAQ,kBAAkB,KAAK,QAAQ,CAAC;AAEtE,qBAAiB,UAAU,YAAY,GAAG,GAAG;AAC3C,YAAM,aAAa,qBAAqB,QAAQ,KAAK,UAAU,MAAM,EAAE,QAAQ,CAAC;AAChF,YAAM,SAAS,MAAM,OAAO,SAAS,UAAU;AAC/C,YAAM,KAAK,QAAQ,IAAI;AAAA,IACzB;AAAA,EACF,CAAC;AAED,SAAO;AACT;;;ACrCA,SAAS,WAAAC,gBAAe;AAcjB,SAAS,0BAAmC;AACjD,QAAM,MAAM,IAAIC,SAAQ,mBAAmB,EACxC,YAAY,gDAAgD,EAC5D,SAAS,SAAS,2CAA2C,EAC7D;AAAA,IACC;AAAA,IACA;AAAA,IACA;AAAA,IACA,CAAC;AAAA,EACH;AAEF,yBAAuB,GAAG;AAE1B,MAAI,OAAO,OAAO,KAAa,SAAgC;AAC7D,UAAM,YAAY,KAAK,YAAY,CAAC;AACpC,QAAI,UAAU,WAAW,GAAG;AAC1B,YAAM,IAAI,MAAM,qCAAqC;AAAA,IACvD;AACA,UAAM,SAAS,MAAM,cAAc,EAAE,MAAM,KAAK,OAAO,CAAC;AACxD,UAAM,UAAU,MAAM,aAAa,KAAK,OAAO;AAC/C,UAAM,SAAS,aAAa,EAAE,QAAQ,kBAAkB,KAAK,QAAQ,CAAC;AAEtE,qBAAiB,UAAU,YAAY,GAAG,GAAG;AAC3C,YAAM,aAAa,6BAA6B,QAAQ,WAAW,MAAM,EAAE,QAAQ,CAAC;AACpF,YAAM,SAAS,MAAM,OAAO,iBAAiB,UAAU;AACvD,YAAM,KAAK,QAAQ,IAAI;AAAA,IACzB;AAAA,EACF,CAAC;AAED,SAAO;AACT;AAEA,SAAS,aAAa,OAAe,UAA8B;AACjE,SAAO,CAAC,GAAG,UAAU,KAAK;AAC5B;;;AChDA,SAAS,WAAAC,gBAAe;;;ACmBxB,SAAS,YAAYC,WAAU;AAC/B,SAAS,WAAAC,gBAAe;AACxB,SAAS,WAAAC,UAAS,QAAAC,OAAM,eAAe;AACvC,SAAS,qBAAqB;AA0BvB,IAAM,UAAoC;AAAA;AAAA,EAE/C,EAAE,IAAI,eAAe,OAAO,eAAe,MAAM,OAAO,MAAM,QAAQ,MAAM,gCAAgC;AAAA;AAAA;AAAA,EAG5G,EAAE,IAAI,UAAU,OAAO,UAAU,MAAM,QAAQ,MAAM,WAAW,MAAM,mCAAmC;AAAA;AAAA,EAEzG,EAAE,IAAI,YAAY,OAAO,YAAY,MAAM,QAAQ,MAAM,WAAW,MAAM,oCAAoC;AAAA;AAAA,EAE9G,EAAE,IAAI,QAAQ,OAAO,QAAQ,MAAM,OAAO,MAAM,QAAQ,MAAM,gCAAgC;AAAA;AAAA,EAE9F,EAAE,IAAI,YAAY,OAAO,YAAY,MAAM,OAAO,MAAM,WAAW,MAAM,kCAAkC;AAAA;AAAA,EAE3G,EAAE,IAAI,UAAU,OAAO,cAAc,MAAM,QAAQ,MAAM,QAAQ,MAAM,8CAA8C;AAAA;AAAA,EAErH,EAAE,IAAI,WAAW,OAAO,kBAAkB,MAAM,QAAQ,MAAM,WAAW,MAAM,kCAAkC;AAAA;AAAA,EAEjH,EAAE,IAAI,WAAW,OAAO,WAAW,MAAM,QAAQ,MAAM,WAAW,MAAM,mCAAmC;AAAA;AAAA,EAE3G,EAAE,IAAI,WAAW,OAAO,WAAW,MAAM,QAAQ,MAAM,WAAW,MAAM,mCAAmC;AAC7G;AAEO,SAAS,WAAW,IAAuC;AAChE,SAAO,QAAQ,KAAK,CAAC,MAAM,EAAE,OAAO,EAAE;AACxC;AAMO,SAAS,kBAA0B;AACxC,QAAM,OAAOD,SAAQ,cAAc,YAAY,GAAG,CAAC;AAGnD,SAAO,QAAQ,MAAM,MAAM,WAAW,kBAAkB,UAAU,gBAAgB;AACpF;AAaO,SAAS,cAAc,QAAuB,OAAuB,CAAC,GAAkB;AAC7F,QAAM,OAAO,OAAO,SAAS,SAAU,KAAK,QAAQD,SAAQ,IAAM,KAAK,OAAO,QAAQ,IAAI;AAC1F,SAAO,EAAE,QAAQ,aAAaE,MAAK,MAAM,OAAO,IAAI,EAAE;AACxD;AAgBA,eAAsB,aACpB,QACA,OAAuB,CAAC,GACA;AACxB,QAAM,SAAS,KAAK,aAAa,gBAAgB;AACjD,QAAM,SAAS,MAAM,WAAW,OAAO,WAAW;AAElD,MAAI,UAAU,CAAC,KAAK,OAAO;AACzB,WAAO,EAAE,QAAQ,QAAQ,iBAAiB;AAAA,EAC5C;AAEA,MAAI,OAAO,OAAO,SAAS,OAAO;AAChC,QAAI,OAAQ,OAAMH,IAAG,GAAG,OAAO,aAAa,EAAE,WAAW,MAAM,OAAO,KAAK,CAAC;AAC5E,UAAM,cAAc,QAAQ,OAAO,WAAW;AAAA,EAChD,OAAO;AACL,UAAM,YAAY,MAAMA,IAAG,SAASG,MAAK,QAAQ,UAAU,GAAG,MAAM;AACpE,UAAMH,IAAG,MAAME,SAAQ,OAAO,WAAW,GAAG,EAAE,WAAW,KAAK,CAAC;AAC/D,UAAMF,IAAG,UAAU,OAAO,aAAa,SAAS;AAAA,EAClD;AAEA,SAAO,EAAE,QAAQ,QAAQ,SAAS,gBAAgB,YAAY;AAChE;AAEA,eAAe,WAAW,MAAgC;AACxD,MAAI;AACF,UAAMA,IAAG,OAAO,IAAI;AACpB,WAAO;AAAA,EACT,QAAQ;AACN,WAAO;AAAA,EACT;AACF;AAEA,eAAe,cAAc,QAAgB,aAAoC;AAE/E,QAAMA,IAAG,GAAG,QAAQ,aAAa,EAAE,WAAW,MAAM,OAAO,KAAK,CAAC;AACnE;;;AD3IO,SAAS,eAAwB;AACtC,QAAM,MAAM,IAAII,SAAQ,OAAO,EAAE,YAAY,gDAAgD;AAE7F,MACG,QAAQ,OAAO,EACf,YAAY,qEAAqE,EACjF;AAAA,IACC;AAAA,IACA,sCAAsC,QAAQ,IAAI,CAAC,MAAM,EAAE,EAAE,EAAE,KAAK,IAAI,CAAC;AAAA,IACzE;AAAA,IACA,CAAC;AAAA,EACH,EACC,OAAO,aAAa,qCAAqC,EACzD,OAAO,eAAe,gCAAgC,EACtD,OAAO,OAAO,SAA0B;AACvC,UAAM,WAAW,iBAAiB,IAAI;AACtC,QAAI,SAAS,WAAW,GAAG;AACzB,YAAM,IAAI;AAAA,QACR;AAAA,MACF;AAAA,IACF;AAEA,eAAW,UAAU,UAAU;AAC7B,YAAM,SAAS,cAAc,MAAM;AACnC,YAAM,SAAS,MAAM,aAAa,QAAQ,EAAE,OAAO,KAAK,MAAM,CAAC;AAC/D,cAAQ,OAAO,MAAM,GAAG,OAAO,KAAK,KAAK,OAAO,MAAM,WAAM,OAAO,WAAW;AAAA,CAAI;AAAA,IACpF;AAAA,EACF,CAAC;AAEH,MACG,QAAQ,QAAQ,EAAE,QAAQ,MAAM,CAAC,EACjC,YAAY,0CAA0C,EACtD,OAAO,MAAM;AACZ,eAAW,KAAK,SAAS;AACvB,YAAM,SAAS,cAAc,CAAC;AAC9B,cAAQ,OAAO,MAAM,GAAG,EAAE,GAAG,OAAO,EAAE,CAAC,IAAI,EAAE,MAAM,OAAO,EAAE,CAAC,IAAI,OAAO,WAAW;AAAA,CAAI;AAAA,IACzF;AAAA,EACF,CAAC;AAEH,SAAO;AACT;AAEA,SAAS,QAAQ,OAAe,UAA8B;AAC5D,SAAO,CAAC,GAAG,UAAU,KAAK;AAC5B;AAEA,SAAS,iBAAiB,MAAwC;AAChE,MAAI,KAAK,IAAK,QAAO,CAAC,GAAG,OAAO;AAChC,QAAM,MAAM,KAAK,UAAU,CAAC;AAC5B,SAAO,IAAI,IAAI,CAAC,OAAO;AACrB,UAAM,SAAS,WAAW,EAAE;AAC5B,QAAI,CAAC,OAAQ,OAAM,IAAI,MAAM,mBAAmB,EAAE,EAAE;AACpD,WAAO;AAAA,EACT,CAAC;AACH;;;AEjEA,SAAS,WAAAC,UAAS,UAAAC,eAAc;AAezB,SAAS,cAAuB;AACrC,QAAM,MAAM,IAAIC,SAAQ,MAAM,EAC3B,YAAY,kCAAkC,EAC9C,SAAS,SAAS,2CAA2C,EAC7D;AAAA,IACC,IAAIC,QAAO,uBAAuB,sBAAsB,EAAE,QAAQ,CAAC,SAAS,QAAQ,KAAK,CAAC;AAAA,EAC5F,EACC,OAAO,kBAAkB,4CAA4C;AAExE,yBAAuB,GAAG;AAE1B,MAAI,OAAO,OAAO,KAAa,SAAoB;AACjD,UAAM,SAAS,MAAM,cAAc,EAAE,MAAM,KAAK,OAAO,CAAC;AACxD,UAAM,UAAU,MAAM,aAAa,KAAK,OAAO;AAC/C,UAAM,SAAS,aAAa,EAAE,QAAQ,kBAAkB,KAAK,QAAQ,CAAC;AAEtE,qBAAiB,UAAU,YAAY,GAAG,GAAG;AAC3C,YAAM,aAAa,iBAAiB,QAAQ,MAAM,EAAE,QAAQ,GAAG;AAAA,QAC7D,YAAY,KAAK;AAAA,QACjB,aAAa,KAAK;AAAA,MACpB,CAAC;AACD,YAAM,SAAS,MAAM,OAAO,KAAK,UAAU;AAC3C,YAAM,KAAK,QAAQ,IAAI;AAAA,IACzB;AAAA,EACF,CAAC;AAED,SAAO;AACT;;;AClCA;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,OACK;AAEA,IAAM,aAAa;AAAA,EACxB,IAAI;AAAA,EACJ,SAAS;AAAA,EACT,OAAO;AAAA,EACP,YAAY;AAAA,EACZ,MAAM;AAAA,EACN,iBAAiB;AAAA,EACjB,WAAW;AAAA,EACX,aAAa;AAAA,EACb,SAAS;AAAA,EACT,YAAY;AACd;AAEO,SAAS,YAAY,KAAsB;AAChD,MAAI,eAAe,gBAAiB,QAAO,WAAW;AACtD,MAAI,eAAe,oBAAqB,QAAO,WAAW;AAC1D,MAAI,eAAe,qBAAsB,QAAO,WAAW;AAC3D,MAAI,eAAe,eAAgB,QAAO,WAAW;AACrD,MAAI,eAAe,YAAa,QAAO,WAAW;AAClD,MAAI,eAAe,oBAAqB,QAAO,WAAW;AAC1D,MAAI,eAAe,gBAAiB,QAAO,WAAW;AACtD,MAAI,eAAe,mBAAoB,QAAO,WAAW;AACzD,MAAI,eAAe,SAAU,QAAO,WAAW;AAC/C,MAAI,eAAe,mBAAoB,QAAO,WAAW;AACzD,SAAO,WAAW;AACpB;AAEO,SAAS,YAAY,KAAsB;AAChD,MAAI,eAAe,UAAU;AAC3B,UAAM,QAAQ,CAAC,IAAI,OAAO;AAC1B,QAAI,IAAI,OAAQ,OAAM,KAAK,SAAS,IAAI,MAAM,GAAG;AACjD,WAAO,MAAM,KAAK,GAAG;AAAA,EACvB;AACA,MAAI,eAAe,MAAO,QAAO,IAAI;AACrC,SAAO,OAAO,GAAG;AACnB;;;AjBhCA,SAAS,iBAAyB;AAGhC,QAAM,OAAOC,SAAQC,eAAc,YAAY,GAAG,CAAC;AACnD,QAAM,UAAUC,SAAQ,MAAM,MAAM,cAAc;AAClD,QAAM,MAAM,KAAK,MAAM,aAAa,SAAS,MAAM,CAAC;AACpD,SAAO,IAAI;AACb;AAEO,SAAS,eAAwB;AACtC,QAAM,UAAU,IAAIC,UAAQ;AAC5B,UACG,KAAK,gBAAgB,EACrB;AAAA,IACC;AAAA,EAEF,EACC,QAAQ,eAAe,GAAG,iBAAiB,2BAA2B,EACtE,mBAAmB;AAEtB,UAAQ,WAAW,YAAY,CAAC;AAChC,UAAQ,WAAW,YAAY,CAAC;AAChC,UAAQ,WAAW,gBAAgB,CAAC;AACpC,UAAQ,WAAW,wBAAwB,CAAC;AAC5C,UAAQ,WAAW,WAAW,CAAC;AAC/B,UAAQ,WAAW,eAAe,CAAC;AACnC,UAAQ,WAAW,eAAe,CAAC;AACnC,UAAQ,WAAW,YAAY,CAAC;AAChC,UAAQ,WAAW,aAAa,CAAC;AAEjC,SAAO;AACT;AAEA,eAAe,KAAK,MAA+B;AACjD,QAAM,UAAU,aAAa;AAC7B,MAAI;AACF,UAAM,QAAQ,WAAW,IAAI;AAAA,EAC/B,SAAS,KAAK;AACZ,YAAQ,OAAO,MAAM,UAAU,YAAY,GAAG,CAAC;AAAA,CAAI;AACnD,YAAQ,KAAK,YAAY,GAAG,CAAC;AAAA,EAC/B;AACF;AAIA,IAAM,YAAY,QAAQ,KAAK,CAAC,IAAIF,eAAc,YAAY,GAAG,IAAI;AACrE,IAAI,aAAa,QAAQ,KAAK,CAAC,MAAM,WAAW;AAC9C,OAAK,QAAQ,IAAI,EAAE,MAAM,CAAC,QAAiB;AACzC,YAAQ,OAAO,MAAM,UAAU,YAAY,GAAG,CAAC;AAAA,CAAI;AACnD,YAAQ,KAAK,YAAY,GAAG,CAAC;AAAA,EAC/B,CAAC;AACH;","names":["Command","dirname","resolve","fileURLToPath","fs","Command","Option","fs","Command","Option","Command","Command","Command","Command","Command","Option","Command","Option","Command","Option","Command","Option","Command","Command","Command","fs","homedir","dirname","join","Command","Command","Option","Command","Option","dirname","fileURLToPath","resolve","Command"]}
|
|
1
|
+
{"version":3,"sources":["../src/cli.ts","../src/commands/account.ts","../src/lib/config.ts","../src/lib/output.ts","../src/lib/sdk.ts","../src/commands/ask.ts","../src/lib/buildRequest.ts","../src/lib/options.ts","../src/lib/stdin.ts","../src/commands/auth.ts","../src/commands/extract.ts","../src/commands/html.ts","../src/commands/selected.ts","../src/commands/selectedMultiple.ts","../src/commands/setupSkill.ts","../src/lib/skill.ts","../src/commands/text.ts","../src/lib/errors.ts"],"sourcesContent":["/**\n * `webscraping-ai` / `wsai` CLI entry point.\n *\n * Wires every subcommand into a single commander root. Errors thrown from\n * subcommand actions land in the top-level `.catch`, which maps SDK\n * exception classes to stable exit codes (see `lib/errors.ts`).\n */\n\nimport { Command } from 'commander';\nimport { readFileSync } from 'node:fs';\nimport { dirname, resolve } from 'node:path';\nimport { fileURLToPath } from 'node:url';\n\nimport { accountCommand } from './commands/account.js';\nimport { askCommand } from './commands/ask.js';\nimport { authCommand } from './commands/auth.js';\nimport { extractCommand } from './commands/extract.js';\nimport { htmlCommand } from './commands/html.js';\nimport { selectedCommand } from './commands/selected.js';\nimport { selectedMultipleCommand } from './commands/selectedMultiple.js';\nimport { setupCommand } from './commands/setupSkill.js';\nimport { textCommand } from './commands/text.js';\nimport { exitCodeFor, formatError } from './lib/errors.js';\n\nfunction packageVersion(): string {\n // Read package.json at runtime so we don't need a code-gen step. The dist\n // bundle sits at `dist/cli.js`, so package.json is one directory up.\n const here = dirname(fileURLToPath(import.meta.url));\n const pkgPath = resolve(here, '..', 'package.json');\n const raw = JSON.parse(readFileSync(pkgPath, 'utf8')) as { version: string };\n return raw.version;\n}\n\nexport function buildProgram(): Command {\n const program = new Command();\n program\n .name('webscraping-ai')\n .description(\n 'Official CLI for the WebScraping.AI API. Fetch pages, ask AI, extract structured data.\\n' +\n 'Set $WEBSCRAPING_AI_API_KEY or run `webscraping-ai auth set <key>` first.',\n )\n .version(packageVersion(), '-v, --version', 'output the version number')\n .showHelpAfterError();\n\n program.addCommand(htmlCommand());\n program.addCommand(textCommand());\n program.addCommand(selectedCommand());\n program.addCommand(selectedMultipleCommand());\n program.addCommand(askCommand());\n program.addCommand(extractCommand());\n program.addCommand(accountCommand());\n program.addCommand(authCommand());\n program.addCommand(setupCommand());\n\n return program;\n}\n\nasync function main(argv: string[]): Promise<void> {\n const program = buildProgram();\n try {\n await program.parseAsync(argv);\n } catch (err) {\n process.stderr.write(`Error: ${formatError(err)}\\n`);\n process.exit(exitCodeFor(err));\n }\n}\n\n// Only execute when run as a script. When imported (e.g. from tests),\n// `buildProgram` is the public surface and `main` stays dormant.\nconst entryPath = process.argv[1] ? fileURLToPath(import.meta.url) : '';\nif (entryPath && process.argv[1] === entryPath) {\n main(process.argv).catch((err: unknown) => {\n process.stderr.write(`Error: ${formatError(err)}\\n`);\n process.exit(exitCodeFor(err));\n });\n}\n","import { Command } from 'commander';\n\nimport { resolveApiKey } from '../lib/config.js';\nimport { emit } from '../lib/output.js';\nimport { createClient } from '../lib/sdk.js';\n\ninterface AccountFlags {\n apiKey?: string;\n output?: string;\n pretty?: boolean;\n}\n\nexport function accountCommand(): Command {\n return new Command('account')\n .description('Show account quota and usage information')\n .option('-k, --api-key <key>', 'API key (overrides $WEBSCRAPING_AI_API_KEY and config file)')\n .option('-o, --output <file>', 'write result to FILE instead of stdout')\n .option('--pretty', 'pretty-print JSON output (default on TTY)')\n .option('--no-pretty', 'force single-line JSON output')\n .action(async (opts: AccountFlags) => {\n const apiKey = await resolveApiKey({ flag: opts.apiKey });\n const client = createClient({ apiKey });\n const result = await client.account();\n await emit(result, opts);\n });\n}\n","/**\n * API key resolution and persisted config.\n *\n * Resolution order (first non-empty wins):\n * 1. `--api-key` flag (handled by the caller, passed in)\n * 2. `WEBSCRAPING_AI_API_KEY` env var\n * 3. `~/.config/webscraping-ai/config.json` (or `$XDG_CONFIG_HOME/...`)\n *\n * The config file is plain JSON with `{ \"apiKey\": \"...\" }`. We chmod it to\n * 0600 so it's not world-readable.\n */\n\nimport { promises as fs } from 'node:fs';\nimport { homedir } from 'node:os';\nimport { dirname, join } from 'node:path';\n\nconst CONFIG_FILENAME = 'config.json';\n\nexport function configDir(): string {\n const xdg = process.env.XDG_CONFIG_HOME;\n if (xdg && xdg.trim() !== '') return join(xdg, 'webscraping-ai');\n return join(homedir(), '.config', 'webscraping-ai');\n}\n\nexport function configPath(): string {\n return join(configDir(), CONFIG_FILENAME);\n}\n\nexport interface PersistedConfig {\n apiKey?: string;\n}\n\nexport async function readPersistedConfig(): Promise<PersistedConfig> {\n try {\n const text = await fs.readFile(configPath(), 'utf8');\n const parsed = JSON.parse(text) as unknown;\n if (parsed && typeof parsed === 'object' && !Array.isArray(parsed)) {\n return parsed as PersistedConfig;\n }\n return {};\n } catch (err) {\n if ((err as NodeJS.ErrnoException).code === 'ENOENT') return {};\n throw err;\n }\n}\n\nexport async function writePersistedConfig(config: PersistedConfig): Promise<void> {\n const path = configPath();\n await fs.mkdir(dirname(path), { recursive: true });\n await fs.writeFile(path, JSON.stringify(config, null, 2) + '\\n', { mode: 0o600 });\n // `mode` above only applies when the file is freshly created; an existing\n // file keeps its old (possibly world-readable) perms. Enforce 0600\n // unconditionally — chmod on a just-written file is cheap and idempotent.\n // No-op on Windows, but harmless.\n await fs.chmod(path, 0o600);\n}\n\nexport async function clearPersistedConfig(): Promise<boolean> {\n try {\n await fs.unlink(configPath());\n return true;\n } catch (err) {\n if ((err as NodeJS.ErrnoException).code === 'ENOENT') return false;\n throw err;\n }\n}\n\nexport interface ResolveApiKeyOptions {\n /** `--api-key` flag value, if the user passed one. */\n flag?: string;\n}\n\nexport async function resolveApiKey(opts: ResolveApiKeyOptions = {}): Promise<string> {\n if (opts.flag && opts.flag.trim() !== '') return opts.flag.trim();\n\n const env = process.env.WEBSCRAPING_AI_API_KEY;\n if (env && env.trim() !== '') return env.trim();\n\n const persisted = await readPersistedConfig();\n if (persisted.apiKey && persisted.apiKey.trim() !== '') return persisted.apiKey.trim();\n\n throw new Error(\n 'No API key found. Set $WEBSCRAPING_AI_API_KEY, pass --api-key, or run `webscraping-ai auth set <key>`.',\n );\n}\n\nexport function maskApiKey(key: string): string {\n if (key.length <= 8) return '*'.repeat(key.length);\n return `${key.slice(0, 4)}${'*'.repeat(key.length - 8)}${key.slice(-4)}`;\n}\n","/**\n * Output handling for command results.\n *\n * All command handlers funnel through `emit()`. The result type drives format:\n * - strings → written verbatim\n * - objects/arrays → JSON.stringified (pretty by default; `--pretty` is the\n * default for terminals, single-line for pipes/redirects)\n *\n * `--output FILE` writes to disk instead of stdout.\n */\n\nimport { promises as fs } from 'node:fs';\n\nexport interface EmitOptions {\n output?: string;\n pretty?: boolean;\n}\n\nexport async function emit(value: unknown, opts: EmitOptions = {}): Promise<void> {\n const text = stringify(value, opts);\n\n if (opts.output && opts.output.trim() !== '') {\n await fs.writeFile(opts.output, text + (text.endsWith('\\n') ? '' : '\\n'));\n return;\n }\n\n process.stdout.write(text);\n if (!text.endsWith('\\n')) process.stdout.write('\\n');\n}\n\nexport function stringify(value: unknown, opts: EmitOptions = {}): string {\n if (typeof value === 'string') return value;\n\n const pretty = opts.pretty ?? process.stdout.isTTY ?? false;\n return JSON.stringify(value, null, pretty ? 2 : 0);\n}\n","/**\n * SDK adapter.\n *\n * Wraps the published `webscraping-ai` Node SDK. The CLI sends a\n * `from_cli=true` analytics flag on every request (mirrors `from_n8n` and\n * `from_mcp_server` used by the n8n node and MCP server). The SDK's typed\n * options don't accept arbitrary keys, so we go through a single cast point\n * here rather than scattering casts across each command.\n */\n\nimport { WebScrapingAI } from 'webscraping-ai';\nimport type {\n FieldsOptions,\n HtmlOptions,\n QuestionOptions,\n SelectedMultipleOptions,\n SelectedOptions,\n TextOptions,\n} from 'webscraping-ai';\n\nexport interface CreateClientOptions {\n apiKey: string;\n /** Per-request timeout in ms. Maps to the SDK's `timeoutMs`. */\n requestTimeoutMs?: number;\n}\n\n/**\n * Returns a `WebScrapingAI` instance with the CLI's analytics tag baked into\n * every endpoint method. The methods preserve the SDK's public signatures.\n */\nexport function createClient(options: CreateClientOptions): WebScrapingAI {\n const inner = new WebScrapingAI({\n apiKey: options.apiKey,\n timeoutMs: options.requestTimeoutMs,\n });\n\n // Wrap each endpoint method so it injects the CLI analytics tag. We splice\n // the tag in at this seam rather than at every callsite. The cast is local;\n // the SDK passes unknown query params through to the wire.\n const tag = { from_cli: true } as unknown as Record<string, never>;\n\n return new Proxy(inner, {\n get(target, prop, receiver) {\n const value = Reflect.get(target, prop, receiver);\n if (typeof value !== 'function') return value;\n switch (prop) {\n case 'html':\n return (o: HtmlOptions) => target.html({ ...o, ...tag });\n case 'text':\n return (o: TextOptions) => target.text({ ...o, ...tag });\n case 'selected':\n return (o: SelectedOptions) => target.selected({ ...o, ...tag });\n case 'selectedMultiple':\n return (o: SelectedMultipleOptions) => target.selectedMultiple({ ...o, ...tag });\n case 'question':\n return (o: QuestionOptions) => target.question({ ...o, ...tag });\n case 'fields':\n return (o: FieldsOptions) => target.fields({ ...o, ...tag });\n case 'account':\n // `account` takes no options; the SDK builds the request itself, so\n // there's no seam here for the analytics tag — accepted limitation.\n return value.bind(target);\n default:\n return typeof value === 'function' ? value.bind(target) : value;\n }\n },\n });\n}\n","import { Command, Option } from 'commander';\n\nimport { buildQuestionOptions } from '../lib/buildRequest.js';\nimport { resolveApiKey } from '../lib/config.js';\nimport { addCommonScrapeOptions, parseHeaders } from '../lib/options.js';\nimport type { CommonRawFlags } from '../lib/options.js';\nimport { emit } from '../lib/output.js';\nimport { createClient } from '../lib/sdk.js';\nimport { urlIterator } from '../lib/stdin.js';\n\ninterface AskFlags extends CommonRawFlags {\n question?: string;\n}\n\nexport function askCommand(): Command {\n const cmd = new Command('ask')\n .description('Ask an AI question about a page (wraps /ai/question)')\n .argument('<url>', 'page URL (or `-` to read URLs from stdin)')\n .requiredOption('-q, --question <text>', 'the question / instructions for the model')\n .addOption(new Option('--format <fmt>', 'response wrapping').choices(['text', 'json']));\n\n addCommonScrapeOptions(cmd);\n\n cmd.action(async (url: string, opts: AskFlags) => {\n if (!opts.question) throw new Error('--question is required');\n const apiKey = await resolveApiKey({ flag: opts.apiKey });\n const headers = await parseHeaders(opts.headers);\n const client = createClient({ apiKey, requestTimeoutMs: opts.timeout });\n\n for await (const target of urlIterator(url)) {\n const sdkOptions = buildQuestionOptions(target, opts.question, opts, { headers });\n const result = await client.question(sdkOptions);\n await emit(result, opts);\n }\n });\n\n return cmd;\n}\n","/**\n * Pure builder that maps `--flag` values into SDK option objects.\n *\n * Extracted as a separate function so unit tests can pin the mapping without\n * spinning up commander or making HTTP calls. Each command handler reads its\n * own commander options, awaits `parseHeaders` / `parseFields`, then calls\n * the matching `build*` function here.\n */\n\nimport type {\n CommonRequestOptions,\n FieldsOptions,\n HtmlOptions,\n QuestionOptions,\n SelectedMultipleOptions,\n SelectedOptions,\n TextOptions,\n} from 'webscraping-ai';\n\nimport type { CommonRawFlags } from './options.js';\n\nexport interface ResolvedCommonOptions {\n headers?: Record<string, string>;\n}\n\nexport function buildCommonOptions(\n flags: CommonRawFlags,\n resolved: ResolvedCommonOptions = {},\n): CommonRequestOptions {\n const out: CommonRequestOptions = {};\n // `flags.js` is `false` when `--no-js`, `true` when `--js`, `undefined`\n // when neither passed. Pass through only when explicit, so the API default\n // applies otherwise.\n if (flags.js !== undefined) out.js = flags.js;\n if (flags.proxy !== undefined) out.proxy = flags.proxy;\n if (flags.country !== undefined) out.country = flags.country;\n if (flags.device !== undefined) out.device = flags.device;\n if (flags.timeout !== undefined) out.timeout = flags.timeout;\n if (flags.jsTimeout !== undefined) out.js_timeout = flags.jsTimeout;\n if (flags.waitFor !== undefined) out.wait_for = flags.waitFor;\n if (flags.jsScript !== undefined) out.js_script = flags.jsScript;\n if (flags.customProxy !== undefined) out.custom_proxy = flags.customProxy;\n if (flags.errorOn404) out.error_on_404 = true;\n if (flags.errorOnRedirect) out.error_on_redirect = true;\n if (resolved.headers) out.headers = resolved.headers;\n return out;\n}\n\nexport interface HtmlExtras {\n returnScriptResult?: boolean;\n}\n\nexport function buildHtmlOptions(\n url: string,\n flags: CommonRawFlags,\n resolved: ResolvedCommonOptions,\n extras: HtmlExtras = {},\n): HtmlOptions {\n const out: HtmlOptions = { url, ...buildCommonOptions(flags, resolved) };\n if (flags.format) out.format = flags.format;\n if (extras.returnScriptResult) out.return_script_result = true;\n return out;\n}\n\nexport interface TextExtras {\n textFormat?: 'plain' | 'json' | 'xml';\n returnLinks?: boolean;\n}\n\nexport function buildTextOptions(\n url: string,\n flags: CommonRawFlags,\n resolved: ResolvedCommonOptions,\n extras: TextExtras = {},\n): TextOptions {\n const out: TextOptions = { url, ...buildCommonOptions(flags, resolved) };\n if (extras.textFormat) out.text_format = extras.textFormat;\n if (extras.returnLinks) out.return_links = true;\n return out;\n}\n\nexport function buildSelectedOptions(\n url: string,\n selector: string | undefined,\n flags: CommonRawFlags,\n resolved: ResolvedCommonOptions,\n): SelectedOptions {\n // `selector` is optional per the OpenAPI spec: omitting it returns the\n // whole-page HTML. Only attach it when provided. (The installed SDK types\n // still mark it required, so build the object as a partial and cast.)\n const out: Partial<SelectedOptions> = { url, ...buildCommonOptions(flags, resolved) };\n if (selector !== undefined) out.selector = selector;\n if (flags.format) out.format = flags.format;\n return out as SelectedOptions;\n}\n\nexport function buildSelectedMultipleOptions(\n url: string,\n selectors: readonly string[],\n flags: CommonRawFlags,\n resolved: ResolvedCommonOptions,\n): SelectedMultipleOptions {\n // `selectors` is optional per the OpenAPI spec: omitting it returns the\n // whole-page HTML. Only attach it when non-empty. (The installed SDK types\n // still mark it required, so build the object as a partial and cast.)\n const out: Partial<SelectedMultipleOptions> = { url, ...buildCommonOptions(flags, resolved) };\n if (selectors.length > 0) out.selectors = selectors;\n return out as SelectedMultipleOptions;\n}\n\nexport function buildQuestionOptions(\n url: string,\n question: string,\n flags: CommonRawFlags,\n resolved: ResolvedCommonOptions,\n): QuestionOptions {\n const out: QuestionOptions = { url, question, ...buildCommonOptions(flags, resolved) };\n if (flags.format) out.format = flags.format;\n return out;\n}\n\nexport function buildFieldsOptions(\n url: string,\n fields: Record<string, string>,\n flags: CommonRawFlags,\n resolved: ResolvedCommonOptions,\n): FieldsOptions {\n return { url, fields, ...buildCommonOptions(flags, resolved) };\n}\n","/**\n * Shared commander options and the parsers/expanders behind them.\n *\n * All scrape endpoints share the same nine \"common\" flags. We define them in\n * one place and apply via `addCommonScrapeOptions(cmd)` so the surface stays\n * consistent and any change lands everywhere at once.\n */\n\nimport { promises as fs } from 'node:fs';\nimport { Option } from 'commander';\nimport type { Command } from 'commander';\n\nexport interface CommonRawFlags {\n apiKey?: string;\n output?: string;\n format?: 'text' | 'json';\n pretty?: boolean;\n js?: boolean;\n proxy?: 'datacenter' | 'residential' | 'stealth';\n country?: string;\n device?: 'desktop' | 'mobile' | 'tablet';\n timeout?: number;\n jsTimeout?: number;\n waitFor?: string;\n headers?: string;\n jsScript?: string;\n customProxy?: string;\n errorOn404?: boolean;\n errorOnRedirect?: boolean;\n}\n\n/**\n * Add the flags every scrape-endpoint command needs. Adding here keeps the\n * surface aligned across `html`, `text`, `selected`, etc.\n */\nexport function addCommonScrapeOptions(cmd: Command): Command {\n return cmd\n .option('-k, --api-key <key>', 'API key (overrides $WEBSCRAPING_AI_API_KEY and config file)')\n .option('-o, --output <file>', 'write result to FILE instead of stdout')\n .option('--pretty', 'pretty-print JSON output (default on TTY)')\n .addOption(\n new Option('--no-pretty', 'force single-line JSON output').conflicts('pretty'),\n )\n .option('--js', 'execute JS via a headless browser (default: enabled)')\n .option('--no-js', 'disable JS execution (faster, cheaper)')\n .addOption(\n new Option('--proxy <type>', 'proxy pool').choices([\n 'datacenter',\n 'residential',\n 'stealth',\n ]),\n )\n .option('--country <code>', 'two-letter proxy country code, e.g. us, gb, de')\n .addOption(\n new Option('--device <type>', 'device emulation').choices(['desktop', 'mobile', 'tablet']),\n )\n .option('--timeout <ms>', 'page-retrieval timeout in ms (max 30000)', integerParser)\n .option('--js-timeout <ms>', 'JS rendering time in ms after load (max 20000)', integerParser)\n .option('--wait-for <selector>', 'CSS selector to wait for before returning content')\n .option(\n '--headers <json>',\n 'HTTP headers to forward to the target page; JSON object or @file.json',\n )\n .option('--js-script <code>', 'custom JS to evaluate on the target page')\n .option('--custom-proxy <url>', 'custom proxy URL: http://user:pass@host:port')\n .option('--error-on-404', 'return error on 404')\n .option('--error-on-redirect', 'return error on redirect');\n}\n\nexport function integerParser(value: string): number {\n const n = Number.parseInt(value, 10);\n if (!Number.isFinite(n)) throw new Error(`expected integer, got: ${value}`);\n return n;\n}\n\n/**\n * Parse a `--headers` argument. Accepts a JSON object literal or `@path` to\n * load JSON from a file. Returns `undefined` if the input is empty.\n */\nexport async function parseHeaders(input: string | undefined): Promise<Record<string, string> | undefined> {\n if (!input || input.trim() === '') return undefined;\n const text = input.startsWith('@') ? await fs.readFile(input.slice(1), 'utf8') : input;\n return parseJsonObject(text, 'headers');\n}\n\n/**\n * Parse a `--fields` argument for `extract`. Accepts JSON object literal or\n * `@path` to a file.\n */\nexport async function parseFields(input: string): Promise<Record<string, string>> {\n const text = input.startsWith('@') ? await fs.readFile(input.slice(1), 'utf8') : input;\n const parsed = parseJsonObject(text, 'fields');\n if (!parsed || Object.keys(parsed).length === 0) {\n throw new Error('fields must be a non-empty JSON object of { fieldName: description }');\n }\n return parsed;\n}\n\nfunction parseJsonObject(text: string, label: string): Record<string, string> {\n let parsed: unknown;\n try {\n parsed = JSON.parse(text);\n } catch (err) {\n throw new Error(`${label}: invalid JSON — ${(err as Error).message}`);\n }\n if (!parsed || typeof parsed !== 'object' || Array.isArray(parsed)) {\n throw new Error(`${label} must be a JSON object`);\n }\n const out: Record<string, string> = {};\n for (const [k, v] of Object.entries(parsed as Record<string, unknown>)) {\n if (typeof v !== 'string') throw new Error(`${label}.${k}: value must be a string`);\n out[k] = v;\n }\n return out;\n}\n","/**\n * Stdin batching helpers.\n *\n * When a URL argument is `-`, the command reads one URL per line from stdin\n * and runs the operation for each. Empty lines and lines starting with `#`\n * are skipped so users can comment URL lists.\n */\n\nimport readline from 'node:readline';\n\nexport function urlIsStdin(url: string): boolean {\n return url === '-';\n}\n\nexport async function* readUrlsFromStdin(): AsyncIterable<string> {\n const rl = readline.createInterface({ input: process.stdin, crlfDelay: Infinity });\n for await (const raw of rl) {\n const line = raw.trim();\n if (line === '' || line.startsWith('#')) continue;\n yield line;\n }\n}\n\n/**\n * Iterate URLs from either a single CLI arg or from stdin (if the arg is `-`).\n */\nexport async function* urlIterator(arg: string): AsyncIterable<string> {\n if (urlIsStdin(arg)) {\n yield* readUrlsFromStdin();\n return;\n }\n yield arg;\n}\n","import { Command } from 'commander';\n\nimport {\n clearPersistedConfig,\n configPath,\n maskApiKey,\n readPersistedConfig,\n writePersistedConfig,\n} from '../lib/config.js';\n\nexport function authCommand(): Command {\n const cmd = new Command('auth').description('Manage your stored API key');\n\n cmd\n .command('set <key>')\n .description(`Save an API key to ${configPath()}`)\n .action(async (key: string) => {\n if (!key || key.trim() === '') throw new Error('API key is empty');\n await writePersistedConfig({ apiKey: key.trim() });\n process.stdout.write(`API key saved to ${configPath()}\\n`);\n });\n\n cmd\n .command('show')\n .description('Display the currently stored API key (masked)')\n .action(async () => {\n const persisted = await readPersistedConfig();\n const env = process.env.WEBSCRAPING_AI_API_KEY;\n if (env && env.trim() !== '') {\n process.stdout.write(`Active key (env WEBSCRAPING_AI_API_KEY): ${maskApiKey(env.trim())}\\n`);\n }\n if (persisted.apiKey && persisted.apiKey.trim() !== '') {\n process.stdout.write(\n `Stored key (${configPath()}): ${maskApiKey(persisted.apiKey.trim())}\\n`,\n );\n }\n if (!env && !persisted.apiKey) {\n process.stdout.write('No API key configured. Run `webscraping-ai auth set <key>`.\\n');\n }\n });\n\n cmd\n .command('clear')\n .description('Remove the stored API key file')\n .action(async () => {\n const removed = await clearPersistedConfig();\n process.stdout.write(removed ? `Removed ${configPath()}\\n` : 'No stored API key to remove.\\n');\n });\n\n return cmd;\n}\n","import { Command } from 'commander';\n\nimport { buildFieldsOptions } from '../lib/buildRequest.js';\nimport { resolveApiKey } from '../lib/config.js';\nimport { addCommonScrapeOptions, parseFields, parseHeaders } from '../lib/options.js';\nimport type { CommonRawFlags } from '../lib/options.js';\nimport { emit } from '../lib/output.js';\nimport { createClient } from '../lib/sdk.js';\nimport { urlIterator } from '../lib/stdin.js';\n\ninterface ExtractFlags extends CommonRawFlags {\n fields?: string;\n}\n\nexport function extractCommand(): Command {\n const cmd = new Command('extract')\n .description('Extract structured fields from a page using AI (wraps /ai/fields)')\n .argument('<url>', 'page URL (or `-` to read URLs from stdin)')\n .requiredOption(\n '-f, --fields <json>',\n 'fields to extract — JSON object {name: description} or @file.json',\n );\n\n addCommonScrapeOptions(cmd);\n\n cmd.action(async (url: string, opts: ExtractFlags) => {\n if (!opts.fields) throw new Error('--fields is required');\n const apiKey = await resolveApiKey({ flag: opts.apiKey });\n const headers = await parseHeaders(opts.headers);\n const fields = await parseFields(opts.fields);\n const client = createClient({ apiKey, requestTimeoutMs: opts.timeout });\n\n for await (const target of urlIterator(url)) {\n const sdkOptions = buildFieldsOptions(target, fields, opts, { headers });\n const result = await client.fields(sdkOptions);\n await emit(result, opts);\n }\n });\n\n return cmd;\n}\n","import { Command, Option } from 'commander';\n\nimport { buildHtmlOptions } from '../lib/buildRequest.js';\nimport { resolveApiKey } from '../lib/config.js';\nimport { addCommonScrapeOptions, parseHeaders } from '../lib/options.js';\nimport type { CommonRawFlags } from '../lib/options.js';\nimport { emit } from '../lib/output.js';\nimport { createClient } from '../lib/sdk.js';\nimport { urlIterator } from '../lib/stdin.js';\n\ninterface HtmlFlags extends CommonRawFlags {\n returnScriptResult?: boolean;\n}\n\nexport function htmlCommand(): Command {\n const cmd = new Command('html')\n .description('Fetch the full HTML of a page')\n .argument('<url>', 'page URL (or `-` to read URLs from stdin)')\n .addOption(new Option('--return-script-result', 'return the result of --js-script instead of HTML'))\n .addOption(new Option('--format <fmt>', 'response wrapping').choices(['text', 'json']));\n\n addCommonScrapeOptions(cmd);\n\n cmd.action(async (url: string, opts: HtmlFlags) => {\n const apiKey = await resolveApiKey({ flag: opts.apiKey });\n const headers = await parseHeaders(opts.headers);\n const client = createClient({ apiKey, requestTimeoutMs: opts.timeout });\n\n for await (const target of urlIterator(url)) {\n const sdkOptions = buildHtmlOptions(target, opts, { headers }, {\n returnScriptResult: opts.returnScriptResult,\n });\n const result = await client.html(sdkOptions);\n await emit(result, opts);\n }\n });\n\n return cmd;\n}\n","import { Command, Option } from 'commander';\n\nimport { buildSelectedOptions } from '../lib/buildRequest.js';\nimport { resolveApiKey } from '../lib/config.js';\nimport { addCommonScrapeOptions, parseHeaders } from '../lib/options.js';\nimport type { CommonRawFlags } from '../lib/options.js';\nimport { emit } from '../lib/output.js';\nimport { createClient } from '../lib/sdk.js';\nimport { urlIterator } from '../lib/stdin.js';\n\ninterface SelectedFlags extends CommonRawFlags {\n selector?: string;\n}\n\nexport function selectedCommand(): Command {\n const cmd = new Command('selected')\n .description('Fetch HTML of one CSS-selected page area')\n .argument('<url>', 'page URL (or `-` to read URLs from stdin)')\n .option(\n '-s, --selector <css>',\n 'CSS selector for the target element(s); omit for whole-page HTML',\n )\n .addOption(new Option('--format <fmt>', 'response wrapping').choices(['text', 'json']));\n\n addCommonScrapeOptions(cmd);\n\n cmd.action(async (url: string, opts: SelectedFlags) => {\n const apiKey = await resolveApiKey({ flag: opts.apiKey });\n const headers = await parseHeaders(opts.headers);\n const client = createClient({ apiKey, requestTimeoutMs: opts.timeout });\n\n for await (const target of urlIterator(url)) {\n const sdkOptions = buildSelectedOptions(target, opts.selector, opts, { headers });\n const result = await client.selected(sdkOptions);\n await emit(result, opts);\n }\n });\n\n return cmd;\n}\n","import { Command } from 'commander';\n\nimport { buildSelectedMultipleOptions } from '../lib/buildRequest.js';\nimport { resolveApiKey } from '../lib/config.js';\nimport { addCommonScrapeOptions, parseHeaders } from '../lib/options.js';\nimport type { CommonRawFlags } from '../lib/options.js';\nimport { emit } from '../lib/output.js';\nimport { createClient } from '../lib/sdk.js';\nimport { urlIterator } from '../lib/stdin.js';\n\ninterface SelectedMultipleFlags extends CommonRawFlags {\n selector?: string[];\n}\n\nexport function selectedMultipleCommand(): Command {\n const cmd = new Command('selected-multiple')\n .description('Fetch HTML of multiple CSS-selected page areas')\n .argument('<url>', 'page URL (or `-` to read URLs from stdin)')\n .option(\n '-s, --selector <css>',\n 'CSS selector — pass multiple times for multiple selectors',\n collectMulti,\n [] as string[],\n );\n\n addCommonScrapeOptions(cmd);\n\n cmd.action(async (url: string, opts: SelectedMultipleFlags) => {\n // `--selector` is optional: omitting it returns whole-page HTML.\n const selectors = opts.selector ?? [];\n const apiKey = await resolveApiKey({ flag: opts.apiKey });\n const headers = await parseHeaders(opts.headers);\n const client = createClient({ apiKey, requestTimeoutMs: opts.timeout });\n\n for await (const target of urlIterator(url)) {\n const sdkOptions = buildSelectedMultipleOptions(target, selectors, opts, { headers });\n const result = await client.selectedMultiple(sdkOptions);\n await emit(result, opts);\n }\n });\n\n return cmd;\n}\n\nfunction collectMulti(value: string, previous: string[]): string[] {\n return [...previous, value];\n}\n","import { Command } from 'commander';\n\nimport { EDITORS, editorById, installSkill, resolveTarget } from '../lib/skill.js';\nimport type { EditorAdapter } from '../lib/skill.js';\n\ninterface SetupSkillFlags {\n editor?: string[];\n all?: boolean;\n force?: boolean;\n}\n\nexport function setupCommand(): Command {\n const cmd = new Command('setup').description('Install the AI agent skill into your editor(s)');\n\n cmd\n .command('skill')\n .description('Install the WebScraping.AI skill into one or more AI coding editors')\n .option(\n '-e, --editor <id>',\n `editor id (repeatable). Supported: ${EDITORS.map((e) => e.id).join(', ')}`,\n collect,\n [] as string[],\n )\n .option('-a, --all', 'install into every supported editor')\n .option('-f, --force', 'overwrite existing skill files')\n .action(async (opts: SetupSkillFlags) => {\n const selected = resolveSelection(opts);\n if (selected.length === 0) {\n throw new Error(\n 'no editor selected — pass --all or --editor <id> (e.g. --editor claude-code)',\n );\n }\n\n for (const editor of selected) {\n const target = resolveTarget(editor);\n const result = await installSkill(target, { force: opts.force });\n process.stdout.write(`${editor.label}: ${result.status} → ${target.destination}\\n`);\n }\n });\n\n cmd\n .command('list', { hidden: false })\n .description('List supported editors for `setup skill`')\n .action(() => {\n for (const e of EDITORS) {\n const target = resolveTarget(e);\n process.stdout.write(`${e.id.padEnd(14)} ${e.label.padEnd(18)} ${target.destination}\\n`);\n }\n });\n\n return cmd;\n}\n\nfunction collect(value: string, previous: string[]): string[] {\n return [...previous, value];\n}\n\nfunction resolveSelection(opts: SetupSkillFlags): EditorAdapter[] {\n if (opts.all) return [...EDITORS];\n const ids = opts.editor ?? [];\n return ids.map((id) => {\n const editor = editorById(id);\n if (!editor) throw new Error(`unknown editor: ${id}`);\n return editor;\n });\n}\n","/**\n * Multi-editor skill installer.\n *\n * The canonical skill lives in this package at\n * `plugins/webscraping-ai/skills/webscraping-ai/SKILL.md`. Each supported\n * editor reads skill content from a different location and may want either\n * a single Markdown file or a whole skill directory. The `editors` registry\n * captures those differences so `setup skill` is a small driver over it.\n *\n * `mode: 'dir'` copies the entire skill directory (Claude Code-shaped\n * editors that scan a folder per skill). `mode: 'file'` writes the SKILL.md\n * contents to a single file (editors that read flat instructions, like\n * GitHub Copilot's `.github/copilot-instructions.md`).\n *\n * `kind: 'user'` writes to `$HOME`; `kind: 'project'` writes to the current\n * working directory. We prefer user-scope where the editor supports it so\n * the skill becomes globally available without polluting individual repos.\n */\n\nimport { promises as fs } from 'node:fs';\nimport { homedir } from 'node:os';\nimport { dirname, join, resolve } from 'node:path';\nimport { fileURLToPath } from 'node:url';\n\nexport type EditorMode = 'dir' | 'file';\nexport type EditorKind = 'user' | 'project';\n\nexport interface EditorAdapter {\n /** Stable identifier used by `--editor <name>`. */\n id: string;\n /** Human-readable label printed in summaries. */\n label: string;\n mode: EditorMode;\n kind: EditorKind;\n /**\n * Where the skill ends up. For `mode: 'dir'`, the skill directory copies\n * to `{root}/{path}`. For `mode: 'file'`, the SKILL.md contents write to\n * `{root}/{path}` (i.e. `path` ends in a filename).\n *\n * `root` is `$HOME` (user) or CWD (project).\n */\n path: string;\n}\n\n/**\n * Registry of supported editors. Order matters: it's also the order the\n * `setup skill` summary lists them in.\n */\nexport const EDITORS: readonly EditorAdapter[] = [\n // Claude Code: user-scope skills live under ~/.claude/skills/<name>/\n { id: 'claude-code', label: 'Claude Code', mode: 'dir', kind: 'user', path: '.claude/skills/webscraping-ai' },\n // Cursor: project-scope rule file. Cursor doesn't have a user-wide skill\n // dir, so this is the editor's \"install\" surface.\n { id: 'cursor', label: 'Cursor', mode: 'file', kind: 'project', path: '.cursor/rules/webscraping-ai.mdc' },\n // Windsurf: project-scope rules. Same shape as Cursor.\n { id: 'windsurf', label: 'Windsurf', mode: 'file', kind: 'project', path: '.windsurf/rules/webscraping-ai.md' },\n // Kiro: user-scope steering directory.\n { id: 'kiro', label: 'Kiro', mode: 'dir', kind: 'user', path: '.kiro/steering/webscraping-ai' },\n // OpenCode: project-scope skills (mirrors Claude shape).\n { id: 'opencode', label: 'OpenCode', mode: 'dir', kind: 'project', path: '.opencode/skills/webscraping-ai' },\n // Gemini CLI: user-scope extension dir with a GEMINI.md.\n { id: 'gemini', label: 'Gemini CLI', mode: 'file', kind: 'user', path: '.gemini/extensions/webscraping-ai/GEMINI.md' },\n // GitHub Copilot: project-scope custom instructions file. Single file.\n { id: 'copilot', label: 'GitHub Copilot', mode: 'file', kind: 'project', path: '.github/copilot-instructions.md' },\n // Augment: project-scope rules file.\n { id: 'augment', label: 'Augment', mode: 'file', kind: 'project', path: '.augment/rules/webscraping-ai.md' },\n // Factory: project-scope rules file.\n { id: 'factory', label: 'Factory', mode: 'file', kind: 'project', path: '.factory/rules/webscraping-ai.md' },\n] as const;\n\nexport function editorById(id: string): EditorAdapter | undefined {\n return EDITORS.find((e) => e.id === id);\n}\n\n/**\n * Where this package keeps the canonical skill. Resolved relative to the\n * compiled `dist/cli.js` so it works after `npm install -g`.\n */\nexport function bundledSkillDir(): string {\n const here = dirname(fileURLToPath(import.meta.url));\n // dist/ sits at the package root, so the plugins dir is two levels up\n // from any compiled file: dist/cli.js → package root → plugins/...\n return resolve(here, '..', 'plugins', 'webscraping-ai', 'skills', 'webscraping-ai');\n}\n\nexport interface InstallTarget {\n editor: EditorAdapter;\n /** Absolute destination path (file or directory depending on `mode`). */\n destination: string;\n}\n\nexport interface ResolveOptions {\n cwd?: string;\n home?: string;\n}\n\nexport function resolveTarget(editor: EditorAdapter, opts: ResolveOptions = {}): InstallTarget {\n const root = editor.kind === 'user' ? (opts.home ?? homedir()) : (opts.cwd ?? process.cwd());\n return { editor, destination: join(root, editor.path) };\n}\n\nexport interface InstallOptions {\n /** If true, overwrite existing files/dirs. */\n force?: boolean;\n /** Override skill source dir (for tests). */\n sourceDir?: string;\n}\n\nexport type InstallStatus = 'installed' | 'skipped-exists' | 'overwritten';\n\nexport interface InstallResult {\n target: InstallTarget;\n status: InstallStatus;\n}\n\nexport async function installSkill(\n target: InstallTarget,\n opts: InstallOptions = {},\n): Promise<InstallResult> {\n const source = opts.sourceDir ?? bundledSkillDir();\n const exists = await pathExists(target.destination);\n\n if (exists && !opts.force) {\n return { target, status: 'skipped-exists' };\n }\n\n if (target.editor.mode === 'dir') {\n if (exists) await fs.rm(target.destination, { recursive: true, force: true });\n await copyDirectory(source, target.destination);\n } else {\n const skillBody = await fs.readFile(join(source, 'SKILL.md'), 'utf8');\n await fs.mkdir(dirname(target.destination), { recursive: true });\n await fs.writeFile(target.destination, skillBody);\n }\n\n return { target, status: exists ? 'overwritten' : 'installed' };\n}\n\nasync function pathExists(path: string): Promise<boolean> {\n try {\n await fs.access(path);\n return true;\n } catch {\n return false;\n }\n}\n\nasync function copyDirectory(source: string, destination: string): Promise<void> {\n // `fs.cp` with `recursive` exists on Node 16.7+; we target Node 20.\n await fs.cp(source, destination, { recursive: true, force: true });\n}\n","import { Command, Option } from 'commander';\n\nimport { buildTextOptions } from '../lib/buildRequest.js';\nimport { resolveApiKey } from '../lib/config.js';\nimport { addCommonScrapeOptions, parseHeaders } from '../lib/options.js';\nimport type { CommonRawFlags } from '../lib/options.js';\nimport { emit } from '../lib/output.js';\nimport { createClient } from '../lib/sdk.js';\nimport { urlIterator } from '../lib/stdin.js';\n\ninterface TextFlags extends CommonRawFlags {\n textFormat?: 'plain' | 'json' | 'xml';\n returnLinks?: boolean;\n}\n\nexport function textCommand(): Command {\n const cmd = new Command('text')\n .description('Fetch the visible text of a page')\n .argument('<url>', 'page URL (or `-` to read URLs from stdin)')\n .addOption(\n new Option('--text-format <fmt>', 'text response format').choices(['plain', 'json', 'xml']),\n )\n .option('--return-links', 'include page links (text-format=json only)');\n\n addCommonScrapeOptions(cmd);\n\n cmd.action(async (url: string, opts: TextFlags) => {\n const apiKey = await resolveApiKey({ flag: opts.apiKey });\n const headers = await parseHeaders(opts.headers);\n const client = createClient({ apiKey, requestTimeoutMs: opts.timeout });\n\n for await (const target of urlIterator(url)) {\n const sdkOptions = buildTextOptions(target, opts, { headers }, {\n textFormat: opts.textFormat,\n returnLinks: opts.returnLinks,\n });\n const result = await client.text(sdkOptions);\n await emit(result, opts);\n }\n });\n\n return cmd;\n}\n","/**\n * Exit-code mapping for SDK errors.\n *\n * The SDK throws typed subclasses of `WebScrapingAIError`. Map each to a\n * stable exit code so shell scripts can branch on the failure mode without\n * parsing stderr.\n */\n\nimport {\n APIConnectionError,\n APIError,\n APITimeoutError,\n AuthenticationError,\n BadRequestError,\n GatewayTimeoutError,\n PaymentRequiredError,\n RateLimitError,\n ServerError,\n WebScrapingAIError,\n} from 'webscraping-ai';\n\nexport const EXIT_CODES = {\n ok: 0,\n generic: 1,\n usage: 2,\n badRequest: 3,\n auth: 4,\n paymentRequired: 5,\n rateLimit: 6,\n serverError: 7,\n timeout: 8,\n connection: 9,\n} as const;\n\nexport function exitCodeFor(err: unknown): number {\n if (err instanceof BadRequestError) return EXIT_CODES.badRequest;\n if (err instanceof AuthenticationError) return EXIT_CODES.auth;\n if (err instanceof PaymentRequiredError) return EXIT_CODES.paymentRequired;\n if (err instanceof RateLimitError) return EXIT_CODES.rateLimit;\n if (err instanceof ServerError) return EXIT_CODES.serverError;\n if (err instanceof GatewayTimeoutError) return EXIT_CODES.timeout;\n if (err instanceof APITimeoutError) return EXIT_CODES.timeout;\n if (err instanceof APIConnectionError) return EXIT_CODES.connection;\n if (err instanceof APIError) return EXIT_CODES.serverError;\n if (err instanceof WebScrapingAIError) return EXIT_CODES.generic;\n return EXIT_CODES.generic;\n}\n\nexport function formatError(err: unknown): string {\n if (err instanceof APIError) {\n const parts = [err.message];\n if (err.status) parts.push(`(HTTP ${err.status})`);\n return parts.join(' ');\n }\n if (err instanceof Error) return err.message;\n return String(err);\n}\n"],"mappings":"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","names":["Command","dirname","resolve","fileURLToPath","fs","Command","Option","fs","Command","Option","Command","Command","Command","Command","Command","Option","Command","Option","Command","Option","Command","Option","Command","Command","Command","fs","homedir","dirname","join","Command","Command","Option","Command","Option","dirname","fileURLToPath","resolve","Command"]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "webscraping-ai-cli",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.2",
|
|
4
4
|
"description": "Official command-line client for the WebScraping.AI API. Includes a Claude Code skill so AI agents can use it too.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
@@ -9,6 +9,7 @@
|
|
|
9
9
|
},
|
|
10
10
|
"files": [
|
|
11
11
|
"dist",
|
|
12
|
+
".claude-plugin",
|
|
12
13
|
"plugins",
|
|
13
14
|
"README.md",
|
|
14
15
|
"CHANGELOG.md",
|
|
@@ -49,11 +50,14 @@
|
|
|
49
50
|
"bugs": {
|
|
50
51
|
"url": "https://github.com/webscraping-ai/webscraping-ai-cli/issues"
|
|
51
52
|
},
|
|
52
|
-
"homepage": "https://
|
|
53
|
+
"homepage": "https://webscraping.ai",
|
|
53
54
|
"dependencies": {
|
|
54
55
|
"commander": "^12.1.0",
|
|
55
56
|
"webscraping-ai": "^4.0.0"
|
|
56
57
|
},
|
|
58
|
+
"overrides": {
|
|
59
|
+
"esbuild": "^0.28.1"
|
|
60
|
+
},
|
|
57
61
|
"devDependencies": {
|
|
58
62
|
"@eslint/js": "^9.17.0",
|
|
59
63
|
"@types/node": "^22.10.0",
|
|
@@ -66,6 +70,6 @@
|
|
|
66
70
|
"tsup": "^8.3.5",
|
|
67
71
|
"tsx": "^4.19.2",
|
|
68
72
|
"typescript": "^5.7.2",
|
|
69
|
-
"vitest": "^2.
|
|
73
|
+
"vitest": "^3.2.6"
|
|
70
74
|
}
|
|
71
75
|
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "webscraping-ai",
|
|
3
|
+
"description": "Fetch HTML, text, selected elements, or AI-extracted data from any URL — including JS-rendered pages — via the WebScraping.AI CLI.",
|
|
4
|
+
"version": "1.0.0",
|
|
5
|
+
"author": {
|
|
6
|
+
"name": "WebScraping.AI",
|
|
7
|
+
"url": "https://webscraping.ai"
|
|
8
|
+
},
|
|
9
|
+
"homepage": "https://github.com/webscraping-ai/webscraping-ai-cli#readme",
|
|
10
|
+
"repository": "https://github.com/webscraping-ai/webscraping-ai-cli",
|
|
11
|
+
"license": "MIT",
|
|
12
|
+
"keywords": ["web-scraping", "html", "text-extraction", "ai-extraction", "scraping", "cli"]
|
|
13
|
+
}
|
|
@@ -7,6 +7,15 @@ description: Use the WebScraping.AI CLI to fetch HTML, plain text, or AI-extract
|
|
|
7
7
|
|
|
8
8
|
This skill teaches you to use the `webscraping-ai` CLI — the official command-line client for the WebScraping.AI API. It handles JS rendering, residential proxies, and per-country geolocation, and exposes two AI-powered endpoints (`ask`, `extract`) for one-shot question-answering and structured data extraction from any web page.
|
|
9
9
|
|
|
10
|
+
## Getting the CLI
|
|
11
|
+
|
|
12
|
+
This skill runs the `webscraping-ai` command. If it isn't already on your `PATH` (check with `webscraping-ai --version`), use one of:
|
|
13
|
+
|
|
14
|
+
- **Install once (recommended):** `npm install -g webscraping-ai-cli`
|
|
15
|
+
- **Zero-install per call:** prefix any command below with `npx -y webscraping-ai-cli` instead of `webscraping-ai` — e.g. `npx -y webscraping-ai-cli html https://example.com`.
|
|
16
|
+
|
|
17
|
+
Both require Node.js 20+. The `npx` form is the safe fallback when you can't install globally.
|
|
18
|
+
|
|
10
19
|
## When to use this skill
|
|
11
20
|
|
|
12
21
|
Reach for `webscraping-ai` when you need to:
|
|
@@ -1,17 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"$schema": "https://json.schemastore.org/claude-code-plugin-marketplace.json",
|
|
3
|
-
"name": "webscraping-ai",
|
|
4
|
-
"owner": {
|
|
5
|
-
"name": "WebScraping.AI",
|
|
6
|
-
"url": "https://webscraping.ai"
|
|
7
|
-
},
|
|
8
|
-
"plugins": [
|
|
9
|
-
{
|
|
10
|
-
"name": "webscraping-ai",
|
|
11
|
-
"description": "WebScraping.AI CLI skill: fetch HTML, text, selected elements, or AI-extracted data from any URL — including JS-rendered pages.",
|
|
12
|
-
"version": "1.0.0",
|
|
13
|
-
"source": ".",
|
|
14
|
-
"skills": ["./skills/webscraping-ai"]
|
|
15
|
-
}
|
|
16
|
-
]
|
|
17
|
-
}
|