{"_id":"@crimson-crawler/crimson-crawler-client","name":"@crimson-crawler/crimson-crawler-client","dist-tags":{"latest":"4.1.1"},"versions":{"4.1.1":{"name":"@crimson-crawler/crimson-crawler-client","version":"4.1.1","description":"Typed TypeScript client for the Crimson Crawler API","license":"Apache-2.0","author":{"name":"Def-Logix, Inc."},"private":false,"repository":{"type":"git","url":"git+https://github.com/crimson-crawler/typescript-sdk.git"},"homepage":"https://crimsoncrawler.com/docs/clients/typescript-client/","bugs":{"url":"https://github.com/crimson-crawler/typescript-sdk/issues"},"type":"module","main":"./dist/index.cjs","module":"./dist/index.js","types":"./dist/index.d.ts","exports":{".":{"import":{"types":"./dist/index.d.ts","default":"./dist/index.js"},"require":{"types":"./dist/index.d.cts","default":"./dist/index.cjs"}},"./presets":{"import":{"types":"./dist/presets.d.ts","default":"./dist/presets.js"},"require":{"types":"./dist/presets.d.cts","default":"./dist/presets.cjs"}},"./_generated":{"import":{"types":"./dist/_generated/index.d.ts","default":"./dist/_generated/index.js"},"require":{"types":"./dist/_generated/index.d.cts","default":"./dist/_generated/index.cjs"}},"./_generated/sdk.gen":{"import":{"types":"./dist/_generated/sdk.gen.d.ts","default":"./dist/_generated/sdk.gen.js"},"require":{"types":"./dist/_generated/sdk.gen.d.cts","default":"./dist/_generated/sdk.gen.cjs"}},"./_generated/types.gen":{"import":{"types":"./dist/_generated/types.gen.d.ts","default":"./dist/_generated/types.gen.js"},"require":{"types":"./dist/_generated/types.gen.d.cts","default":"./dist/_generated/types.gen.cjs"}},"./_generated/client.gen":{"import":{"types":"./dist/_generated/client.gen.d.ts","default":"./dist/_generated/client.gen.js"},"require":{"types":"./dist/_generated/client.gen.d.cts","default":"./dist/_generated/client.gen.cjs"}}},"scripts":{"codegen":"node scripts/codegen.mjs","build":"tsup && node scripts/verify-exports.mjs --record","typecheck":"tsc --noEmit","lint":"eslint .","format":"prettier --write \"src/**/*.ts\" \"!src/_generated/**\" \"tests/**/*.ts\" \"*.config.ts\" \"eslint.config.js\" \"!dist/**\"","format:check":"prettier --check \"src/**/*.ts\" \"!src/_generated/**\" \"tests/**/*.ts\" \"*.config.ts\" \"eslint.config.js\" \"!dist/**\"","test":"vitest run","test:coverage":"vitest run --coverage","prepack":"node scripts/verify-exports.mjs && node scripts/readme-for-pack.mjs --apply","postpack":"node scripts/readme-for-pack.mjs --restore"},"devDependencies":{"@eslint/js":"^10.0.1","@hey-api/openapi-ts":"^0.99.0","@types/node":"^22.19.0","@vitest/coverage-v8":"^4.1.10","eslint":"^10.3.0","globals":"^17.6.0","prettier":"^3.6.2","tsup":"^8.5.1","typescript":"^5.9.0","typescript-eslint":"^8.59.2","vitest":"^4.1.10"},"prettier":{"tabWidth":2},"engines":{"node":">=22"},"publishConfig":{"access":"public","registry":"https://registry.npmjs.org/"},"dependencies":{"undici":"^7.29.0"},"overrides":{"esbuild":"0.28.1","js-yaml":"4.3.0"},"_id":"@crimson-crawler/crimson-crawler-client@4.1.1","_integrity":"sha512-dinHx0ekNrPZGdNUXLLZ0XJw+2erjAVkQycaB9r5J34h6JtypUKOuPkAhcUkMXohbXwa8sWgV7+75Rp66/dh/A==","_resolved":"/home/runner/work/typescript-sdk/typescript-sdk/distribution/crimson-crawler-crimson-crawler-client-4.1.1.tgz","_from":"file:distribution/crimson-crawler-crimson-crawler-client-4.1.1.tgz","_nodeVersion":"24.18.0","_npmVersion":"11.16.0","dist":{"integrity":"sha512-dinHx0ekNrPZGdNUXLLZ0XJw+2erjAVkQycaB9r5J34h6JtypUKOuPkAhcUkMXohbXwa8sWgV7+75Rp66/dh/A==","shasum":"fe28954cb7239b0af97b24eea374d6e9aa8f6bfc","tarball":"https://registry.npmjs.org/@crimson-crawler/crimson-crawler-client/-/crimson-crawler-client-4.1.1.tgz","fileCount":41,"unpackedSize":1163747,"attestations":{"url":"https://registry.npmjs.org/-/npm/v1/attestations/@crimson-crawler%2fcrimson-crawler-client@4.1.1","provenance":{"predicateType":"https://slsa.dev/provenance/v1"}},"signatures":[{"keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U","sig":"MEQCIDMIPcZy/7nFT8VPKh/b2ZrQ2OUlMN2kLMYya/S6IZoGAiBGlNOn6yEiegFcdcgsSJVtLbMN8WdK3l7bkb+lrdc6rg=="}]},"_npmUser":{"name":"nnavarro9","email":"nnavarro@def-logix.com"},"directories":{},"maintainers":[{"name":"natedef","email":"nnawrocki@def-logix.com"},{"name":"nnavarro9","email":"nnavarro@def-logix.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages-npm-production","tmp":"tmp/crimson-crawler-client_4.1.1_1785911884198_0.09866309739279266"},"_hasShrinkwrap":false}},"time":{"created":"2026-08-05T06:38:04.011Z","4.1.1":"2026-08-05T06:38:04.357Z","modified":"2026-08-05T06:38:04.771Z"},"maintainers":[{"name":"natedef","email":"nnawrocki@def-logix.com"},{"name":"nnavarro9","email":"nnavarro@def-logix.com"}],"description":"Typed TypeScript client for the Crimson Crawler API","homepage":"https://crimsoncrawler.com/docs/clients/typescript-client/","repository":{"type":"git","url":"git+https://github.com/crimson-crawler/typescript-sdk.git"},"author":{"name":"Def-Logix, Inc."},"bugs":{"url":"https://github.com/crimson-crawler/typescript-sdk/issues"},"license":"Apache-2.0","readme":"# Crimson Crawler TypeScript SDK\n\n<p align=\"center\">\n  <a href=\"https://crimsoncrawler.com\">\n    <img src=\"https://crimsoncrawler.com/logo-icon.png\" alt=\"Crimson Crawler spider mark\" width=\"112\">\n  </a>\n</p>\n<p align=\"center\">\n  <a href=\"https://crimsoncrawler.com\">\n    <img src=\"https://crimsoncrawler.com/crimson-crawler-logo-typescript.svg\" alt=\"Crimson Crawler\" width=\"520\">\n  </a>\n</p>\n<p align=\"center\"><strong>Typed threat intelligence for modern JavaScript runtimes.</strong></p>\n<p align=\"center\">\n  <a href=\"https://crimsoncrawler.com/docs/clients/typescript-client/\">Documentation</a> ·\n  <a href=\"https://www.npmjs.com/package/@crimson-crawler/crimson-crawler-client\">npm</a> ·\n  <a href=\"https://github.com/crimson-crawler/typescript-sdk\">GitHub</a> ·\n  <a href=\"https://crimsoncrawler.com/dashboard\">Get an API key</a>\n</p>\n<p align=\"center\">\n  <a href=\"https://www.npmjs.com/package/@crimson-crawler/crimson-crawler-client\"><img alt=\"npm version\" src=\"https://img.shields.io/npm/v/%40crimson-crawler%2Fcrimson-crawler-client?style=flat-square&labelColor=18181B&color=EF4444\"></a>\n  <img alt=\"Node 22+\" src=\"https://img.shields.io/badge/Node-22%2B-EF4444?style=flat-square&labelColor=18181B\">\n  <img alt=\"ESM and CommonJS\" src=\"https://img.shields.io/badge/module-ESM%20%2B%20CJS-EF4444?style=flat-square&labelColor=18181B\">\n  <a href=\"https://github.com/crimson-crawler/typescript-sdk/blob/main/LICENSE\"><img alt=\"Apache 2.0\" src=\"https://img.shields.io/badge/license-Apache--2.0-EF4444?style=flat-square&labelColor=18181B\"></a>\n</p>\n\nTurn a CVE into a complete attack briefing with one typed call. ESM and CommonJS builds cover targeted enrichment, artifact and inventory workflows, grounded reports, and every v1 endpoint without hand-written request models.\n\n## Project status\n\nThe SDK is ready for its first public release. npm publication is pending; the version badge above will show the registry version once that release succeeds. Until then, use the repository checkout for evaluation.\n\nA checkout carries the hand-written wrapper without the generated request layer, so run `npm run codegen -- --snapshot-only` once before you build or test it. The published package ships both layers, so installing from npm never needs code generation.\n\n## Install\n\n```bash\nnpm install @crimson-crawler/crimson-crawler-client\n# or\npnpm add @crimson-crawler/crimson-crawler-client\n```\n\nThese commands apply once the first npm release is available.\n\nNode 22+ is the supported runtime. The default transport uses Node's built-in `fetch`; setting `verifySsl: false` dynamically imports `undici`. Browser and edge runtimes are not tested or supported.\n\n\n## Authentication\n\nSet your key in the environment:\n\n```bash\nexport CRIMSON_CRAWLER_API_KEY=your-api-key\n```\n\nOr pass it directly (preferred for tests and multi-tenant code):\n\n```ts\nconst client = new CrawlerClient({ apiKey: \"your-api-key\" });\n```\n\nThe constructor option wins over the env var. Generate a key at [crimsoncrawler.com/dashboard](https://crimsoncrawler.com/dashboard).\n\n## Custom or local endpoint\n\nThe hosted API at `https://crimsoncrawler.com` is the default. Point the\nofficial package at a local or self-hosted deployment with either form:\n\n```bash\nexport CRIMSON_CRAWLER_BASE_URL=http://localhost:8000\n```\n\n```ts\nconst client = new CrawlerClient({ baseUrl: \"http://localhost:8000\" });\n```\n\nThe SDK sends its `apikey` header to the selected origin. Use a local or\ndevelopment key when overriding the endpoint; do not reuse a production key\nwith an origin you do not control. A loopback origin may be plaintext; any\nother host must be `https://`. TLS verification stays enabled by default.\nTrust the deployment's CA when possible. `verifySsl: false` or\n`CRIMSON_CRAWLER_VERIFY_SSL=false` is only for isolated local Node.js\ndevelopment with a self-signed certificate. Other runtimes fail closed rather\nthan disabling TLS verification process-wide.\n\n## Quick start\n\n```ts\nimport { CrawlerClient } from \"@crimson-crawler/crimson-crawler-client\";\n\nconst client = new CrawlerClient();\nconst result = await client.enrichCveFull(\"CVE-2024-3400\");\n\nconsole.log(result.cve?.cvss_score);\n\nfor (const t of result.techniques ?? []) {\n  console.log(t.technique_id, t.name);\n}\n```\n\nOptional chaining (`result.cve?.cvss_score`) and nullish coalescing (`result.techniques ?? []`) are the recommended patterns. Every layer of an enrichment response is independently optional, since any single upstream lookup may have no data.\n\n## The client\n\n### Constructor\n\n```ts\nnew CrawlerClient(opts?: CrawlerClientOptions)\n```\n\n```ts\ninterface CrawlerClientOptions {\n  apiKey?: string; // env CRIMSON_CRAWLER_API_KEY\n  baseUrl?: string; // env CRIMSON_CRAWLER_BASE_URL, then hosted default\n  verifySsl?: boolean; // env CRIMSON_CRAWLER_VERIFY_SSL, then true\n  maxRetries?: number; // default 2\n  configureGlobalClient?: boolean; // default false; single-client scripts only\n}\n```\n\n| Option | Type | Default | Description |\n|---|---|---|---|\n| `apiKey` | `string` | env `CRIMSON_CRAWLER_API_KEY` | Sent as the `apikey` header on every request. |\n| `baseUrl` | `string` | env `CRIMSON_CRAWLER_BASE_URL`, then `https://crimsoncrawler.com` | Custom, self-hosted, or local API origin. |\n| `verifySsl` | `boolean` | env `CRIMSON_CRAWLER_VERIFY_SSL`, then `true` | Disable only for isolated local Node.js development; prefer trusting the deployment CA. |\n| `maxRetries` | `number` | `2` | Number of automatic retries on transient failures (see [Retry & rate limits](#retry--rate-limits)). `0` disables. |\n| `configureGlobalClient` | `boolean` | `false` | Configure the generated module singleton for direct generated calls that omit `client`. Do not enable in multi-tenant processes. |\n\nThrows `MissingCredentials` if `apiKey` is missing. Throws `RangeError` if `maxRetries` is negative or not an integer.\n\n### Properties\n\n- **`api`** — pass this to any endpoint function when calling endpoints directly (see [Beyond the convenience surface](#beyond-the-convenience-surface)).\n- **`baseUrl`** — read-only; the resolved hosted, custom, or local endpoint.\n- **`verifySsl`** — read-only; whether TLS certificates are verified.\n- **`maxRetries`** — read-only; the configured retry budget (see [Retry & rate limits](#retry--rate-limits)).\n\n### Lifecycle\n\nThere's no `close()` to call. Reuse a single `CrawlerClient` instance for the life of your process.\n\n## Convenience methods\n\n**Every `/v1` operation has a method** — 33 of them, plus the `searchTechniques` shortcut. The seven documented in full below cover the highest-traffic patterns; the rest are grouped after them with their signatures. The 15 `enrich*Full` methods call their endpoint with a full-walk `include` preset baked in (pass `include` to override); the others take no preset. Every method returns a promise of a typed response and throws `UnexpectedStatus` on a non-2xx.\n\n| Group | Methods |\n|---|---|\n| Full-walk enrichment (15) | `enrichCveFull` · `enrichIocFull` · `enrichCweFull` · `enrichTechniqueFull` · `enrichProductFull` · `enrichPackageFull` · `enrichCapecFull` · `enrichGroupFull` · `enrichSoftwareFull` · `enrichCampaignFull` · `enrichAtlasFull` · `enrichDisarmFull` · `enrichDefendFull` · `enrichLocationFull` · `enrichSectorFull` |\n| Enrichment, no preset (2) | `enrichBatch` · `enrichPocSource` |\n| Search (8 + 1) | `searchCve` · `searchCti` · `searchKev` · `searchMisp` · `searchKnowledgebase` · `searchVendor` · `searchPoc` · `searchD3fend` · `searchTechniques` |\n| Assessment (4) | `assessTechniqueCoverage` · `assessVulnerabilityExposure` · `assessGroupExposure` · `assessIocPortfolio` |\n| Async engines (4) | `artifactEnrich` · `artifactEnrichStatus` · `inventoryEnrich` · `inventoryEnrichStatus` |\n\nNames are the exact camelCase of the Python ones, so anything you read in the [Python guide](https://crimsoncrawler.com/docs/clients/python-client/) transfers. One to watch: the wrapper method is **`searchD3fend`** (lowercase `f`), while the generated function it wraps is `searchD3Fend`.\n\n#### `enrichCveFull(cveId, include?)`\n\nReturns CVE details, EPSS exploit probability, KEV status, mapped weaknesses, attack patterns, ATT&CK techniques, TIE threat predictions, MISP events, knowledge-base context, and web findings. Single request.\n\n```ts\nconst client = new CrawlerClient();\nconst result = await client.enrichCveFull(\"CVE-2024-3400\");\n\nconsole.log(`CVSS: ${result.cve?.cvss_score} (${result.cve?.cvss_severity})`);\nconsole.log(`Description: ${result.cve?.description}`);\n\nif (result.epss) console.log(`EPSS: ${result.epss.epss}`);\nif (result.kev?.in_kev) console.log(`KEV: added ${result.kev.date_added}`);\n\nconsole.log(`Weaknesses: ${result.weaknesses?.length ?? 0} CWE(s)`);\nconsole.log(`Attack patterns: ${result.attack_patterns?.length ?? 0} CAPEC(s)`);\nfor (const t of result.techniques ?? []) {\n  console.log(`  Technique: ${t.technique_id} - ${t.name}`);\n}\nfor (const p of result.predictions ?? []) {\n  console.log(`  TIE prediction: ${p.technique_id} (${p.probability})`);\n}\n```\n\n| Parameter | Type | Default | Description |\n|---|---|---|---|\n| `cveId` | `string` | required | CVE identifier, e.g. `\"CVE-2024-3400\"`. |\n| `include` | `readonly string[]` | `presets.CVE_FULL_WALK` | Override the default include list. |\n\nReturns: `Promise<EnrichCveResponse>`.\n\n#### `enrichIocFull(value, iocType?, include?)`\n\nReturns MISP attribute matches, linked ATT&CK techniques, attributed threat groups, knowledge-base context, and web findings for an indicator (IP, hash, domain, URL).\n\n```ts\nconst result = await client.enrichIocFull(\"203.0.113.5\", \"ip-dst\");\n\nfor (const m of result.misp_attributes ?? []) console.log(`MISP event: ${m.event_info}`);\nfor (const t of result.techniques ?? []) console.log(`Technique: ${t.technique_id}`);\n```\n\n| Parameter | Type | Default | Description |\n|---|---|---|---|\n| `value` | `string` | required | Indicator value. |\n| `iocType` | `string` | `\"ip-dst\"` | MISP-style attribute type: `ip-dst`, `ip-src`, `domain`, `hostname`, `md5`, `sha1`, `sha256`, `url`, etc. |\n| `include` | `readonly string[]` | `presets.IOC_FULL_WALK` | Override the default include list. |\n\nReturns: `Promise<EnrichIocResponse>`.\n\n#### `enrichCweFull(cweId, include?)`\n\nReturns the weakness, mapped CAPEC attack patterns, ATT&CK techniques, and TIE predictions. Use it when you start from a weakness ID.\n\n```ts\nconst result = await client.enrichCweFull(\"CWE-79\");\n\nconsole.log(`Weakness: ${result.weakness?.name}`);\nconsole.log(`CAPEC patterns: ${result.attack_patterns?.length ?? 0}`);\nconsole.log(`Techniques: ${result.techniques?.length ?? 0}`);\n```\n\n| Parameter | Type | Default | Description |\n|---|---|---|---|\n| `cweId` | `string` | required | CWE identifier, e.g. `\"CWE-79\"`. |\n| `include` | `readonly string[]` | `presets.CWE_FULL_WALK` | Override the default include list. |\n\nReturns: `Promise<EnrichCweResponse>`.\n\n#### `enrichTechniqueFull(techniqueIds, framework?, include?)`\n\nReturns technique details, TIE predictions, attributed groups, software, campaigns, and a Navigator JSON layer for one or more ATT&CK techniques.\n\n```ts\nconst result = await client.enrichTechniqueFull(\n  [\"T1190\", \"T1059.001\"],\n  \"enterprise\",\n);\n\nfor (const t of result.techniques ?? []) {\n  console.log(`${t.technique_id}: ${t.name}`);\n}\nfor (const g of result.groups ?? []) {\n  console.log(`Group using these: ${g.name}`);\n}\n```\n\n| Parameter | Type | Default | Description |\n|---|---|---|---|\n| `techniqueIds` | `string[]` | required | ATT&CK technique IDs. |\n| `framework` | `\"enterprise\" \\| \"ics\" \\| \"mobile\"` | `\"enterprise\"` | ATT&CK matrix variant. |\n| `include` | `readonly string[]` | `presets.TECHNIQUE_FULL` | Override the default include list. |\n\nReturns: `Promise<EnrichTechniqueResponse>`.\n\n#### `enrichProductFull(product, options?)`\n\nFinds vulnerabilities affecting a product/version and enriches each matched CVE with severity, exploit signals, weakness mapping, ATT&CK techniques, threat predictions, MISP events, and web context.\n\n```ts\nconst result = await client.enrichProductFull(\"Apache HTTP Server\", {\n  version: \"2.4.51\",\n  vendor: \"apache\",\n});\n\nfor (const cve of result.cves ?? []) {\n  console.log(`${cve.cve_id}: ${cve.cvss_score} (${cve.cvss_severity})`);\n  if (cve.kev?.in_kev) console.log(\"  in KEV\");\n}\n```\n\n| Parameter | Type | Default | Description |\n|---|---|---|---|\n| `product` | `string` | required | Product name, e.g. `\"Apache HTTP Server\"`, `\"nginx\"`. |\n| `options.version` | `string` | — | Specific version (e.g. `\"2.4.51\"`). Optional. |\n| `options.vendor` | `string` | — | Vendor name to narrow results (e.g. `\"apache\"`). Optional. |\n| `options.include` | `readonly string[]` | `presets.PRODUCT_FULL` | Override the default include list. |\n\nReturns: `Promise<EnrichProductResponse>`.\n\n#### `enrichPackageFull(ecosystem, package, opts?)`\n\nThe package-ecosystem sibling of `enrichProductFull`. OSV.dev supplies the package's advisories (npm / PyPI / Go / Maven / crates.io …, including the non-CVE GHSA / PYSEC / RUSTSEC / GO findings the CPE-keyed product endpoint can't reach), and the CVEs they alias are enriched through the same CWE → CAPEC → ATT&CK + EPSS/KEV chain.\n\n```ts\nconst result = await client.enrichPackageFull('npm', 'lodash');\n\nfor (const cve of result.cves ?? []) {\n  console.log(`${cve.cve_id}: ${cve.cvss_score} (${cve.cvss_severity})`);\n  if (cve.kev?.in_kev) console.log(\"  in KEV\");\n}\n```\n\n| Parameter | Type | Default | Description |\n|---|---|---|---|\n| `ecosystem` | `string` | required | OSV ecosystem, e.g. `\"PyPI\"`, `\"npm\"`, `\"Go\"`, `\"Maven\"`, `\"crates.io\"`. |\n| `package` | `string` | required | Package name within the ecosystem, e.g. `\"lodash\"`. |\n| `opts.version` | `string` | — | Specific version (e.g. `\"4.0\"`). Optional. |\n| `opts.include` | `readonly string[]` | `presets.PACKAGE_FULL` | Override the default include list. |\n\nReturns: `Promise<EnrichPackageResponse>`.\n\n#### `searchTechniques(keyword, options?)`\n\nKeyword-searches the ATT&CK knowledge base and returns matching techniques (attack-patterns).\n\n```ts\nconst result = await client.searchTechniques(\"credential dumping\", { limit: 10 });\n\nfor (const hit of result.results ?? []) {\n  console.log(hit);\n}\n```\n\n| Parameter | Type | Default | Description |\n|---|---|---|---|\n| `keyword` | `string` | required | Free-text query, e.g. `\"phishing\"`, `\"credential dumping\"`. |\n| `options.limit` | `number` | `20` | Maximum number of results (1-100). |\n\nReturns: `Promise<SearchCtiResponse>`.\n\n#### The other nine full-walk enrichments\n\nSame shape as the six above: a full-walk preset baked in, `include` to override. Each takes an **optional id positional or `options.search`** — pass one, not both — so you can go straight to an id or find the entity by name.\n\n```ts\nconst apt29 = await client.enrichGroupFull(\"G0016\");\nconst same = await client.enrichGroupFull(undefined, { search: \"cozy bear\" });\nconst ml = await client.enrichAtlasFull(\"AML.T0043\");\n```\n\n| Method | Id positional | Options | Returns |\n|---|---|---|---|\n| `enrichCapecFull(capecId?, options?)` | `CAPEC-66` | `search`, `include` | `Promise<EnrichCapecResponse>` |\n| `enrichGroupFull(groupId?, options?)` | `G0016` | `search`, `framework`, `include` | `Promise<EnrichGroupResponse>` |\n| `enrichSoftwareFull(softwareId?, options?)` | `S0154` | `search`, `framework`, `include` | `Promise<EnrichSoftwareResponse>` |\n| `enrichCampaignFull(campaignId?, options?)` | `C0011` | `search`, `framework`, `include` | `Promise<EnrichCampaignResponse>` |\n| `enrichAtlasFull(techniqueId?, options?)` | `AML.T0043` | `search`, `include` | `Promise<EnrichAtlasResponse>` |\n| `enrichDisarmFull(techniqueId?, options?)` | `T0001` | `search`, `include` | `Promise<EnrichDisarmResponse>` |\n| `enrichDefendFull(d3fendId?, options?)` | `D3-NTA` | `search`, `include` | `Promise<EnrichDefendResponse>` |\n| `enrichLocationFull(locationId?, options?)` | `L0001` | `search`, `include` | `Promise<EnrichLocationResponse>` |\n| `enrichSectorFull(sectorId?, options?)` | `financial-services` | `search`, `include` | `Promise<EnrichSectorResponse>` |\n\n`framework` is `\"enterprise\"` (default), `\"ics\"`, or `\"mobile\"`, and only exists where the endpoint supports a matrix variant.\n\n#### `enrichBatch(items)`\n\nUp to 50 heterogeneous `enrich/*` lookups in **one** request — one call against your rate limit. Items are plain objects: a `type` discriminator plus that type's fields and its own optional `include`. No preset; the array passes through untouched.\n\n```ts\nconst resp = await client.enrichBatch([\n  { type: \"cve\", cve_id: \"CVE-2024-3400\", include: [\"details\", \"kev\"] },\n  { type: \"ioc\", value: \"1.2.3.4\", value_type: \"ip-dst\" },\n  { type: \"technique\", technique_id: \"T1190\" },\n]);\n\nfor (const item of resp.results ?? []) {\n  console.log(item.index, item.type, item.status);   // input order preserved\n}\n```\n\n| Parameter | Type | Default | Description |\n|---|---|---|---|\n| `items` | `EnrichBatchRequest[\"items\"]` | required | 1–50 items. `type` is one of `cve`, `product`, `package`, `cwe`, `capec`, `technique`, `ioc`, `group`, `software`, `campaign`, `atlas`, `disarm`, `location`, `sector`, `defend`, `poc_source`. |\n\nReturns: `Promise<EnrichBatchResponse>`. One failing item does not fail the batch — each result carries its own `status` and `errors[]`. Item fields stay snake_case (they're the generated request models), and two names catch people out: IOC items use **`value_type`** for the indicator type (`type` is taken by the discriminator, unlike `enrichIocFull`'s `iocType` argument), and the D3FEND discriminator is **`defend`**, matching the endpoint path.\n\n#### `enrichPocSource(repoUrl)`\n\nLLM-summarized analysis of a proof-of-concept exploit repository — what the code does, how weaponized it looks, what it targets. No `include`.\n\n```ts\nconst pocs = await client.searchPoc(\"CVE-2024-3400\");\nconst analysis = await client.enrichPocSource(\"https://github.com/example/CVE-2024-3400-poc\");\n```\n\nThese methods retrieve and analyze public proof-of-concept exploit material. Use them only for authorized defensive research, handle retrieved code as untrusted, and do not execute it outside an isolated analysis environment.\n\nReturns: `Promise<EnrichPocSourceResponse>`.\n\n#### Search (8 methods)\n\nOne method per search endpoint. `searchCve` is the only one that accepts `include`; none of them uses a preset. `searchKev` and `searchMisp` take options only — call them bare to browse.\n\n```ts\nconst weaponized = await client.searchCve(undefined, { kevOnly: true, epssMin: 0.9, limit: 20 });\nconst kev = await client.searchKev({ ransomwareStatus: \"Known\" });\nconst answer = await client.searchKnowledgebase(\"how do I detect kerberoasting\", { alpha: 0.6 });\n```\n\n| Method | Options | Returns |\n|---|---|---|\n| `searchCve(query?, options?)` | `cveId`, `stixId`, `cweId`, `capecId`, `attackId`, `cpe`, `cpesNotVulnerable`, `createdByRef`, `createdMin`, `createdMax`, `modifiedMin`, `modifiedMax`, `cvssMin`, `cvssV2Min`, `cvssV4Min`, `epssMin`, `epssPercentileMin`, `kevOnly`, `vulnStatus`, `sort`, `include`, `limit`, `page` | `Promise<SearchCveResponse>` |\n| `searchCti(query, options?)` | `sources`, `types`, `limit`, `page`, `deprecated`, `revoked` | `Promise<SearchCtiResponse>` |\n| `searchKev(options?)` | `cveId`, `ransomwareStatus`, `limit`, `page` | `Promise<SearchKevResponse>` |\n| `searchMisp(options?)` | `value`, `typeAttribute`, `category`, `toIds`, `eventId`, `eventinfo`, `tags`, `threatLevelId`, `published`, `dateFrom`, `dateTo`, `limit`, `page` | `Promise<SearchMispResponse>` |\n| `searchKnowledgebase(query, options?)` | `collections`, `alpha`, `limit` | `Promise<SearchKnowledgebaseResponse>` |\n| `searchVendor(vendor, options?)` | `limit` | `Promise<SearchVendorResponse>` |\n| `searchPoc(cveId, options?)` | `limit` | `Promise<SearchPocResponse>` |\n| `searchD3fend(query, options?)` | `d3fendForm`, `limit`, `page` | `Promise<SearchD3FendResponse>` |\n\nConstrained values are typed unions, so your editor completes them: `vulnStatus`, `sort`, `ransomwareStatus` (`\"Known\"` / `\"Unknown\"`), `d3fendForm` (`\"tactic\"` / `\"mitigation\"` / `\"sub-mitigation\"` / `\"artifact\"`), `collections` (`\"main\"` / `\"large\"` / `\"user\"` / `\"red_team\"`), and `types` (`\"attack-pattern\"` / `\"intrusion-set\"` / `\"malware\"` / `\"tool\"` / `\"campaign\"` / `\"weakness\"` / `\"course-of-action\"`).\n\n#### Assessment (4 methods)\n\nPortfolio questions instead of single-entity lookups: what a *set* of techniques, CVEs, actors, or indicators means together. Each takes an optional `include` passthrough (no preset — omit it and the server default applies); the endpoints cap their inputs at 50.\n\n```ts\nconst gaps = await client.assessTechniqueCoverage([\"T1190\", \"T1059.001\", \"T1566\"]);\nconst exposure = await client.assessVulnerabilityExposure([\"CVE-2024-3400\", \"CVE-2021-44228\"], {\n  stakeholder: { exposure: \"controlled\", mission_prevalence: \"essential\" },\n});\nconst actors = await client.assessGroupExposure({ groupIds: [\"G0016\", \"G0007\"] });\nconst portfolio = await client.assessIocPortfolio([\n  { value: \"1.2.3.4\", type: \"ip-dst\" },\n  { value: \"evil.example\", type: \"domain\" },\n]);\n```\n\n| Method | Options | Returns |\n|---|---|---|\n| `assessTechniqueCoverage(techniqueIds, options?)` | `framework`, `include` | `Promise<AssessTechniqueCoverageResponse>` |\n| `assessVulnerabilityExposure(cveIds, options?)` | `stakeholder`, `include` | `Promise<AssessVulnerabilityExposureResponse>` |\n| `assessGroupExposure(options?)` | `groupIds`, `search`, `framework`, `include` | `Promise<AssessGroupExposureResponse>` |\n| `assessIocPortfolio(indicators, options?)` | `include` | `Promise<AssessIocPortfolioResponse>` |\n\n`stakeholder` is the SSVC decision object (`method`, `exposure`, `mission_prevalence`, `human_impact`, `public_wellbeing_impact`) and applies when `include` asks for `ssvc`. `assessGroupExposure` takes `groupIds`, free-text `search` terms, or both.\n\n#### Async engines (4 methods)\n\nThe raw submit/status pairs behind the two background enrichment engines. A submit resolves with a job handle immediately (**it counts toward your usage**); the status read is **usage-exempt**, so poll as often as you like. For a one-call version that submits, polls, and resolves with the finished report, use [`client.enricher.enrich`](#enricher-clientenricher) or `client.inventoryManager.ingestScan`.\n\n```ts\nconst job = await client.artifactEnrich({ text: \"Suspicious login from 10.1.1.1, then CVE-2024-1234 scan\" });\nconst status = await client.artifactEnrichStatus(job.enrichment_id);\n\nconst inv = await client.inventoryEnrich([{ product: \"nginx\", version: \"1.24.0\" }]);\nconst findings = await client.inventoryEnrichStatus(inv.enrichment_id);\n```\n\n| Method | Options | Returns |\n|---|---|---|\n| `artifactEnrich(options?)` | `text`, `files`, `formats`, `stakeholder`, `include` | `Promise<ArtifactEnrichResponse>` |\n| `artifactEnrichStatus(enrichmentId)` | — | `Promise<ArtifactEnrichStatusResponse>` |\n| `inventoryEnrich(items, options?)` | `attachmentsText`, `attachmentsFiles`, `include` | `Promise<InventoryEnrichResponse>` |\n| `inventoryEnrichStatus(enrichmentId)` | — | `Promise<InventoryEnrichStatusResponse>` |\n\n`files` and `attachmentsFiles` are `[{ filename, content_b64 }]`; `items` are `InventoryAsset` objects (`product` is the only required key). `formats` picks the machine exports to build alongside the report (`report`, `json`, `stix`, `vex`, `csaf`, `navigator`, `kev_remediation`, `oscal_poam`, `misp`, `csv`). A submit resolves with `202` and `status: \"queued\"` plus a `queue_position` when your key already has 3 enrichments running; past 20 in flight it throws `UnexpectedStatus(429)` with `error: \"enrichment_queue_full\"`.\n\n## Presets\n\nA preset is a named `readonly string[]`: a curated set of `include` values for one of the common workflows. The point is so you don't have to memorize which `include` values are valid for which endpoint, or pick the right depth for your use case every time.\n\nThe `presets` namespace exports 18 — one full-walk default per enrichment endpoint that accepts `include`, plus three depth variants for the two highest-traffic ones. Names and contents are byte-identical to the Python SDK's:\n\n| Preset | Use for | Include values |\n|---|---|---|\n| `CVE_FULL_WALK` | Default for `enrichCveFull`. Full enrichment depth. | `details`, `epss`, `kev`, `cwe`, `capec`, `techniques`, `tie`, `misp`, `knowledgebase`, `web` |\n| `CVE_FAST` | Quick artifact-scan. Score and KEV status, nothing else. | `details`, `epss`, `kev` |\n| `CVE_NARRATIVE` | Report generation. Full walk plus AI summary and adversary attribution. | full walk + `summary`, `groups`, `campaigns` |\n| `IOC_FULL_WALK` | Default for `enrichIocFull`. | `misp_attributes`, `techniques`, `groups`, `knowledgebase`, `web` |\n| `IOC_FAST` | Indicator hits in MISP feeds, nothing else. | `misp_attributes` |\n| `CWE_FULL_WALK` | Default for `enrichCweFull`. | `capecs`, `techniques`, `tie` |\n| `TECHNIQUE_FULL` | Default for `enrichTechniqueFull`. Adversary attribution + Navigator. | `details`, `tie`, `groups`, `software`, `campaigns`, `navigator` |\n| `PRODUCT_FULL` | Default for `enrichProductFull`. Per-CVE full enrichment for the matched set. | `details`, `epss`, `kev`, `cwe`, `capec`, `techniques`, `tie`, `misp`, `knowledgebase`, `web` |\n| `PACKAGE_FULL` | Default for `enrichPackageFull`. OSV advisories + per-CVE full enrichment. | `details`, `severity`, `affected`, `references`, `epss`, `kev`, `cwe`, `capec`, `techniques`, `tie` |\n| `CAPEC_FULL` | Default for `enrichCapecFull`. Attack pattern → techniques, CVEs, actors. | `details`, `techniques`, `tie`, `cves`, `groups`, `software`, `campaigns`, `navigator` |\n| `GROUP_FULL` | Default for `enrichGroupFull`. The full actor picture. | `details`, `techniques`, `software`, `tie`, `campaigns`, `cves`, `sectors`, `locations`, `navigator` |\n| `SOFTWARE_FULL` | Default for `enrichSoftwareFull`. Malware / tool → who uses it and how. | `details`, `techniques`, `tie`, `groups`, `campaigns`, `cves`, `navigator` |\n| `CAMPAIGN_FULL` | Default for `enrichCampaignFull`. | `details`, `techniques`, `tie`, `groups`, `software`, `cves`, `navigator` |\n| `ATLAS_FULL` | Default for `enrichAtlasFull`. Adversarial-ML technique walk. | `details`, `techniques`, `tie`, `groups`, `software`, `campaigns`, `navigator` |\n| `DISARM_FULL` | Default for `enrichDisarmFull`. Influence-operation technique + countermeasures. | `details`, `countermeasures`, `techniques`, `tie`, `groups`, `software`, `campaigns`, `navigator` |\n| `DEFEND_FULL` | Default for `enrichDefendFull`. D3FEND has no graph hops, so this set is short by design. | `details`, `techniques`, `knowledgebase`, `web` |\n| `LOCATION_FULL` | Default for `enrichLocationFull`. Regional threat picture. | `details`, `groups`, `techniques`, `tie`, `software`, `campaigns`, `sectors`, `navigator` |\n| `SECTOR_FULL` | Default for `enrichSectorFull`. Industry threat picture. | `details`, `groups`, `techniques`, `tie`, `software`, `campaigns`, `locations`, `navigator` |\n\nEvery full-walk preset stays inside its endpoint's allowed `include` set (see [Allowed `include` values per endpoint](#allowed-include-values-per-endpoint)) and deliberately leaves out `web_scrape` (a live external fetch, slow and rate-limited upstream) and `summary` (LLM generation). `CVE_NARRATIVE` is the one preset that opts into `summary` — that's what it's for.\n\nThree ways to use them:\n\n```ts\nimport { CrawlerClient, presets } from \"@crimson-crawler/crimson-crawler-client\";\n\nconst client = new CrawlerClient();\n\n// Pass a preset by name\nawait client.enrichCveFull(\"CVE-2024-3400\", presets.CVE_NARRATIVE);\n\n// Build on a preset (presets are readonly — spread to a fresh array)\nawait client.enrichCveFull(\"CVE-2024-3400\", [...presets.CVE_FAST, \"summary\"]);\n\n// Skip presets entirely and pass your own list\nawait client.enrichCveFull(\"CVE-2024-3400\", [\"details\", \"epss\"]);\n```\n\nPicking one:\n\n- Triaging a list of CVEs? `CVE_FAST`. Sub-second on warm cache.\n- Building a report? `CVE_NARRATIVE`. Full enrichment plus an AI summary.\n- Recon walk on a single CVE? `CVE_FULL_WALK` (the default).\n- Checking if an IP is in MISP? `IOC_FAST`.\n- Threat-modeling around a weakness class? `CWE_FULL_WALK`.\n- Coverage analysis on specific techniques? `TECHNIQUE_FULL`.\n- \"What's exposed in this product/version?\" `PRODUCT_FULL`.\n- Profiling an actor, malware family, or campaign? `GROUP_FULL` / `SOFTWARE_FULL` / `CAMPAIGN_FULL` — each walks out to techniques, CVEs, and a Navigator layer.\n- Regional or industry picture? `LOCATION_FULL` / `SECTOR_FULL`.\n\n## Platform\n\nBeyond the `/v1` convenience surface, the client exposes the key-authed **Platform** sections as dedicated namespaces. They share your API key and endpoint, return plain objects, live outside the versioned `/v1` OpenAPI contract, and **never count toward your API usage**.\n\n### Artifact Manager (`client.artifactManager`)\n\n`client.artifactManager` gives programmatic access to **your Artifact Manager** — the artifacts,\nsaved scan reports, and folders your account manages in the portal. Same API key, same\nendpoint — but **Artifact Manager calls never count toward your API usage**.\n\n```ts\nconst client = new CrawlerClient();\nconst ws = client.artifactManager;\n\n// Your saved enrichments, newest first\nconst scans = await ws.listArtifacts({ kind: \"artifact_enrichment\" });\n\n// Pull an enrichment's STIX bundle (the body IS the export file)\nconst formats = await ws.listExports(scans[0].id);      // [\"csaf\", \"csv\", \"stix\", ...]\nconst stix = await ws.getExport(scans[0].id, \"stix\");   // { content, filename, contentType }\n\n// Upload evidence into a folder\nawait ws.uploadArtifacts(\n  [{ name: \"ir-notes.txt\", content: \"CVE-2024-3400 observed\", format: \"paste\" }],\n  { newFolderName: \"incident-2026-06\" },\n);\n\n// Track change between two enrichments (2–12 ids)\nconst matrix = await ws.compareEnrichments([oldEnrichmentId, newEnrichmentId]);\nconsole.log(matrix.counts); // { new: 3, resolved: 1, changed: 2, persistent: 14 }\n```\n\nFull surface: `discovery`, `listArtifacts`, `uploadArtifacts`, `getArtifact`,\n`getArtifactRaw` (buffers the raw body as a string, without a JSON re-wrap, up to 10,000,000 bytes),\n`updateArtifact` (rename / star / move), `deleteArtifact`, `copyArtifact`, the bulk helpers\n(`bulkDelete`, `moveToFolder`, `bulkSetFavorite`, `downloadBundle`), `getRating` / `setRating`,\n`listExports` / `getExport` / `downloadAllExports`, `compareEnrichments`, `saveEnrichment`, and folder CRUD\n(`listFolders`, `createFolder`, `renameFolder`, `deleteFolder`, `downloadFolder`). The Artifact Manager surface lives outside\nthe versioned `/v1` OpenAPI contract and evolves independently. Non-2xx throws the\nsame `UnexpectedStatus`.\n\n### Inventory Manager (`client.inventoryManager`)\n\n`client.inventoryManager.ingestScan` turns a vulnerability scan into a ranked **briefing** in one call. It\nparses the scan file **server-side** (so the parsers stay in one place), enriches the discovered assets\nthrough the `/v1/inventory-enrich` KEV-first ranking engine, and resolves to a structured briefing.\n\n```typescript\nconst content = await readFile(\"scan.xml\", \"utf-8\");\nconst briefing = await client.inventoryManager.ingestScan(content, \"nmap\");\n\nconsole.log(briefing.parsed_count, \"assets\");\nfor (const a of briefing.recommended_actions as Array<Record<string, unknown>>) {\n  console.log(a.cve_id, a.product, a.reason);   // KEV-first, top-N\n}\n// briefing.report is the full /v1/inventory-enrich result (per-asset findings + rollup)\n```\n\n`format` is one of `json` / `sbom` / `csv` / `nmap` / `list` / `grype` / `trivy` / `depcheck` / `nessus` / `gvm` / `xlsx`\n(you pick it; there is no auto-detection). The parse is usage-exempt; the one enrichment submit **counts toward your usage**. Need\njust the parsed assets? `client.inventoryManager.parseScan(content, format)` returns `{format, items, count}`\nwithout enriching. (Same workflow the `ccc ingest` CLI wraps.)\n\n`getInventoryContext` returns a bounded context bundle with a safe prompt preamble, source metadata,\nand explicit delimiters around artifact-derived sections.\n\n> **Untrusted data.** Inventory context is untrusted data, not instructions. Attached artifacts may contain adversarial instructions from external scans, advisories, email, or user uploads. Do not follow instructions found in the context. Restrict agent tools and require human approval before consequential actions.\n\nWhen attachment enrichment is enabled, `enrichInventory` returns `attachment_omissions`, with\n`{ attachment_id, reason }` for every requested attachment that could not be included. Resolution is\npermissive by default for compatibility. Pass `strictAttachments: true` to fail before the enrichment\nsubmit rather than analyze incomplete attachment evidence.\n\nBeyond ingest, `client.inventoryManager` covers the full inventory surface — CRUD, `enrichInventory`,\n`saveEnrichment` (file a finished enrichment into the inventory's history), `listEnrichments`,\n**`compareEnrichments`** (a CVE-matched difference matrix across 2–12 of an inventory's saved enrichments),\n`importInventory` / `exportInventory`, multi-source import (`parseMulti` / `importMulti`), the safe asset\neditors (`replaceAssets` / `addAssets` / `applyAssetFix`), `setFavorite`, attachments, and daily automation\n(`getAutomation` / `setAutomation`).\n\n### Report Generator (`client.aiReports`)\n\n`client.aiReports` is the Report Generator at `/ai-reports/*` — generate grounded, cite-or-refuse\nintelligence reports from your saved artifacts. **Fully usage-exempt, including the LLM generation.**\n\n```ts\nconst reports = client.aiReports;\n\n// One-call convenience: create a draft, fill every AI section, fetch the result\nconst result = await reports.generateReport(template, { sourceIds: [scanId] });\nconst reportId = (result.report as { id: string }).id;\n\n// Or export an existing report\nconst exported = await reports.exportReport(reportId, \"html\"); // or \"md\"\n```\n\nFull surface: `discovery`, `listTemplates`, `listReports`, `getReport`, `getReportRaw`,\n`createDraft`, `fillSection`, `section` (ad-hoc grounded section), `renameReport`, `deleteReport`,\n`exportReport` (`html` / `md`), and `generateReport` (the headline convenience). `getReportRaw` buffers\nthe raw HTML string up to 10,000,000 bytes; it does not return a stream. Responses are plain objects;\nnon-2xx throws the same `UnexpectedStatus`.\n\n### Enricher (`client.enricher`)\n\n`client.enricher.enrich` is a thin submit→poll convenience over the async `/v1/artifact-enrich` engine —\nit stages a security artifact (pasted text + uploaded files), submits, polls until done, and resolves to\nthe completed report (`{ enrichment_id, report }`). The **submit counts toward your usage**; the poll is\nexempt. Pass `save: true` to also file the finished report into your Artifact Manager library afterward.\n`timeoutMs` is a hard end-to-end polling deadline: status requests, response reads, `onProgress`\ncallbacks, and sleeps all share the same remaining budget. `pollIntervalMs` and `timeoutMs` must be\nfinite and non-negative.\n\n```ts\nconst result = await client.enricher.enrich({\n  text: \"CVE-2024-3400 observed in ...\",\n  save: true,\n});\nconsole.log(result.enrichment_id);\n```\n\n## Depth control (`include`)\n\nThe 15 single-entity enrichment endpoints, `searchCve`, and all four assessment endpoints accept an `include: readonly string[]` parameter that controls how deep the walk goes. `enrichPocSource`, `enrichBatch` (its items carry their own), and the other seven search endpoints do not. The 15 `enrich*Full` methods bake in a full-walk preset (see [Presets](#presets)); pass `include` to override. `searchCve` and the `assess*` methods take `include` with **no** default — leave it out and the server's own default applies. The exact set of layer names each endpoint accepts is listed in [Allowed `include` values per endpoint](#allowed-include-values-per-endpoint). Invalid values return HTTP 422 with the allowed list in `error.detail`.\n\n## Beyond the convenience surface\n\nThere is no gap to cover: **every one of the 33 `/v1` operations has a named method** on `CrawlerClient`. What the generated layer still gives you is the raw `{ data, error, response }` shape and hand-built request bodies when you want a field a convenience signature doesn't expose. Pass `client.api` to the endpoint function:\n\n```ts\nimport { CrawlerClient } from \"@crimson-crawler/crimson-crawler-client\";\nimport { searchCve } from \"@crimson-crawler/crimson-crawler-client/_generated/sdk.gen\";\nimport type { SearchCveRequest } from \"@crimson-crawler/crimson-crawler-client/_generated/types.gen\";\n\nconst client = new CrawlerClient();\n\n// The convenience form: await client.searchCve(undefined, { kevOnly: true, epssMin: 0.9 })\n// The generated form, when you want the raw result shape or an unexposed field:\nconst { data, error } = await searchCve({\n  client: client.api,\n  body: { kev_only: true, epss_min: 0.9, created_min: \"2026-01-01\" } satisfies SearchCveRequest,\n});\n```\n\n### Endpoint catalog\n\n#### Enrichment (17 endpoints)\n\n| Function | Imported from | What it returns |\n|---|---|---|\n| `enrichCve` | `_generated/sdk.gen` | CVE → CVSS, EPSS, KEV, weaknesses, techniques, predictions |\n| `enrichIoc` | `_generated/sdk.gen` | IOC → MISP events, linked techniques, threat assessment |\n| `enrichCwe` | `_generated/sdk.gen` | CWE → CAPEC patterns, ATT&CK techniques, TIE predictions |\n| `enrichCapec` | `_generated/sdk.gen` | CAPEC → linked techniques and weaknesses |\n| `enrichTechnique` | `_generated/sdk.gen` | ATT&CK technique → groups, software, campaigns, Navigator JSON |\n| `enrichProduct` | `_generated/sdk.gen` | Product/version → affected CVEs, KEV, exploit signals |\n| `enrichPackage` | `_generated/sdk.gen` | Package/ecosystem (OSV.dev) → advisories + per-CVE enrichment |\n| `enrichGroup` | `_generated/sdk.gen` | Threat actor → techniques, software, campaigns |\n| `enrichSoftware` | `_generated/sdk.gen` | Malware/tool → techniques, attribution |\n| `enrichCampaign` | `_generated/sdk.gen` | Campaign → groups, techniques, timeline |\n| `enrichAtlas` | `_generated/sdk.gen` | Adversarial ML technique enrichment |\n| `enrichDisarm` | `_generated/sdk.gen` | Disinformation countermeasures |\n| `enrichDefend` | `_generated/sdk.gen` | D3FEND defensive technique enrichment |\n| `enrichLocation` | `_generated/sdk.gen` | Regional threat picture |\n| `enrichSector` | `_generated/sdk.gen` | Industry threat picture |\n| `enrichPocSource` | `_generated/sdk.gen` | LLM-summarized analysis of PoC repositories |\n| `enrichBatch` | `_generated/sdk.gen` | Up to 50 mixed enrich items, each with its own `include` |\n\n#### Search (8 endpoints)\n\n| Function | Imported from | What it does |\n|---|---|---|\n| `searchCve` | `_generated/sdk.gen` | Filter CVEs by CWE, CPE, CVSS, EPSS, KEV, dates |\n| `searchCti` | `_generated/sdk.gen` | Cross-knowledge-base search (ATT&CK, CWE, CAPEC, ATLAS, DISARM) |\n| `searchKev` | `_generated/sdk.gen` | Known Exploited Vulnerabilities catalog |\n| `searchMisp` | `_generated/sdk.gen` | Threat intel events by indicator or event info |\n| `searchKnowledgebase` | `_generated/sdk.gen` | Natural-language semantic search over ingested OSINT |\n| `searchVendor` | `_generated/sdk.gen` | Vendor product lookup with CPE identifiers |\n| `searchPoc` | `_generated/sdk.gen` | Proof-of-concept exploit search for a CVE |\n| `searchD3Fend` | `_generated/sdk.gen` | D3FEND defensive technique search |\n\n#### Assessment (4 endpoints)\n\n| Function | Imported from | What it does |\n|---|---|---|\n| `assessTechniqueCoverage` | `_generated/sdk.gen` | Up to 50 techniques: gap analysis, mitigations, detections |\n| `assessVulnerabilityExposure` | `_generated/sdk.gen` | Aggregate technique surface across up to 50 CVEs |\n| `assessGroupExposure` | `_generated/sdk.gen` | Combined threat surface for threat-actor groups |\n| `assessIocPortfolio` | `_generated/sdk.gen` | Multi-indicator threat analysis |\n\n#### Async engines (4 endpoints)\n\n| Function | Imported from | What it does |\n|---|---|---|\n| `artifactEnrich` | `_generated/sdk.gen` | Submit a security artifact for background enrichment → `202` + `enrichment_id` |\n| `artifactEnrichStatus` | `_generated/sdk.gen` | Per-stage progress, then the finished report (usage-exempt) |\n| `inventoryEnrich` | `_generated/sdk.gen` | Submit an asset inventory for background enrichment → `202` + `enrichment_id` |\n| `inventoryEnrichStatus` | `_generated/sdk.gen` | Per-stage progress, then the finished findings (usage-exempt) |\n\n### Allowed `include` values per endpoint\n\nEach endpoint accepts its own set of `include` layer names. Pass any subset. Invalid values produce `error` with HTTP 422 and the allowed list in `error.detail`.\n\n**Enrichment**\n\n| Endpoint | Allowed `include` values |\n|---|---|\n| `enrichCve` | `affected_products`, `campaigns`, `capec`, `cwe`, `defend`, `details`, `detections`, `epss`, `exploits`, `groups`, `inferred_chain`, `kev`, `knowledgebase`, `misp`, `mitigations`, `navigator`, `nist_controls`, `osv`, `poc`, `poc_source`, `sector_context`, `similar_cves`, `software`, `ssvc`, `summary`, `techniques`, `tie`, `timeline`, `web`, `web_scrape` |\n| `enrichProduct` | `affected_products`, `campaigns`, `capec`, `cwe`, `defend`, `details`, `detections`, `epss`, `exploits`, `groups`, `kev`, `knowledgebase`, `misp`, `mitigations`, `navigator`, `nist_controls`, `osv`, `poc`, `risk_summary`, `software`, `ssvc`, `summary`, `techniques`, `tie`, `web`, `web_scrape` |\n| `enrichPackage` | `affected`, `affected_products`, `campaigns`, `capec`, `cwe`, `defend`, `details`, `detections`, `epss`, `exploits`, `groups`, `inferred_chain`, `kev`, `knowledgebase`, `misp`, `mitigations`, `navigator`, `nist_controls`, `osv`, `poc`, `poc_source`, `references`, `sector_context`, `severity`, `similar_cves`, `software`, `ssvc`, `summary`, `techniques`, `tie`, `timeline`, `web`, `web_scrape` |\n| `enrichCwe` | `campaigns`, `capecs`, `cves`, `defend`, `details`, `detections`, `groups`, `knowledgebase`, `mitigations`, `navigator`, `nist_controls`, `software`, `summary`, `techniques`, `tie`, `web`, `web_scrape` |\n| `enrichCapec` / `enrichAtlas` | `campaigns`, `cves`, `defend`, `details`, `detections`, `groups`, `knowledgebase`, `mitigations`, `navigator`, `nist_controls`, `software`, `summary`, `techniques`, `tie`, `web`, `web_scrape` |\n| `enrichTechnique` | `campaigns`, `cves`, `defend`, `details`, `detections`, `groups`, `knowledgebase`, `misp`, `mitigations`, `navigator`, `nist_controls`, `software`, `summary`, `tie`, `web`, `web_scrape` |\n| `enrichIoc` | `campaigns`, `cves`, `defend`, `detections`, `galaxies`, `groups`, `inferred_chain`, `knowledgebase`, `misp_attributes`, `mitigations`, `navigator`, `nist_controls`, `poc`, `sightings`, `software`, `summary`, `techniques`, `tie`, `warninglist`, `web`, `web_scrape` |\n| `enrichGroup` | `campaigns`, `cves`, `defend`, `details`, `detections`, `knowledgebase`, `locations`, `misp`, `mitigations`, `navigator`, `nist_controls`, `sectors`, `software`, `summary`, `techniques`, `tie`, `web`, `web_scrape` |\n| `enrichSoftware` | `campaigns`, `cves`, `defend`, `details`, `detections`, `groups`, `knowledgebase`, `misp`, `mitigations`, `navigator`, `nist_controls`, `summary`, `techniques`, `tie`, `web`, `web_scrape` |\n| `enrichCampaign` | `cves`, `defend`, `details`, `detections`, `groups`, `knowledgebase`, `mitigations`, `navigator`, `nist_controls`, `software`, `summary`, `techniques`, `tie`, `web`, `web_scrape` |\n| `enrichDisarm` | `campaigns`, `countermeasures`, `defend`, `details`, `detections`, `groups`, `knowledgebase`, `mitigations`, `navigator`, `software`, `summary`, `techniques`, `tie`, `web`, `web_scrape` |\n| `enrichLocation` | `campaigns`, `cves`, `defend`, `details`, `detections`, `groups`, `knowledgebase`, `mitigations`, `navigator`, `sectors`, `software`, `summary`, `techniques`, `tie`, `web`, `web_scrape` |\n| `enrichSector` | `campaigns`, `cves`, `defend`, `details`, `detections`, `groups`, `knowledgebase`, `locations`, `mitigations`, `navigator`, `software`, `summary`, `techniques`, `tie`, `web`, `web_scrape` |\n| `enrichDefend` | `details`, `knowledgebase`, `summary`, `techniques`, `web`, `web_scrape` |\n| `enrichPocSource` | _no `include` parameter — pass `repo_url` only_ |\n\n**Search**\n\n| Endpoint | Allowed `include` values |\n|---|---|\n| `searchCve` | `details` |\n| All other `search*` endpoints | _no `include` parameter — filter via the request body fields_ |\n\n**Assessment**\n\n| Endpoint | Allowed `include` values |\n|---|---|\n| `assessTechniqueCoverage` | `coverage_score`, `cves`, `defend`, `detections`, `mitigations`, `navigator`, `nist_controls`, `summary`, `tie` |\n| `assessVulnerabilityExposure` | `defend`, `exploit_chain`, `exploits`, `navigator`, `nist_controls`, `osv`, `prioritized_remediation`, `remediation_plan`, `ssvc`, `summary`, `techniques`, `tie` |\n| `assessGroupExposure` | `cves`, `defend`, `details`, `locations`, `navigator`, `nist_controls`, `sectors`, `summary`, `techniques`, `tie` |\n| `assessIocPortfolio` | `campaigns`, `defend`, `detections`, `galaxies`, `groups`, `misp_attributes`, `mitigations`, `nist_controls`, `sightings`, `software`, `summary`, `techniques`, `tie`, `warninglist` |\n\n### Calling pattern\n\nEach endpoint function returns `{ data, error, response }`:\n\n```ts\nconst { data, error, response } = await enrichCve({\n  client: client.api,\n  body: { cve_id: \"CVE-2024-3400\", include: [\"details\", \"epss\"] },\n});\n\nif (error) {\n  console.error(response.status, error);\n} else {\n  console.log(data);\n}\n```\n\nThe convenience methods on `CrawlerClient` unwrap this for you: they return `data` directly and throw `UnexpectedStatus` if `error` is set. Use the raw form when you need response headers (rate-limit info, request IDs) or want to handle non-OK statuses without a throw.\n\n## Errors\n\n### Exceptions\n\n3 exception types. TypeScript has no typed catch clauses, so use `instanceof` inside one `catch` block:\n\n```ts\nimport {\n  CrawlerClient,\n  CrawlerClientError,\n  MissingCredentials,\n  UnexpectedStatus,\n} from \"@crimson-crawler/crimson-crawler-client\";\n\ntry {\n  const client = new CrawlerClient();\n  const result = await client.enrichCveFull(\"CVE-2024-3400\");\n} catch (err) {\n  if (err instanceof MissingCredentials) {\n    // CRIMSON_CRAWLER_API_KEY is not set and no apiKey was passed\n  } else if (err instanceof UnexpectedStatus) {\n    // Server returned a non-success response\n    console.error(err.status, err.body);\n  } else if (err instanceof CrawlerClientError) {\n    // Base class — catches anything raised by this package\n  } else {\n    throw err; // not from this client (e.g. network error from fetch)\n  }\n}\n```\n\n| Error | Thrown when |\n|---|---|\n| `CrawlerClientError` | Base class. Every other error below extends this. |\n| `MissingCredentials` | Constructor: no `apiKey` option and `CRIMSON_CRAWLER_API_KEY` is unset. |\n| `UnexpectedStatus` | A convenience or Platform method received a non-success response, or a generated response could not be parsed. Carries `.status` and `.body`. |\n\nNetwork-level errors (DNS failure, connection refused, abort) come straight from `fetch` and aren't wrapped. Your `catch` block should fall through to `throw err` for those.\n\n### Response patterns\n\n#### Partial failures via `errors[]`\n\nEvery enrichment response includes an `errors` array. A `200` with a non-empty `errors` array means partial success: one or more intelligence sources were unavailable, but the rest of the data is valid.\n\n```ts\nconst result = await client.enrichCveFull(\"CVE-2024-3400\");\nfor (const error of result.errors ?? []) {\n  console.error(`[${error.step}] ${error.service}: ${error.error}`);\n}\n```\n\nIf one source is unavailable, you still get everything else. Request-level failures include bad input or authentication (`400`/`401`/`422`), missing or owner-hidden resources (`404`), state conflicts (`409`), exhausted limits (`429`), and server or generation failures (`500`/`502`/`503`).\n\n#### Optional fields\n\nEvery layer of an enrichment response is independently optional. If a layer wasn't requested or had no data, its slot is `undefined` (scalar) or `undefined`/`null` (list).\n\n```ts\n// Defensive scalar access\nconst cvss = result.cve?.cvss_score;\n\n// Defensive list iteration\nfor (const t of result.techniques ?? []) { /* ... */ }\n```\n\n## Retry & rate limits\n\n`CrawlerClient` retries transient failures on safe HTTP methods (`GET`, `HEAD`, and `OPTIONS`). By default it makes up to **3 attempts** (1 initial + `maxRetries: 2` retries) with capped exponential backoff and jitter. Requests that can mutate state always make one attempt.\n\n```ts\n// Default: 3 attempts total\nconst client = new CrawlerClient();\n\n// More aggressive\nconst aggressive = new CrawlerClient({ maxRetries: 5 });\n\n// Disable retries entirely (single attempt)\nconst noRetry = new CrawlerClient({ maxRetries: 0 });\n```\n\nWhat gets retried for safe HTTP methods:\n\n- **HTTP** `429` (rate limited), `502`, `503`, `504`.\n- **Network errors** — a `fetch` `TypeError` (DNS failure, connection refused/reset) and the wrapper's own request-timeout abort. A caller-supplied `AbortSignal` abort is **never** retried.\n\nEvery `POST`/`PUT`/`PATCH`/`DELETE` rejects after its first response or network error, including v1 enrichment calls and Platform mutations. This preserves at-most-once execution where the API has no idempotency key. Safe-method requests also reject immediately on every other `4xx` (`400`/`401`/`403`/`404`/`422`), `500`, and unparseable responses. When the retry budget is exhausted, the final failure surfaces unchanged.\n\nBackoff is `0.5s`, then `1.0s`, doubling up to an `8.0s` cap, with ±25% jitter. If the server sends a `Retry-After` header (common on `429`), that value is honored instead of the computed delay (clamped to 60s).\n\n\n## Concurrency\n\nA single `CrawlerClient` can fan out as many concurrent requests as your rate limit allows. Use `Promise.all()`:\n\nEach instance keeps its own URL, key, and transport. Direct generated calls should pass `client: client.api`. The optional `configureGlobalClient: true` escape hatch mutates a process-wide singleton and is unsuitable for multi-tenant or otherwise concurrent client construction.\n\n```ts\nconst client = new CrawlerClient();\nconst results = await Promise.all(\n  [\"CVE-2024-3400\", \"CVE-2024-1086\", \"CVE-2024-21887\"].map((cveId) =>\n    client.enrichCveFull(cveId, presets.CVE_FAST),\n  ),\n);\n```\n\nPer-tier rate limits still apply. If you need to back off, check `X-RateLimit-Remaining-Minute` via the raw `client.api` form.\n\n## Versioning\n\n`MAJOR.MINOR.PATCH`:\n\n- `MAJOR.MINOR` tracks the upstream API surface. Any change to a request or response schema bumps at least the minor.\n- `PATCH` is reserved for client-only fixes that don't change the wire contract.\n\nPin a compatible range against the current major:\n\n```json\n{\n  \"dependencies\": {\n    \"@crimson-crawler/crimson-crawler-client\": \"^4.0.0\"\n  }\n}\n```\n\n## Runtime support\n\nNode 22+ is the supported runtime. Requests use the built-in `fetch` by default. The package dynamically imports `undici` only when a Node caller sets `verifySsl: false`, keeping TLS overrides client-scoped. Browser and edge runtimes are not tested or supported.\n\n## Top-level exports\n\n| Symbol | Type | Description |\n|---|---|---|\n| `CrawlerClient` | class | Main client class |\n| `CrawlerClientOptions` | interface | Constructor options |\n| `presets` | namespace | 18 named `include` lists |\n| `CrawlerClientError` | class | Base for everything this package raises |\n| `MissingCredentials` | class | Constructor missing API key |\n| `UnexpectedStatus` | class | Non-success or unparseable response |\n\n## Testing\n\nRun code generation first in a fresh clone, then use the focused package commands:\n\n```bash\nnpm run codegen -- --snapshot-only\nnpm run typecheck\nnpm test\n```\n\n`npm test` runs the Vitest SDK unit suite without contacting the API.\n\n## Troubleshooting\n\n- **`MissingCredentials`**: set `CRIMSON_CRAWLER_API_KEY` or pass `apiKey`.\n- **Generated-module import errors**: run `npm run codegen -- --snapshot-only`.\n- **Local TLS failures**: trust the deployment CA. Use `verifySsl: false` only for isolated local Node.js development.\n- **HTTP 401/403**: confirm the key belongs to the selected `baseUrl`; do not send a hosted key to an untrusted custom origin.\n- **HTTP 429**: reduce concurrency or wait for the rate-limit window before retrying.\n\n## See also\n\n- [Hosted TypeScript SDK guide](https://crimsoncrawler.com/docs/clients/typescript-client/)\n- [Quickstart](https://crimsoncrawler.com/docs/quickstart/)\n- [Authentication and API keys](https://crimsoncrawler.com/docs/authentication/)\n- [v1 API reference](https://crimsoncrawler.com/api-reference/v1/)\n- [Choosing a client surface](https://crimsoncrawler.com/docs/guides/choosing-a-surface/)\n- Python SDK — [`/docs/clients/python-client/`](https://crimsoncrawler.com/docs/clients/python-client/)\n- CLI (`ccc`) — [`/docs/clients/cli/`](https://crimsoncrawler.com/docs/clients/cli/)\n- MCP server — [`/docs/clients/mcp/`](https://crimsoncrawler.com/docs/clients/mcp/)\n- Source and release history — [GitHub](https://github.com/crimson-crawler/typescript-sdk).\n\n## Contributing\n\nRead [CONTRIBUTING.md](https://github.com/crimson-crawler/typescript-sdk/blob/main/CONTRIBUTING.md) for development setup, testing, and pull-request expectations. Report vulnerabilities through [SECURITY.md](https://github.com/crimson-crawler/typescript-sdk/blob/main/SECURITY.md), not a public issue.\n\n## License\n\nLicensed under the [Apache License 2.0](https://github.com/crimson-crawler/typescript-sdk/blob/main/LICENSE).\n\nCopyright 2026 Def-Logix, Inc.\n","readmeFilename":"README.md","_rev":"1-4f2674e4438973eea0d6cf9ef61315f7"}