polite-crawler 0.1.4 → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +28 -121
- package/build/{browser-CKRNbIDT.mjs → browser-56dBBLec.mjs} +7 -3
- package/build/browser-56dBBLec.mjs.map +1 -0
- package/build/classify.d.mts +50 -2
- package/build/{cli-daemon-ZYRdy6EW.mjs → cli-daemon-qS4PH2bM.mjs} +2 -2
- package/build/{cli-daemon-ZYRdy6EW.mjs.map → cli-daemon-qS4PH2bM.mjs.map} +1 -1
- package/build/{cli-recipes-Bci6HREA.mjs → cli-recipes-qbLRVThF.mjs} +2 -2
- package/build/{cli-recipes-Bci6HREA.mjs.map → cli-recipes-qbLRVThF.mjs.map} +1 -1
- package/build/cli.mjs +41 -16
- package/build/cli.mjs.map +1 -1
- package/build/{daemon-DK6U51_v.mjs → daemon-BXBbUrni.mjs} +11 -7
- package/build/daemon-BXBbUrni.mjs.map +1 -0
- package/build/{diff-BUM91Rev.mjs → diff-CpKN0Jdc.mjs} +7 -3
- package/build/{diff-BUM91Rev.mjs.map → diff-CpKN0Jdc.mjs.map} +1 -1
- package/build/{extract-CefB5Vfn.mjs → extract-DqeqiNHb.mjs} +12 -4
- package/build/extract-DqeqiNHb.mjs.map +1 -0
- package/build/{html-BACljlkf.d.mts → html-DMgIeQfJ.d.mts} +16 -2
- package/build/html.d.mts +1 -2
- package/build/index.d.mts +4227 -40
- package/build/index.mjs +17 -17
- package/build/{log-Dx-wXjJl.mjs → log-CmBWcyQm.mjs} +5 -1
- package/build/log-CmBWcyQm.mjs.map +1 -0
- package/build/mcp-package-Y0bREnOS.mjs +40 -0
- package/build/mcp-package-Y0bREnOS.mjs.map +1 -0
- package/build/{ollama-BJxkjny4.mjs → ollama-D1xRh3hY.mjs} +7 -3
- package/build/ollama-D1xRh3hY.mjs.map +1 -0
- package/build/{open-CbpSCEZc.mjs → open-CEo22Hpu.mjs} +78 -18
- package/build/open-CEo22Hpu.mjs.map +1 -0
- package/build/politeness.d.mts +366 -2
- package/build/{recipe-GiFQojmo.mjs → recipe-B2SkepDZ.mjs} +10 -4
- package/build/recipe-B2SkepDZ.mjs.map +1 -0
- package/build/{recipe-product-Dellqudu.mjs → recipe-product-CyZE3Ukd.mjs} +29 -7
- package/build/recipe-product-CyZE3Ukd.mjs.map +1 -0
- package/build/{render-C1CUraWF.mjs → render-CKE2LILH.mjs} +10 -3
- package/build/render-CKE2LILH.mjs.map +1 -0
- package/build/robots.d.mts +143 -2
- package/build/robots.mjs +1 -1
- package/build/robots.mjs.map +1 -1
- package/build/{rolldown-runtime-D7D4PA-g.mjs → rolldown-runtime-8H4AJuhK.mjs} +1 -0
- package/build/{runtime-D_r2SYge.mjs → runtime-C_2BQibH.mjs} +84 -18
- package/build/runtime-C_2BQibH.mjs.map +1 -0
- package/build/signals.d.mts +45 -2
- package/build/{transformers-BQMlaGj4.mjs → transformers-BNiuiLxT.mjs} +3 -3
- package/build/transformers-BNiuiLxT.mjs.map +1 -0
- package/build/{version-BPem-XZd.mjs → version-Djva7JkN.mjs} +7 -2
- package/build/version-Djva7JkN.mjs.map +1 -0
- package/build/{wacz-QyzLp4mC.mjs → wacz-DKk2HBmx.mjs} +4 -4
- package/build/{wacz-QyzLp4mC.mjs.map → wacz-DKk2HBmx.mjs.map} +1 -1
- package/build/{warc-BHcaRKHw.mjs → warc-C_ew76bV.mjs} +4 -4
- package/build/{warc-BHcaRKHw.mjs.map → warc-C_ew76bV.mjs.map} +1 -1
- package/build/{webbotauth-CjJdTAfE.mjs → webbotauth-Dq_dA_u3.mjs} +2 -2
- package/build/{webbotauth-CjJdTAfE.mjs.map → webbotauth-Dq_dA_u3.mjs.map} +1 -1
- package/package.json +5 -11
- package/build/browser-CKRNbIDT.mjs.map +0 -1
- package/build/classify-CrfyXcwo.d.mts +0 -51
- package/build/daemon-DK6U51_v.mjs.map +0 -1
- package/build/daemon-wwIWsUmU.d.mts +0 -3777
- package/build/extract-CefB5Vfn.mjs.map +0 -1
- package/build/log-Dx-wXjJl.mjs.map +0 -1
- package/build/loopback-C_QYowSf.mjs +0 -22
- package/build/loopback-C_QYowSf.mjs.map +0 -1
- package/build/mcp.d.mts +0 -199
- package/build/mcp.mjs +0 -1555
- package/build/mcp.mjs.map +0 -1
- package/build/ollama-BJxkjny4.mjs.map +0 -1
- package/build/open-CbpSCEZc.mjs.map +0 -1
- package/build/politeness-yI3xBEx9.d.mts +0 -367
- package/build/recipe-GiFQojmo.mjs.map +0 -1
- package/build/recipe-product-Dellqudu.mjs.map +0 -1
- package/build/render-C1CUraWF.mjs.map +0 -1
- package/build/robots-R-qaEBOb.d.mts +0 -144
- package/build/runtime-D_r2SYge.mjs.map +0 -1
- package/build/signals-BHpy2xb2.d.mts +0 -46
- package/build/transformers-BQMlaGj4.mjs.map +0 -1
- package/build/urls-BTDSgDLC.d.mts +0 -16
- package/build/version-BPem-XZd.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, 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 { Day, HoursComparison, Interval, Week } from \"./hours.ts\";\nimport type { EmbedResult } from \"./hybrid.ts\";\nimport { isLoopbackHost } from \"./loopback.ts\";\nimport type { Money, ProductResult } from \"./recipe-product.ts\";\nimport type { VenuePage, VenueReading, VenueResult } from \"./recipe-venue.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 // ── recipes ──\n name: { type: \"string\" },\n \"require-name\": { type: \"boolean\" },\n address: { type: \"string\" },\n lat: { type: \"string\" },\n lon: { type: \"string\" },\n osm: { type: \"string\" },\n pages: { type: \"string\" },\n compare: { type: \"string\" },\n \"on-behalf-of\": { type: \"string\", multiple: true },\n // ── end of recipes ──\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 `venue` only. */\nexport const VENUE_OPTIONS: readonly string[] = [\n \"name\",\n \"require-name\",\n \"address\",\n \"lat\",\n \"lon\",\n \"osm\",\n \"pages\",\n \"compare\",\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 venue product\",\n \"max-age\": \"fetch crawl discover venue product\",\n limit: \"find\",\n kind: \"find embed\",\n rank: \"find\",\n from: \"read diff\",\n length: \"read\",\n \"user-provided\": \"read extract venue product\",\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 venue product\",\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 venue product\",\n \"archive-despite-robots\": \"fetch venue product\",\n \"sign-key\": \"fetch discover crawl watch mcp key-directory daemon venue product\",\n \"signature-agent\": \"fetch discover crawl watch mcp key-directory daemon venue product\",\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 // ── recipes ──\n ...Object.fromEntries(VENUE_OPTIONS.map((option) => [option, \"venue\"])),\n \"on-behalf-of\": \"venue product\",\n // ── end of recipes ──\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 // ── recipes ──\n venue: { min: 1, max: 1, wants: \"venue wants the URL of the site of one venue\" },\n product: { min: 1, max: 1, wants: \"product wants the URL of one product page\" },\n // ── end of recipes ──\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// ── recipes ─────────────────────────────────────────────────────────────────\n\n/** This value is the most pages of `venue` after the home page. It is `VENUE_LIMITS.maxPages` of `recipe-venue.ts`. */\nexport const VENUE_MAX_PAGES = 8;\n\n/** This exit code of `venue` and `product` tells that the command read nothing. */\nexport const NOTHING_READ = 3;\n\n/** This exit code of `venue` and `product` tells that the site refused the crawler (a wall). */\nexport const WALL = 4;\n\n/** The place of a venue, as `VenueLocation` of `recipe-venue.ts` gives it. */\nexport interface CliLocation {\n readonly address?: string | undefined;\n readonly lat?: number | undefined;\n readonly lon?: number | undefined;\n}\n\n/** These are the options of `venue` and `product`, read from the command line before the store opens. */\nexport interface RecipeLine {\n readonly recipe: \"venue\" | \"product\";\n readonly url: string;\n readonly render: \"auto\" | \"never\" | \"always\";\n /** This value is `--max-age` in milliseconds. */\n readonly maxAge: number;\n /** This value is `--budget` in milliseconds, or undefined with no `--budget`. */\n readonly budget: number | undefined;\n /** These tokens name the agents of `--on-behalf-of`. Their robots.txt rules apply too. */\n readonly onBehalfOf: readonly string[] | undefined;\n readonly userProvided: boolean;\n readonly fallback: \"archive\" | undefined;\n readonly archiveDespiteRobots: boolean;\n /** This text is `--name` of `venue`. */\n readonly name: string | undefined;\n readonly requireName: boolean;\n /** This value is the place of `--address`, `--lat` and `--lon`. */\n readonly location: CliLocation | undefined;\n /** This text is the OpenStreetMap value of `--osm`. */\n readonly osm: string | undefined;\n readonly pages: number | undefined;\n /** This text is the file of `--compare`. The command reads it before the store opens. */\n readonly compare: string | undefined;\n}\n\n/** This function reads a latitude or a longitude of the command line. */\nconst coordinateOf = (name: \"lat\" | \"lon\", text: string, limit: number): number => {\n const value = /^\\s*-?\\d+(?:\\.\\d+)?\\s*$/.test(text) ? Number(text) : Number.NaN;\n if (!Number.isFinite(value) || Math.abs(value) > limit) {\n throw new UsageError(\n `--${name} wants a number from -${String(limit)} to ${String(limit)}, not \"${text}\"`,\n );\n }\n return value;\n};\n\n/** This function checks the options of `venue` and `product`, and gives the options of the recipe. */\nexport const recipeLineOf = (\n recipe: \"venue\" | \"product\",\n values: Values,\n rest: readonly string[],\n page: {\n readonly render: \"auto\" | \"never\" | \"always\";\n readonly maxAge: number;\n readonly budget: number | undefined;\n },\n): RecipeLine => {\n const url = rest[0] ?? \"\";\n if (!/^https?:\\/\\//i.test(url) || !URL.canParse(url))\n throw new UsageError(`${recipe} wants an http or https URL, not \"${url}\"`);\n for (const agent of values[\"on-behalf-of\"] ?? []) {\n if (!/^[A-Za-z0-9_-]+$/.test(agent)) {\n throw new UsageError(\n `--on-behalf-of wants the robots.txt token of an agent, such as Claude-User, not \"${agent}\"`,\n );\n }\n }\n const name = values.name;\n if (name !== undefined && name.trim() === \"\")\n throw new UsageError(\"--name wants the name of the venue\");\n if (values[\"require-name\"] === true && name === undefined)\n throw new UsageError(\"--require-name goes with --name\");\n const address = values.address;\n if (address !== undefined && address.trim() === \"\")\n throw new UsageError(\"--address wants a street address\");\n if ((values.lat === undefined) !== (values.lon === undefined))\n throw new UsageError(\"--lat and --lon go together\");\n const lat = values.lat === undefined ? undefined : coordinateOf(\"lat\", values.lat, 90);\n const lon = values.lon === undefined ? undefined : coordinateOf(\"lon\", values.lon, 180);\n const osm = values.osm;\n if (osm !== undefined && osm.trim() === \"\")\n throw new UsageError('--osm wants an opening_hours value, such as \"Mo-Fr 08:00-18:00\"');\n const pages = values.pages === undefined ? undefined : whole(\"pages\", values.pages, 0, 0);\n if (pages !== undefined && pages > VENUE_MAX_PAGES)\n throw new UsageError(`--pages wants a whole number from 0 to ${String(VENUE_MAX_PAGES)}`);\n const compare = values.compare;\n if (compare !== undefined && compare.trim() === \"\")\n throw new UsageError(\"--compare wants a file\");\n return {\n recipe,\n url,\n render: page.render,\n maxAge: page.maxAge,\n budget: page.budget,\n onBehalfOf: values[\"on-behalf-of\"],\n userProvided: values[\"user-provided\"] === true,\n fallback: values[\"archive-fallback\"] === true ? \"archive\" : undefined,\n archiveDespiteRobots: values[\"archive-despite-robots\"] === true,\n name,\n requireName: values[\"require-name\"] === true,\n location:\n address === undefined && lat === undefined\n ? undefined\n : {\n ...(address === undefined ? {} : { address }),\n ...(lat === undefined ? {} : { lat, lon }),\n },\n osm,\n pages,\n compare,\n };\n};\n\n/** The days of a week in their order, as `DAYS` of `hours.ts`. */\nconst WEEK_DAYS: readonly Day[] = [\"mon\", \"tue\", \"wed\", \"thu\", \"fri\", \"sat\", \"sun\"];\n\nconst DAY_LABELS: Readonly<Record<Day, string>> = {\n mon: \"Mon\",\n tue: \"Tue\",\n wed: \"Wed\",\n thu: \"Thu\",\n fri: \"Fri\",\n sat: \"Sat\",\n sun: \"Sun\",\n};\n\nconst isRecord = (value: unknown): value is Readonly<Record<string, unknown>> =>\n typeof value === \"object\" && value !== null && !Array.isArray(value);\n\n/** This function tells if a value is a time of a kept week: `HH:MM` or `H:MM`, to `24:00`. */\nconst isClock = (value: unknown): value is string => {\n if (typeof value !== \"string\") return false;\n const match = /^(\\d{1,2}):(\\d{2})$/.exec(value);\n return match !== null && Number(match[1]) <= 24 && Number(match[2]) < 60;\n};\n\nconst intervalOf = (value: unknown): Interval | undefined =>\n Array.isArray(value) && value.length === 2 && isClock(value[0]) && isClock(value[1])\n ? [value[0], value[1]]\n : undefined;\n\n/** The kept week of `--compare`, and the file that it came from. */\nexport interface KeptWeek {\n readonly file: string;\n readonly week: Week;\n}\n\n/**\n * This function reads the kept week of `--compare`, a JSON file. The file is a week, or an object\n * with the field `week` (a reading of `venue --json`) or `hours_week`. Each day gives a list of\n * intervals (`[]` for closed), one interval, or null for closed. An error names the file.\n */\nexport const keptWeekOf = (path: string): KeptWeek => {\n const file = fileOf(path);\n let text: string;\n try {\n text = readFileSync(file, \"utf8\");\n } catch (error) {\n const code = (error as { code?: unknown }).code;\n throw new UsageError(\n `--compare: the file ${file} does not open (${typeof code === \"string\" ? code : \"error\"})`,\n { cause: error },\n );\n }\n let data: unknown;\n try {\n // a byte order mark at the start is not JSON, and some editors of Windows write one\n data = JSON.parse(text.charCodeAt(0) === 0xfeff ? text.slice(1) : text);\n } catch (error) {\n throw new UsageError(`--compare: the file ${file} is not JSON`, { cause: error });\n }\n if (isRecord(data) && (isRecord(data[\"week\"]) || isRecord(data[\"hours_week\"])))\n data = isRecord(data[\"week\"]) ? data[\"week\"] : data[\"hours_week\"];\n const wants = `--compare: the file ${file} is not a week. Write a list of intervals, one interval or null for each day, such as {\"mon\": [[\"09:00\", \"17:00\"]], \"sun\": []}`;\n if (!isRecord(data)) throw new UsageError(wants);\n const week: Partial<Record<Day, readonly Interval[]>> = {};\n for (const [key, value] of Object.entries(data)) {\n const day = WEEK_DAYS.find((one) => one === key);\n if (day === undefined) {\n throw new UsageError(\n `--compare: the file ${file} names \"${key}\", not a day: use mon, tue, wed, thu, fri, sat or sun`,\n );\n }\n const one = intervalOf(value);\n const intervals =\n value === null\n ? []\n : one !== undefined\n ? [one]\n : Array.isArray(value)\n ? value.map(intervalOf)\n : [undefined];\n if (intervals.some((interval) => interval === undefined))\n throw new UsageError(`${wants} (the value of ${key} is not)`);\n week[day] = intervals as Interval[];\n }\n return { file, week };\n};\n\n/** This function writes the intervals of one day, as `describeWeek` of `hours.ts` does. */\nexport const dayText = (intervals: readonly Interval[] | undefined): string =>\n intervals === undefined\n ? \"not stated\"\n : intervals.length === 0\n ? \"closed\"\n : intervals\n .map(([open, close]) =>\n open === \"00:00\" && close === \"00:00\" ? \"open 24 hours\" : `${open}–${close}`,\n )\n .join(\", \");\n\n/** The result of `--compare`: the kept week and its comparison with the week of the site. */\nexport interface VenueCompare {\n readonly file: string;\n readonly kept: Week;\n /** This value is null when the result has no reading of the site with no guess (`from_site`). */\n readonly comparison: HoursComparison | null;\n}\n\n/** One day of `--compare`. */\nexport interface CompareDay {\n readonly day: Day;\n /** The value `not read` tells that only the kept week states the day. */\n readonly status: \"same\" | \"changed\" | \"new\" | \"skipped\" | \"not read\";\n readonly kept: readonly Interval[] | undefined;\n readonly read: readonly Interval[] | undefined;\n}\n\n/** This function gives the days of a comparison in their order. A day that no week states is not there. */\nexport const compareDays = (kept: Week, read: Week, comparison: HoursComparison): CompareDay[] => {\n const days: CompareDay[] = [];\n for (const day of WEEK_DAYS) {\n const one = { day, kept: kept[day], read: read[day] };\n if (one.kept === undefined && one.read === undefined) continue;\n const status: CompareDay[\"status\"] =\n comparison.status === \"new\" || comparison.added.includes(day)\n ? \"new\"\n : comparison.days.includes(day)\n ? \"changed\"\n : comparison.skipped.includes(day)\n ? \"skipped\"\n : one.read === undefined\n ? \"not read\"\n : \"same\";\n if (comparison.status === \"new\" && one.read === undefined) continue;\n days.push({ ...one, status });\n }\n return days;\n};\n\nconst QUOTE_CHARS = 200;\n\nconst shortText = (text: string): string =>\n text.length > QUOTE_CHARS ? `${text.slice(0, QUOTE_CHARS)}…` : text;\n\n/** This function gives the lines of one reading: its rank, its source, its week and its quote. */\nconst readingLines = (one: VenueReading): string[] => {\n const marks = [\n one.schedule === \"other\" ? \"not the opening hours\" : null,\n one.inferred ? \"am or pm guessed\" : null,\n one.partial ? \"a rule left out\" : null,\n one.season === null ? null : `season ${one.season}`,\n ].filter((mark) => mark !== null);\n const where = [\n one.url,\n one.path,\n one.capture === null ? null : `capture ${one.capture}`,\n one.archived === undefined\n ? null\n : `archived copy (${one.archived.source}, captured ${one.archived.timestamp})`,\n ].filter((part) => part !== null);\n return [\n ` rank ${String(one.rank)} ${one.from}${one.label === null ? \"\" : ` \"${one.label}\"`} ${one.hours === \"\" ? \"no day\" : one.hours}${marks.length > 0 ? ` (${marks.join(\", \")})` : \"\"}`,\n ...(where.length > 0 ? [` ${where.join(\" · \")}`] : []),\n one.from === \"text\"\n ? ` quote: ${JSON.stringify(shortText(one.raw))}`\n : ` value: ${shortText(one.raw.replace(/\\s+/g, \" \"))}`,\n ];\n};\n\nconst pageLine = (page: VenuePage): string => {\n const facts = [\n `${page.outcome} ${String(page.status)}`,\n page.from_cache ? \"from the store\" : null,\n page.rendered ? \"rendered\" : null,\n page.held_back ? \"held back\" : null,\n page.archived === undefined ? null : `archived copy (${page.archived.source})`,\n ].filter((fact) => fact !== null);\n const read = page.outcome === \"ok\" || page.outcome === \"not-modified\";\n return ` ${read || page.archived !== undefined ? \"✓\" : \"✗\"} ${page.url} ${facts.join(\" · \")}`;\n};\n\nconst WALL_OUTCOMES: ReadonlySet<string> = new Set([\"blocked\", \"challenge\", \"payment-required\"]);\n\n/**\n * This function tells if a site refused the crawler, with the rule of `wall` of `readProduct`. The\n * causes are a 401, a 402 or a 403, a challenge, a bot check, robots.txt or a stop of the host.\n */\nexport const refusedPage = (page: {\n readonly outcome: string;\n readonly doc: string | null;\n readonly note: string | null;\n}): boolean =>\n WALL_OUTCOMES.has(page.outcome) ||\n (page.outcome === \"skipped\" && /^(?:robots\\.txt disallows|host stopped)/.test(page.note ?? \"\")) ||\n (page.doc === null && /\\bbot check\\b/.test(page.note ?? \"\"));\n\n/**\n * This function gives the exit code of `venue`. It is 0 when the site gave a reading of the opening\n * hours. It is `WALL` when the home page refused the crawler, and `NOTHING_READ` for a different\n * result. An OpenStreetMap value alone does not count.\n */\nexport const venueCode = (result: VenueResult): number => {\n if (result.readings.some((one) => one.from !== \"osm\" && one.schedule === \"opening\")) return 0;\n const home = result.pages[0];\n return home !== undefined && refusedPage(home) ? WALL : NOTHING_READ;\n};\n\n/** This function gives the exit code of `product`: 0 for Product data, `WALL` for a refusal, else `NOTHING_READ`. */\nexport const productCode = (result: ProductResult): number =>\n result.found ? 0 : result.wall ? WALL : NOTHING_READ;\n\n/** This line tells how to read a held-back page. */\nconst HELD_BACK_LINE =\n \"Add --user-provided if you gave this URL yourself. The crawler logs that read.\";\n\n/** This function gives the line of a page that needs a browser, when rendering is off. */\nconst renderLine = (\n notes: ReadonlyArray<string | null>,\n renderOff: string | undefined,\n): string[] =>\n renderOff !== undefined && notes.some((note) => note !== null && /no browser/i.test(note))\n ? [`rendering is off: ${renderOff}`]\n : [];\n\n/**\n * This function gives the lines of `venue`: the week of the site, and each reading with its source.\n * Then come the days where the readings disagree, the menus, the phone, the pages and the notes.\n * With `--compare`, the lines give each day of the comparison.\n */\nexport const describeVenue = (\n result: VenueResult,\n compare?: VenueCompare,\n renderOff?: string,\n): string[] => {\n const code = venueCode(result);\n const lines = [`${code === 0 ? \"✓\" : \"✗\"} ${result.home ?? result.url}`];\n const site = result.from_site;\n const home = result.pages[0];\n if (site !== null) {\n const source = [\n `${site.from}, rank ${String(site.rank)}`,\n site.url,\n site.capture === null ? null : `capture ${site.capture}`,\n ].filter((part) => part !== null);\n lines.push(` the site: ${site.hours}`, ` from ${source.join(\", \")}`);\n } else if (result.best !== null) {\n lines.push(\n ` no reading of the site with no guess. The best reading (${result.best.from}, rank ${String(result.best.rank)}): ${result.best.hours}`,\n );\n } else if (code === WALL && home !== undefined) {\n lines.push(\n ` wall: the site refused the crawler (${home.outcome} ${String(home.status)}). The crawler does not try a different identity.`,\n );\n } else lines.push(\" no opening hours found\");\n if (result.readings.length > 0) {\n lines.push(\"readings, the best first:\");\n for (const one of result.readings) lines.push(...readingLines(one));\n }\n for (const day of result.disagree) {\n const said = result.readings\n .filter((one) => one.rank <= 2 && one.week[day] !== undefined)\n .map((one) => `${dayText(one.week[day])} (rank ${String(one.rank)} ${one.from})`);\n lines.push(`the readings disagree on ${DAY_LABELS[day]}: ${said.join(\" · \")}`);\n }\n if (result.menus.length > 0) {\n lines.push(\n \"menus:\",\n ...result.menus.map((menu) => ` ${menu.url}${menu.text === \"\" ? \"\" : ` ${menu.text}`}`),\n );\n }\n if (result.phone !== null) lines.push(`phone: ${result.phone}`);\n if (result.moved !== null) lines.push(`moved: the site now goes to ${result.moved}`);\n if (result.names_venue !== null) {\n lines.push(\n result.names_venue\n ? \"names the venue: yes\"\n : \"names the venue: no. The domain can belong to a different business now.\",\n );\n }\n if (result.held_back) {\n lines.push(\n `held back: a page asked not to be AI input, so the recipe read nothing from it. ${HELD_BACK_LINE}`,\n );\n }\n if (result.pages.length > 0)\n lines.push(`pages (${String(result.pages.length)}):`, ...result.pages.map(pageLine));\n if (compare !== undefined) {\n const comparison = compare.comparison;\n if (comparison === null || site === null) {\n lines.push(\n `compare with ${compare.file}: no reading of the site with no guess, so nothing to compare`,\n );\n } else {\n const changed =\n comparison.days.length > 0\n ? ` (${comparison.days.map((day) => DAY_LABELS[day]).join(\", \")})`\n : \"\";\n lines.push(`compare with ${compare.file}: ${comparison.status}${changed}`);\n for (const one of compareDays(compare.kept, site.week, comparison)) {\n const text =\n one.status === \"changed\" || one.status === \"skipped\"\n ? `${dayText(one.kept)} → ${dayText(one.read)}`\n : one.status === \"not read\"\n ? `kept ${dayText(one.kept)}`\n : dayText(one.read);\n lines.push(` ${DAY_LABELS[one.day]} ${one.status.padEnd(8)} ${text}`);\n }\n }\n }\n if (result.note !== null) lines.push(`note: ${result.note}`);\n lines.push(...renderLine([result.note], renderOff));\n return lines;\n};\n\n/** This function gives the number of digits after the decimal point of a currency: 2 for EUR, 0 for JPY. */\nconst fractionOf = (currency: string): number => {\n try {\n return (\n new Intl.NumberFormat(\"en\", { style: \"currency\", currency }).resolvedOptions()\n .maximumFractionDigits ?? 2\n );\n } catch {\n return 2;\n }\n};\n\n/** This function writes an amount of money with its ISO 4217 code: `1,299.00 EUR`, `980 JPY`. */\nexport const moneyText = (money: Money): string => {\n const digits = fractionOf(money.currency);\n const amount = new Intl.NumberFormat(\"en\", {\n minimumFractionDigits: digits,\n maximumFractionDigits: Math.max(digits, 4),\n }).format(money.amount);\n return `${amount} ${money.currency}`;\n};\n\n/**\n * This function gives the lines of `product`. They are the name, the brand and the codes, the price\n * as money, the availability, the variant, the seller, the page and the notes. A wall and a\n * held-back page say so.\n */\nexport const describeProduct = (result: ProductResult, renderOff?: string): string[] => {\n const code = productCode(result);\n const lines = [`${code === 0 ? \"✓\" : \"✗\"} ${result.name ?? result.url}`];\n if (result.name !== null) lines.push(` ${result.url}`);\n if (code === WALL) {\n lines.push(\n ` wall: the shop refused the crawler (${result.outcome} ${String(result.status)}). The crawler does not try a different identity.`,\n );\n }\n if (result.held_back)\n lines.push(` held back: the page asked not to be AI input. ${HELD_BACK_LINE}`);\n const codes = [\n result.brand === null ? null : `brand ${result.brand}`,\n result.sku === null ? null : `sku ${result.sku}`,\n result.gtin === null ? null : `gtin ${result.gtin}`,\n ].filter((part) => part !== null);\n if (codes.length > 0) lines.push(` ${codes.join(\" · \")}`);\n if (result.price !== null) {\n lines.push(\n result.price_to === null\n ? ` price ${moneyText(result.price)}`\n : ` price ${moneyText(result.price)} to ${moneyText(result.price_to)}, the range of the offers`,\n );\n }\n if (result.availability !== null) {\n lines.push(\n ` availability ${result.availability}${result.availabilities.length > 1 ? ` (the offers: ${result.availabilities.join(\", \")})` : \"\"}`,\n );\n }\n const variant = result.variant;\n if (variant !== null) {\n const parts = [\n variant.name,\n variant.sku === null ? null : `sku ${variant.sku}`,\n variant.price === null ? null : moneyText(variant.price),\n variant.availability,\n ].filter((part) => part !== null);\n lines.push(` variant ${parts.length > 0 ? parts.join(\" · \") : \"with no data\"}`);\n }\n if (result.seller !== null) lines.push(` seller ${result.seller}`);\n if (result.image !== null) lines.push(` image ${result.image}`);\n if (result.archived !== undefined) {\n lines.push(\n ` archived copy (${result.archived.source}, captured ${result.archived.timestamp}): ${result.archived.url}`,\n );\n }\n const facts = [\n `${result.outcome} ${String(result.status)}`,\n result.doc === null ? null : `doc ${result.doc}`,\n result.capture === null ? null : `capture ${result.capture}`,\n result.rendered ? \"rendered\" : null,\n result.from_cache ? \"from the store\" : null,\n ].filter((fact) => fact !== null);\n lines.push(` ${facts.join(\" · \")}`);\n if (result.note !== null) lines.push(` note: ${result.note}`);\n lines.push(...renderLine([result.note], renderOff).map((line) => ` ${line}`));\n return lines;\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 /** These are the options of `venue` and `product`, or undefined for a different command. */\n readonly recipeLine: RecipeLine | 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 const recipeLine =\n command === \"venue\" || command === \"product\"\n ? recipeLineOf(command, values, rest, {\n render,\n maxAge,\n budget: values.budget === undefined ? undefined : crawlLimits.budgetMs,\n })\n : undefined;\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 recipeLine,\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 polite-crawler venue <url> read the opening hours of a venue from its own site\n polite-crawler product <url> read the schema.org Product data of a product 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 --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 venue --name <text> the name of the venue: the result tells if the home page names it\n --require-name with --name: read no hours from a site that does not name the venue\n --address <text> the street address, for a page that lists more than one location\n --lat <n> --lon <n> the position of the venue, for the same use (within 150 m);\n write a value below 0 after =, such as --lon=-20.5\n --osm <value> an OpenStreetMap opening_hours value, read as one more reading;\n no request goes to OpenStreetMap\n --pages <n> the pages after the home page: 0 to 8 (default 3)\n --compare <file> compare the week of the site with a kept week (JSON), day by day\n venue, product --render, --max-age, --archive-fallback, --archive-despite-robots as for fetch\n --budget <time> no request starts after this time, such as 2m (default: no limit)\n --on-behalf-of <agent> the robots.txt rules of this agent apply too, such as\n Claude-User (repeat it)\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, 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 3 venue, product: nothing read: no opening hours of the site or no Product data, a page\n held back, or a page that did not come\n 4 venue, product: the site refused the crawler (a wall): a 401, a 402 or a 403, a\n challenge, a bot check, robots.txt or a stopped host. The crawler does not try a\n different identity\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 // ── recipes: the file of --compare and the value of --osm are checked before the store opens ──\n const recipeLine = line.recipeLine;\n const recipes = recipeLine === undefined ? undefined : await import(\"./cli-recipes.ts\");\n const recipeInput =\n recipes === undefined || recipeLine === undefined\n ? undefined\n : recipes.prepareRecipe(recipeLine);\n // ── end of recipes ──\n\n const { openCrawler } = await import(\"./open.ts\");\n // only fetch, crawl, watch run and the recipes 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 =\n command === \"fetch\" ||\n command === \"crawl\" ||\n recipeLine !== undefined ||\n 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 // ── recipes ──\n case \"venue\":\n case \"product\": {\n const io = { json: values.json === true, out, renderOff };\n return command === \"venue\"\n ? await recipes!.venueCommand(crawler, recipeLine!, recipeInput!, io)\n : await recipes!.productCommand(crawler, recipeLine!, io);\n }\n // ── end of recipes ──\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/loopback.ts","../src/cli-line.ts","../src/cli.ts"],"sourcesContent":["import { isIP } from \"node:net\";\n\n/**\n * This function tells if a host name or an address is a loopback host: `localhost`, an address in\n * `127.0.0.0/8`, or `::1`. The MCP server over HTTP binds to a loopback host unless the operator\n * permits a different host. The function imports only Node.js, so the command line uses it before\n * the store opens.\n */\nexport const isLoopbackHost = (host: string): boolean => {\n const name = host\n .trim()\n .toLowerCase()\n .replace(/^\\[(.*)\\]$/, \"$1\");\n if (name === \"localhost\" || name.endsWith(\".localhost\")) return true;\n const mapped = name.startsWith(\"::ffff:\") ? name.slice(7) : name;\n if (isIP(mapped) === 4) return mapped.startsWith(\"127.\");\n if (isIP(name) === 6) return /^(?:0{0,4}:){7}0{0,3}1$|^::1$/.test(name);\n return false;\n};\n\n/** This function gives a host for a URL: an IPv6 address goes in brackets. */\nexport const urlHost = (host: string): string => (isIP(host) === 6 ? `[${host}]` : host);\n","/*\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 { Day, HoursComparison, Interval, Week } from \"./hours.ts\";\nimport type { EmbedResult } from \"./hybrid.ts\";\nimport { isLoopbackHost } from \"./loopback.ts\";\nimport type { Money, ProductResult } from \"./recipe-product.ts\";\nimport type { VenuePage, VenueReading, VenueResult } from \"./recipe-venue.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 // ── recipes ──\n name: { type: \"string\" },\n \"require-name\": { type: \"boolean\" },\n address: { type: \"string\" },\n lat: { type: \"string\" },\n lon: { type: \"string\" },\n osm: { type: \"string\" },\n pages: { type: \"string\" },\n compare: { type: \"string\" },\n \"on-behalf-of\": { type: \"string\", multiple: true },\n // ── end of recipes ──\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 `venue` only. */\nexport const VENUE_OPTIONS: readonly string[] = [\n \"name\",\n \"require-name\",\n \"address\",\n \"lat\",\n \"lon\",\n \"osm\",\n \"pages\",\n \"compare\",\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 venue product\",\n \"max-age\": \"fetch crawl discover venue product\",\n limit: \"find\",\n kind: \"find embed\",\n rank: \"find\",\n from: \"read diff\",\n length: \"read\",\n \"user-provided\": \"read extract venue product\",\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 venue product\",\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 venue product\",\n \"archive-despite-robots\": \"fetch venue product\",\n \"sign-key\": \"fetch discover crawl watch mcp key-directory daemon venue product\",\n \"signature-agent\": \"fetch discover crawl watch mcp key-directory daemon venue product\",\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 // ── recipes ──\n ...Object.fromEntries(VENUE_OPTIONS.map((option) => [option, \"venue\"])),\n \"on-behalf-of\": \"venue product\",\n // ── end of recipes ──\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 // ── recipes ──\n venue: { min: 1, max: 1, wants: \"venue wants the URL of the site of one venue\" },\n product: { min: 1, max: 1, wants: \"product wants the URL of one product page\" },\n // ── end of recipes ──\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// ── recipes ─────────────────────────────────────────────────────────────────\n\n/** This value is the most pages of `venue` after the home page. It is `VENUE_LIMITS.maxPages` of `recipe-venue.ts`. */\nexport const VENUE_MAX_PAGES = 8;\n\n/** This exit code of `venue` and `product` tells that the command read nothing. */\nexport const NOTHING_READ = 3;\n\n/** This exit code of `venue` and `product` tells that the site refused the crawler (a wall). */\nexport const WALL = 4;\n\n/** The place of a venue, as `VenueLocation` of `recipe-venue.ts` gives it. */\nexport interface CliLocation {\n readonly address?: string | undefined;\n readonly lat?: number | undefined;\n readonly lon?: number | undefined;\n}\n\n/** These are the options of `venue` and `product`, read from the command line before the store opens. */\nexport interface RecipeLine {\n readonly recipe: \"venue\" | \"product\";\n readonly url: string;\n readonly render: \"auto\" | \"never\" | \"always\";\n /** This value is `--max-age` in milliseconds. */\n readonly maxAge: number;\n /** This value is `--budget` in milliseconds, or undefined with no `--budget`. */\n readonly budget: number | undefined;\n /** These tokens name the agents of `--on-behalf-of`. Their robots.txt rules apply too. */\n readonly onBehalfOf: readonly string[] | undefined;\n readonly userProvided: boolean;\n readonly fallback: \"archive\" | undefined;\n readonly archiveDespiteRobots: boolean;\n /** This text is `--name` of `venue`. */\n readonly name: string | undefined;\n readonly requireName: boolean;\n /** This value is the place of `--address`, `--lat` and `--lon`. */\n readonly location: CliLocation | undefined;\n /** This text is the OpenStreetMap value of `--osm`. */\n readonly osm: string | undefined;\n readonly pages: number | undefined;\n /** This text is the file of `--compare`. The command reads it before the store opens. */\n readonly compare: string | undefined;\n}\n\n/** This function reads a latitude or a longitude of the command line. */\nconst coordinateOf = (name: \"lat\" | \"lon\", text: string, limit: number): number => {\n const value = /^\\s*-?\\d+(?:\\.\\d+)?\\s*$/.test(text) ? Number(text) : Number.NaN;\n if (!Number.isFinite(value) || Math.abs(value) > limit) {\n throw new UsageError(\n `--${name} wants a number from -${String(limit)} to ${String(limit)}, not \"${text}\"`,\n );\n }\n return value;\n};\n\n/** This function checks the options of `venue` and `product`, and gives the options of the recipe. */\nexport const recipeLineOf = (\n recipe: \"venue\" | \"product\",\n values: Values,\n rest: readonly string[],\n page: {\n readonly render: \"auto\" | \"never\" | \"always\";\n readonly maxAge: number;\n readonly budget: number | undefined;\n },\n): RecipeLine => {\n const url = rest[0] ?? \"\";\n if (!/^https?:\\/\\//i.test(url) || !URL.canParse(url))\n throw new UsageError(`${recipe} wants an http or https URL, not \"${url}\"`);\n for (const agent of values[\"on-behalf-of\"] ?? []) {\n if (!/^[A-Za-z0-9_-]+$/.test(agent)) {\n throw new UsageError(\n `--on-behalf-of wants the robots.txt token of an agent, such as Claude-User, not \"${agent}\"`,\n );\n }\n }\n const name = values.name;\n if (name !== undefined && name.trim() === \"\")\n throw new UsageError(\"--name wants the name of the venue\");\n if (values[\"require-name\"] === true && name === undefined)\n throw new UsageError(\"--require-name goes with --name\");\n const address = values.address;\n if (address !== undefined && address.trim() === \"\")\n throw new UsageError(\"--address wants a street address\");\n if ((values.lat === undefined) !== (values.lon === undefined))\n throw new UsageError(\"--lat and --lon go together\");\n const lat = values.lat === undefined ? undefined : coordinateOf(\"lat\", values.lat, 90);\n const lon = values.lon === undefined ? undefined : coordinateOf(\"lon\", values.lon, 180);\n const osm = values.osm;\n if (osm !== undefined && osm.trim() === \"\")\n throw new UsageError('--osm wants an opening_hours value, such as \"Mo-Fr 08:00-18:00\"');\n const pages = values.pages === undefined ? undefined : whole(\"pages\", values.pages, 0, 0);\n if (pages !== undefined && pages > VENUE_MAX_PAGES)\n throw new UsageError(`--pages wants a whole number from 0 to ${String(VENUE_MAX_PAGES)}`);\n const compare = values.compare;\n if (compare !== undefined && compare.trim() === \"\")\n throw new UsageError(\"--compare wants a file\");\n return {\n recipe,\n url,\n render: page.render,\n maxAge: page.maxAge,\n budget: page.budget,\n onBehalfOf: values[\"on-behalf-of\"],\n userProvided: values[\"user-provided\"] === true,\n fallback: values[\"archive-fallback\"] === true ? \"archive\" : undefined,\n archiveDespiteRobots: values[\"archive-despite-robots\"] === true,\n name,\n requireName: values[\"require-name\"] === true,\n location:\n address === undefined && lat === undefined\n ? undefined\n : {\n ...(address === undefined ? {} : { address }),\n ...(lat === undefined ? {} : { lat, lon }),\n },\n osm,\n pages,\n compare,\n };\n};\n\n/** The days of a week in their order, as `DAYS` of `hours.ts`. */\nconst WEEK_DAYS: readonly Day[] = [\"mon\", \"tue\", \"wed\", \"thu\", \"fri\", \"sat\", \"sun\"];\n\nconst DAY_LABELS: Readonly<Record<Day, string>> = {\n mon: \"Mon\",\n tue: \"Tue\",\n wed: \"Wed\",\n thu: \"Thu\",\n fri: \"Fri\",\n sat: \"Sat\",\n sun: \"Sun\",\n};\n\nconst isRecord = (value: unknown): value is Readonly<Record<string, unknown>> =>\n typeof value === \"object\" && value !== null && !Array.isArray(value);\n\n/** This function tells if a value is a time of a kept week: `HH:MM` or `H:MM`, to `24:00`. */\nconst isClock = (value: unknown): value is string => {\n if (typeof value !== \"string\") return false;\n const match = /^(\\d{1,2}):(\\d{2})$/.exec(value);\n return match !== null && Number(match[1]) <= 24 && Number(match[2]) < 60;\n};\n\nconst intervalOf = (value: unknown): Interval | undefined =>\n Array.isArray(value) && value.length === 2 && isClock(value[0]) && isClock(value[1])\n ? [value[0], value[1]]\n : undefined;\n\n/** The kept week of `--compare`, and the file that it came from. */\nexport interface KeptWeek {\n readonly file: string;\n readonly week: Week;\n}\n\n/**\n * This function reads the kept week of `--compare`, a JSON file. The file is a week, or an object\n * with the field `week` (a reading of `venue --json`) or `hours_week`. Each day gives a list of\n * intervals (`[]` for closed), one interval, or null for closed. An error names the file.\n */\nexport const keptWeekOf = (path: string): KeptWeek => {\n const file = fileOf(path);\n let text: string;\n try {\n text = readFileSync(file, \"utf8\");\n } catch (error) {\n const code = (error as { code?: unknown }).code;\n throw new UsageError(\n `--compare: the file ${file} does not open (${typeof code === \"string\" ? code : \"error\"})`,\n { cause: error },\n );\n }\n let data: unknown;\n try {\n // a byte order mark at the start is not JSON, and some editors of Windows write one\n data = JSON.parse(text.charCodeAt(0) === 0xfeff ? text.slice(1) : text);\n } catch (error) {\n throw new UsageError(`--compare: the file ${file} is not JSON`, { cause: error });\n }\n if (isRecord(data) && (isRecord(data[\"week\"]) || isRecord(data[\"hours_week\"])))\n data = isRecord(data[\"week\"]) ? data[\"week\"] : data[\"hours_week\"];\n const wants = `--compare: the file ${file} is not a week. Write a list of intervals, one interval or null for each day, such as {\"mon\": [[\"09:00\", \"17:00\"]], \"sun\": []}`;\n if (!isRecord(data)) throw new UsageError(wants);\n const week: Partial<Record<Day, readonly Interval[]>> = {};\n for (const [key, value] of Object.entries(data)) {\n const day = WEEK_DAYS.find((one) => one === key);\n if (day === undefined) {\n throw new UsageError(\n `--compare: the file ${file} names \"${key}\", not a day: use mon, tue, wed, thu, fri, sat or sun`,\n );\n }\n const one = intervalOf(value);\n const intervals =\n value === null\n ? []\n : one !== undefined\n ? [one]\n : Array.isArray(value)\n ? value.map(intervalOf)\n : [undefined];\n if (intervals.some((interval) => interval === undefined))\n throw new UsageError(`${wants} (the value of ${key} is not)`);\n week[day] = intervals as Interval[];\n }\n return { file, week };\n};\n\n/** This function writes the intervals of one day, as `describeWeek` of `hours.ts` does. */\nexport const dayText = (intervals: readonly Interval[] | undefined): string =>\n intervals === undefined\n ? \"not stated\"\n : intervals.length === 0\n ? \"closed\"\n : intervals\n .map(([open, close]) =>\n open === \"00:00\" && close === \"00:00\" ? \"open 24 hours\" : `${open}–${close}`,\n )\n .join(\", \");\n\n/** The result of `--compare`: the kept week and its comparison with the week of the site. */\nexport interface VenueCompare {\n readonly file: string;\n readonly kept: Week;\n /** This value is null when the result has no reading of the site with no guess (`from_site`). */\n readonly comparison: HoursComparison | null;\n}\n\n/** One day of `--compare`. */\nexport interface CompareDay {\n readonly day: Day;\n /** The value `not read` tells that only the kept week states the day. */\n readonly status: \"same\" | \"changed\" | \"new\" | \"skipped\" | \"not read\";\n readonly kept: readonly Interval[] | undefined;\n readonly read: readonly Interval[] | undefined;\n}\n\n/** This function gives the days of a comparison in their order. A day that no week states is not there. */\nexport const compareDays = (kept: Week, read: Week, comparison: HoursComparison): CompareDay[] => {\n const days: CompareDay[] = [];\n for (const day of WEEK_DAYS) {\n const one = { day, kept: kept[day], read: read[day] };\n if (one.kept === undefined && one.read === undefined) continue;\n const status: CompareDay[\"status\"] =\n comparison.status === \"new\" || comparison.added.includes(day)\n ? \"new\"\n : comparison.days.includes(day)\n ? \"changed\"\n : comparison.skipped.includes(day)\n ? \"skipped\"\n : one.read === undefined\n ? \"not read\"\n : \"same\";\n if (comparison.status === \"new\" && one.read === undefined) continue;\n days.push({ ...one, status });\n }\n return days;\n};\n\nconst QUOTE_CHARS = 200;\n\nconst shortText = (text: string): string =>\n text.length > QUOTE_CHARS ? `${text.slice(0, QUOTE_CHARS)}…` : text;\n\n/** This function gives the lines of one reading: its rank, its source, its week and its quote. */\nconst readingLines = (one: VenueReading): string[] => {\n const marks = [\n one.schedule === \"other\" ? \"not the opening hours\" : null,\n one.inferred ? \"am or pm guessed\" : null,\n one.partial ? \"a rule left out\" : null,\n one.season === null ? null : `season ${one.season}`,\n ].filter((mark) => mark !== null);\n const where = [\n one.url,\n one.path,\n one.capture === null ? null : `capture ${one.capture}`,\n one.archived === undefined\n ? null\n : `archived copy (${one.archived.source}, captured ${one.archived.timestamp})`,\n ].filter((part) => part !== null);\n return [\n ` rank ${String(one.rank)} ${one.from}${one.label === null ? \"\" : ` \"${one.label}\"`} ${one.hours === \"\" ? \"no day\" : one.hours}${marks.length > 0 ? ` (${marks.join(\", \")})` : \"\"}`,\n ...(where.length > 0 ? [` ${where.join(\" · \")}`] : []),\n one.from === \"text\"\n ? ` quote: ${JSON.stringify(shortText(one.raw))}`\n : ` value: ${shortText(one.raw.replace(/\\s+/g, \" \"))}`,\n ];\n};\n\nconst pageLine = (page: VenuePage): string => {\n const facts = [\n `${page.outcome} ${String(page.status)}`,\n page.from_cache ? \"from the store\" : null,\n page.rendered ? \"rendered\" : null,\n page.held_back ? \"held back\" : null,\n page.archived === undefined ? null : `archived copy (${page.archived.source})`,\n ].filter((fact) => fact !== null);\n const read = page.outcome === \"ok\" || page.outcome === \"not-modified\";\n return ` ${read || page.archived !== undefined ? \"✓\" : \"✗\"} ${page.url} ${facts.join(\" · \")}`;\n};\n\nconst WALL_OUTCOMES: ReadonlySet<string> = new Set([\"blocked\", \"challenge\", \"payment-required\"]);\n\n/**\n * This function tells if a site refused the crawler, with the rule of `wall` of `readProduct`. The\n * causes are a 401, a 402 or a 403, a challenge, a bot check, robots.txt or a stop of the host.\n */\nexport const refusedPage = (page: {\n readonly outcome: string;\n readonly doc: string | null;\n readonly note: string | null;\n}): boolean =>\n WALL_OUTCOMES.has(page.outcome) ||\n (page.outcome === \"skipped\" && /^(?:robots\\.txt disallows|host stopped)/.test(page.note ?? \"\")) ||\n (page.doc === null && /\\bbot check\\b/.test(page.note ?? \"\"));\n\n/**\n * This function gives the exit code of `venue`. It is 0 when the site gave a reading of the opening\n * hours. It is `WALL` when the home page refused the crawler, and `NOTHING_READ` for a different\n * result. An OpenStreetMap value alone does not count.\n */\nexport const venueCode = (result: VenueResult): number => {\n if (result.readings.some((one) => one.from !== \"osm\" && one.schedule === \"opening\")) return 0;\n const home = result.pages[0];\n return home !== undefined && refusedPage(home) ? WALL : NOTHING_READ;\n};\n\n/** This function gives the exit code of `product`: 0 for Product data, `WALL` for a refusal, else `NOTHING_READ`. */\nexport const productCode = (result: ProductResult): number =>\n result.found ? 0 : result.wall ? WALL : NOTHING_READ;\n\n/** This line tells how to read a held-back page. */\nconst HELD_BACK_LINE =\n \"Add --user-provided if you gave this URL yourself. The crawler logs that read.\";\n\n/** This function gives the line of a page that needs a browser, when rendering is off. */\nconst renderLine = (\n notes: ReadonlyArray<string | null>,\n renderOff: string | undefined,\n): string[] =>\n renderOff !== undefined && notes.some((note) => note !== null && /no browser/i.test(note))\n ? [`rendering is off: ${renderOff}`]\n : [];\n\n/**\n * This function gives the lines of `venue`: the week of the site, and each reading with its source.\n * Then come the days where the readings disagree, the menus, the phone, the pages and the notes.\n * With `--compare`, the lines give each day of the comparison.\n */\nexport const describeVenue = (\n result: VenueResult,\n compare?: VenueCompare,\n renderOff?: string,\n): string[] => {\n const code = venueCode(result);\n const lines = [`${code === 0 ? \"✓\" : \"✗\"} ${result.home ?? result.url}`];\n const site = result.from_site;\n const home = result.pages[0];\n if (site !== null) {\n const source = [\n `${site.from}, rank ${String(site.rank)}`,\n site.url,\n site.capture === null ? null : `capture ${site.capture}`,\n ].filter((part) => part !== null);\n lines.push(` the site: ${site.hours}`, ` from ${source.join(\", \")}`);\n } else if (result.best !== null) {\n lines.push(\n ` no reading of the site with no guess. The best reading (${result.best.from}, rank ${String(result.best.rank)}): ${result.best.hours}`,\n );\n } else if (code === WALL && home !== undefined) {\n lines.push(\n ` wall: the site refused the crawler (${home.outcome} ${String(home.status)}). The crawler does not try a different identity.`,\n );\n } else lines.push(\" no opening hours found\");\n if (result.readings.length > 0) {\n lines.push(\"readings, the best first:\");\n for (const one of result.readings) lines.push(...readingLines(one));\n }\n for (const day of result.disagree) {\n const said = result.readings\n .filter((one) => one.rank <= 2 && one.week[day] !== undefined)\n .map((one) => `${dayText(one.week[day])} (rank ${String(one.rank)} ${one.from})`);\n lines.push(`the readings disagree on ${DAY_LABELS[day]}: ${said.join(\" · \")}`);\n }\n if (result.menus.length > 0) {\n lines.push(\n \"menus:\",\n ...result.menus.map((menu) => ` ${menu.url}${menu.text === \"\" ? \"\" : ` ${menu.text}`}`),\n );\n }\n if (result.phone !== null) lines.push(`phone: ${result.phone}`);\n if (result.moved !== null) lines.push(`moved: the site now goes to ${result.moved}`);\n if (result.names_venue !== null) {\n lines.push(\n result.names_venue\n ? \"names the venue: yes\"\n : \"names the venue: no. The domain can belong to a different business now.\",\n );\n }\n if (result.held_back) {\n lines.push(\n `held back: a page asked not to be AI input, so the recipe read nothing from it. ${HELD_BACK_LINE}`,\n );\n }\n if (result.pages.length > 0)\n lines.push(`pages (${String(result.pages.length)}):`, ...result.pages.map(pageLine));\n if (compare !== undefined) {\n const comparison = compare.comparison;\n if (comparison === null || site === null) {\n lines.push(\n `compare with ${compare.file}: no reading of the site with no guess, so nothing to compare`,\n );\n } else {\n const changed =\n comparison.days.length > 0\n ? ` (${comparison.days.map((day) => DAY_LABELS[day]).join(\", \")})`\n : \"\";\n lines.push(`compare with ${compare.file}: ${comparison.status}${changed}`);\n for (const one of compareDays(compare.kept, site.week, comparison)) {\n const text =\n one.status === \"changed\" || one.status === \"skipped\"\n ? `${dayText(one.kept)} → ${dayText(one.read)}`\n : one.status === \"not read\"\n ? `kept ${dayText(one.kept)}`\n : dayText(one.read);\n lines.push(` ${DAY_LABELS[one.day]} ${one.status.padEnd(8)} ${text}`);\n }\n }\n }\n if (result.note !== null) lines.push(`note: ${result.note}`);\n lines.push(...renderLine([result.note], renderOff));\n return lines;\n};\n\n/** This function gives the number of digits after the decimal point of a currency: 2 for EUR, 0 for JPY. */\nconst fractionOf = (currency: string): number => {\n try {\n return (\n new Intl.NumberFormat(\"en\", { style: \"currency\", currency }).resolvedOptions()\n .maximumFractionDigits ?? 2\n );\n } catch {\n return 2;\n }\n};\n\n/** This function writes an amount of money with its ISO 4217 code: `1,299.00 EUR`, `980 JPY`. */\nexport const moneyText = (money: Money): string => {\n const digits = fractionOf(money.currency);\n const amount = new Intl.NumberFormat(\"en\", {\n minimumFractionDigits: digits,\n maximumFractionDigits: Math.max(digits, 4),\n }).format(money.amount);\n return `${amount} ${money.currency}`;\n};\n\n/**\n * This function gives the lines of `product`. They are the name, the brand and the codes, the price\n * as money, the availability, the variant, the seller, the page and the notes. A wall and a\n * held-back page say so.\n */\nexport const describeProduct = (result: ProductResult, renderOff?: string): string[] => {\n const code = productCode(result);\n const lines = [`${code === 0 ? \"✓\" : \"✗\"} ${result.name ?? result.url}`];\n if (result.name !== null) lines.push(` ${result.url}`);\n if (code === WALL) {\n lines.push(\n ` wall: the shop refused the crawler (${result.outcome} ${String(result.status)}). The crawler does not try a different identity.`,\n );\n }\n if (result.held_back)\n lines.push(` held back: the page asked not to be AI input. ${HELD_BACK_LINE}`);\n const codes = [\n result.brand === null ? null : `brand ${result.brand}`,\n result.sku === null ? null : `sku ${result.sku}`,\n result.gtin === null ? null : `gtin ${result.gtin}`,\n ].filter((part) => part !== null);\n if (codes.length > 0) lines.push(` ${codes.join(\" · \")}`);\n if (result.price !== null) {\n lines.push(\n result.price_to === null\n ? ` price ${moneyText(result.price)}`\n : ` price ${moneyText(result.price)} to ${moneyText(result.price_to)}, the range of the offers`,\n );\n }\n if (result.availability !== null) {\n lines.push(\n ` availability ${result.availability}${result.availabilities.length > 1 ? ` (the offers: ${result.availabilities.join(\", \")})` : \"\"}`,\n );\n }\n const variant = result.variant;\n if (variant !== null) {\n const parts = [\n variant.name,\n variant.sku === null ? null : `sku ${variant.sku}`,\n variant.price === null ? null : moneyText(variant.price),\n variant.availability,\n ].filter((part) => part !== null);\n lines.push(` variant ${parts.length > 0 ? parts.join(\" · \") : \"with no data\"}`);\n }\n if (result.seller !== null) lines.push(` seller ${result.seller}`);\n if (result.image !== null) lines.push(` image ${result.image}`);\n if (result.archived !== undefined) {\n lines.push(\n ` archived copy (${result.archived.source}, captured ${result.archived.timestamp}): ${result.archived.url}`,\n );\n }\n const facts = [\n `${result.outcome} ${String(result.status)}`,\n result.doc === null ? null : `doc ${result.doc}`,\n result.capture === null ? null : `capture ${result.capture}`,\n result.rendered ? \"rendered\" : null,\n result.from_cache ? \"from the store\" : null,\n ].filter((fact) => fact !== null);\n lines.push(` ${facts.join(\" · \")}`);\n if (result.note !== null) lines.push(` note: ${result.note}`);\n lines.push(...renderLine([result.note], renderOff).map((line) => ` ${line}`));\n return lines;\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 /** These are the options of `venue` and `product`, or undefined for a different command. */\n readonly recipeLine: RecipeLine | 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 const recipeLine =\n command === \"venue\" || command === \"product\"\n ? recipeLineOf(command, values, rest, {\n render,\n maxAge,\n budget: values.budget === undefined ? undefined : crawlLimits.budgetMs,\n })\n : undefined;\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 recipeLine,\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 (the package\n// polite-crawler-mcp), 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 start the MCP server of polite-crawler-mcp (deprecated)\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 polite-crawler venue <url> read the opening hours of a venue from its own site\n polite-crawler product <url> read the schema.org Product data of a product 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 --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 venue --name <text> the name of the venue: the result tells if the home page names it\n --require-name with --name: read no hours from a site that does not name the venue\n --address <text> the street address, for a page that lists more than one location\n --lat <n> --lon <n> the position of the venue, for the same use (within 150 m);\n write a value below 0 after =, such as --lon=-20.5\n --osm <value> an OpenStreetMap opening_hours value, read as one more reading;\n no request goes to OpenStreetMap\n --pages <n> the pages after the home page: 0 to 8 (default 3)\n --compare <file> compare the week of the site with a kept week (JSON), day by day\n venue, product --render, --max-age, --archive-fallback, --archive-despite-robots as for fetch\n --budget <time> no request starts after this time, such as 2m (default: no limit)\n --on-behalf-of <agent> the robots.txt rules of this agent apply too, such as\n Claude-User (repeat it)\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, 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 3 venue, product: nothing read: no opening hours of the site or no Product data, a page\n held back, or a page that did not come\n 4 venue, product: the site refused the crawler (a wall): a 401, a 402 or a 403, a\n challenge, a bot check, robots.txt or a stopped host. The crawler does not try a\n different identity\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 // ── the MCP server is the package polite-crawler-mcp: this command starts it for one minor version ──\n if (command === \"mcp\") {\n const { McpPackageMissing, loadMcpPackage } = await import(\"./mcp-package.ts\");\n let mcp;\n try {\n mcp = await loadMcpPackage();\n } catch (error) {\n if (!(error instanceof McpPackageMissing)) throw error;\n process.stderr.write(`polite-crawler: ${error.message}\\n`);\n return 2;\n }\n process.stderr.write(\n \"polite-crawler: the command mcp is deprecated, and 0.3.0 removes it. Start npx polite-crawler-mcp.\\n\",\n );\n await mcp.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 // ── recipes: the file of --compare and the value of --osm are checked before the store opens ──\n const recipeLine = line.recipeLine;\n const recipes = recipeLine === undefined ? undefined : await import(\"./cli-recipes.ts\");\n const recipeInput =\n recipes === undefined || recipeLine === undefined\n ? undefined\n : recipes.prepareRecipe(recipeLine);\n // ── end of recipes ──\n\n const { openCrawler } = await import(\"./open.ts\");\n // only fetch, crawl, watch run and the recipes 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 =\n command === \"fetch\" ||\n command === \"crawl\" ||\n recipeLine !== undefined ||\n 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 // ── recipes ──\n case \"venue\":\n case \"product\": {\n const io = { json: values.json === true, out, renderOff };\n return command === \"venue\"\n ? await recipes!.venueCommand(crawler, recipeLine!, recipeInput!, io)\n : await recipes!.productCommand(crawler, recipeLine!, io);\n }\n // ── end of recipes ──\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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"}
|