polite-crawler 0.1.2 → 0.1.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (40) hide show
  1. package/README.md +533 -8
  2. package/build/cli-daemon--cTmq0Z5.mjs +38 -0
  3. package/build/cli-daemon--cTmq0Z5.mjs.map +1 -0
  4. package/build/cli.mjs +288 -20
  5. package/build/cli.mjs.map +1 -1
  6. package/build/{open-Ca1lcWR3.d.mts → daemon-DP4EqjFL.d.mts} +913 -16
  7. package/build/daemon-fZTE24P5.mjs +515 -0
  8. package/build/daemon-fZTE24P5.mjs.map +1 -0
  9. package/build/{diff-Dk2eSvi-.mjs → diff-NQcQqKe9.mjs} +2 -2
  10. package/build/{diff-Dk2eSvi-.mjs.map → diff-NQcQqKe9.mjs.map} +1 -1
  11. package/build/{extract-DcwNrTK7.mjs → extract-ByFfc8Zk.mjs} +2 -2
  12. package/build/{extract-DcwNrTK7.mjs.map → extract-ByFfc8Zk.mjs.map} +1 -1
  13. package/build/hours.d.mts +26 -0
  14. package/build/hours.mjs +48 -1
  15. package/build/hours.mjs.map +1 -1
  16. package/build/index.d.mts +491 -11
  17. package/build/index.mjs +898 -9
  18. package/build/index.mjs.map +1 -0
  19. package/build/loopback-C_QYowSf.mjs +22 -0
  20. package/build/loopback-C_QYowSf.mjs.map +1 -0
  21. package/build/mcp.d.mts +158 -2
  22. package/build/mcp.mjs +567 -18
  23. package/build/mcp.mjs.map +1 -1
  24. package/build/ollama-BJxkjny4.mjs +160 -0
  25. package/build/ollama-BJxkjny4.mjs.map +1 -0
  26. package/build/{open-DnrWgkC0.mjs → open-C4fhXJgD.mjs} +1404 -21
  27. package/build/open-C4fhXJgD.mjs.map +1 -0
  28. package/build/{runtime-k8mo1_7U.mjs → runtime-JToRO63o.mjs} +3 -3
  29. package/build/{runtime-k8mo1_7U.mjs.map → runtime-JToRO63o.mjs.map} +1 -1
  30. package/build/transformers-BQMlaGj4.mjs +133 -0
  31. package/build/transformers-BQMlaGj4.mjs.map +1 -0
  32. package/build/{duration-iIuMMofi.mjs → version-ntbY-6wh.mjs} +10 -10
  33. package/build/version-ntbY-6wh.mjs.map +1 -0
  34. package/build/{wacz-sfOLT8e9.mjs → wacz-BFNG42Kt.mjs} +3 -3
  35. package/build/{wacz-sfOLT8e9.mjs.map → wacz-BFNG42Kt.mjs.map} +1 -1
  36. package/build/{warc-VIPscWxY.mjs → warc-CVS0t1fW.mjs} +2 -2
  37. package/build/{warc-VIPscWxY.mjs.map → warc-CVS0t1fW.mjs.map} +1 -1
  38. package/package.json +10 -2
  39. package/build/duration-iIuMMofi.mjs.map +0 -1
  40. package/build/open-DnrWgkC0.mjs.map +0 -1
package/build/cli.mjs.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"cli.mjs","names":[],"sources":["../src/cli-line.ts","../src/cli.ts"],"sourcesContent":["/*\n * The command line of polite-crawler without its work: the options, the checks of the whole command\n * line, and the text of each result. `cli.ts` does the work. This file imports only Node.js and the\n * duration parser, so `--help` and `--version` answer at once. The tests use it in their own\n * process, so the checks and the texts count in the coverage of the package.\n */\nimport { readFileSync } from \"node:fs\";\nimport { homedir } from \"node:os\";\nimport { join, resolve } from \"node:path\";\nimport { parseArgs } from \"node:util\";\nimport type { VersionChoice } from \"./diff.ts\";\nimport type { DiscoverResult } from \"./discover.ts\";\nimport type { Found } from \"./docs.ts\";\nimport { durationMs } from \"./duration.ts\";\nimport type { ExtractResult } from \"./extract.ts\";\nimport type { CrawlSummary } from \"./frontier.ts\";\nimport type { PageResult, ReadResult, ReindexResult, VerifyResult } from \"./runtime.ts\";\nimport type { WaczResult } from \"./wacz.ts\";\nimport type { WarcResult } from \"./warc.ts\";\nimport type { WatchEntry, WatchEvent, WatchSummary } from \"./watch.ts\";\n\n/** A usage error is a wrong command line. It exits with code 2. */\nexport class UsageError extends Error {}\n\n/** These options belong to the command `export`. */\nexport const EXPORT_OPTIONS = {\n format: { type: \"string\" },\n out: { type: \"string\" },\n since: { type: \"string\" },\n until: { type: \"string\" },\n host: { type: \"string\" },\n prefix: { type: \"string\" },\n id: { type: \"string\", multiple: true },\n \"include-refusals\": { type: \"boolean\" },\n overwrite: { type: \"boolean\" },\n} as const;\n\n/** These are the values of the command line that the command `export` reads. */\nexport interface ExportValues {\n readonly format?: string | undefined;\n readonly out?: string | undefined;\n readonly since?: string | undefined;\n readonly until?: string | undefined;\n readonly host?: string | undefined;\n readonly prefix?: string | undefined;\n readonly id?: readonly string[] | undefined;\n readonly \"include-refusals\"?: boolean | undefined;\n readonly overwrite?: boolean | undefined;\n readonly json?: boolean | undefined;\n}\n\n/** These options belong to each command, or to the commands that `OWNER` names. */\nconst COMMAND_OPTIONS = {\n dir: { type: \"string\" },\n contact: { type: \"string\" },\n \"allow-private\": { type: \"boolean\" },\n \"no-browser\": { type: \"boolean\" },\n json: { type: \"boolean\" },\n version: { type: \"boolean\", short: \"v\" },\n help: { type: \"boolean\", short: \"h\" },\n render: { type: \"string\" },\n \"max-age\": { type: \"string\" },\n limit: { type: \"string\" },\n kind: { type: \"string\" },\n rank: { type: \"string\" },\n from: { type: \"string\" },\n length: { type: \"string\" },\n \"user-provided\": { type: \"boolean\" },\n \"allow-user-provided\": { type: \"boolean\" },\n // ── discover and crawl ──\n \"max-urls\": { type: \"string\" },\n feed: { type: \"string\", multiple: true },\n \"max-pages\": { type: \"string\" },\n \"max-depth\": { type: \"string\" },\n budget: { type: \"string\" },\n include: { type: \"string\", multiple: true },\n exclude: { type: \"string\", multiple: true },\n sitemaps: { type: \"boolean\" },\n \"allow-host\": { type: \"string\", multiple: true },\n // ── end of discover and crawl ──\n // ── time travel, diff and watch ──\n at: { type: \"string\" },\n to: { type: \"string\" },\n every: { type: \"string\" },\n confirm: { type: \"string\" },\n scope: { type: \"string\" },\n label: { type: \"string\", multiple: true },\n // ── end of time travel, diff and watch ──\n // ── archived fallback and signed requests ──\n \"archive-fallback\": { type: \"boolean\" },\n \"archive-despite-robots\": { type: \"boolean\" },\n \"sign-key\": { type: \"string\" },\n \"signature-agent\": { type: \"string\" },\n // ── end of archived fallback and signed requests ──\n // ── extract ──\n field: { type: \"string\", multiple: true },\n capture: { type: \"string\" },\n // ── end of extract ──\n} as const;\n\n/** These are the options of the command line: the options of the commands and of `export`. */\nconst OPTIONS: typeof COMMAND_OPTIONS & typeof EXPORT_OPTIONS = {\n ...COMMAND_OPTIONS,\n ...EXPORT_OPTIONS,\n};\n\n/** This is the configuration of `parseArgs` for the command line. */\ninterface ParseConfig {\n args: string[];\n allowPositionals: true;\n strict: true;\n options: typeof OPTIONS;\n}\n\n/** This is the result of `parseArgs` for the command line. */\ntype ParsedArgs = ReturnType<typeof parseArgs<ParseConfig>>;\n\n/** These options belong to one command, or to the commands that the text names. */\nexport const OWNER: Readonly<Record<string, string>> = {\n render: \"fetch crawl watch\",\n \"max-age\": \"fetch crawl discover\",\n limit: \"find\",\n kind: \"find\",\n rank: \"find\",\n from: \"read diff\",\n length: \"read\",\n \"user-provided\": \"read extract\",\n \"allow-user-provided\": \"mcp\",\n ...Object.fromEntries(Object.keys(EXPORT_OPTIONS).map((option) => [option, \"export\"])),\n // ── discover and crawl ──\n \"max-urls\": \"discover\",\n feed: \"discover\",\n \"max-pages\": \"crawl watch\",\n \"max-depth\": \"crawl\",\n budget: \"crawl watch\",\n include: \"crawl\",\n exclude: \"crawl\",\n sitemaps: \"crawl\",\n \"allow-host\": \"crawl discover\",\n // ── end of discover and crawl ──\n // ── time travel, diff and watch ──\n at: \"read verify extract\",\n to: \"diff\",\n every: \"watch\",\n confirm: \"watch\",\n scope: \"watch\",\n label: \"watch\",\n // ── end of time travel, diff and watch ──\n // ── archived fallback and signed requests ──\n \"archive-fallback\": \"fetch\",\n \"archive-despite-robots\": \"fetch\",\n \"sign-key\": \"fetch discover crawl watch mcp key-directory\",\n \"signature-agent\": \"fetch discover crawl watch mcp key-directory\",\n // ── end of archived fallback and signed requests ──\n // ── extract ──\n field: \"extract\",\n capture: \"extract\",\n // ── end of extract ──\n};\n\n/** These are the commands, with the smallest and the largest count of arguments. */\nexport const COMMANDS: Readonly<\n Record<string, { readonly min: number; readonly max: number; readonly wants: string }>\n> = {\n fetch: { min: 1, max: Infinity, wants: \"fetch wants at least one URL\" },\n find: { min: 1, max: Infinity, wants: \"find wants a query\" },\n read: { min: 1, max: 1, wants: \"read wants one document id\" },\n verify: { min: 2, max: Infinity, wants: \"verify wants a document id and a quote\" },\n reindex: { min: 0, max: 0, wants: \"reindex takes no argument\" },\n mcp: { min: 0, max: 0, wants: \"mcp takes no argument\" },\n export: { min: 0, max: 0, wants: \"export takes no argument: give the file with --out\" },\n // ── discover and crawl ──\n discover: { min: 1, max: 1, wants: \"discover wants one site URL\" },\n crawl: { min: 1, max: Infinity, wants: \"crawl wants at least one URL\" },\n // ── end of discover and crawl ──\n // ── diff and watch ──\n diff: { min: 1, max: 1, wants: \"diff wants one document id\" },\n watch: { min: 1, max: Infinity, wants: \"watch wants add, remove, list or run\" },\n // ── end of diff and watch ──\n // ── signed requests ──\n \"key-directory\": {\n min: 0,\n max: 0,\n wants: \"key-directory takes no argument: give the key with --sign-key\",\n },\n // ── end of signed requests ──\n // ── extract ──\n extract: { min: 1, max: 1, wants: \"extract wants one document id and --field name=value\" },\n // ── end of extract ──\n};\n\nconst parse = (args: readonly string[]): ParsedArgs => {\n try {\n return parseArgs<ParseConfig>({\n args: [...args],\n allowPositionals: true,\n strict: true,\n options: OPTIONS,\n });\n } catch (error) {\n // parseArgs gives a TypeError with an ERR_PARSE_ARGS code for a wrong command line\n const code = (error as { code?: unknown }).code;\n if (typeof code === \"string\" && code.startsWith(\"ERR_PARSE_ARGS\")) {\n throw new UsageError((error as Error).message, { cause: error });\n }\n throw error;\n }\n};\n\nexport const whole = (\n name: string,\n text: string | undefined,\n fallback: number,\n min: number,\n): number => {\n if (text === undefined) return fallback;\n if (!/^\\s*\\d+\\s*$/.test(text))\n throw new UsageError(`--${name} wants a whole number, not \"${text}\"`);\n const value = Number(text);\n if (value < min) throw new UsageError(`--${name} wants ${String(min)} or more`);\n return value;\n};\n\n/** An environment variable that switches a feature on is 1 or 0 (also true or false, yes or no). */\nexport const envFlag = (\n env: Readonly<Record<string, string | undefined>>,\n name: string,\n): boolean => {\n const raw = env[name];\n const value = raw?.trim().toLowerCase() ?? \"\";\n if ([\"\", \"0\", \"false\", \"no\", \"off\"].includes(value)) return false;\n if ([\"1\", \"true\", \"yes\", \"on\"].includes(value)) return true;\n throw new UsageError(`${name} is 1 or 0, not \"${raw ?? \"\"}\"`);\n};\n\n/** The store: a leading `~` is the home folder, and a relative path starts at the working folder. */\nexport const storeDir = (text: string | undefined): string => {\n if (text === undefined) return join(homedir(), \".polite-crawler\");\n if (text.trim() === \"\") throw new UsageError(\"--dir wants a folder\");\n if (text === \"~\") return homedir();\n if (/^~[\\\\/]/.test(text)) return resolve(homedir(), text.slice(2));\n return resolve(text);\n};\n\nexport const FS_CODES: ReadonlySet<string> = new Set([\n \"EEXIST\",\n \"ENOENT\",\n \"ENOTDIR\",\n \"EACCES\",\n \"EPERM\",\n \"EROFS\",\n \"EISDIR\",\n]);\n\nexport const ok = (page: PageResult): boolean =>\n page.outcome === \"ok\" || page.outcome === \"not-modified\";\n\n/** A fetch is done when the page came, or when an archived copy came in its place. */\nexport const done = (page: PageResult): boolean => ok(page) || page.archived !== undefined;\n\nexport const describePage = (page: PageResult): string => {\n const head = `${ok(page) ? \"✓\" : page.archived !== undefined ? \"~\" : \"✗\"} ${page.url}`;\n const facts = [\n `${page.outcome} ${String(page.status)}`,\n page.doc === null ? null : `doc ${page.doc}`,\n page.chars > 0 ? `${String(page.chars)} chars` : null,\n page.rendered ? \"rendered\" : null,\n page.structured.length > 0 ? `JSON-LD: ${page.structured.join(\", \")}` : null,\n ].filter((fact) => fact !== null);\n // ── archived fallback: an archived copy says so on a line of its own ──\n const archived =\n page.archived === undefined\n ? []\n : [\n ` archived copy (${page.archived.source}, captured ${page.archived.timestamp}): ${page.archived.url}`,\n ];\n return [\n head,\n ` ${facts.join(\" · \")}`,\n ...archived,\n ...(page.note === null ? [] : [` ${page.note}`]),\n ].join(\"\\n\");\n};\n\n/** This function reads the key file of `--sign-key`. An error names the file, and not the key. */\nexport const keyFile = (path: string): string => {\n const file =\n path === \"~\"\n ? homedir()\n : /^~[\\\\/]/.test(path)\n ? resolve(homedir(), path.slice(2))\n : resolve(path);\n try {\n return readFileSync(file, \"utf8\");\n } catch (error) {\n const code = (error as { code?: unknown }).code;\n throw new UsageError(\n `--sign-key: the file ${file} does not open (${typeof code === \"string\" ? code : \"error\"})`,\n { cause: error },\n );\n }\n};\n\n/**\n * This function gives the type of the URL of `--signature-agent`. An origin names a key directory at\n * the well-known path. A URL with a path names a JSON Web Key Set (`jwks_uri`), for a host that cannot\n * serve the well-known path, for example a project site of GitHub Pages.\n */\nexport const agentTypeOf = (url: string): \"directory\" | \"jwks_uri\" => {\n try {\n const parsed = new URL(url);\n return parsed.pathname === \"/\" && parsed.search === \"\" ? \"directory\" : \"jwks_uri\";\n } catch {\n // the signer gives the error of a URL that does not parse\n return \"directory\";\n }\n};\n\n/** This is the result of a URL that the crawler did not fetch. */\nexport const failedPage = (url: string, note: string): PageResult => ({\n url,\n doc: null,\n outcome: \"error\",\n status: 0,\n title: null,\n chars: 0,\n rendered: false,\n structured: [],\n links: 0,\n captures: [],\n capture: null,\n capture_count: 0,\n fetched_at: null,\n from_cache: false,\n held_back: false,\n note,\n});\n\n/** The options of `discover` and `crawl`, read from the command line before the store opens. */\nexport interface CrawlLimits {\n readonly maxUrls: number;\n readonly feeds: readonly string[];\n readonly maxPages: number;\n readonly maxDepth: number;\n readonly budgetMs: number;\n readonly include: readonly string[];\n readonly exclude: readonly string[];\n readonly sitemaps: boolean;\n readonly allowHosts: readonly string[] | undefined;\n}\n\nexport const crawlOptionsOf = (values: Values): CrawlLimits => {\n let budgetMs = 600_000;\n if (values.budget !== undefined) {\n try {\n budgetMs = durationMs(values.budget);\n } catch (error) {\n throw new UsageError(`--budget: ${(error as Error).message}`, { cause: error });\n }\n }\n return {\n maxUrls: whole(\"max-urls\", values[\"max-urls\"], 1000, 1),\n feeds: values.feed ?? [],\n maxPages: whole(\"max-pages\", values[\"max-pages\"], 100, 1),\n maxDepth: whole(\"max-depth\", values[\"max-depth\"], 3, 0),\n budgetMs,\n include: values.include ?? [],\n exclude: values.exclude ?? [],\n sitemaps: values.sitemaps === true,\n allowHosts: values[\"allow-host\"],\n };\n};\n\nexport const describeSummary = (summary: CrawlSummary): string =>\n `${String(summary.pages)} pages: ${String(summary.fetched)} fetched (${String(summary.from_cache)} from the store), ${String(summary.refused)} refused, ${String(summary.failed)} failed. ${String(summary.skipped)} links not followed, ${String(summary.queued)} URLs waiting. Stopped (${summary.stopped}): ${summary.reason}.`;\n\n/** A text that starts with a date is a time. A different text is a capture id. */\nexport const DATE_START: RegExp = /^\\d{4}-\\d{2}-\\d{2}/;\n\n/** This function reads a time of the command line. A text that is not a time is a usage error. */\nexport const timeOf = (name: string, text: string | undefined): string | undefined => {\n if (text === undefined) return undefined;\n if (!DATE_START.test(text) || !Number.isFinite(Date.parse(text)))\n throw new UsageError(`--${name} wants a time, such as 2026-10-01T12:00:00Z, not \"${text}\"`);\n return text;\n};\n\n/** This function reads a version of `diff`: a time, or a capture id. */\nexport const versionOf = (name: string, text: string | undefined): VersionChoice | undefined => {\n if (text === undefined) return undefined;\n if (text.trim() === \"\") throw new UsageError(`--${name} wants a capture id or a time`);\n return DATE_START.test(text) ? { at: timeOf(name, text)! } : text;\n};\n\n/** These are the options of `watch`, read from the command line before the store opens. */\nexport interface WatchLine {\n readonly action: \"add\" | \"remove\" | \"list\" | \"run\";\n readonly urls: readonly string[];\n readonly every: string | undefined;\n readonly confirm: 1 | 2 | undefined;\n readonly labels: Readonly<Record<string, string>> | undefined;\n}\n\nexport const ACTIONS: Readonly<Record<string, readonly [number, number]>> = {\n add: [1, Infinity],\n remove: [1, Infinity],\n list: [0, 0],\n run: [0, 0],\n};\n\n/** These options belong to one action of `watch`. */\nexport const WATCH_OWNER: Readonly<Record<string, string>> = {\n every: \"add\",\n confirm: \"add\",\n scope: \"add\",\n label: \"add\",\n render: \"add\",\n budget: \"run\",\n \"max-pages\": \"run\",\n \"sign-key\": \"run\",\n \"signature-agent\": \"run\",\n};\n\nexport const watchLineOf = (values: Values, rest: readonly string[]): WatchLine => {\n const [action, ...urls] = rest;\n const counts = action === undefined ? undefined : ACTIONS[action];\n if (counts === undefined)\n throw new UsageError(`watch wants add, remove, list or run, not \"${action ?? \"\"}\"`);\n if (urls.length < counts[0] || urls.length > counts[1]) {\n throw new UsageError(\n counts[0] > 0 ? `watch ${action!} wants at least one URL` : `watch ${action!} takes no URL`,\n );\n }\n for (const [option, owner] of Object.entries(WATCH_OWNER)) {\n if (values[option as keyof typeof values] !== undefined && owner !== action)\n throw new UsageError(`--${option} is an option of watch ${owner}`);\n }\n if (values.every !== undefined) {\n let ms: number;\n try {\n ms = durationMs(values.every);\n } catch (error) {\n throw new UsageError(`--every: ${(error as Error).message}`, { cause: error });\n }\n if (ms < 3_600_000)\n throw new UsageError(\"--every wants 1h or more, so a watch cannot hammer a site\");\n }\n if (values.confirm !== undefined && values.confirm !== \"1\" && values.confirm !== \"2\")\n throw new UsageError(`--confirm is 1 or 2, not \"${values.confirm}\"`);\n let labels: Record<string, string> | undefined;\n for (const label of values.label ?? []) {\n const at = label.indexOf(\"=\");\n if (at < 1) throw new UsageError(`--label wants name=value, not \"${label}\"`);\n labels = { ...labels, [label.slice(0, at)]: label.slice(at + 1) };\n }\n for (const url of urls) {\n if (!/^https?:\\/\\//i.test(url) || !URL.canParse(url))\n throw new UsageError(`watch ${action!} wants http or https URLs, not \"${url}\"`);\n }\n return {\n action: action as WatchLine[\"action\"],\n urls: [...new Set(urls)],\n every: values.every,\n confirm: values.confirm === undefined ? undefined : values.confirm === \"1\" ? 1 : 2,\n labels,\n };\n};\n\nexport const everyText = (ms: number): string =>\n ms % 86_400_000 === 0\n ? `${String(ms / 86_400_000)}d`\n : ms % 3_600_000 === 0\n ? `${String(ms / 3_600_000)}h`\n : `${String(Math.round(ms / 60_000))}m`;\n\nexport const describeEntry = (entry: WatchEntry): string => {\n const facts = [\n `every ${everyText(entry.every_ms)}`,\n `next ${entry.next_at}`,\n entry.checked_at === null\n ? \"not checked yet\"\n : `last ${entry.last_kind ?? \"check\"} at ${entry.checked_at}`,\n `${String(entry.checks)} checks, ${String(entry.changes)} changes`,\n ];\n return [\n entry.url,\n ` ${facts.join(\" · \")}`,\n ...(entry.pending === null\n ? []\n : [` suspected: ${entry.pending.kind} since ${entry.pending.since}`]),\n ].join(\"\\n\");\n};\n\nexport const describeEvent = (event: WatchEvent): string => {\n const state = event.suspected ? \" (suspected)\" : event.confirmed ? \" (confirmed)\" : \"\";\n const facts = [\n `${event.outcome} ${String(event.status)}`,\n event.capture === null ? null : `capture ${event.capture}`,\n event.previous === null || event.previous === event.capture ? null : `from ${event.previous}`,\n event.diff === null\n ? null\n : `+${String(event.diff.lines_added)} −${String(event.diff.lines_removed)} lines, ${String(event.diff.meta_changes)} metadata changes`,\n event.held_back ? \"held back\" : null,\n ].filter((fact) => fact !== null);\n return [\n `${event.kind}${state} ${event.url}`,\n ` ${facts.join(\" · \")}`,\n ...(event.note === null ? [] : [` ${event.note}`]),\n ].join(\"\\n\");\n};\n\nexport const describeWatchSummary = (summary: WatchSummary): string =>\n `${String(summary.checked)} checked: ${String(summary.new)} new, ${String(summary.changed)} changed, ${String(summary.unchanged)} unchanged, ${String(summary.gone)} gone, ${String(summary.refused)} refused, ${String(summary.error)} errors. ${String(summary.suspected)} suspected, ${String(summary.confirmed)} confirmed, ${String(summary.due)} still due. Stopped (${summary.stopped}): ${summary.reason}.`;\n\n/** These are the kinds of a field (`FIELD_KINDS` of `extract.ts`). The command line knows them before it loads the crawler. */\nexport const KINDS: readonly string[] = [\n \"text\",\n \"number\",\n \"money\",\n \"date\",\n \"url\",\n \"email\",\n \"phone\",\n];\n\nexport interface CliField {\n readonly value: string;\n readonly kind?: string;\n}\n\n/** This function reads the fields of the command line: `name=value`, with `:kind` at the end. The name and the value cannot be empty. */\nexport const fieldsOf = (given: readonly string[] | undefined): Record<string, CliField> => {\n if (given === undefined || given.length === 0)\n throw new UsageError(\"extract wants at least one --field name=value[:kind]\");\n const fields: Record<string, CliField> = {};\n for (const text of given) {\n const equals = text.indexOf(\"=\");\n if (equals <= 0) throw new UsageError(`--field wants name=value[:kind], not \"${text}\"`);\n const name = text.slice(0, equals);\n let value = text.slice(equals + 1);\n let kind: string | undefined;\n const jsonLd = value.lastIndexOf(\":json-ld:\");\n const colon = value.lastIndexOf(\":\");\n if (jsonLd !== -1) {\n kind = value.slice(jsonLd + 1);\n value = value.slice(0, jsonLd);\n if (kind === \"json-ld:\" || /\\s/.test(kind))\n throw new UsageError(\n `--field ${name} wants a path after json-ld:, such as json-ld:Product.offers.price`,\n );\n } else if (colon !== -1 && KINDS.includes(value.slice(colon + 1))) {\n kind = value.slice(colon + 1);\n value = value.slice(0, colon);\n }\n if (value.trim() === \"\") throw new UsageError(`--field ${name} wants a value`);\n if (fields[name] !== undefined) throw new UsageError(`--field ${name} is given two times`);\n fields[name] = kind === undefined ? { value } : { value, kind };\n }\n return fields;\n};\n\n// ── the texts of the results ────────────────────────────────────────────────\n\n/** This function gives the pages of `fetch`: a rejected fetch is an error of its URL only. A page that needs a browser names the reason when rendering is off. */\nexport const fetchedPages = (\n urls: readonly string[],\n settled: ReadonlyArray<PromiseSettledResult<PageResult>>,\n renderOff: string | undefined,\n): PageResult[] =>\n settled.map((result, at) => {\n const url = urls[at]!;\n if (result.status === \"rejected\") {\n const reason: unknown = result.reason;\n return failedPage(\n url,\n `${url}: ${reason instanceof Error ? reason.message : String(reason)}`,\n );\n }\n const page = result.value;\n return renderOff !== undefined && page.note !== null && /no browser/i.test(page.note)\n ? { ...page, note: `this page needs JavaScript, and rendering is off: ${renderOff}` }\n : page;\n });\n\n/** This function gives the text of the results of `find`. An archived copy says so. */\nexport const describeFound = (found: readonly Found[]): string =>\n found.length === 0\n ? \"nothing stored matches\"\n : found\n .map(\n (hit) =>\n `${hit.doc} ${hit.title}${hit.archived === undefined ? \"\" : ` [archived copy, captured ${hit.archived.timestamp}]`}\\n ${hit.snippet.replace(/\\s+/g, \" \")}`,\n )\n .join(\"\\n\");\n\n/** This function gives the text of `read`. With `--at`, the head names the capture and its fetch time. */\nexport const describeRead = (read: ReadResult, at: string | undefined): string => {\n const version = at === undefined ? \"\" : ` ${read.capture} (${read.fetched_at})`;\n return read.before_first === true\n ? (read.note ?? \"\")\n : read.held_back\n ? `${read.note ?? \"\"}\\nAdd --user-provided if you gave this URL yourself. The crawler logs that read.`\n : `${read.title}\\n${read.url ?? \"\"}\\n${read.archived === undefined ? \"\" : `${read.note ?? \"\"}\\n`}[${String(read.from)}–${String(read.to)} of ${String(read.total)}]${version}\\n\\n${read.text}`;\n};\n\n/** This function gives the text of `verify`. An archived copy and an earlier capture say so. */\nexport const describeVerify = (verified: VerifyResult): string =>\n verified.found\n ? `found at ${String(verified.start)}–${String(verified.end)} in ${verified.capture ?? \"\"}${verified.stale ? \" (an earlier capture)\" : \"\"}${verified.archived === undefined ? \"\" : ` (an archived copy from ${verified.archived.source}, captured ${verified.archived.timestamp})`}: ${verified.excerpt ?? \"\"}`\n : verified.held_back || verified.before_first === true\n ? (verified.note ?? \"held back\")\n : \"not found\";\n\n/** This function gives the lines of `discover`: each URL with its date, then the count of the files. */\nexport const describeDiscover = (found: DiscoverResult): string[] => {\n const read = found.sources.filter((source) => source.kind !== null).length;\n return [\n ...found.urls.map((url) =>\n url.lastmod === null ? url.url : `${url.url} ${url.lastmod.slice(0, 10)}`,\n ),\n `${String(found.urls.length)} URLs from ${String(read)} of ${String(found.sources.length)} files (${found.stopped}${found.note === null ? \"\" : `: ${found.note}`})`,\n ];\n};\n\n/** This function gives the text of `reindex`: the counts, the documents that stay, and the captures that it skipped. */\nexport const describeReindex = (counts: ReindexResult): string =>\n [\n `${String(counts.captures)} captures replayed, ${String(counts.docs)} documents, ${String(counts.urls)} URLs`,\n ...(counts.carried > 0\n ? [\n `${String(counts.carried)} documents of other recipes kept: ${counts.unknown_recipes.join(\", \")}`,\n ]\n : []),\n ...(counts.failed + counts.missing + counts.damaged > 0\n ? [\n `skipped: ${String(counts.failed)} captures whose reader failed, ${String(counts.missing)} with no stored body, ${String(counts.damaged)} damaged lines of the log`,\n ]\n : []),\n ].join(\"\\n\");\n\n/** This function gives the lines of `extract`: a line for each field, its candidates and its note, then the counts. */\nexport const describeExtract = (result: ExtractResult): string[] => {\n const lines: string[] = [];\n for (const [name, field] of Object.entries(result.fields)) {\n const mark = field.status === \"verified\" || field.status === \"normalized\" ? \"✓\" : \"✗\";\n const place =\n field.span !== null\n ? ` at ${String(field.span.start)}–${String(field.span.end)}${field.span.in === \"full_text\" ? \" of the whole text\" : \"\"}`\n : field.path !== null\n ? ` at ${field.path}`\n : \"\";\n const how = field.normalization === null ? \"\" : ` (${field.normalization})`;\n const stored = field.raw === null ? \"\" : `: ${JSON.stringify(field.raw)}`;\n lines.push(`${mark} ${name}: ${field.status}${how}${place}${stored}`);\n for (const candidate of field.candidates) {\n const at =\n candidate.span !== null\n ? `${String(candidate.span.start)}–${String(candidate.span.end)}`\n : (candidate.path ?? \"\");\n lines.push(\n ` ${at}: ${JSON.stringify(candidate.raw)} can be ${candidate.readings.join(\" or \")}`,\n );\n }\n if (field.note !== null && field.status !== \"held-back\") lines.push(` ${field.note}`);\n }\n const summary = result.summary;\n lines.push(\n result.held_back\n ? `${result.note ?? \"held back\"}\\nAdd --user-provided if you gave this URL yourself. The crawler logs that check.`\n : `${String(summary.fields)} fields: ${String(summary.verified)} verified, ${String(summary.normalized)} normalized, ${String(summary.ambiguous)} ambiguous, ${String(summary.not_found)} not found, ${String(summary.invalid)} not valid, in ${result.capture ?? \"no capture\"}${result.note === null ? \"\" : `. ${result.note}`}`,\n );\n return lines;\n};\n\n/** This function gives the exit code of `extract`: 0 when each field is verified or normalized. */\nexport const extractCode = (result: ExtractResult): number => {\n const done = result.summary.verified + result.summary.normalized;\n return done === result.summary.fields && done > 0 ? 0 : 1;\n};\n\n// ── the command `export` ────────────────────────────────────────────────────\n\n/** These are the format and the options of an export, read from the command line before the export starts. */\nexport interface ExportPlan {\n readonly format: \"warc\" | \"wacz\";\n readonly options: {\n readonly out: string;\n readonly since: string | undefined;\n readonly until: string | undefined;\n readonly host: string | undefined;\n readonly urlPrefix: string | undefined;\n readonly ids: readonly string[] | undefined;\n readonly includeRefusals: boolean;\n readonly overwrite: boolean;\n };\n}\n\n/** This function checks the options of `export`, and gives its format and its options. */\nexport const exportPlan = (values: ExportValues): ExportPlan => {\n const out = values.out;\n if (out === undefined || out.trim() === \"\") throw new UsageError(\"export wants --out <file>\");\n const format =\n values.format ??\n (/\\.wacz$/i.test(out) ? \"wacz\" : /\\.warc\\.gz$/i.test(out) ? \"warc\" : undefined);\n if (format !== \"warc\" && format !== \"wacz\") {\n throw new UsageError(\n values.format === undefined\n ? \"export wants --format warc or wacz, or a file that ends in .warc.gz or .wacz\"\n : `--format is warc or wacz, not \"${values.format}\"`,\n );\n }\n for (const name of [\"since\", \"until\"] as const) {\n const value = values[name];\n if (value !== undefined && !Number.isFinite(Date.parse(value)))\n throw new UsageError(`--${name} wants a time, such as 2026-10-01, not \"${value}\"`);\n }\n if (values.host !== undefined && (values.host.trim() === \"\" || /[/:@\\s]/.test(values.host)))\n throw new UsageError(`--host wants a host name, such as example.com, not \"${values.host}\"`);\n const file =\n out === \"~\" ? homedir() : /^~[\\\\/]/.test(out) ? resolve(homedir(), out.slice(2)) : resolve(out);\n return {\n format,\n options: {\n out: file,\n since: values.since,\n until: values.until,\n host: values.host,\n urlPrefix: values.prefix,\n ids: values.id,\n includeRefusals: values[\"include-refusals\"] === true,\n overwrite: values.overwrite === true,\n },\n };\n};\n\n/** This function gives the text of an export: the file, the counts, and what the archive left out. */\nexport const describeExport = (\n result: WarcResult | WaczResult,\n includeRefusals: boolean,\n): string => {\n const left = [\n result.bodiless > 0 && !includeRefusals\n ? `${String(result.bodiless)} captures with no body (add --include-refusals to keep them as metadata)`\n : null,\n result.missing > 0 ? `${String(result.missing)} captures with no stored body` : null,\n result.damaged > 0 ? `${String(result.damaged)} damaged lines of the log` : null,\n ].filter((line) => line !== null);\n return [\n `wrote ${result.out} (${String(result.bytes)} bytes)`,\n ` ${String(result.captures)} captures: ${String(result.responses)} responses, ${String(result.revisits)} revisits, ${String(result.conversions)} rendered pages, ${String(result.metadata)} metadata records${result.format === \"wacz\" ? `, ${String(result.pages)} pages` : \"\"}`,\n ...(left.length > 0 ? [` left out: ${left.join(\", \")}`] : []),\n ].join(\"\\n\");\n};\n\n// ── the whole command line ──────────────────────────────────────────────────\n\n/** These are the values of the command line, as `parseArgs` gives them. */\nexport type Values = ParsedArgs[\"values\"];\n\n/** The environment variables that the command line reads. */\nexport type Env = Readonly<Record<string, string | undefined>>;\n\n/** This is a command line that is valid, read before the store opens. */\nexport interface Line {\n readonly command: string;\n readonly rest: readonly string[];\n readonly values: Values;\n readonly render: \"auto\" | \"never\" | \"always\";\n readonly rank: \"exact\" | \"fast\";\n readonly limit: number;\n /** This value is the start of `read`. `diff` reads `--from` as a version (`diffFrom`). */\n readonly from: number;\n readonly length: number;\n readonly maxAge: number;\n readonly crawlLimits: CrawlLimits;\n readonly at: string | undefined;\n readonly diffFrom: VersionChoice | undefined;\n readonly diffTo: VersionChoice | undefined;\n readonly watchLine: WatchLine | undefined;\n readonly extractFields: Record<string, CliField> | undefined;\n readonly exportPlan: ExportPlan | undefined;\n readonly dir: string;\n readonly contact: string | undefined;\n readonly allowPrivate: boolean;\n readonly allowUserProvided: boolean;\n /** This file is the key of `--sign-key` or `POLITE_CRAWLER_SIGN_KEY`. The command reads it after the checks. */\n readonly signKey: string | undefined;\n readonly signatureAgent: string | undefined;\n}\n\n/** This is the result of `readLine`: the version, the help, or a command to do. */\nexport type Parsed =\n { readonly kind: \"version\" } | { readonly kind: \"help\" } | ({ readonly kind: \"command\" } & Line);\n\n/**\n * This function reads the whole command line before anything opens the store. A wrong command line\n * throws a `UsageError`, so the command exits with code 2 and makes no store.\n */\nexport const readLine = (args: readonly string[], env: Env): Parsed => {\n const { values, positionals } = parse(args);\n if (values.version === true) return { kind: \"version\" };\n const [command, ...rest] = positionals;\n if (values.help === true || command === undefined) return { kind: \"help\" };\n const spec = COMMANDS[command];\n if (spec === undefined) throw new UsageError(`no command \"${command}\"`);\n if (rest.length < spec.min || rest.length > spec.max) throw new UsageError(spec.wants);\n for (const [option, owner] of Object.entries(OWNER)) {\n const owners = owner.split(\" \");\n if (values[option as keyof typeof values] !== undefined && !owners.includes(command))\n throw new UsageError(`--${option} is an option of ${owners.join(\" or \")}`);\n }\n const render = values.render ?? \"auto\";\n if (render !== \"auto\" && render !== \"never\" && render !== \"always\")\n throw new UsageError(`--render is auto, never or always, not \"${render}\"`);\n const rank = values.rank ?? \"exact\";\n if (rank !== \"exact\" && rank !== \"fast\")\n throw new UsageError(`--rank is exact or fast, not \"${rank}\"`);\n const limit = whole(\"limit\", values.limit, 10, 1);\n // diff reads --from as a version, and read as a position\n const from = command === \"read\" ? whole(\"from\", values.from, 0, 0) : 0;\n const length = whole(\"length\", values.length, 4000, 1);\n const crawlLimits = crawlOptionsOf(values);\n const at = timeOf(\"at\", values.at);\n const diffFrom = command === \"diff\" ? versionOf(\"from\", values.from) : undefined;\n const diffTo = versionOf(\"to\", values.to);\n const watchLine = command === \"watch\" ? watchLineOf(values, rest) : undefined;\n const extractFields = command === \"extract\" ? fieldsOf(values.field) : undefined;\n const dir = storeDir(values.dir ?? (env[\"POLITE_CRAWLER_DIR\"] || undefined));\n const allowPrivate =\n values[\"allow-private\"] === true || envFlag(env, \"POLITE_CRAWLER_ALLOW_PRIVATE\");\n const allowUserProvided =\n values[\"allow-user-provided\"] === true ||\n (command === \"mcp\" && envFlag(env, \"POLITE_CRAWLER_ALLOW_USER_PROVIDED\"));\n if (values[\"archive-despite-robots\"] === true && values[\"archive-fallback\"] !== true)\n throw new UsageError(\"--archive-despite-robots works only with --archive-fallback\");\n // of the actions of watch, only run sends requests, so only run signs\n const signs =\n OWNER[\"sign-key\"]!.split(\" \").includes(command) &&\n (watchLine === undefined || watchLine.action === \"run\");\n const signKey =\n values[\"sign-key\"] ?? (signs ? env[\"POLITE_CRAWLER_SIGN_KEY\"] || undefined : undefined);\n const signatureAgent =\n values[\"signature-agent\"] ??\n (signs ? env[\"POLITE_CRAWLER_SIGNATURE_AGENT\"] || undefined : undefined);\n if (command === \"key-directory\") {\n if (signKey === undefined) throw new UsageError(\"key-directory wants --sign-key <file>\");\n } else if ((signKey === undefined) !== (signatureAgent === undefined)) {\n throw new UsageError(\n \"--sign-key and --signature-agent go together: the key signs, and the agent tells a site where its public key is\",\n );\n }\n let maxAge: number;\n try {\n maxAge = durationMs(values[\"max-age\"] ?? \"24h\");\n } catch (error) {\n throw new UsageError(`--max-age: ${(error as Error).message}`, { cause: error });\n }\n return {\n kind: \"command\",\n command,\n rest,\n values,\n render,\n rank,\n limit,\n from,\n length,\n maxAge,\n crawlLimits,\n at,\n diffFrom,\n diffTo,\n watchLine,\n extractFields,\n exportPlan: command === \"export\" ? exportPlan(values) : undefined,\n dir,\n contact: values.contact ?? (env[\"POLITE_CRAWLER_CONTACT\"] || undefined),\n allowPrivate,\n allowUserProvided,\n signKey,\n signatureAgent,\n };\n};\n","#!/usr/bin/env node\nimport type { CliField, CrawlLimits, ExportPlan, Values, WatchLine } from \"./cli-line.ts\";\nimport {\n FS_CODES,\n UsageError,\n agentTypeOf,\n describeDiscover,\n describeEntry,\n describeEvent,\n describeExport,\n describeExtract,\n describeFound,\n describePage,\n describeRead,\n describeReindex,\n describeSummary,\n describeVerify,\n describeWatchSummary,\n done,\n extractCode,\n fetchedPages,\n keyFile,\n readLine,\n} from \"./cli-line.ts\";\nimport type { DiffResult } from \"./diff.ts\";\nimport type { CrawlPage } from \"./frontier.ts\";\nimport type { OpenOptions, OpenedCrawler, PoliteCrawler } from \"./open.ts\";\nimport { VERSION } from \"./version.ts\";\nimport type { WatchEntry, WatchEvent } from \"./watch.ts\";\n\n// This file imports only Node.js, the version and `cli-line.ts` at start. A command loads the\n// crawler, and only `mcp` loads the MCP server, so `--version` and `--help` answer at once.\n\n// node:sqlite of Node.js 22 writes \"SQLite is an experimental feature\" to the standard error at\n// each start. The command removes only that warning, before the store loads node:sqlite.\nconst emitWarning = process.emitWarning.bind(process) as (\n warning: string | Error,\n ...rest: unknown[]\n) => void;\nprocess.emitWarning = ((warning: string | Error, ...rest: unknown[]): void => {\n const first: unknown = rest[0];\n const kind =\n typeof first === \"string\"\n ? first\n : typeof first === \"object\" && first !== null && \"type\" in first\n ? first.type\n : warning instanceof Error\n ? warning.name\n : undefined;\n const text = typeof warning === \"string\" ? warning : warning.message;\n if (kind === \"ExperimentalWarning\" && text.startsWith(\"SQLite is an experimental feature\"))\n return;\n emitWarning(warning, ...rest);\n}) as typeof process.emitWarning;\n\nconst HELP = `polite-crawler ${VERSION}: fetch a source once, keep it, read it many times.\n\nUsage:\n polite-crawler fetch <url...> fetch pages into the store (robots.txt obeyed)\n polite-crawler find <query> search the stored pages, with no request\n polite-crawler read <doc> print a range of a stored document\n polite-crawler verify <doc> <quote> check that a quote is in a stored document\n polite-crawler reindex rebuild the search index from the capture log\n polite-crawler mcp serve the tools over MCP (standard input and output)\n polite-crawler export --out <file> write the stored captures as a WARC or WACZ archive\n polite-crawler discover <site> list the pages of a site from its sitemaps and feeds\n polite-crawler crawl <url...> fetch a site and follow its links, within a budget\n polite-crawler diff <doc> show what changed between two versions of a document\n polite-crawler watch add <url...> check these pages again on a schedule\n polite-crawler watch remove <url...> stop the watch of these pages\n polite-crawler watch list print the watch list\n polite-crawler watch run check the pages that are due, within a budget\n polite-crawler key-directory print the key directory (JWKS) of --sign-key, to host\n polite-crawler extract <doc> --field name=value[:kind]...\n check proposed field values against a stored page\n\nOptions:\n --dir <path> the store (default $POLITE_CRAWLER_DIR, or ~/.polite-crawler)\n --contact <text> an email (From and User-Agent) or a URL (User-Agent only)\n ($POLITE_CRAWLER_CONTACT)\n --allow-private fetch loopback and private addresses too ($POLITE_CRAWLER_ALLOW_PRIVATE=1)\n --no-browser never open the headless browser\n --sign-key <file> sign each request (Web Bot Auth) with this Ed25519 key: PKCS#8 PEM or JWK\n ($POLITE_CRAWLER_SIGN_KEY)\n --signature-agent <url> the https origin of your key directory, or the URL of a\n JSON Web Key Set on a path, for --sign-key ($POLITE_CRAWLER_SIGNATURE_AGENT)\n --json print JSON\n -v, --version print the version\n -h, --help print this help\n\n fetch --render <mode> auto (default), never or always\n --max-age <age> reuse a stored copy this young: 30m, 24h (default), 7d; 0 fetches again\n --archive-fallback when the site refuses or is down, give a copy from the Wayback\n Machine or Common Crawl, marked archived (not a fresh fetch)\n --archive-despite-robots with --archive-fallback: also when the robots.txt of the\n site disallows the page now\n find --limit <n> how many results (default 10)\n --kind <kind> only this kind of document, such as page or pdf\n --rank <mode> exact (default): BM25 over each match; fast: leave common words\n out of the one-word part, faster for a long question\n read --from <n> where the range starts (default 0)\n --length <n> how many characters (default 4000)\n --user-provided you gave this URL yourself: read a page that refuses AI input (logged)\n read, verify, extract --at <time> use the version that was current then, such as\n 2026-10-01T12:00Z\n reindex rebuild; the documents of recipes that this command cannot read stay\n mcp --allow-user-provided let user_provided work when the client cannot ask you\n ($POLITE_CRAWLER_ALLOW_USER_PROVIDED=1)\n export --out <file> the archive to write: a name that ends in .warc.gz or .wacz\n --format <f> warc or wacz (default: from the name of the file)\n --since <time> only the captures from this time on, such as 2026-10-01\n --until <time> only the captures before this time\n --host <host> only the captures of this host and its subdomains\n --prefix <url> only the captures whose URL starts with this text\n --id <capture> only this capture; give --id again for more captures\n --include-refusals a metadata record for each capture with no body too\n --overwrite replace the file when it exists\n discover --max-urls <n> how many URLs (default 1000)\n --feed <url> read this RSS or Atom feed too (repeat it)\n --max-age <age> as for fetch\n crawl --max-pages <n> how many pages (default 100)\n --max-depth <n> how many links from a seed (default 3; 0 fetches the seeds only)\n --budget <time> the longest time: 30s, 2m, 10m (default); no request starts after it\n --include <pattern> follow only the URLs that match, for example /blog/* (repeat it)\n --exclude <pattern> follow no URL that matches, for example *.pdf$ (repeat it)\n --sitemaps add the pages of the sitemaps of the site\n --render, --max-age as for fetch\n discover, crawl --allow-host <host> keep the URLs on this host too (repeat it)\n diff --from <version> the old version: a capture id or a time (default: the one before --to)\n --to <version> the new version: a capture id or a time (default: the newest)\n watch add --every <time> the time between two checks: 1h or more (default 24h)\n --confirm <n> 2 (default): a change counts when two checks see it; 1: at once\n --scope <text>, --label <name=value> marks for the captures (repeat --label)\n --render <mode> as for fetch\n watch run --budget <time> the longest time (default 10m); no request starts after it\n --max-pages <n> how many pages to check (default 100)\n extract --field <name=value[:kind]> a value to check, never made up by the crawler (repeat it);\n kind: text (default), number, money, date, url, email, phone,\n or json-ld:<path> such as json-ld:Product.offers.price\n --capture <id> check the text of this capture, not the current text\n --user-provided as for read\n\nExit codes:\n 0 done\n 1 not done: a page not fetched, no match, a quote not found, a document unknown or\n held back, a watch check that failed or that is still due, a field value that extract\n did not find or that is not clear, or an error\n (fetch: a page with an archived copy counts as done)\n 2 the command line is wrong\n`;\n\n// ---- The command `export`: the options, the checks and the work of the WARC and WACZ export ----\n\n/**\n * This function does the command `export`, and gives the exit code. `readLine` checked the whole\n * command line first. The export only reads the store: it makes no store, and it sends no request.\n */\nconst exportCommand = async (plan: ExportPlan, dir: string, json: boolean): Promise<number> => {\n const result =\n plan.format === \"wacz\"\n ? await (await import(\"./wacz.ts\")).exportWacz(dir, plan.options)\n : await (await import(\"./warc.ts\")).exportWarc(dir, plan.options);\n process.stdout.write(\n `${json ? JSON.stringify(result, null, 2) : describeExport(result, plan.options.includeRefusals)}\\n`,\n );\n return 0;\n};\n\n// ---- The end of the command `export` ----\n\n// ── signed requests ──────────────────────────────────────────────────────────\n\n/** This function prints the key directory of a key file, and tells where to host it. */\nconst keyDirectoryCommand = async (key: string, agent: string | undefined): Promise<number> => {\n const { KEY_DIRECTORY_PATH, KEY_DIRECTORY_TYPE, keyDirectory } = await import(\"./webbotauth.ts\");\n let directory;\n try {\n directory = keyDirectory([key]);\n } catch (error) {\n throw new UsageError(`--sign-key: ${(error as Error).message}`, { cause: error });\n }\n process.stdout.write(`${JSON.stringify(directory, null, 2)}\\n`);\n const where =\n agent !== undefined && agentTypeOf(agent) === \"jwks_uri\"\n ? `${agent} (a JSON Web Key Set, the type jwks_uri)`\n : `${agent === undefined ? \"https://<your origin>\" : agent.replace(/\\/$/, \"\")}${KEY_DIRECTORY_PATH} with Content-Type: ${KEY_DIRECTORY_TYPE}`;\n process.stderr.write(`polite-crawler: serve this JSON at ${where}\\n`);\n return 0;\n};\n\n// ── end of signed requests ───────────────────────────────────────────────────\n\n// ── discover and crawl ──────────────────────────────────────────────────────\n\n/** This function lists the pages of a site. The exit code is 0 when it found a URL. */\nconst runDiscover = async (\n crawler: OpenedCrawler,\n site: string,\n limits: CrawlLimits,\n maxAge: number,\n json: boolean,\n out: (text: string) => void,\n): Promise<number> => {\n const found = await crawler.discover(site, {\n maxUrls: limits.maxUrls,\n feeds: limits.feeds,\n maxAge,\n ...(limits.allowHosts === undefined ? {} : { allowHosts: limits.allowHosts }),\n });\n if (json) out(JSON.stringify(found, null, 2));\n else for (const line of describeDiscover(found)) out(line);\n return found.urls.length > 0 ? 0 : 1;\n};\n\n/** This function crawls from the seeds. It prints each page when it ends. The exit code is 0 when the crawl got a page. */\nconst runCrawl = async (\n crawler: OpenedCrawler,\n seeds: readonly string[],\n limits: CrawlLimits,\n page: { readonly render: \"auto\" | \"never\" | \"always\"; readonly maxAge: number },\n json: boolean,\n out: (text: string) => void,\n): Promise<number> => {\n // the first Ctrl+C stops the crawl cleanly: the fetches in progress end, and the summary prints\n const controller = new AbortController();\n const stop = (): void => controller.abort(new Error(\"interrupted\"));\n process.once(\"SIGINT\", stop);\n try {\n const run = crawler.crawl([...new Set(seeds)], {\n maxPages: limits.maxPages,\n maxDepth: limits.maxDepth,\n budgetMs: limits.budgetMs,\n include: limits.include,\n exclude: limits.exclude,\n useSitemaps: limits.sitemaps,\n render: page.render,\n maxAge: page.maxAge,\n signal: controller.signal,\n ...(limits.allowHosts === undefined ? {} : { allowHosts: limits.allowHosts }),\n });\n const pages: CrawlPage[] = [];\n for await (const result of run) {\n pages.push(result);\n if (!json) out(`${describePage(result)}\\n depth ${String(result.depth)}, ${result.source}`);\n }\n const summary = await run.summary;\n out(json ? JSON.stringify({ pages, summary }, null, 2) : describeSummary(summary));\n return summary.fetched > 0 ? 0 : 1;\n } finally {\n process.removeListener(\"SIGINT\", stop);\n }\n};\n\n// ── end of discover and crawl ───────────────────────────────────────────────\n\n// ── time travel, diff and watch ─────────────────────────────────────────────\n\n/** This function does the command `watch`, and gives the exit code. */\nconst runWatchCommand = async (\n crawler: OpenedCrawler,\n line: WatchLine,\n values: Values,\n limits: CrawlLimits,\n json: boolean,\n out: (text: string) => void,\n): Promise<number> => {\n const print = (value: unknown, text: () => string): void =>\n out(json ? JSON.stringify(value, null, 2) : text());\n switch (line.action) {\n case \"add\": {\n const added: WatchEntry[] = [];\n for (const url of line.urls) {\n added.push(\n await crawler.watch.add(url, {\n ...(line.every === undefined ? {} : { every: line.every }),\n ...(line.confirm === undefined ? {} : { confirm: line.confirm }),\n ...(values.scope === undefined ? {} : { scope: values.scope }),\n ...(line.labels === undefined ? {} : { labels: line.labels }),\n ...(values.render === undefined\n ? {}\n : { render: values.render as \"auto\" | \"never\" | \"always\" }),\n }),\n );\n }\n print(added, () => added.map(describeEntry).join(\"\\n\"));\n return 0;\n }\n case \"remove\": {\n const removed: Array<{ readonly url: string; readonly removed: boolean }> = [];\n for (const url of line.urls) removed.push({ url, removed: await crawler.watch.remove(url) });\n print(removed, () =>\n removed\n .map((one) => `${one.removed ? \"removed\" : \"not on the list\"} ${one.url}`)\n .join(\"\\n\"),\n );\n return removed.every((one) => one.removed) ? 0 : 1;\n }\n case \"list\": {\n const entries = await crawler.watch.list();\n print(entries, () =>\n entries.length === 0 ? \"the watch list is empty\" : entries.map(describeEntry).join(\"\\n\"),\n );\n return 0;\n }\n default: {\n // the first Ctrl+C stops the run cleanly: the checks in progress end, and the summary prints\n const controller = new AbortController();\n const stop = (): void => controller.abort(new Error(\"interrupted\"));\n process.once(\"SIGINT\", stop);\n try {\n const run = crawler.watch.run({\n budgetMs: values.budget === undefined ? undefined : limits.budgetMs,\n maxPages: values[\"max-pages\"] === undefined ? undefined : limits.maxPages,\n signal: controller.signal,\n });\n const events: WatchEvent[] = [];\n for await (const event of run) {\n events.push(event);\n if (!json) out(describeEvent(event));\n }\n const summary = await run.summary;\n out(json ? JSON.stringify({ events, summary }, null, 2) : describeWatchSummary(summary));\n return summary.refused + summary.error === 0 && summary.due === 0 ? 0 : 1;\n } finally {\n process.removeListener(\"SIGINT\", stop);\n }\n }\n }\n};\n\n/** This function gives the text of a diff for a person: the two captures, then the text of `formatDiff`. */\nconst describeDiff = async (diff: DiffResult): Promise<string> => {\n const { formatDiff } = await import(\"./diff.ts\");\n return [\n `${diff.doc} ${diff.from.capture} (${diff.from.fetched_at}) -> ${diff.to.capture} (${diff.to.fetched_at})`,\n formatDiff(diff),\n ...(diff.same === true ? [\"the two versions are the same\"] : []),\n ...(diff.note === null ? [] : [diff.note]),\n ].join(\"\\n\");\n};\n\n// ── end of time travel, diff and watch ──────────────────────────────────────\n// ── extract ─────────────────────────────────────────────────────────────────\n\n/** This function checks the fields against a stored document, and prints a line for each field. The exit code is 0 when each field is verified or normalized. */\nconst runExtract = async (\n crawler: PoliteCrawler,\n doc: string,\n fields: Record<string, CliField>,\n options: {\n readonly capture: string | undefined;\n readonly userProvided: boolean;\n readonly at: string | undefined;\n },\n json: boolean,\n out: (text: string) => void,\n): Promise<number> => {\n const { extractDocument } = await import(\"./extract.ts\");\n const claims = fields as Parameters<typeof extractDocument>[2];\n const result =\n crawler.extract === undefined\n ? await extractDocument(crawler.runtime, doc, claims, options)\n : await crawler.extract(doc, claims, options);\n if (json) out(JSON.stringify(result, null, 2));\n else for (const line of describeExtract(result)) out(line);\n return extractCode(result);\n};\n\n// ── end of extract ──────────────────────────────────────────────────────────\n\n/** This function does the command of the command line, and gives the exit code. */\nconst main = async (args: readonly string[]): Promise<number> => {\n const out = (text: string): void => void process.stdout.write(`${text}\\n`);\n // the whole command line is valid before anything opens the store\n const line = readLine(args, process.env);\n if (line.kind === \"version\") {\n out(VERSION);\n return 0;\n }\n if (line.kind === \"help\") {\n process.stdout.write(HELP);\n return 0;\n }\n const {\n command,\n rest,\n values,\n render,\n rank,\n limit,\n from,\n length,\n maxAge,\n crawlLimits,\n at,\n diffFrom,\n diffTo,\n watchLine,\n extractFields,\n dir,\n signKey,\n signatureAgent,\n } = line;\n // ── signed requests: the key file opens after the checks of the command line ──\n if (command === \"key-directory\") return keyDirectoryCommand(keyFile(signKey!), signatureAgent);\n let signer;\n if (signKey !== undefined && signatureAgent !== undefined) {\n const { createWebBotAuthSigner } = await import(\"./webbotauth.ts\");\n const privateKey = keyFile(signKey);\n try {\n signer = createWebBotAuthSigner({\n privateKey,\n signatureAgent,\n agentType: agentTypeOf(signatureAgent),\n });\n } catch (error) {\n throw new UsageError(`--sign-key: ${(error as Error).message}`, { cause: error });\n }\n }\n // ── end of signed requests ──\n const options: OpenOptions = {\n dir,\n contact: line.contact,\n ...(values[\"no-browser\"] === true ? { renderer: false as const } : {}),\n ...(line.allowPrivate ? { egress: { allowPrivate: true } } : {}),\n ...(signer === undefined ? {} : { signer }),\n };\n\n if (command === \"mcp\") {\n const { startMcpServer } = await import(\"./mcp.ts\");\n await startMcpServer({ ...options, allowUserProvided: line.allowUserProvided });\n return 0;\n }\n\n // the export only reads the store, so it opens no crawler and no index\n if (line.exportPlan !== undefined)\n return exportCommand(line.exportPlan, dir, values.json === true);\n\n const { openCrawler } = await import(\"./open.ts\");\n // only fetch, crawl and watch run render; a missing browser turns rendering off one time, before the first page\n let renderOff: string | undefined;\n let renderer = options.renderer;\n const renders = command === \"fetch\" || command === \"crawl\" || watchLine?.action === \"run\";\n if (!renders || render === \"never\") renderer = false;\n else if (renderer === undefined) {\n const { browserMissing } = await import(\"./browser.ts\");\n renderOff = await browserMissing();\n if (renderOff !== undefined) renderer = false;\n }\n\n let crawler: OpenedCrawler;\n try {\n crawler = openCrawler({ ...options, renderer });\n } catch (error) {\n const code = (error as { code?: unknown }).code;\n if (typeof code === \"string\" && FS_CODES.has(code)) {\n throw new Error(\n `the store ${dir} does not open: it is not a folder that polite-crawler can write (${code})`,\n {\n cause: error,\n },\n );\n }\n throw error;\n }\n const print = (value: unknown, text: () => string): void =>\n out(values.json === true ? JSON.stringify(value, null, 2) : text());\n try {\n switch (command) {\n case \"fetch\": {\n // the same URL twice is one fetch; one bad URL is an error of that URL only\n const urls = [...new Set(rest)];\n const fallback = values[\"archive-fallback\"] === true ? (\"archive\" as const) : undefined;\n const archiveDespiteRobots = values[\"archive-despite-robots\"] === true;\n const settled = await Promise.allSettled(\n urls.map((url) => crawler.fetch(url, { render, maxAge, fallback, archiveDespiteRobots })),\n );\n const pages = fetchedPages(urls, settled, renderOff);\n print(pages, () => pages.map(describePage).join(\"\\n\"));\n return pages.every(done) ? 0 : 1;\n }\n case \"find\": {\n const found = await crawler.find(rest.join(\" \"), {\n limit,\n kind: values.kind ?? null,\n rank,\n });\n print(found, () => describeFound(found));\n return found.length === 0 ? 1 : 0;\n }\n case \"read\": {\n const userProvided = values[\"user-provided\"] === true;\n const read = await crawler.read(rest[0]!, { from, length, userProvided, at });\n print(read, () => describeRead(read, at));\n return read.held_back || read.before_first === true ? 1 : 0;\n }\n case \"verify\": {\n const [doc, ...quote] = rest;\n const verified = await crawler.verify(doc!, quote.join(\" \"), { at });\n print(verified, () => describeVerify(verified));\n return verified.found ? 0 : 1;\n }\n // ── discover and crawl ──\n case \"discover\":\n return await runDiscover(crawler, rest[0]!, crawlLimits, maxAge, values.json === true, out);\n case \"crawl\":\n return await runCrawl(\n crawler,\n rest,\n crawlLimits,\n { render, maxAge },\n values.json === true,\n out,\n );\n // ── end of discover and crawl ──\n // ── diff and watch ──\n case \"diff\": {\n const diff = await crawler.diff(rest[0]!, {\n ...(diffFrom === undefined ? {} : { from: diffFrom }),\n ...(diffTo === undefined ? {} : { to: diffTo }),\n });\n const text = values.json === true ? \"\" : await describeDiff(diff);\n print(diff, () => text);\n return diff.held_back ? 1 : 0;\n }\n case \"watch\":\n return await runWatchCommand(\n crawler,\n watchLine!,\n values,\n crawlLimits,\n values.json === true,\n out,\n );\n // ── end of diff and watch ──\n // ── extract ──\n case \"extract\":\n return await runExtract(\n crawler,\n rest[0]!,\n extractFields!,\n { capture: values.capture, userProvided: values[\"user-provided\"] === true, at },\n values.json === true,\n out,\n );\n // ── end of extract ──\n default: {\n // the documents of a recipe that this command cannot read stay, from the old index\n const counts = await crawler.reindex();\n print(counts, () => describeReindex(counts));\n return 0;\n }\n }\n } finally {\n await crawler.close();\n }\n};\n\nmain(process.argv.slice(2)).then(\n (code) => {\n process.exitCode = code;\n },\n (error: unknown) => {\n const message = error instanceof Error ? error.message : String(error);\n const usage = error instanceof UsageError;\n process.stderr.write(\n `polite-crawler: ${message}${usage ? \" (polite-crawler --help lists the commands)\" : \"\"}\\n`,\n );\n process.exitCode = usage ? 2 : 1;\n },\n);\n"],"mappings":"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"}
1
+ {"version":3,"file":"cli.mjs","names":[],"sources":["../src/cli-line.ts","../src/cli.ts"],"sourcesContent":["/*\n * The command line of polite-crawler without its work: the options, the checks of the whole command\n * line, and the text of each result. `cli.ts` does the work. This file imports only Node.js, the\n * duration parser and the loopback check, so `--help` and `--version` answer at once. The tests use it in their own\n * process, so the checks and the texts count in the coverage of the package.\n */\nimport { readFileSync } from \"node:fs\";\nimport { homedir } from \"node:os\";\nimport { join, resolve } from \"node:path\";\nimport { parseArgs } from \"node:util\";\nimport type { VersionChoice } from \"./diff.ts\";\nimport type { DiscoverResult } from \"./discover.ts\";\nimport type { Found } from \"./docs.ts\";\nimport type { DaemonState } from \"./daemon.ts\";\nimport { durationMs } from \"./duration.ts\";\nimport type { ExtractResult } from \"./extract.ts\";\nimport type { CrawlSummary } from \"./frontier.ts\";\nimport type { EmbedResult } from \"./hybrid.ts\";\nimport { isLoopbackHost } from \"./loopback.ts\";\nimport type { PageResult, ReadResult, ReindexResult, VerifyResult } from \"./runtime.ts\";\nimport type { WaczResult } from \"./wacz.ts\";\nimport type { WarcResult } from \"./warc.ts\";\nimport type { WatchEntry, WatchEvent, WatchSummary } from \"./watch.ts\";\n\n/** A usage error is a wrong command line. It exits with code 2. */\nexport class UsageError extends Error {}\n\n/** These options belong to the command `export`. */\nexport const EXPORT_OPTIONS = {\n format: { type: \"string\" },\n out: { type: \"string\" },\n since: { type: \"string\" },\n until: { type: \"string\" },\n host: { type: \"string\" },\n prefix: { type: \"string\" },\n id: { type: \"string\", multiple: true },\n \"include-refusals\": { type: \"boolean\" },\n overwrite: { type: \"boolean\" },\n} as const;\n\n/** These are the values of the command line that the command `export` reads. */\nexport interface ExportValues {\n readonly format?: string | undefined;\n readonly out?: string | undefined;\n readonly since?: string | undefined;\n readonly until?: string | undefined;\n readonly host?: string | undefined;\n readonly prefix?: string | undefined;\n readonly id?: readonly string[] | undefined;\n readonly \"include-refusals\"?: boolean | undefined;\n readonly overwrite?: boolean | undefined;\n readonly json?: boolean | undefined;\n}\n\n/** These options belong to each command, or to the commands that `OWNER` names. */\nconst COMMAND_OPTIONS = {\n dir: { type: \"string\" },\n contact: { type: \"string\" },\n \"allow-private\": { type: \"boolean\" },\n \"no-browser\": { type: \"boolean\" },\n json: { type: \"boolean\" },\n version: { type: \"boolean\", short: \"v\" },\n help: { type: \"boolean\", short: \"h\" },\n render: { type: \"string\" },\n \"max-age\": { type: \"string\" },\n limit: { type: \"string\" },\n kind: { type: \"string\" },\n rank: { type: \"string\" },\n from: { type: \"string\" },\n length: { type: \"string\" },\n \"user-provided\": { type: \"boolean\" },\n \"allow-user-provided\": { type: \"boolean\" },\n // ── discover and crawl ──\n \"max-urls\": { type: \"string\" },\n feed: { type: \"string\", multiple: true },\n \"max-pages\": { type: \"string\" },\n \"max-depth\": { type: \"string\" },\n budget: { type: \"string\" },\n include: { type: \"string\", multiple: true },\n exclude: { type: \"string\", multiple: true },\n sitemaps: { type: \"boolean\" },\n \"allow-host\": { type: \"string\", multiple: true },\n // ── end of discover and crawl ──\n // ── time travel, diff and watch ──\n at: { type: \"string\" },\n to: { type: \"string\" },\n every: { type: \"string\" },\n confirm: { type: \"string\" },\n scope: { type: \"string\" },\n label: { type: \"string\", multiple: true },\n // ── end of time travel, diff and watch ──\n // ── archived fallback and signed requests ──\n \"archive-fallback\": { type: \"boolean\" },\n \"archive-despite-robots\": { type: \"boolean\" },\n \"sign-key\": { type: \"string\" },\n \"signature-agent\": { type: \"string\" },\n // ── end of archived fallback and signed requests ──\n // ── daemon ──\n tick: { type: \"string\" },\n jitter: { type: \"string\" },\n \"tick-budget\": { type: \"string\" },\n \"tick-pages\": { type: \"string\" },\n grace: { type: \"string\" },\n events: { type: \"string\" },\n \"on-change\": { type: \"string\" },\n \"mcp-http\": { type: \"boolean\" },\n \"mcp-port\": { type: \"string\" },\n \"mcp-host\": { type: \"string\" },\n \"mcp-token-file\": { type: \"string\" },\n \"allow-remote\": { type: \"boolean\" },\n // ── end of daemon ──\n // ── extract ──\n field: { type: \"string\", multiple: true },\n capture: { type: \"string\" },\n // ── end of extract ──\n // ── hybrid search ──\n embedder: { type: \"string\" },\n \"embed-model\": { type: \"string\" },\n \"ollama-url\": { type: \"string\" },\n \"allow-remote-ollama\": { type: \"boolean\" },\n \"max-docs\": { type: \"string\" },\n // ── end of hybrid search ──\n} as const;\n\n/** These are the options of the command line: the options of the commands and of `export`. */\nconst OPTIONS: typeof COMMAND_OPTIONS & typeof EXPORT_OPTIONS = {\n ...COMMAND_OPTIONS,\n ...EXPORT_OPTIONS,\n};\n\n/** This is the configuration of `parseArgs` for the command line. */\ninterface ParseConfig {\n args: string[];\n allowPositionals: true;\n strict: true;\n options: typeof OPTIONS;\n}\n\n/** This is the result of `parseArgs` for the command line. */\ntype ParsedArgs = ReturnType<typeof parseArgs<ParseConfig>>;\n\n/** These options belong to `daemon`, and not to `daemon status`. */\nexport const DAEMON_RUN_OPTIONS: readonly string[] = [\n \"tick\",\n \"jitter\",\n \"tick-budget\",\n \"tick-pages\",\n \"grace\",\n \"events\",\n \"on-change\",\n \"mcp-http\",\n \"mcp-port\",\n \"mcp-host\",\n \"mcp-token-file\",\n \"allow-remote\",\n];\n\n/** These options belong to one command, or to the commands that the text names. */\nexport const OWNER: Readonly<Record<string, string>> = {\n render: \"fetch crawl watch\",\n \"max-age\": \"fetch crawl discover\",\n limit: \"find\",\n kind: \"find embed\",\n rank: \"find\",\n from: \"read diff\",\n length: \"read\",\n \"user-provided\": \"read extract\",\n \"allow-user-provided\": \"mcp daemon\",\n ...Object.fromEntries(Object.keys(EXPORT_OPTIONS).map((option) => [option, \"export\"])),\n // ── discover and crawl ──\n \"max-urls\": \"discover\",\n feed: \"discover\",\n \"max-pages\": \"crawl watch\",\n \"max-depth\": \"crawl\",\n budget: \"crawl watch embed\",\n include: \"crawl\",\n exclude: \"crawl\",\n sitemaps: \"crawl\",\n \"allow-host\": \"crawl discover\",\n // ── end of discover and crawl ──\n // ── time travel, diff and watch ──\n at: \"read verify extract\",\n to: \"diff\",\n every: \"watch\",\n confirm: \"watch\",\n scope: \"watch\",\n label: \"watch\",\n // ── end of time travel, diff and watch ──\n // ── archived fallback and signed requests ──\n \"archive-fallback\": \"fetch\",\n \"archive-despite-robots\": \"fetch\",\n \"sign-key\": \"fetch discover crawl watch mcp key-directory daemon\",\n \"signature-agent\": \"fetch discover crawl watch mcp key-directory daemon\",\n // ── end of archived fallback and signed requests ──\n // ── daemon ──\n ...Object.fromEntries(DAEMON_RUN_OPTIONS.map((option) => [option, \"daemon\"])),\n // ── end of daemon ──\n // ── extract ──\n field: \"extract\",\n capture: \"extract\",\n // ── end of extract ──\n // ── hybrid search ──\n embedder: \"find embed mcp\",\n \"embed-model\": \"find embed mcp\",\n \"ollama-url\": \"find embed mcp\",\n \"allow-remote-ollama\": \"find embed mcp\",\n \"max-docs\": \"embed\",\n // ── end of hybrid search ──\n};\n\n/** These are the commands, with the smallest and the largest count of arguments. */\nexport const COMMANDS: Readonly<\n Record<string, { readonly min: number; readonly max: number; readonly wants: string }>\n> = {\n fetch: { min: 1, max: Infinity, wants: \"fetch wants at least one URL\" },\n find: { min: 1, max: Infinity, wants: \"find wants a query\" },\n read: { min: 1, max: 1, wants: \"read wants one document id\" },\n verify: { min: 2, max: Infinity, wants: \"verify wants a document id and a quote\" },\n reindex: { min: 0, max: 0, wants: \"reindex takes no argument\" },\n mcp: { min: 0, max: 0, wants: \"mcp takes no argument\" },\n export: { min: 0, max: 0, wants: \"export takes no argument: give the file with --out\" },\n // ── discover and crawl ──\n discover: { min: 1, max: 1, wants: \"discover wants one site URL\" },\n crawl: { min: 1, max: Infinity, wants: \"crawl wants at least one URL\" },\n // ── end of discover and crawl ──\n // ── diff and watch ──\n diff: { min: 1, max: 1, wants: \"diff wants one document id\" },\n watch: { min: 1, max: Infinity, wants: \"watch wants add, remove, list or run\" },\n // ── end of diff and watch ──\n // ── daemon ──\n daemon: { min: 0, max: 1, wants: \"daemon takes no argument, or status\" },\n // ── end of daemon ──\n // ── signed requests ──\n \"key-directory\": {\n min: 0,\n max: 0,\n wants: \"key-directory takes no argument: give the key with --sign-key\",\n },\n // ── end of signed requests ──\n // ── extract ──\n extract: { min: 1, max: 1, wants: \"extract wants one document id and --field name=value\" },\n // ── end of extract ──\n // ── hybrid search ──\n embed: { min: 0, max: 0, wants: \"embed takes no argument: give the model with --embedder\" },\n // ── end of hybrid search ──\n};\n\nconst parse = (args: readonly string[]): ParsedArgs => {\n try {\n return parseArgs<ParseConfig>({\n args: [...args],\n allowPositionals: true,\n strict: true,\n options: OPTIONS,\n });\n } catch (error) {\n // parseArgs gives a TypeError with an ERR_PARSE_ARGS code for a wrong command line\n const code = (error as { code?: unknown }).code;\n if (typeof code === \"string\" && code.startsWith(\"ERR_PARSE_ARGS\")) {\n throw new UsageError((error as Error).message, { cause: error });\n }\n throw error;\n }\n};\n\nexport const whole = (\n name: string,\n text: string | undefined,\n fallback: number,\n min: number,\n): number => {\n if (text === undefined) return fallback;\n if (!/^\\s*\\d+\\s*$/.test(text))\n throw new UsageError(`--${name} wants a whole number, not \"${text}\"`);\n const value = Number(text);\n if (value < min) throw new UsageError(`--${name} wants ${String(min)} or more`);\n return value;\n};\n\n/** An environment variable that switches a feature on is 1 or 0 (also true or false, yes or no). */\nexport const envFlag = (\n env: Readonly<Record<string, string | undefined>>,\n name: string,\n): boolean => {\n const raw = env[name];\n const value = raw?.trim().toLowerCase() ?? \"\";\n if ([\"\", \"0\", \"false\", \"no\", \"off\"].includes(value)) return false;\n if ([\"1\", \"true\", \"yes\", \"on\"].includes(value)) return true;\n throw new UsageError(`${name} is 1 or 0, not \"${raw ?? \"\"}\"`);\n};\n\n/** The store: a leading `~` is the home folder, and a relative path starts at the working folder. */\nexport const storeDir = (text: string | undefined): string => {\n if (text === undefined) return join(homedir(), \".polite-crawler\");\n if (text.trim() === \"\") throw new UsageError(\"--dir wants a folder\");\n if (text === \"~\") return homedir();\n if (/^~[\\\\/]/.test(text)) return resolve(homedir(), text.slice(2));\n return resolve(text);\n};\n\nexport const FS_CODES: ReadonlySet<string> = new Set([\n \"EEXIST\",\n \"ENOENT\",\n \"ENOTDIR\",\n \"EACCES\",\n \"EPERM\",\n \"EROFS\",\n \"EISDIR\",\n]);\n\nexport const ok = (page: PageResult): boolean =>\n page.outcome === \"ok\" || page.outcome === \"not-modified\";\n\n/** A fetch is done when the page came, or when an archived copy came in its place. */\nexport const done = (page: PageResult): boolean => ok(page) || page.archived !== undefined;\n\nexport const describePage = (page: PageResult): string => {\n const head = `${ok(page) ? \"✓\" : page.archived !== undefined ? \"~\" : \"✗\"} ${page.url}`;\n const facts = [\n `${page.outcome} ${String(page.status)}`,\n page.doc === null ? null : `doc ${page.doc}`,\n page.chars > 0 ? `${String(page.chars)} chars` : null,\n page.rendered ? \"rendered\" : null,\n page.structured.length > 0 ? `JSON-LD: ${page.structured.join(\", \")}` : null,\n ].filter((fact) => fact !== null);\n // ── archived fallback: an archived copy says so on a line of its own ──\n const archived =\n page.archived === undefined\n ? []\n : [\n ` archived copy (${page.archived.source}, captured ${page.archived.timestamp}): ${page.archived.url}`,\n ];\n return [\n head,\n ` ${facts.join(\" · \")}`,\n ...archived,\n ...(page.note === null ? [] : [` ${page.note}`]),\n ].join(\"\\n\");\n};\n\n/** This function reads the key file of `--sign-key`. An error names the file, and not the key. */\nexport const keyFile = (path: string): string => {\n const file =\n path === \"~\"\n ? homedir()\n : /^~[\\\\/]/.test(path)\n ? resolve(homedir(), path.slice(2))\n : resolve(path);\n try {\n return readFileSync(file, \"utf8\");\n } catch (error) {\n const code = (error as { code?: unknown }).code;\n throw new UsageError(\n `--sign-key: the file ${file} does not open (${typeof code === \"string\" ? code : \"error\"})`,\n { cause: error },\n );\n }\n};\n\n/**\n * This function gives the type of the URL of `--signature-agent`. An origin names a key directory at\n * the well-known path. A URL with a path names a JSON Web Key Set (`jwks_uri`), for a host that cannot\n * serve the well-known path, for example a project site of GitHub Pages.\n */\nexport const agentTypeOf = (url: string): \"directory\" | \"jwks_uri\" => {\n try {\n const parsed = new URL(url);\n return parsed.pathname === \"/\" && parsed.search === \"\" ? \"directory\" : \"jwks_uri\";\n } catch {\n // the signer gives the error of a URL that does not parse\n return \"directory\";\n }\n};\n\n/** This is the result of a URL that the crawler did not fetch. */\nexport const failedPage = (url: string, note: string): PageResult => ({\n url,\n doc: null,\n outcome: \"error\",\n status: 0,\n title: null,\n chars: 0,\n rendered: false,\n structured: [],\n links: 0,\n captures: [],\n capture: null,\n capture_count: 0,\n fetched_at: null,\n from_cache: false,\n held_back: false,\n note,\n});\n\n/** The options of `discover` and `crawl`, read from the command line before the store opens. */\nexport interface CrawlLimits {\n readonly maxUrls: number;\n readonly feeds: readonly string[];\n readonly maxPages: number;\n readonly maxDepth: number;\n readonly budgetMs: number;\n readonly include: readonly string[];\n readonly exclude: readonly string[];\n readonly sitemaps: boolean;\n readonly allowHosts: readonly string[] | undefined;\n}\n\nexport const crawlOptionsOf = (values: Values): CrawlLimits => {\n let budgetMs = 600_000;\n if (values.budget !== undefined) {\n try {\n budgetMs = durationMs(values.budget);\n } catch (error) {\n throw new UsageError(`--budget: ${(error as Error).message}`, { cause: error });\n }\n }\n return {\n maxUrls: whole(\"max-urls\", values[\"max-urls\"], 1000, 1),\n feeds: values.feed ?? [],\n maxPages: whole(\"max-pages\", values[\"max-pages\"], 100, 1),\n maxDepth: whole(\"max-depth\", values[\"max-depth\"], 3, 0),\n budgetMs,\n include: values.include ?? [],\n exclude: values.exclude ?? [],\n sitemaps: values.sitemaps === true,\n allowHosts: values[\"allow-host\"],\n };\n};\n\nexport const describeSummary = (summary: CrawlSummary): string =>\n `${String(summary.pages)} pages: ${String(summary.fetched)} fetched (${String(summary.from_cache)} from the store), ${String(summary.refused)} refused, ${String(summary.failed)} failed. ${String(summary.skipped)} links not followed, ${String(summary.queued)} URLs waiting. Stopped (${summary.stopped}): ${summary.reason}.`;\n\n/** A text that starts with a date is a time. A different text is a capture id. */\nexport const DATE_START: RegExp = /^\\d{4}-\\d{2}-\\d{2}/;\n\n/** This function reads a time of the command line. A text that is not a time is a usage error. */\nexport const timeOf = (name: string, text: string | undefined): string | undefined => {\n if (text === undefined) return undefined;\n if (!DATE_START.test(text) || !Number.isFinite(Date.parse(text)))\n throw new UsageError(`--${name} wants a time, such as 2026-10-01T12:00:00Z, not \"${text}\"`);\n return text;\n};\n\n/** This function reads a version of `diff`: a time, or a capture id. */\nexport const versionOf = (name: string, text: string | undefined): VersionChoice | undefined => {\n if (text === undefined) return undefined;\n if (text.trim() === \"\") throw new UsageError(`--${name} wants a capture id or a time`);\n return DATE_START.test(text) ? { at: timeOf(name, text)! } : text;\n};\n\n/** These are the options of `watch`, read from the command line before the store opens. */\nexport interface WatchLine {\n readonly action: \"add\" | \"remove\" | \"list\" | \"run\";\n readonly urls: readonly string[];\n readonly every: string | undefined;\n readonly confirm: 1 | 2 | undefined;\n readonly labels: Readonly<Record<string, string>> | undefined;\n}\n\nexport const ACTIONS: Readonly<Record<string, readonly [number, number]>> = {\n add: [1, Infinity],\n remove: [1, Infinity],\n list: [0, 0],\n run: [0, 0],\n};\n\n/** These options belong to one action of `watch`. */\nexport const WATCH_OWNER: Readonly<Record<string, string>> = {\n every: \"add\",\n confirm: \"add\",\n scope: \"add\",\n label: \"add\",\n render: \"add\",\n budget: \"run\",\n \"max-pages\": \"run\",\n \"sign-key\": \"run\",\n \"signature-agent\": \"run\",\n};\n\nexport const watchLineOf = (values: Values, rest: readonly string[]): WatchLine => {\n const [action, ...urls] = rest;\n const counts = action === undefined ? undefined : ACTIONS[action];\n if (counts === undefined)\n throw new UsageError(`watch wants add, remove, list or run, not \"${action ?? \"\"}\"`);\n if (urls.length < counts[0] || urls.length > counts[1]) {\n throw new UsageError(\n counts[0] > 0 ? `watch ${action!} wants at least one URL` : `watch ${action!} takes no URL`,\n );\n }\n for (const [option, owner] of Object.entries(WATCH_OWNER)) {\n if (values[option as keyof typeof values] !== undefined && owner !== action)\n throw new UsageError(`--${option} is an option of watch ${owner}`);\n }\n if (values.every !== undefined) {\n let ms: number;\n try {\n ms = durationMs(values.every);\n } catch (error) {\n throw new UsageError(`--every: ${(error as Error).message}`, { cause: error });\n }\n if (ms < 3_600_000)\n throw new UsageError(\"--every wants 1h or more, so a watch cannot hammer a site\");\n }\n if (values.confirm !== undefined && values.confirm !== \"1\" && values.confirm !== \"2\")\n throw new UsageError(`--confirm is 1 or 2, not \"${values.confirm}\"`);\n let labels: Record<string, string> | undefined;\n for (const label of values.label ?? []) {\n const at = label.indexOf(\"=\");\n if (at < 1) throw new UsageError(`--label wants name=value, not \"${label}\"`);\n labels = { ...labels, [label.slice(0, at)]: label.slice(at + 1) };\n }\n for (const url of urls) {\n if (!/^https?:\\/\\//i.test(url) || !URL.canParse(url))\n throw new UsageError(`watch ${action!} wants http or https URLs, not \"${url}\"`);\n }\n return {\n action: action as WatchLine[\"action\"],\n urls: [...new Set(urls)],\n every: values.every,\n confirm: values.confirm === undefined ? undefined : values.confirm === \"1\" ? 1 : 2,\n labels,\n };\n};\n\nexport const everyText = (ms: number): string =>\n ms % 86_400_000 === 0\n ? `${String(ms / 86_400_000)}d`\n : ms % 3_600_000 === 0\n ? `${String(ms / 3_600_000)}h`\n : `${String(Math.round(ms / 60_000))}m`;\n\nexport const describeEntry = (entry: WatchEntry): string => {\n const facts = [\n `every ${everyText(entry.every_ms)}`,\n `next ${entry.next_at}`,\n entry.checked_at === null\n ? \"not checked yet\"\n : `last ${entry.last_kind ?? \"check\"} at ${entry.checked_at}`,\n `${String(entry.checks)} checks, ${String(entry.changes)} changes`,\n ];\n return [\n entry.url,\n ` ${facts.join(\" · \")}`,\n ...(entry.pending === null\n ? []\n : [` suspected: ${entry.pending.kind} since ${entry.pending.since}`]),\n ].join(\"\\n\");\n};\n\nexport const describeEvent = (event: WatchEvent): string => {\n const state = event.suspected ? \" (suspected)\" : event.confirmed ? \" (confirmed)\" : \"\";\n const facts = [\n `${event.outcome} ${String(event.status)}`,\n event.capture === null ? null : `capture ${event.capture}`,\n event.previous === null || event.previous === event.capture ? null : `from ${event.previous}`,\n event.diff === null\n ? null\n : `+${String(event.diff.lines_added)} −${String(event.diff.lines_removed)} lines, ${String(event.diff.meta_changes)} metadata changes`,\n event.held_back ? \"held back\" : null,\n ].filter((fact) => fact !== null);\n return [\n `${event.kind}${state} ${event.url}`,\n ` ${facts.join(\" · \")}`,\n ...(event.note === null ? [] : [` ${event.note}`]),\n ].join(\"\\n\");\n};\n\nexport const describeWatchSummary = (summary: WatchSummary): string =>\n `${String(summary.checked)} checked: ${String(summary.new)} new, ${String(summary.changed)} changed, ${String(summary.unchanged)} unchanged, ${String(summary.gone)} gone, ${String(summary.refused)} refused, ${String(summary.error)} errors. ${String(summary.suspected)} suspected, ${String(summary.confirmed)} confirmed, ${String(summary.due)} still due. Stopped (${summary.stopped}): ${summary.reason}.`;\n\n// ── daemon ──────────────────────────────────────────────────────────────────\n\n/** These are the options of `daemon`, read from the command line before the store opens. */\nexport interface DaemonLine {\n /** This value is `run` (the daemon itself) or `status`. */\n readonly action: \"run\" | \"status\";\n readonly tickMs: number | undefined;\n readonly jitterMs: number | undefined;\n readonly budgetMs: number | undefined;\n readonly maxPages: number | undefined;\n readonly graceMs: number | undefined;\n /** This file gets the change events. Without it, they go to the standard output. */\n readonly events: string | undefined;\n /** This command of the operator gets each change event on its standard input. */\n readonly onChange: string | undefined;\n /** These options serve MCP over HTTP, or undefined without `--mcp-http`. */\n readonly mcp:\n | {\n readonly port: number;\n readonly host: string;\n readonly allowRemote: boolean;\n readonly tokenFile: string | undefined;\n }\n | undefined;\n}\n\n/** This function reads a file name of the command line: a leading `~` is the home folder. */\nconst fileOf = (text: string): string =>\n text === \"~\"\n ? homedir()\n : /^~[\\\\/]/.test(text)\n ? resolve(homedir(), text.slice(2))\n : resolve(text);\n\n/** This function reads a duration of `daemon`, with a floor in milliseconds. */\nconst daemonDuration = (\n name: string,\n text: string | undefined,\n floor: number,\n): number | undefined => {\n if (text === undefined) return undefined;\n let ms: number;\n try {\n ms = durationMs(text);\n } catch (error) {\n throw new UsageError(`--${name}: ${(error as Error).message}`, { cause: error });\n }\n if (ms < floor) {\n throw new UsageError(\n floor === 0\n ? `--${name} wants 0 or more`\n : `--${name} wants ${String(floor / 1000)}s or more`,\n );\n }\n return ms;\n};\n\n/** These options of `daemon` belong to its MCP server, so they need `--mcp-http`. */\nconst MCP_OPTIONS = [\"mcp-port\", \"mcp-host\", \"mcp-token-file\", \"allow-remote\"] as const;\n\n/** This function checks the options of `daemon` and of `daemon status`. */\nexport const daemonLineOf = (values: Values, rest: readonly string[]): DaemonLine => {\n const [action] = rest;\n if (action !== undefined && action !== \"status\")\n throw new UsageError(`daemon takes no argument, or status, not \"${action}\"`);\n if (action === \"status\") {\n const run = [...DAEMON_RUN_OPTIONS, \"allow-user-provided\", \"sign-key\", \"signature-agent\"];\n for (const option of run) {\n if (values[option as keyof Values] !== undefined)\n throw new UsageError(`--${option} is an option of daemon, not of daemon status`);\n }\n }\n if (values[\"mcp-http\"] !== true) {\n for (const option of MCP_OPTIONS) {\n if (values[option] !== undefined) throw new UsageError(`--${option} goes with --mcp-http`);\n }\n }\n const host = (values[\"mcp-host\"] ?? \"127.0.0.1\").trim();\n if (host === \"\") throw new UsageError(\"--mcp-host wants a host\");\n const allowRemote = values[\"allow-remote\"] === true;\n if (values[\"mcp-http\"] === true && !isLoopbackHost(host) && !allowRemote) {\n throw new UsageError(\n `--mcp-host ${host} is not a loopback address: add --allow-remote to serve other computers, after you read the warning in the README`,\n );\n }\n const port = whole(\"mcp-port\", values[\"mcp-port\"], 9307, 0);\n if (port > 65_535) throw new UsageError(\"--mcp-port wants a port from 0 to 65535\");\n const onChange = values[\"on-change\"];\n if (onChange !== undefined && onChange.trim() === \"\")\n throw new UsageError(\"--on-change wants a command\");\n const events = values.events;\n if (events !== undefined && events.trim() === \"\") throw new UsageError(\"--events wants a file\");\n const tokenFile = values[\"mcp-token-file\"];\n if (tokenFile !== undefined && tokenFile.trim() === \"\")\n throw new UsageError(\"--mcp-token-file wants a file\");\n return {\n action: action === \"status\" ? \"status\" : \"run\",\n tickMs: daemonDuration(\"tick\", values.tick, 1000),\n jitterMs: daemonDuration(\"jitter\", values.jitter, 0),\n budgetMs: daemonDuration(\"tick-budget\", values[\"tick-budget\"], 1000),\n maxPages:\n values[\"tick-pages\"] === undefined\n ? undefined\n : whole(\"tick-pages\", values[\"tick-pages\"], 1, 1),\n graceMs: daemonDuration(\"grace\", values.grace, 0),\n events: events === undefined ? undefined : fileOf(events),\n onChange,\n mcp:\n values[\"mcp-http\"] === true\n ? {\n port,\n host,\n allowRemote,\n tokenFile: tokenFile === undefined ? undefined : fileOf(tokenFile),\n }\n : undefined,\n };\n};\n\n/** This function gives the first line of `daemon status`: the daemon runs, stopped, or stopped with no clean stop. */\nconst daemonHead = (state: DaemonState): string =>\n state.alive\n ? `the daemon runs (process ${String(state.pid)}) since ${state.started_at}, on the store ${state.store}`\n : state.running\n ? `the daemon of process ${String(state.pid)} does not run: it stopped with no clean stop. Its state is of ${state.updated_at}`\n : `the daemon stopped at ${state.stopped_at ?? state.updated_at}`;\n\n/** This function gives the line of the last tick of `daemon status`. */\nconst lastTick = (state: DaemonState): string => {\n const tick = state.last_tick;\n if (tick === null) return \"last tick: none yet\";\n if (tick.summary === null) return `last tick: ${tick.ended_at}, failed: ${tick.error ?? \"error\"}`;\n const summary = tick.summary;\n return `last tick: ${tick.ended_at}, ${String(summary.checked)} checked, ${String(summary.confirmed)} changes, ${String(summary.refused)} refused, ${String(summary.error)} errors (${summary.stopped})`;\n};\n\n/** This function gives the text of `daemon status`. */\nexport const describeDaemonStatus = (state: DaemonState | undefined, dir: string): string => {\n if (state === undefined) return `no daemon has run on the store ${dir}`;\n const lines = [daemonHead(state), lastTick(state)];\n if (state.alive && state.next_tick_at !== null) lines.push(`next tick: ${state.next_tick_at}`);\n lines.push(\n `${String(state.ticks)} ticks, ${String(state.checked)} checks, ${String(state.changes)} changes`,\n );\n if (state.next_due.length === 0) lines.push(\"next due: no watch\");\n else {\n lines.push(\"next due:\");\n for (const due of state.next_due) {\n lines.push(\n ` ${due.next_at} ${due.url} (every ${everyText(due.every_ms)}, last ${due.last_kind ?? \"none\"})`,\n );\n }\n }\n if (state.mcp !== null) {\n const where = state.mcp.token_file === null ? \"\" : ` (the token is in ${state.mcp.token_file})`;\n lines.push(`MCP: ${state.mcp.url}${where}`);\n lines.push(\n state.tasks.length === 0\n ? \"tasks: none running\"\n : `tasks: ${String(state.tasks.length)} running`,\n );\n for (const task of state.tasks) {\n lines.push(\n ` ${task.id} ${task.tool} ${task.status}${task.message === null ? \"\" : `: ${task.message}`}`,\n );\n }\n }\n lines.push(\n state.last_error === null\n ? \"last error: none\"\n : `last error: ${state.last_error.at}: ${state.last_error.message}`,\n );\n return lines.join(\"\\n\");\n};\n\n// ── end of daemon ───────────────────────────────────────────────────────────\n\n/** These are the kinds of a field (`FIELD_KINDS` of `extract.ts`). The command line knows them before it loads the crawler. */\nexport const KINDS: readonly string[] = [\n \"text\",\n \"number\",\n \"money\",\n \"date\",\n \"url\",\n \"email\",\n \"phone\",\n];\n\nexport interface CliField {\n readonly value: string;\n readonly kind?: string;\n}\n\n/** This function reads the fields of the command line: `name=value`, with `:kind` at the end. The name and the value cannot be empty. */\nexport const fieldsOf = (given: readonly string[] | undefined): Record<string, CliField> => {\n if (given === undefined || given.length === 0)\n throw new UsageError(\"extract wants at least one --field name=value[:kind]\");\n const fields: Record<string, CliField> = {};\n for (const text of given) {\n const equals = text.indexOf(\"=\");\n if (equals <= 0) throw new UsageError(`--field wants name=value[:kind], not \"${text}\"`);\n const name = text.slice(0, equals);\n let value = text.slice(equals + 1);\n let kind: string | undefined;\n const jsonLd = value.lastIndexOf(\":json-ld:\");\n const colon = value.lastIndexOf(\":\");\n if (jsonLd !== -1) {\n kind = value.slice(jsonLd + 1);\n value = value.slice(0, jsonLd);\n if (kind === \"json-ld:\" || /\\s/.test(kind))\n throw new UsageError(\n `--field ${name} wants a path after json-ld:, such as json-ld:Product.offers.price`,\n );\n } else if (colon !== -1 && KINDS.includes(value.slice(colon + 1))) {\n kind = value.slice(colon + 1);\n value = value.slice(0, colon);\n }\n if (value.trim() === \"\") throw new UsageError(`--field ${name} wants a value`);\n if (fields[name] !== undefined) throw new UsageError(`--field ${name} is given two times`);\n fields[name] = kind === undefined ? { value } : { value, kind };\n }\n return fields;\n};\n\n// ── hybrid search ───────────────────────────────────────────────────────────\n\n/** This is the embedder of the command line: its adapter, its model and the URL of Ollama. */\nexport interface EmbedderChoice {\n readonly kind: \"transformers\" | \"ollama\";\n readonly model: string | undefined;\n readonly url: string | undefined;\n readonly allowRemote: boolean;\n}\n\n/**\n * This function reads the embedder of the command line, or of the environment for the commands\n * that use one. `--embed-model` and `--ollama-url` need `--embedder`.\n */\nexport const embedderOf = (\n values: Values,\n env: Readonly<Record<string, string | undefined>>,\n command: string,\n): EmbedderChoice | undefined => {\n const uses = OWNER[\"embedder\"]!.split(\" \").includes(command);\n const fromEnv = (name: string): string | undefined => (uses ? env[name] || undefined : undefined);\n const name = values.embedder ?? fromEnv(\"POLITE_CRAWLER_EMBEDDER\");\n const model = values[\"embed-model\"] ?? fromEnv(\"POLITE_CRAWLER_EMBED_MODEL\");\n const url = values[\"ollama-url\"] ?? fromEnv(\"POLITE_CRAWLER_OLLAMA_URL\");\n const allowRemote =\n values[\"allow-remote-ollama\"] === true ||\n (uses && envFlag(env, \"POLITE_CRAWLER_ALLOW_REMOTE_OLLAMA\"));\n if (name === undefined) {\n if (values[\"embed-model\"] !== undefined || values[\"ollama-url\"] !== undefined)\n throw new UsageError(\n \"--embed-model and --ollama-url go with --embedder transformers or ollama\",\n );\n return undefined;\n }\n if (name !== \"transformers\" && name !== \"ollama\")\n throw new UsageError(`--embedder is transformers or ollama, not \"${name}\"`);\n if (model !== undefined && model.trim() === \"\")\n throw new UsageError(\"--embed-model wants a model\");\n if (values[\"ollama-url\"] !== undefined && name !== \"ollama\")\n throw new UsageError(\"--ollama-url goes with --embedder ollama\");\n return {\n kind: name,\n model,\n url: name === \"ollama\" ? url : undefined,\n allowRemote,\n };\n};\n\n/** This function gives the text of `embed`: the counts and the reason of the stop. */\nexport const describeEmbed = (result: EmbedResult): string =>\n [\n `${String(result.embedded)} texts got vectors (${String(result.chunks)} chunks); ${String(result.reused)} documents had vectors; ${String(result.held_back)} held back, so not sent to the model`,\n `${String(result.docs)} documents examined in ${String(result.ms)} ms with ${result.embedder}. Stopped (${result.stopped})${result.stopped === \"done\" ? \"\" : \": start embed again for the rest\"}.`,\n ].join(\"\\n\");\n\n// ── the texts of the results ────────────────────────────────────────────────\n\n/** This function gives the pages of `fetch`: a rejected fetch is an error of its URL only. A page that needs a browser names the reason when rendering is off. */\nexport const fetchedPages = (\n urls: readonly string[],\n settled: ReadonlyArray<PromiseSettledResult<PageResult>>,\n renderOff: string | undefined,\n): PageResult[] =>\n settled.map((result, at) => {\n const url = urls[at]!;\n if (result.status === \"rejected\") {\n const reason: unknown = result.reason;\n return failedPage(\n url,\n `${url}: ${reason instanceof Error ? reason.message : String(reason)}`,\n );\n }\n const page = result.value;\n return renderOff !== undefined && page.note !== null && /no browser/i.test(page.note)\n ? { ...page, note: `this page needs JavaScript, and rendering is off: ${renderOff}` }\n : page;\n });\n\n/** This function gives the text of the results of `find`. An archived copy says so. */\nexport const describeFound = (found: readonly Found[]): string =>\n found.length === 0\n ? \"nothing stored matches\"\n : found\n .map(\n (hit) =>\n `${hit.doc} ${hit.title}${hit.archived === undefined ? \"\" : ` [archived copy, captured ${hit.archived.timestamp}]`}${hit.passage === undefined ? \"\" : ` [passage ${String(hit.passage.start)}–${String(hit.passage.end)}]`}\\n ${hit.snippet.replace(/\\s+/g, \" \")}`,\n )\n .join(\"\\n\");\n\n/** This function gives the text of `read`. With `--at`, the head names the capture and its fetch time. */\nexport const describeRead = (read: ReadResult, at: string | undefined): string => {\n const version = at === undefined ? \"\" : ` ${read.capture} (${read.fetched_at})`;\n return read.before_first === true\n ? (read.note ?? \"\")\n : read.held_back\n ? `${read.note ?? \"\"}\\nAdd --user-provided if you gave this URL yourself. The crawler logs that read.`\n : `${read.title}\\n${read.url ?? \"\"}\\n${read.archived === undefined ? \"\" : `${read.note ?? \"\"}\\n`}[${String(read.from)}–${String(read.to)} of ${String(read.total)}]${version}\\n\\n${read.text}`;\n};\n\n/** This function gives the text of `verify`. An archived copy and an earlier capture say so. */\nexport const describeVerify = (verified: VerifyResult): string =>\n verified.found\n ? `found at ${String(verified.start)}–${String(verified.end)} in ${verified.capture ?? \"\"}${verified.stale ? \" (an earlier capture)\" : \"\"}${verified.archived === undefined ? \"\" : ` (an archived copy from ${verified.archived.source}, captured ${verified.archived.timestamp})`}: ${verified.excerpt ?? \"\"}`\n : verified.held_back || verified.before_first === true\n ? (verified.note ?? \"held back\")\n : \"not found\";\n\n/** This function gives the lines of `discover`: each URL with its date, then the count of the files. */\nexport const describeDiscover = (found: DiscoverResult): string[] => {\n const read = found.sources.filter((source) => source.kind !== null).length;\n return [\n ...found.urls.map((url) =>\n url.lastmod === null ? url.url : `${url.url} ${url.lastmod.slice(0, 10)}`,\n ),\n `${String(found.urls.length)} URLs from ${String(read)} of ${String(found.sources.length)} files (${found.stopped}${found.note === null ? \"\" : `: ${found.note}`})`,\n ];\n};\n\n/** This function gives the text of `reindex`: the counts, the documents that stay, and the captures that it skipped. */\nexport const describeReindex = (counts: ReindexResult): string =>\n [\n `${String(counts.captures)} captures replayed, ${String(counts.docs)} documents, ${String(counts.urls)} URLs`,\n ...(counts.carried > 0\n ? [\n `${String(counts.carried)} documents of other recipes kept: ${counts.unknown_recipes.join(\", \")}`,\n ]\n : []),\n ...(counts.failed + counts.missing + counts.damaged > 0\n ? [\n `skipped: ${String(counts.failed)} captures whose reader failed, ${String(counts.missing)} with no stored body, ${String(counts.damaged)} damaged lines of the log`,\n ]\n : []),\n ].join(\"\\n\");\n\n/** This function gives the lines of `extract`: a line for each field, its candidates and its note, then the counts. */\nexport const describeExtract = (result: ExtractResult): string[] => {\n const lines: string[] = [];\n for (const [name, field] of Object.entries(result.fields)) {\n const mark = field.status === \"verified\" || field.status === \"normalized\" ? \"✓\" : \"✗\";\n const place =\n field.span !== null\n ? ` at ${String(field.span.start)}–${String(field.span.end)}${field.span.in === \"full_text\" ? \" of the whole text\" : \"\"}`\n : field.path !== null\n ? ` at ${field.path}`\n : \"\";\n const how = field.normalization === null ? \"\" : ` (${field.normalization})`;\n const stored = field.raw === null ? \"\" : `: ${JSON.stringify(field.raw)}`;\n lines.push(`${mark} ${name}: ${field.status}${how}${place}${stored}`);\n for (const candidate of field.candidates) {\n const at =\n candidate.span !== null\n ? `${String(candidate.span.start)}–${String(candidate.span.end)}`\n : (candidate.path ?? \"\");\n lines.push(\n ` ${at}: ${JSON.stringify(candidate.raw)} can be ${candidate.readings.join(\" or \")}`,\n );\n }\n if (field.note !== null && field.status !== \"held-back\") lines.push(` ${field.note}`);\n }\n const summary = result.summary;\n lines.push(\n result.held_back\n ? `${result.note ?? \"held back\"}\\nAdd --user-provided if you gave this URL yourself. The crawler logs that check.`\n : `${String(summary.fields)} fields: ${String(summary.verified)} verified, ${String(summary.normalized)} normalized, ${String(summary.ambiguous)} ambiguous, ${String(summary.not_found)} not found, ${String(summary.invalid)} not valid, in ${result.capture ?? \"no capture\"}${result.note === null ? \"\" : `. ${result.note}`}`,\n );\n return lines;\n};\n\n/** This function gives the exit code of `extract`: 0 when each field is verified or normalized. */\nexport const extractCode = (result: ExtractResult): number => {\n const done = result.summary.verified + result.summary.normalized;\n return done === result.summary.fields && done > 0 ? 0 : 1;\n};\n\n// ── the command `export` ────────────────────────────────────────────────────\n\n/** These are the format and the options of an export, read from the command line before the export starts. */\nexport interface ExportPlan {\n readonly format: \"warc\" | \"wacz\";\n readonly options: {\n readonly out: string;\n readonly since: string | undefined;\n readonly until: string | undefined;\n readonly host: string | undefined;\n readonly urlPrefix: string | undefined;\n readonly ids: readonly string[] | undefined;\n readonly includeRefusals: boolean;\n readonly overwrite: boolean;\n };\n}\n\n/** This function checks the options of `export`, and gives its format and its options. */\nexport const exportPlan = (values: ExportValues): ExportPlan => {\n const out = values.out;\n if (out === undefined || out.trim() === \"\") throw new UsageError(\"export wants --out <file>\");\n const format =\n values.format ??\n (/\\.wacz$/i.test(out) ? \"wacz\" : /\\.warc\\.gz$/i.test(out) ? \"warc\" : undefined);\n if (format !== \"warc\" && format !== \"wacz\") {\n throw new UsageError(\n values.format === undefined\n ? \"export wants --format warc or wacz, or a file that ends in .warc.gz or .wacz\"\n : `--format is warc or wacz, not \"${values.format}\"`,\n );\n }\n for (const name of [\"since\", \"until\"] as const) {\n const value = values[name];\n if (value !== undefined && !Number.isFinite(Date.parse(value)))\n throw new UsageError(`--${name} wants a time, such as 2026-10-01, not \"${value}\"`);\n }\n if (values.host !== undefined && (values.host.trim() === \"\" || /[/:@\\s]/.test(values.host)))\n throw new UsageError(`--host wants a host name, such as example.com, not \"${values.host}\"`);\n const file =\n out === \"~\" ? homedir() : /^~[\\\\/]/.test(out) ? resolve(homedir(), out.slice(2)) : resolve(out);\n return {\n format,\n options: {\n out: file,\n since: values.since,\n until: values.until,\n host: values.host,\n urlPrefix: values.prefix,\n ids: values.id,\n includeRefusals: values[\"include-refusals\"] === true,\n overwrite: values.overwrite === true,\n },\n };\n};\n\n/** This function gives the text of an export: the file, the counts, and what the archive left out. */\nexport const describeExport = (\n result: WarcResult | WaczResult,\n includeRefusals: boolean,\n): string => {\n const left = [\n result.bodiless > 0 && !includeRefusals\n ? `${String(result.bodiless)} captures with no body (add --include-refusals to keep them as metadata)`\n : null,\n result.missing > 0 ? `${String(result.missing)} captures with no stored body` : null,\n result.damaged > 0 ? `${String(result.damaged)} damaged lines of the log` : null,\n ].filter((line) => line !== null);\n return [\n `wrote ${result.out} (${String(result.bytes)} bytes)`,\n ` ${String(result.captures)} captures: ${String(result.responses)} responses, ${String(result.revisits)} revisits, ${String(result.conversions)} rendered pages, ${String(result.metadata)} metadata records${result.format === \"wacz\" ? `, ${String(result.pages)} pages` : \"\"}`,\n ...(left.length > 0 ? [` left out: ${left.join(\", \")}`] : []),\n ].join(\"\\n\");\n};\n\n// ── the whole command line ──────────────────────────────────────────────────\n\n/** These are the values of the command line, as `parseArgs` gives them. */\nexport type Values = ParsedArgs[\"values\"];\n\n/** The environment variables that the command line reads. */\nexport type Env = Readonly<Record<string, string | undefined>>;\n\n/** This is a command line that is valid, read before the store opens. */\nexport interface Line {\n readonly command: string;\n readonly rest: readonly string[];\n readonly values: Values;\n readonly render: \"auto\" | \"never\" | \"always\";\n readonly rank: \"exact\" | \"fast\" | \"hybrid\";\n readonly limit: number;\n /** This value is the start of `read`. `diff` reads `--from` as a version (`diffFrom`). */\n readonly from: number;\n readonly length: number;\n readonly maxAge: number;\n readonly crawlLimits: CrawlLimits;\n readonly at: string | undefined;\n readonly diffFrom: VersionChoice | undefined;\n readonly diffTo: VersionChoice | undefined;\n readonly watchLine: WatchLine | undefined;\n /** These are the options of `daemon`, or undefined for a different command. */\n readonly daemonLine: DaemonLine | undefined;\n readonly extractFields: Record<string, CliField> | undefined;\n readonly exportPlan: ExportPlan | undefined;\n readonly dir: string;\n readonly contact: string | undefined;\n readonly allowPrivate: boolean;\n readonly allowUserProvided: boolean;\n /** This file is the key of `--sign-key` or `POLITE_CRAWLER_SIGN_KEY`. The command reads it after the checks. */\n readonly signKey: string | undefined;\n readonly signatureAgent: string | undefined;\n /** This value is the embedder of hybrid search, from `--embedder` or `POLITE_CRAWLER_EMBEDDER`. */\n readonly embedder: EmbedderChoice | undefined;\n /** This value is `--max-docs` of `embed`. */\n readonly maxDocs: number | undefined;\n}\n\n/** This is the result of `readLine`: the version, the help, or a command to do. */\nexport type Parsed =\n { readonly kind: \"version\" } | { readonly kind: \"help\" } | ({ readonly kind: \"command\" } & Line);\n\n/**\n * This function reads the whole command line before anything opens the store. A wrong command line\n * throws a `UsageError`, so the command exits with code 2 and makes no store.\n */\nexport const readLine = (args: readonly string[], env: Env): Parsed => {\n const { values, positionals } = parse(args);\n if (values.version === true) return { kind: \"version\" };\n const [command, ...rest] = positionals;\n if (values.help === true || command === undefined) return { kind: \"help\" };\n const spec = COMMANDS[command];\n if (spec === undefined) throw new UsageError(`no command \"${command}\"`);\n if (rest.length < spec.min || rest.length > spec.max) throw new UsageError(spec.wants);\n for (const [option, owner] of Object.entries(OWNER)) {\n const owners = owner.split(\" \");\n if (values[option as keyof typeof values] !== undefined && !owners.includes(command))\n throw new UsageError(`--${option} is an option of ${owners.join(\" or \")}`);\n }\n const render = values.render ?? \"auto\";\n if (render !== \"auto\" && render !== \"never\" && render !== \"always\")\n throw new UsageError(`--render is auto, never or always, not \"${render}\"`);\n const rank = values.rank ?? \"exact\";\n if (rank !== \"exact\" && rank !== \"fast\" && rank !== \"hybrid\")\n throw new UsageError(`--rank is exact, fast or hybrid, not \"${rank}\"`);\n const embedder = embedderOf(values, env, command);\n if (rank === \"hybrid\" && embedder === undefined)\n throw new UsageError(\"--rank hybrid wants --embedder transformers or ollama\");\n if (command === \"embed\" && embedder === undefined)\n throw new UsageError(\"embed wants --embedder transformers or ollama\");\n const maxDocs =\n values[\"max-docs\"] === undefined ? undefined : whole(\"max-docs\", values[\"max-docs\"], 1, 1);\n const limit = whole(\"limit\", values.limit, 10, 1);\n // diff reads --from as a version, and read as a position\n const from = command === \"read\" ? whole(\"from\", values.from, 0, 0) : 0;\n const length = whole(\"length\", values.length, 4000, 1);\n const crawlLimits = crawlOptionsOf(values);\n const at = timeOf(\"at\", values.at);\n const diffFrom = command === \"diff\" ? versionOf(\"from\", values.from) : undefined;\n const diffTo = versionOf(\"to\", values.to);\n const watchLine = command === \"watch\" ? watchLineOf(values, rest) : undefined;\n const extractFields = command === \"extract\" ? fieldsOf(values.field) : undefined;\n const daemonLine = command === \"daemon\" ? daemonLineOf(values, rest) : undefined;\n const dir = storeDir(values.dir ?? (env[\"POLITE_CRAWLER_DIR\"] || undefined));\n const allowPrivate =\n values[\"allow-private\"] === true || envFlag(env, \"POLITE_CRAWLER_ALLOW_PRIVATE\");\n const allowUserProvided =\n values[\"allow-user-provided\"] === true ||\n ((command === \"mcp\" || daemonLine?.action === \"run\") &&\n envFlag(env, \"POLITE_CRAWLER_ALLOW_USER_PROVIDED\"));\n if (values[\"archive-despite-robots\"] === true && values[\"archive-fallback\"] !== true)\n throw new UsageError(\"--archive-despite-robots works only with --archive-fallback\");\n // of the actions of watch, only run sends requests, so only run signs\n const signs =\n OWNER[\"sign-key\"]!.split(\" \").includes(command) &&\n (watchLine === undefined || watchLine.action === \"run\") &&\n (daemonLine === undefined || daemonLine.action === \"run\");\n const signKey =\n values[\"sign-key\"] ?? (signs ? env[\"POLITE_CRAWLER_SIGN_KEY\"] || undefined : undefined);\n const signatureAgent =\n values[\"signature-agent\"] ??\n (signs ? env[\"POLITE_CRAWLER_SIGNATURE_AGENT\"] || undefined : undefined);\n if (command === \"key-directory\") {\n if (signKey === undefined) throw new UsageError(\"key-directory wants --sign-key <file>\");\n } else if ((signKey === undefined) !== (signatureAgent === undefined)) {\n throw new UsageError(\n \"--sign-key and --signature-agent go together: the key signs, and the agent tells a site where its public key is\",\n );\n }\n let maxAge: number;\n try {\n maxAge = durationMs(values[\"max-age\"] ?? \"24h\");\n } catch (error) {\n throw new UsageError(`--max-age: ${(error as Error).message}`, { cause: error });\n }\n return {\n kind: \"command\",\n command,\n rest,\n values,\n render,\n rank,\n limit,\n from,\n length,\n maxAge,\n crawlLimits,\n at,\n diffFrom,\n diffTo,\n watchLine,\n daemonLine,\n extractFields,\n exportPlan: command === \"export\" ? exportPlan(values) : undefined,\n dir,\n contact: values.contact ?? (env[\"POLITE_CRAWLER_CONTACT\"] || undefined),\n allowPrivate,\n allowUserProvided,\n signKey,\n signatureAgent,\n embedder,\n maxDocs,\n };\n};\n","#!/usr/bin/env node\nimport { join } from \"node:path\";\nimport type {\n CliField,\n CrawlLimits,\n EmbedderChoice,\n ExportPlan,\n Values,\n WatchLine,\n} from \"./cli-line.ts\";\nimport {\n FS_CODES,\n UsageError,\n agentTypeOf,\n describeDiscover,\n describeEmbed,\n describeEntry,\n describeEvent,\n describeExport,\n describeExtract,\n describeFound,\n describePage,\n describeRead,\n describeReindex,\n describeSummary,\n describeVerify,\n describeWatchSummary,\n done,\n extractCode,\n fetchedPages,\n keyFile,\n readLine,\n} from \"./cli-line.ts\";\nimport type { DiffResult } from \"./diff.ts\";\nimport type { Embedder } from \"./embed.ts\";\nimport type { CrawlPage } from \"./frontier.ts\";\nimport type { HybridCrawler, OpenOptions, OpenedCrawler, PoliteCrawler } from \"./open.ts\";\nimport { VERSION } from \"./version.ts\";\nimport type { WatchEntry, WatchEvent } from \"./watch.ts\";\n\n// This file imports only Node.js, the version and `cli-line.ts` at start. A command loads the\n// crawler, and only `mcp` and `daemon --mcp-http` load the MCP server, so `--version` and `--help`\n// answer at once.\n\n// node:sqlite of Node.js 22 writes \"SQLite is an experimental feature\" to the standard error at\n// each start. The command removes only that warning, before the store loads node:sqlite.\nconst emitWarning = process.emitWarning.bind(process) as (\n warning: string | Error,\n ...rest: unknown[]\n) => void;\nprocess.emitWarning = ((warning: string | Error, ...rest: unknown[]): void => {\n const first: unknown = rest[0];\n const kind =\n typeof first === \"string\"\n ? first\n : typeof first === \"object\" && first !== null && \"type\" in first\n ? first.type\n : warning instanceof Error\n ? warning.name\n : undefined;\n const text = typeof warning === \"string\" ? warning : warning.message;\n if (kind === \"ExperimentalWarning\" && text.startsWith(\"SQLite is an experimental feature\"))\n return;\n emitWarning(warning, ...rest);\n}) as typeof process.emitWarning;\n\nconst HELP = `polite-crawler ${VERSION}: fetch a source once, keep it, read it many times.\n\nUsage:\n polite-crawler fetch <url...> fetch pages into the store (robots.txt obeyed)\n polite-crawler find <query> search the stored pages, with no request\n polite-crawler read <doc> print a range of a stored document\n polite-crawler verify <doc> <quote> check that a quote is in a stored document\n polite-crawler reindex rebuild the search index from the capture log\n polite-crawler mcp serve the tools over MCP (standard input and output)\n polite-crawler export --out <file> write the stored captures as a WARC or WACZ archive\n polite-crawler discover <site> list the pages of a site from its sitemaps and feeds\n polite-crawler crawl <url...> fetch a site and follow its links, within a budget\n polite-crawler diff <doc> show what changed between two versions of a document\n polite-crawler watch add <url...> check these pages again on a schedule\n polite-crawler watch remove <url...> stop the watch of these pages\n polite-crawler watch list print the watch list\n polite-crawler watch run check the pages that are due, within a budget\n polite-crawler daemon check the watch list on a schedule, until SIGINT or SIGTERM;\n with --mcp-http, serve the MCP tools over local HTTP too\n polite-crawler daemon status print the state of the daemon of the store\n polite-crawler key-directory print the key directory (JWKS) of --sign-key, to host\n polite-crawler extract <doc> --field name=value[:kind]...\n check proposed field values against a stored page\n polite-crawler embed --embedder <name> give vectors to the stored pages, for find --rank hybrid\n\nOptions:\n --dir <path> the store (default $POLITE_CRAWLER_DIR, or ~/.polite-crawler)\n --contact <text> an email (From and User-Agent) or a URL (User-Agent only)\n ($POLITE_CRAWLER_CONTACT)\n --allow-private fetch loopback and private addresses too ($POLITE_CRAWLER_ALLOW_PRIVATE=1)\n --no-browser never open the headless browser\n --sign-key <file> sign each request (Web Bot Auth) with this Ed25519 key: PKCS#8 PEM or JWK\n ($POLITE_CRAWLER_SIGN_KEY)\n --signature-agent <url> the https origin of your key directory, or the URL of a\n JSON Web Key Set on a path, for --sign-key ($POLITE_CRAWLER_SIGNATURE_AGENT)\n --embedder <name> the local model of hybrid search, for find, embed and mcp: transformers\n (transformers.js in this process) or ollama ($POLITE_CRAWLER_EMBEDDER)\n --embed-model <m> the model: Xenova/bge-small-en-v1.5 (transformers) or nomic-embed-text\n (ollama) by default ($POLITE_CRAWLER_EMBED_MODEL)\n --ollama-url <url> the Ollama server, http://127.0.0.1:11434 by default ($POLITE_CRAWLER_OLLAMA_URL)\n --allow-remote-ollama permit an Ollama server on a different computer: the page text goes\n to it ($POLITE_CRAWLER_ALLOW_REMOTE_OLLAMA=1)\n --json print JSON\n -v, --version print the version\n -h, --help print this help\n\n fetch --render <mode> auto (default), never or always\n --max-age <age> reuse a stored copy this young: 30m, 24h (default), 7d; 0 fetches again\n --archive-fallback when the site refuses or is down, give a copy from the Wayback\n Machine or Common Crawl, marked archived (not a fresh fetch)\n --archive-despite-robots with --archive-fallback: also when the robots.txt of the\n site disallows the page now\n find --limit <n> how many results (default 10)\n --kind <kind> only this kind of document, such as page or pdf\n --rank <mode> exact (default): BM25 over each match; fast: leave common words\n out of the one-word part, faster for a long question; hybrid: BM25\n and the meaning of the text, fused (needs --embedder)\n read --from <n> where the range starts (default 0)\n --length <n> how many characters (default 4000)\n --user-provided you gave this URL yourself: read a page that refuses AI input (logged)\n read, verify, extract --at <time> use the version that was current then, such as\n 2026-10-01T12:00Z\n reindex rebuild; the documents of recipes that this command cannot read stay\n mcp --allow-user-provided let user_provided work when the client cannot ask you\n ($POLITE_CRAWLER_ALLOW_USER_PROVIDED=1)\n export --out <file> the archive to write: a name that ends in .warc.gz or .wacz\n --format <f> warc or wacz (default: from the name of the file)\n --since <time> only the captures from this time on, such as 2026-10-01\n --until <time> only the captures before this time\n --host <host> only the captures of this host and its subdomains\n --prefix <url> only the captures whose URL starts with this text\n --id <capture> only this capture; give --id again for more captures\n --include-refusals a metadata record for each capture with no body too\n --overwrite replace the file when it exists\n discover --max-urls <n> how many URLs (default 1000)\n --feed <url> read this RSS or Atom feed too (repeat it)\n --max-age <age> as for fetch\n crawl --max-pages <n> how many pages (default 100)\n --max-depth <n> how many links from a seed (default 3; 0 fetches the seeds only)\n --budget <time> the longest time: 30s, 2m, 10m (default); no request starts after it\n --include <pattern> follow only the URLs that match, for example /blog/* (repeat it)\n --exclude <pattern> follow no URL that matches, for example *.pdf$ (repeat it)\n --sitemaps add the pages of the sitemaps of the site\n --render, --max-age as for fetch\n discover, crawl --allow-host <host> keep the URLs on this host too (repeat it)\n diff --from <version> the old version: a capture id or a time (default: the one before --to)\n --to <version> the new version: a capture id or a time (default: the newest)\n watch add --every <time> the time between two checks: 1h or more (default 24h)\n --confirm <n> 2 (default): a change counts when two checks see it; 1: at once\n --scope <text>, --label <name=value> marks for the captures (repeat --label)\n --render <mode> as for fetch\n watch run --budget <time> the longest time (default 10m); no request starts after it\n --max-pages <n> how many pages to check (default 100)\n daemon --tick <time> the longest wait between two ticks: 1s or more (default 1m)\n --jitter <time> a random wait from 0 to this after each tick (default 10s)\n --tick-budget <time> the longest time of the checks of one tick (default 10m)\n --tick-pages <n> how many pages one tick checks at most (default 100)\n --grace <time> at a stop, the checks in progress get this time to end (default 10s)\n --events <file> write each change as one JSON line to this file (default: the\n standard output)\n --on-change <command> start this command of yours for each change: it gets the\n event as one JSON line on its standard input\n --mcp-http serve the MCP tools over HTTP; each request needs the bearer token\n --mcp-port <n> the port (default 9307)\n --mcp-host <host> the host to bind (default 127.0.0.1)\n --mcp-token-file <file> read the token from this file (default: a new token at each\n start, in <store>/daemon/mcp-token)\n --allow-remote permit a --mcp-host that is not loopback: read the README first\n --allow-user-provided as for mcp\n daemon status exit code 0 when a daemon runs on the store, 1 when none runs\n extract --field <name=value[:kind]> a value to check, never made up by the crawler (repeat it);\n kind: text (default), number, money, date, url, email, phone,\n or json-ld:<path> such as json-ld:Product.offers.price\n --capture <id> check the text of this capture, not the current text\n --user-provided as for read\n embed --budget <time> the longest time (default 10m); no embedding starts after it\n --max-docs <n> how many texts get vectors at most\n --kind <kind> only this kind of document\n\nExit codes:\n 0 done\n 1 not done: a page not fetched, no match, a quote not found, a document unknown or\n held back, a watch check that failed or that is still due, a field value that extract\n did not find or that is not clear, embed that stopped before each page had vectors,\n or an error\n (fetch: a page with an archived copy counts as done)\n 2 the command line is wrong\n`;\n\n// ---- The command `export`: the options, the checks and the work of the WARC and WACZ export ----\n\n/**\n * This function does the command `export`, and gives the exit code. `readLine` checked the whole\n * command line first. The export only reads the store: it makes no store, and it sends no request.\n */\nconst exportCommand = async (plan: ExportPlan, dir: string, json: boolean): Promise<number> => {\n const result =\n plan.format === \"wacz\"\n ? await (await import(\"./wacz.ts\")).exportWacz(dir, plan.options)\n : await (await import(\"./warc.ts\")).exportWarc(dir, plan.options);\n process.stdout.write(\n `${json ? JSON.stringify(result, null, 2) : describeExport(result, plan.options.includeRefusals)}\\n`,\n );\n return 0;\n};\n\n// ---- The end of the command `export` ----\n\n// ── signed requests ──────────────────────────────────────────────────────────\n\n/** This function prints the key directory of a key file, and tells where to host it. */\nconst keyDirectoryCommand = async (key: string, agent: string | undefined): Promise<number> => {\n const { KEY_DIRECTORY_PATH, KEY_DIRECTORY_TYPE, keyDirectory } = await import(\"./webbotauth.ts\");\n let directory;\n try {\n directory = keyDirectory([key]);\n } catch (error) {\n throw new UsageError(`--sign-key: ${(error as Error).message}`, { cause: error });\n }\n process.stdout.write(`${JSON.stringify(directory, null, 2)}\\n`);\n const where =\n agent !== undefined && agentTypeOf(agent) === \"jwks_uri\"\n ? `${agent} (a JSON Web Key Set, the type jwks_uri)`\n : `${agent === undefined ? \"https://<your origin>\" : agent.replace(/\\/$/, \"\")}${KEY_DIRECTORY_PATH} with Content-Type: ${KEY_DIRECTORY_TYPE}`;\n process.stderr.write(`polite-crawler: serve this JSON at ${where}\\n`);\n return 0;\n};\n\n// ── end of signed requests ───────────────────────────────────────────────────\n\n// ── discover and crawl ──────────────────────────────────────────────────────\n\n/** This function lists the pages of a site. The exit code is 0 when it found a URL. */\nconst runDiscover = async (\n crawler: OpenedCrawler,\n site: string,\n limits: CrawlLimits,\n maxAge: number,\n json: boolean,\n out: (text: string) => void,\n): Promise<number> => {\n const found = await crawler.discover(site, {\n maxUrls: limits.maxUrls,\n feeds: limits.feeds,\n maxAge,\n ...(limits.allowHosts === undefined ? {} : { allowHosts: limits.allowHosts }),\n });\n if (json) out(JSON.stringify(found, null, 2));\n else for (const line of describeDiscover(found)) out(line);\n return found.urls.length > 0 ? 0 : 1;\n};\n\n/** This function crawls from the seeds. It prints each page when it ends. The exit code is 0 when the crawl got a page. */\nconst runCrawl = async (\n crawler: OpenedCrawler,\n seeds: readonly string[],\n limits: CrawlLimits,\n page: { readonly render: \"auto\" | \"never\" | \"always\"; readonly maxAge: number },\n json: boolean,\n out: (text: string) => void,\n): Promise<number> => {\n // the first Ctrl+C stops the crawl cleanly: the fetches in progress end, and the summary prints\n const controller = new AbortController();\n const stop = (): void => controller.abort(new Error(\"interrupted\"));\n process.once(\"SIGINT\", stop);\n try {\n const run = crawler.crawl([...new Set(seeds)], {\n maxPages: limits.maxPages,\n maxDepth: limits.maxDepth,\n budgetMs: limits.budgetMs,\n include: limits.include,\n exclude: limits.exclude,\n useSitemaps: limits.sitemaps,\n render: page.render,\n maxAge: page.maxAge,\n signal: controller.signal,\n ...(limits.allowHosts === undefined ? {} : { allowHosts: limits.allowHosts }),\n });\n const pages: CrawlPage[] = [];\n for await (const result of run) {\n pages.push(result);\n if (!json) out(`${describePage(result)}\\n depth ${String(result.depth)}, ${result.source}`);\n }\n const summary = await run.summary;\n out(json ? JSON.stringify({ pages, summary }, null, 2) : describeSummary(summary));\n return summary.fetched > 0 ? 0 : 1;\n } finally {\n process.removeListener(\"SIGINT\", stop);\n }\n};\n\n// ── end of discover and crawl ───────────────────────────────────────────────\n\n// ── time travel, diff and watch ─────────────────────────────────────────────\n\n/** This function does the command `watch`, and gives the exit code. */\nconst runWatchCommand = async (\n crawler: OpenedCrawler,\n line: WatchLine,\n values: Values,\n limits: CrawlLimits,\n json: boolean,\n out: (text: string) => void,\n): Promise<number> => {\n const print = (value: unknown, text: () => string): void =>\n out(json ? JSON.stringify(value, null, 2) : text());\n switch (line.action) {\n case \"add\": {\n const added: WatchEntry[] = [];\n for (const url of line.urls) {\n added.push(\n await crawler.watch.add(url, {\n ...(line.every === undefined ? {} : { every: line.every }),\n ...(line.confirm === undefined ? {} : { confirm: line.confirm }),\n ...(values.scope === undefined ? {} : { scope: values.scope }),\n ...(line.labels === undefined ? {} : { labels: line.labels }),\n ...(values.render === undefined\n ? {}\n : { render: values.render as \"auto\" | \"never\" | \"always\" }),\n }),\n );\n }\n print(added, () => added.map(describeEntry).join(\"\\n\"));\n return 0;\n }\n case \"remove\": {\n const removed: Array<{ readonly url: string; readonly removed: boolean }> = [];\n for (const url of line.urls) removed.push({ url, removed: await crawler.watch.remove(url) });\n print(removed, () =>\n removed\n .map((one) => `${one.removed ? \"removed\" : \"not on the list\"} ${one.url}`)\n .join(\"\\n\"),\n );\n return removed.every((one) => one.removed) ? 0 : 1;\n }\n case \"list\": {\n const entries = await crawler.watch.list();\n print(entries, () =>\n entries.length === 0 ? \"the watch list is empty\" : entries.map(describeEntry).join(\"\\n\"),\n );\n return 0;\n }\n default: {\n // the first Ctrl+C stops the run cleanly: the checks in progress end, and the summary prints\n const controller = new AbortController();\n const stop = (): void => controller.abort(new Error(\"interrupted\"));\n process.once(\"SIGINT\", stop);\n try {\n const run = crawler.watch.run({\n budgetMs: values.budget === undefined ? undefined : limits.budgetMs,\n maxPages: values[\"max-pages\"] === undefined ? undefined : limits.maxPages,\n signal: controller.signal,\n });\n const events: WatchEvent[] = [];\n for await (const event of run) {\n events.push(event);\n if (!json) out(describeEvent(event));\n }\n const summary = await run.summary;\n out(json ? JSON.stringify({ events, summary }, null, 2) : describeWatchSummary(summary));\n return summary.refused + summary.error === 0 && summary.due === 0 ? 0 : 1;\n } finally {\n process.removeListener(\"SIGINT\", stop);\n }\n }\n }\n};\n\n/** This function gives the text of a diff for a person: the two captures, then the text of `formatDiff`. */\nconst describeDiff = async (diff: DiffResult): Promise<string> => {\n const { formatDiff } = await import(\"./diff.ts\");\n return [\n `${diff.doc} ${diff.from.capture} (${diff.from.fetched_at}) -> ${diff.to.capture} (${diff.to.fetched_at})`,\n formatDiff(diff),\n ...(diff.same === true ? [\"the two versions are the same\"] : []),\n ...(diff.note === null ? [] : [diff.note]),\n ].join(\"\\n\");\n};\n\n// ── end of time travel, diff and watch ──────────────────────────────────────\n// ── extract ─────────────────────────────────────────────────────────────────\n\n/** This function checks the fields against a stored document, and prints a line for each field. The exit code is 0 when each field is verified or normalized. */\nconst runExtract = async (\n crawler: PoliteCrawler,\n doc: string,\n fields: Record<string, CliField>,\n options: {\n readonly capture: string | undefined;\n readonly userProvided: boolean;\n readonly at: string | undefined;\n },\n json: boolean,\n out: (text: string) => void,\n): Promise<number> => {\n const { extractDocument } = await import(\"./extract.ts\");\n const claims = fields as Parameters<typeof extractDocument>[2];\n const result =\n crawler.extract === undefined\n ? await extractDocument(crawler.runtime, doc, claims, options)\n : await crawler.extract(doc, claims, options);\n if (json) out(JSON.stringify(result, null, 2));\n else for (const line of describeExtract(result)) out(line);\n return extractCode(result);\n};\n\n// ── end of extract ──────────────────────────────────────────────────────────\n// ── hybrid search ───────────────────────────────────────────────────────────\n\n/**\n * This function makes the embedder of the command line. It loads no model: the model loads at the\n * first text. The transformers embedder keeps the files of its model in `<store>/models`.\n */\nconst embedderFor = async (choice: EmbedderChoice, dir: string): Promise<Embedder> => {\n try {\n if (choice.kind === \"ollama\") {\n const { ollamaEmbedder } = await import(\"./ollama.ts\");\n return ollamaEmbedder({\n ...(choice.model === undefined ? {} : { model: choice.model }),\n ...(choice.url === undefined ? {} : { baseUrl: choice.url }),\n allowRemote: choice.allowRemote,\n });\n }\n const { transformersEmbedder } = await import(\"./transformers.ts\");\n return transformersEmbedder({\n ...(choice.model === undefined ? {} : { model: choice.model }),\n cacheDir: join(dir, \"models\"),\n });\n } catch (error) {\n throw new UsageError(`--embedder ${choice.kind}: ${(error as Error).message}`, {\n cause: error,\n });\n }\n};\n\n/** This function gives vectors to the stored pages within the budget. The exit code is 0 when each page has vectors. */\nconst runEmbed = async (\n crawler: HybridCrawler,\n limits: CrawlLimits,\n options: { readonly maxDocs: number | undefined; readonly kind: string | undefined },\n json: boolean,\n out: (text: string) => void,\n): Promise<number> => {\n // the first Ctrl+C stops the work cleanly, and the counts print\n const controller = new AbortController();\n const stop = (): void => controller.abort(new Error(\"interrupted\"));\n process.once(\"SIGINT\", stop);\n try {\n const result = await crawler.embed({\n budget: limits.budgetMs,\n signal: controller.signal,\n ...(options.maxDocs === undefined ? {} : { maxDocs: options.maxDocs }),\n ...(options.kind === undefined ? {} : { kind: options.kind }),\n });\n out(json ? JSON.stringify(result, null, 2) : describeEmbed(result));\n return result.stopped === \"done\" ? 0 : 1;\n } finally {\n process.removeListener(\"SIGINT\", stop);\n }\n};\n\n// ── end of hybrid search ────────────────────────────────────────────────────\n\n/** This function does the command of the command line, and gives the exit code. */\nconst main = async (args: readonly string[]): Promise<number> => {\n const out = (text: string): void => void process.stdout.write(`${text}\\n`);\n // the whole command line is valid before anything opens the store\n const line = readLine(args, process.env);\n if (line.kind === \"version\") {\n out(VERSION);\n return 0;\n }\n if (line.kind === \"help\") {\n process.stdout.write(HELP);\n return 0;\n }\n const {\n command,\n rest,\n values,\n render,\n rank,\n limit,\n from,\n length,\n maxAge,\n crawlLimits,\n at,\n diffFrom,\n diffTo,\n watchLine,\n extractFields,\n dir,\n signKey,\n signatureAgent,\n } = line;\n // ── hybrid search: the embedder loads no model before its first text ──\n const embedder = line.embedder === undefined ? undefined : await embedderFor(line.embedder, dir);\n // ── signed requests: the key file opens after the checks of the command line ──\n if (command === \"key-directory\") return keyDirectoryCommand(keyFile(signKey!), signatureAgent);\n let signer;\n if (signKey !== undefined && signatureAgent !== undefined) {\n const { createWebBotAuthSigner } = await import(\"./webbotauth.ts\");\n const privateKey = keyFile(signKey);\n try {\n signer = createWebBotAuthSigner({\n privateKey,\n signatureAgent,\n agentType: agentTypeOf(signatureAgent),\n });\n } catch (error) {\n throw new UsageError(`--sign-key: ${(error as Error).message}`, { cause: error });\n }\n }\n // ── end of signed requests ──\n const options: OpenOptions = {\n dir,\n contact: line.contact,\n ...(values[\"no-browser\"] === true ? { renderer: false as const } : {}),\n ...(line.allowPrivate ? { egress: { allowPrivate: true } } : {}),\n ...(signer === undefined ? {} : { signer }),\n ...(embedder === undefined ? {} : { embedder }),\n };\n\n if (command === \"mcp\") {\n const { startMcpServer } = await import(\"./mcp.ts\");\n await startMcpServer({ ...options, allowUserProvided: line.allowUserProvided });\n return 0;\n }\n\n // ── daemon: the command loads the daemon, and the daemon loads MCP only for --mcp-http ──\n if (command === \"daemon\") {\n const { daemonCommand } = await import(\"./cli-daemon.ts\");\n return daemonCommand(line.daemonLine!, options, {\n allowUserProvided: line.allowUserProvided,\n json: values.json === true,\n out,\n });\n }\n // ── end of daemon ──\n\n // the export only reads the store, so it opens no crawler and no index\n if (line.exportPlan !== undefined)\n return exportCommand(line.exportPlan, dir, values.json === true);\n\n const { openCrawler } = await import(\"./open.ts\");\n // only fetch, crawl and watch run render; a missing browser turns rendering off one time, before the first page\n let renderOff: string | undefined;\n let renderer = options.renderer;\n const renders = command === \"fetch\" || command === \"crawl\" || watchLine?.action === \"run\";\n if (!renders || render === \"never\") renderer = false;\n else if (renderer === undefined) {\n const { browserMissing } = await import(\"./browser.ts\");\n renderOff = await browserMissing();\n if (renderOff !== undefined) renderer = false;\n }\n\n let crawler: HybridCrawler;\n try {\n crawler = openCrawler({ ...options, renderer });\n } catch (error) {\n const code = (error as { code?: unknown }).code;\n if (typeof code === \"string\" && FS_CODES.has(code)) {\n throw new Error(\n `the store ${dir} does not open: it is not a folder that polite-crawler can write (${code})`,\n {\n cause: error,\n },\n );\n }\n throw error;\n }\n const print = (value: unknown, text: () => string): void =>\n out(values.json === true ? JSON.stringify(value, null, 2) : text());\n try {\n switch (command) {\n case \"fetch\": {\n // the same URL twice is one fetch; one bad URL is an error of that URL only\n const urls = [...new Set(rest)];\n const fallback = values[\"archive-fallback\"] === true ? (\"archive\" as const) : undefined;\n const archiveDespiteRobots = values[\"archive-despite-robots\"] === true;\n const settled = await Promise.allSettled(\n urls.map((url) => crawler.fetch(url, { render, maxAge, fallback, archiveDespiteRobots })),\n );\n const pages = fetchedPages(urls, settled, renderOff);\n print(pages, () => pages.map(describePage).join(\"\\n\"));\n return pages.every(done) ? 0 : 1;\n }\n case \"find\": {\n const found = await crawler.find(rest.join(\" \"), {\n limit,\n kind: values.kind ?? null,\n rank,\n });\n print(found, () => describeFound(found));\n return found.length === 0 ? 1 : 0;\n }\n case \"read\": {\n const userProvided = values[\"user-provided\"] === true;\n const read = await crawler.read(rest[0]!, { from, length, userProvided, at });\n print(read, () => describeRead(read, at));\n return read.held_back || read.before_first === true ? 1 : 0;\n }\n case \"verify\": {\n const [doc, ...quote] = rest;\n const verified = await crawler.verify(doc!, quote.join(\" \"), { at });\n print(verified, () => describeVerify(verified));\n return verified.found ? 0 : 1;\n }\n // ── discover and crawl ──\n case \"discover\":\n return await runDiscover(crawler, rest[0]!, crawlLimits, maxAge, values.json === true, out);\n case \"crawl\":\n return await runCrawl(\n crawler,\n rest,\n crawlLimits,\n { render, maxAge },\n values.json === true,\n out,\n );\n // ── end of discover and crawl ──\n // ── diff and watch ──\n case \"diff\": {\n const diff = await crawler.diff(rest[0]!, {\n ...(diffFrom === undefined ? {} : { from: diffFrom }),\n ...(diffTo === undefined ? {} : { to: diffTo }),\n });\n const text = values.json === true ? \"\" : await describeDiff(diff);\n print(diff, () => text);\n return diff.held_back ? 1 : 0;\n }\n case \"watch\":\n return await runWatchCommand(\n crawler,\n watchLine!,\n values,\n crawlLimits,\n values.json === true,\n out,\n );\n // ── end of diff and watch ──\n // ── extract ──\n case \"extract\":\n return await runExtract(\n crawler,\n rest[0]!,\n extractFields!,\n { capture: values.capture, userProvided: values[\"user-provided\"] === true, at },\n values.json === true,\n out,\n );\n // ── end of extract ──\n case \"embed\":\n return await runEmbed(\n crawler,\n crawlLimits,\n { maxDocs: line.maxDocs, kind: values.kind },\n values.json === true,\n out,\n );\n default: {\n // the documents of a recipe that this command cannot read stay, from the old index\n const counts = await crawler.reindex();\n print(counts, () => describeReindex(counts));\n return 0;\n }\n }\n } finally {\n await crawler.close();\n }\n};\n\nmain(process.argv.slice(2)).then(\n (code) => {\n process.exitCode = code;\n },\n (error: unknown) => {\n const message = error instanceof Error ? error.message : String(error);\n const usage = error instanceof UsageError;\n process.stderr.write(\n `polite-crawler: ${message}${usage ? \" (polite-crawler --help lists the commands)\" : \"\"}\\n`,\n );\n process.exitCode = usage ? 2 : 1;\n },\n);\n"],"mappings":"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"}