imperium-crawl 2.5.3 → 2.6.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.
Files changed (235) hide show
  1. package/README.md +15 -14
  2. package/dist/batch/job-store.js +1 -1
  3. package/dist/batch/job-store.js.map +1 -1
  4. package/dist/brave-api/index.js +1 -1
  5. package/dist/brave-api/index.js.map +1 -1
  6. package/dist/cli/config.d.ts +21 -0
  7. package/dist/cli/config.d.ts.map +1 -0
  8. package/dist/cli/config.js +51 -0
  9. package/dist/cli/config.js.map +1 -0
  10. package/dist/cli/engine.d.ts +17 -0
  11. package/dist/cli/engine.d.ts.map +1 -0
  12. package/dist/cli/engine.js +440 -0
  13. package/dist/cli/engine.js.map +1 -0
  14. package/dist/cli/explore.d.ts +30 -0
  15. package/dist/cli/explore.d.ts.map +1 -0
  16. package/dist/cli/explore.js +427 -0
  17. package/dist/cli/explore.js.map +1 -0
  18. package/dist/cli/onboarding.d.ts +10 -0
  19. package/dist/cli/onboarding.d.ts.map +1 -0
  20. package/dist/cli/onboarding.js +128 -0
  21. package/dist/cli/onboarding.js.map +1 -0
  22. package/dist/cli/recorder.d.ts +44 -0
  23. package/dist/cli/recorder.d.ts.map +1 -0
  24. package/dist/cli/recorder.js +67 -0
  25. package/dist/cli/recorder.js.map +1 -0
  26. package/dist/cli/tui.d.ts +12 -0
  27. package/dist/cli/tui.d.ts.map +1 -0
  28. package/dist/cli/tui.js +945 -0
  29. package/dist/cli/tui.js.map +1 -0
  30. package/dist/cli/ui.d.ts +26 -0
  31. package/dist/cli/ui.d.ts.map +1 -0
  32. package/dist/cli/ui.js +58 -0
  33. package/dist/cli/ui.js.map +1 -0
  34. package/dist/core/action-executor.d.ts +66 -0
  35. package/dist/core/action-executor.d.ts.map +1 -0
  36. package/dist/core/action-executor.js +403 -0
  37. package/dist/core/action-executor.js.map +1 -0
  38. package/dist/core/config.d.ts +16 -0
  39. package/dist/core/config.d.ts.map +1 -0
  40. package/dist/core/config.js +56 -0
  41. package/dist/core/config.js.map +1 -0
  42. package/dist/core/constants.d.ts +40 -0
  43. package/dist/core/constants.d.ts.map +1 -0
  44. package/dist/core/constants.js +86 -0
  45. package/dist/core/constants.js.map +1 -0
  46. package/dist/core/formatters.d.ts +36 -0
  47. package/dist/core/formatters.d.ts.map +1 -0
  48. package/dist/core/formatters.js +147 -0
  49. package/dist/core/formatters.js.map +1 -0
  50. package/dist/engines/camofox.d.ts +27 -0
  51. package/dist/engines/camofox.d.ts.map +1 -0
  52. package/dist/engines/camofox.js +432 -0
  53. package/dist/engines/camofox.js.map +1 -0
  54. package/dist/engines/index.d.ts +13 -0
  55. package/dist/engines/index.d.ts.map +1 -0
  56. package/dist/engines/index.js +41 -0
  57. package/dist/engines/index.js.map +1 -0
  58. package/dist/engines/types.d.ts +63 -0
  59. package/dist/engines/types.d.ts.map +1 -0
  60. package/dist/engines/types.js +8 -0
  61. package/dist/engines/types.js.map +1 -0
  62. package/dist/flows/engine.js +3 -3
  63. package/dist/flows/engine.js.map +1 -1
  64. package/dist/flows/storage.js +1 -1
  65. package/dist/flows/storage.js.map +1 -1
  66. package/dist/flows/templates.js +1 -1
  67. package/dist/flows/templates.js.map +1 -1
  68. package/dist/flows/types.d.ts +405 -405
  69. package/dist/index.js +4 -4
  70. package/dist/index.js.map +1 -1
  71. package/dist/knowledge/store.js +1 -1
  72. package/dist/knowledge/store.js.map +1 -1
  73. package/dist/network/index.d.ts +3 -0
  74. package/dist/network/index.d.ts.map +1 -0
  75. package/dist/network/index.js +2 -0
  76. package/dist/network/index.js.map +1 -0
  77. package/dist/recipes/data/crypto-websocket.json +11 -0
  78. package/dist/recipes/data/ecommerce-product.json +25 -0
  79. package/dist/recipes/data/github-trending.json +19 -0
  80. package/dist/recipes/data/hn-top-stories.json +22 -0
  81. package/dist/recipes/data/influencer-competitor-spy.json +14 -0
  82. package/dist/recipes/data/influencer-content-scout.json +14 -0
  83. package/dist/recipes/data/influencer-hashtag-scout.json +14 -0
  84. package/dist/recipes/data/influencer-niche-discovery.json +14 -0
  85. package/dist/recipes/data/job-listings-greenhouse.json +17 -0
  86. package/dist/recipes/data/news-article-reader.json +9 -0
  87. package/dist/recipes/data/product-reviews.json +33 -0
  88. package/dist/recipes/data/reddit-posts.json +8 -0
  89. package/dist/recipes/data/seo-page-audit.json +26 -0
  90. package/dist/recipes/data/social-media-mentions.json +31 -0
  91. package/dist/recipes/index.d.ts +1 -1
  92. package/dist/recipes/index.d.ts.map +1 -1
  93. package/dist/recipes/index.js +14 -14
  94. package/dist/recipes/index.js.map +1 -1
  95. package/dist/security/auth-vault.js +1 -1
  96. package/dist/security/auth-vault.js.map +1 -1
  97. package/dist/security/index.d.ts +6 -0
  98. package/dist/security/index.d.ts.map +1 -0
  99. package/dist/security/index.js +4 -0
  100. package/dist/security/index.js.map +1 -0
  101. package/dist/sessions/browser-state.js +1 -1
  102. package/dist/sessions/browser-state.js.map +1 -1
  103. package/dist/sessions/encryption.js +1 -1
  104. package/dist/sessions/encryption.js.map +1 -1
  105. package/dist/sessions/manager.js +1 -1
  106. package/dist/sessions/manager.js.map +1 -1
  107. package/dist/skills/detector.js +1 -1
  108. package/dist/skills/detector.js.map +1 -1
  109. package/dist/skills/index.d.ts +9 -0
  110. package/dist/skills/index.d.ts.map +1 -0
  111. package/dist/skills/index.js +6 -0
  112. package/dist/skills/index.js.map +1 -0
  113. package/dist/skills/manager.js +1 -1
  114. package/dist/skills/manager.js.map +1 -1
  115. package/dist/snapshot/store.js +1 -1
  116. package/dist/snapshot/store.js.map +1 -1
  117. package/dist/social/index.d.ts +7 -0
  118. package/dist/social/index.d.ts.map +1 -0
  119. package/dist/social/index.js +4 -0
  120. package/dist/social/index.js.map +1 -0
  121. package/dist/stealth/browser-pool.js +1 -1
  122. package/dist/stealth/browser-pool.js.map +1 -1
  123. package/dist/stealth/browser.js +2 -2
  124. package/dist/stealth/browser.js.map +1 -1
  125. package/dist/stealth/chrome-profile.js +2 -2
  126. package/dist/stealth/chrome-profile.js.map +1 -1
  127. package/dist/stealth/index.js +2 -2
  128. package/dist/stealth/index.js.map +1 -1
  129. package/dist/tools/ai-extract.js +1 -1
  130. package/dist/tools/ai-extract.js.map +1 -1
  131. package/dist/tools/batch-download.d.ts +1 -1
  132. package/dist/tools/batch-download.js +1 -1
  133. package/dist/tools/batch-download.js.map +1 -1
  134. package/dist/tools/batch-scrape.d.ts +2 -2
  135. package/dist/tools/batch-scrape.js +1 -1
  136. package/dist/tools/batch-scrape.js.map +1 -1
  137. package/dist/tools/browser.d.ts +8 -8
  138. package/dist/tools/browser.js +4 -4
  139. package/dist/tools/browser.js.map +1 -1
  140. package/dist/tools/camofox-status.d.ts +14 -0
  141. package/dist/tools/camofox-status.d.ts.map +1 -0
  142. package/dist/tools/camofox-status.js +61 -0
  143. package/dist/tools/camofox-status.js.map +1 -0
  144. package/dist/tools/camofox-update.d.ts +29 -0
  145. package/dist/tools/camofox-update.d.ts.map +1 -0
  146. package/dist/tools/camofox-update.js +108 -0
  147. package/dist/tools/camofox-update.js.map +1 -0
  148. package/dist/tools/crawl.d.ts +2 -2
  149. package/dist/tools/crawl.js +1 -1
  150. package/dist/tools/crawl.js.map +1 -1
  151. package/dist/tools/create-skill.js +3 -3
  152. package/dist/tools/create-skill.js.map +1 -1
  153. package/dist/tools/discover-apis.d.ts +1 -1
  154. package/dist/tools/discover-apis.js +1 -1
  155. package/dist/tools/discover-apis.js.map +1 -1
  156. package/dist/tools/download.d.ts +7 -7
  157. package/dist/tools/download.js +1 -1
  158. package/dist/tools/download.js.map +1 -1
  159. package/dist/tools/extract.d.ts +1 -1
  160. package/dist/tools/extract.js +1 -1
  161. package/dist/tools/extract.js.map +1 -1
  162. package/dist/tools/image-search.d.ts +1 -1
  163. package/dist/tools/image-search.js +2 -2
  164. package/dist/tools/image-search.js.map +1 -1
  165. package/dist/tools/index.d.ts.map +1 -1
  166. package/dist/tools/index.js +5 -0
  167. package/dist/tools/index.js.map +1 -1
  168. package/dist/tools/instagram.d.ts +1 -1
  169. package/dist/tools/instagram.js +3 -3
  170. package/dist/tools/instagram.js.map +1 -1
  171. package/dist/tools/interact.d.ts +86 -86
  172. package/dist/tools/interact.js +5 -5
  173. package/dist/tools/interact.js.map +1 -1
  174. package/dist/tools/knowledge.js +1 -1
  175. package/dist/tools/knowledge.js.map +1 -1
  176. package/dist/tools/list-skills.js +1 -1
  177. package/dist/tools/list-skills.js.map +1 -1
  178. package/dist/tools/manifest.d.ts.map +1 -1
  179. package/dist/tools/manifest.js +9 -0
  180. package/dist/tools/manifest.js.map +1 -1
  181. package/dist/tools/map.js +1 -1
  182. package/dist/tools/map.js.map +1 -1
  183. package/dist/tools/monitor-websocket.d.ts +1 -1
  184. package/dist/tools/monitor-websocket.js +1 -1
  185. package/dist/tools/monitor-websocket.js.map +1 -1
  186. package/dist/tools/monitor.d.ts +2 -2
  187. package/dist/tools/news-search.d.ts +1 -1
  188. package/dist/tools/news-search.js +2 -2
  189. package/dist/tools/news-search.js.map +1 -1
  190. package/dist/tools/query-api.d.ts +5 -5
  191. package/dist/tools/query-api.js +1 -1
  192. package/dist/tools/query-api.js.map +1 -1
  193. package/dist/tools/readability.js +1 -1
  194. package/dist/tools/readability.js.map +1 -1
  195. package/dist/tools/record-flow.d.ts +2 -2
  196. package/dist/tools/record-flow.js +3 -3
  197. package/dist/tools/record-flow.js.map +1 -1
  198. package/dist/tools/reddit.d.ts +2 -2
  199. package/dist/tools/reddit.js +3 -3
  200. package/dist/tools/reddit.js.map +1 -1
  201. package/dist/tools/rss.js +1 -1
  202. package/dist/tools/rss.js.map +1 -1
  203. package/dist/tools/run-flow.d.ts +6 -6
  204. package/dist/tools/run-skill.d.ts +6 -6
  205. package/dist/tools/run-skill.js +2 -2
  206. package/dist/tools/run-skill.js.map +1 -1
  207. package/dist/tools/scrape.d.ts +4 -4
  208. package/dist/tools/scrape.js +1 -1
  209. package/dist/tools/scrape.js.map +1 -1
  210. package/dist/tools/screenshot.js +1 -1
  211. package/dist/tools/screenshot.js.map +1 -1
  212. package/dist/tools/search.d.ts +1 -1
  213. package/dist/tools/search.js +2 -2
  214. package/dist/tools/search.js.map +1 -1
  215. package/dist/tools/snapshot.d.ts +5 -5
  216. package/dist/tools/snapshot.js +2 -2
  217. package/dist/tools/snapshot.js.map +1 -1
  218. package/dist/tools/video-search.d.ts +1 -1
  219. package/dist/tools/video-search.js +2 -2
  220. package/dist/tools/video-search.js.map +1 -1
  221. package/dist/tools/watch.d.ts +2 -2
  222. package/dist/tools/watch.js +1 -1
  223. package/dist/tools/watch.js.map +1 -1
  224. package/dist/tools/youtube.d.ts +1 -1
  225. package/dist/tools/youtube.js +4 -4
  226. package/dist/tools/youtube.js.map +1 -1
  227. package/dist/types.d.ts +14 -0
  228. package/dist/types.d.ts.map +1 -0
  229. package/dist/types.js +5 -0
  230. package/dist/types.js.map +1 -0
  231. package/dist/utils/fetcher.js +1 -1
  232. package/dist/utils/fetcher.js.map +1 -1
  233. package/dist/utils/robots.js +1 -1
  234. package/dist/utils/robots.js.map +1 -1
  235. package/package.json +7 -3
package/README.md CHANGED
@@ -1,12 +1,12 @@
1
1
  <div align="center">
2
2
 
3
- <img src="assets/hero-banner.png" alt="imperium-crawl — 3-level auto-escalating stealth engine" width="800" />
3
+ <img src="assets/hero-banner.png" alt="imperium-crawl — 4-level auto-escalating stealth engine with CamoFox" width="800" />
4
4
 
5
5
  # imperium-crawl
6
6
 
7
7
  **The most powerful open-source CLI tool for web scraping, crawling, and data extraction.**
8
8
 
9
- 39 tools. Zero API keys required. One `npx` command.
9
+ 41 tools. CamoFox C++ anti-detection. Zero API keys required. One `npx` command.
10
10
 
11
11
  [![npm version](https://img.shields.io/npm/v/imperium-crawl.svg)](https://www.npmjs.com/package/imperium-crawl)
12
12
  [![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](./LICENSE)
@@ -17,16 +17,17 @@
17
17
 
18
18
  ---
19
19
 
20
- ## What's new in 2.5.1
20
+ ## What's new in 2.6.0
21
21
 
22
- **Browser-based image extraction overhaul** — 100% coverage on any website:
22
+ **CamoFox browser engine** — Firefox fork with C++ anti-fingerprinting that bypasses Cloudflare, Google, and most bot detection:
23
23
 
24
- - **Full browser rendering (L3)** for image discovery — JavaScript, lazy-load, shadow DOM, same-origin iframes
25
- - **7 image sources**: `<img>`, `<picture>`, CSS `background-image`, shadow DOM, JSON-LD, inline scripts, iframes
26
- - **Precise targeting**: `--selector`, `--index`, `--alt-match`, `--min-width`, `--max-width`
27
- - **Auto-click** "Load more" / "Gallery" buttons with multilingual keyword matching
28
- - **Referer injection** fixes 403 errors on image CDN anti-hotlink protection
29
- - **New `auto_click` action** in `interact` tool for standalone browser automation
24
+ - **C++-level patches** `navigator.hardwareConcurrency`, WebGL, AudioContext, WebRTC spoofed before JavaScript sees them
25
+ - **Engine abstraction** Switch between Playwright and CamoFox with a single `engine` flag
26
+ - **Auto-update** `imperiumcrawl camofox-update` pulls the latest CamoFox release from npm
27
+ - **Engine factory** `import { resolveEngine } from "imperium-crawl/engines"` for agent-native use
28
+ - **Zero breaking changes** Same tool API, same response format, same env vars. Just add `engine: "camofox"`.
29
+ - **Codebase reorganized** — CLI in `src/cli/`, core in `src/core/`, tests in 14 category folders.
30
+ - **41 tools total** — Added `camofox_status` and `camofox_update`.
30
31
 
31
32
  ```bash
32
33
  # Download ALL images from any page (100% coverage)
@@ -65,7 +66,7 @@ npm install -g imperium-crawl
65
66
  npm install -g ./imperium-crawl-2.5.2.tgz
66
67
  ```
67
68
 
68
- > That's it. 33 of 39 tools work with zero API keys. Add optional keys later to unlock search, AI extraction, and CAPTCHA solving.
69
+ > That's it. 33 of 41 tools work with zero API keys. Add optional keys later to unlock search, AI extraction, and CAPTCHA solving.
69
70
 
70
71
  ---
71
72
 
@@ -169,7 +170,7 @@ Scraping 4 URLs (concurrency: 3)...
169
170
  ## Why imperium-crawl?
170
171
 
171
172
  🔓 **Zero API Keys Required**
172
- 33 of 39 tools work out of the box. No accounts, no tokens, no credit cards. Just `npx` and go.
173
+ 33 of 41 tools work out of the box. No accounts, no tokens, no credit cards. Just `npx` and go.
173
174
 
174
175
  🛡️ **3-Level Auto-Escalating Stealth**
175
176
  Headers → TLS fingerprinting → headless browser + CAPTCHA solving. Automatically escalates until it gets through.
@@ -612,7 +613,7 @@ Turn any website into an API. No documentation needed.
612
613
 
613
614
  ## AI Agent Guide
614
615
 
615
- imperium-crawl ships with [`SKILL/`](./SKILL/) — a structured guide that teaches AI agents how to use all 39 tools effectively. Includes proven workflows, decision trees, error recovery, and advanced patterns.
616
+ imperium-crawl ships with [`SKILL/`](./SKILL/) — a structured guide that teaches AI agents how to use all 41 tools effectively. Includes proven workflows, decision trees, error recovery, and advanced patterns.
616
617
 
617
618
  ### Two Ways to Connect
618
619
 
@@ -680,7 +681,7 @@ Every tool tested against production websites with real anti-bot defenses:
680
681
  | 📥 **download** | YouTube video, web page images | Auto-detect URL type, download media files — images, video, og:image |
681
682
  | 📡 **rss** | Hacker News RSS | Parsed feed items with title, link, date, author, categories |
682
683
 
683
- > **39 tools. 34 hidden APIs on Airbnb. Live BTC feed. Reusable browser flows. Zero API keys for scraping.**
684
+ > **41 tools. 34 hidden APIs on Airbnb. Live BTC feed. Reusable browser flows. Zero API keys for scraping.**
684
685
 
685
686
  ---
686
687
 
@@ -1,6 +1,6 @@
1
1
  import fs from "node:fs/promises";
2
2
  import path from "node:path";
3
- import { getJobsDir } from "../config.js";
3
+ import { getJobsDir } from "../core/config.js";
4
4
  export class JobStore {
5
5
  cache = new Map();
6
6
  dir;
@@ -1 +1 @@
1
- {"version":3,"file":"job-store.js","sourceRoot":"","sources":["../../src/batch/job-store.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,kBAAkB,CAAC;AAClC,OAAO,IAAI,MAAM,WAAW,CAAC;AAC7B,OAAO,EAAE,UAAU,EAAE,MAAM,cAAc,CAAC;AAG1C,MAAM,OAAO,QAAQ;IACX,KAAK,GAAG,IAAI,GAAG,EAAoB,CAAC;IACpC,GAAG,CAAS;IAEpB,YAAY,GAAY;QACtB,IAAI,CAAC,GAAG,GAAG,GAAG,IAAI,UAAU,EAAE,CAAC;IACjC,CAAC;IAEO,OAAO,CAAC,EAAU;QACxB,wCAAwC;QACxC,MAAM,IAAI,GAAG,EAAE,CAAC,OAAO,CAAC,kBAAkB,EAAE,GAAG,CAAC,CAAC;QACjD,OAAO,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,EAAE,GAAG,IAAI,OAAO,CAAC,CAAC;IAC7C,CAAC;IAED,KAAK,CAAC,IAAI,CAAC,GAAa;QACtB,MAAM,OAAO,GAAa,EAAE,GAAG,GAAG,EAAE,UAAU,EAAE,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE,EAAE,CAAC;QAC3E,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,GAAG,CAAC,EAAE,EAAE,OAAO,CAAC,CAAC;QAEhC,MAAM,EAAE,CAAC,KAAK,CAAC,IAAI,CAAC,GAAG,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;QAC9C,MAAM,QAAQ,GAAG,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;QACtC,MAAM,OAAO,GAAG,QAAQ,GAAG,MAAM,CAAC;QAClC,MAAM,EAAE,CAAC,SAAS,CAAC,OAAO,EAAE,IAAI,CAAC,SAAS,CAAC,OAAO,EAAE,IAAI,EAAE,CAAC,CAAC,EAAE,OAAO,CAAC,CAAC;QACvE,MAAM,EAAE,CAAC,MAAM,CAAC,OAAO,EAAE,QAAQ,CAAC,CAAC;IACrC,CAAC;IAED,KAAK,CAAC,IAAI,CAAC,EAAU;QACnB,IAAI,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,EAAE,CAAC;YAAE,OAAO,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,EAAE,CAAE,CAAC;QAEnD,IAAI,CAAC;YACH,MAAM,IAAI,GAAG,MAAM,EAAE,CAAC,QAAQ,CAAC,IAAI,CAAC,OAAO,CAAC,EAAE,CAAC,EAAE,OAAO,CAAC,CAAC;YAC1D,MAAM,GAAG,GAAG,IAAI,CAAC,KAAK,CAAC,IAAI,CAAa,CAAC;YACzC,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,EAAE,EAAE,GAAG,CAAC,CAAC;YACxB,OAAO,GAAG,CAAC;QACb,CAAC;QAAC,OAAO,GAAY,EAAE,CAAC;YACtB,MAAM,QAAQ,GACZ,GAAG;gBACH,OAAO,GAAG,KAAK,QAAQ;gBACvB,MAAM,IAAI,GAAG;gBACZ,GAA6B,CAAC,IAAI,KAAK,QAAQ,CAAC;YACnD,IAAI,CAAC,QAAQ,EAAE,CAAC;gBACd,OAAO,CAAC,KAAK,CACX,6BAA6B,EAC7B,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,CACjD,CAAC;YACJ,CAAC;YACD,OAAO,IAAI,CAAC;QACd,CAAC;IACH,CAAC;IAED,KAAK,CAAC,MAAM,CAAC,EAAU;QACrB,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC;QACtB,IAAI,CAAC;YACH,MAAM,EAAE,CAAC,MAAM,CAAC,IAAI,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC,CAAC;QACpC,CAAC;QAAC,MAAM,CAAC;YACP,kCAAkC;QACpC,CAAC;IACH,CAAC;IAED,KAAK,CAAC,IAAI;QACR,IAAI,CAAC;YACH,MAAM,KAAK,GAAG,MAAM,EAAE,CAAC,OAAO,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;YACzC,OAAO,KAAK;iBACT,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,QAAQ,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,QAAQ,CAAC,WAAW,CAAC,CAAC;iBAC9D,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,OAAO,CAAC,SAAS,EAAE,EAAE,CAAC,CAAC,CAAC;QAC1C,CAAC;QAAC,MAAM,CAAC;YACP,OAAO,EAAE,CAAC;QACZ,CAAC;IACH,CAAC;CACF;AAED,kBAAkB;AAElB,IAAI,KAAK,GAAoB,IAAI,CAAC;AAElC,MAAM,UAAU,WAAW;IACzB,IAAI,CAAC,KAAK,EAAE,CAAC;QACX,KAAK,GAAG,IAAI,QAAQ,EAAE,CAAC;IACzB,CAAC;IACD,OAAO,KAAK,CAAC;AACf,CAAC;AAED,oCAAoC;AACpC,MAAM,UAAU,aAAa;IAC3B,KAAK,GAAG,IAAI,CAAC;AACf,CAAC"}
1
+ {"version":3,"file":"job-store.js","sourceRoot":"","sources":["../../src/batch/job-store.ts"],"names":[],"mappings":"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"}
@@ -1,4 +1,4 @@
1
- import { BRAVE_API_BASE } from "../constants.js";
1
+ import { BRAVE_API_BASE } from "../core/constants.js";
2
2
  export class BraveApiError extends Error {
3
3
  status;
4
4
  constructor(status, message) {
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/brave-api/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAE,MAAM,iBAAiB,CAAC;AAEjD,MAAM,OAAO,aAAc,SAAQ,KAAK;IAE7B;IADT,YACS,MAAc,EACrB,OAAe;QAEf,KAAK,CAAC,OAAO,CAAC,CAAC;QAHR,WAAM,GAAN,MAAM,CAAQ;QAIrB,IAAI,CAAC,IAAI,GAAG,eAAe,CAAC;IAC9B,CAAC;CACF;AAED,MAAM,CAAC,KAAK,UAAU,YAAY,CAChC,MAAc,EACd,QAAgB,EAChB,MAAmD;IAEnD,MAAM,GAAG,GAAG,IAAI,GAAG,CAAC,GAAG,cAAc,GAAG,QAAQ,EAAE,CAAC,CAAC;IACpD,KAAK,MAAM,CAAC,GAAG,EAAE,KAAK,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,MAAM,CAAC,EAAE,CAAC;QAClD,IAAI,KAAK,KAAK,SAAS,EAAE,CAAC;YACxB,GAAG,CAAC,YAAY,CAAC,GAAG,CAAC,GAAG,EAAE,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC;QAC3C,CAAC;IACH,CAAC;IAED,MAAM,GAAG,GAAG,MAAM,KAAK,CAAC,GAAG,CAAC,QAAQ,EAAE,EAAE;QACtC,OAAO,EAAE;YACP,MAAM,EAAE,kBAAkB;YAC1B,iBAAiB,EAAE,MAAM;YACzB,sBAAsB,EAAE,MAAM;SAC/B;QACD,MAAM,EAAE,WAAW,CAAC,OAAO,CAAC,MAAM,CAAC;KACpC,CAAC,CAAC;IAEH,IAAI,CAAC,GAAG,CAAC,EAAE,EAAE,CAAC;QACZ,MAAM,IAAI,GAAG,MAAM,GAAG,CAAC,IAAI,EAAE,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC,eAAe,CAAC,CAAC;QAC3D,MAAM,IAAI,aAAa,CAAC,GAAG,CAAC,MAAM,EAAE,aAAa,GAAG,CAAC,MAAM,KAAK,IAAI,EAAE,CAAC,CAAC;IAC1E,CAAC;IAED,OAAO,GAAG,CAAC,IAAI,EAAE,CAAC;AACpB,CAAC"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/brave-api/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC;AAEtD,MAAM,OAAO,aAAc,SAAQ,KAAK;IAE7B;IADT,YACS,MAAc,EACrB,OAAe;QAEf,KAAK,CAAC,OAAO,CAAC,CAAC;QAHR,WAAM,GAAN,MAAM,CAAQ;QAIrB,IAAI,CAAC,IAAI,GAAG,eAAe,CAAC;IAC9B,CAAC;CACF;AAED,MAAM,CAAC,KAAK,UAAU,YAAY,CAChC,MAAc,EACd,QAAgB,EAChB,MAAmD;IAEnD,MAAM,GAAG,GAAG,IAAI,GAAG,CAAC,GAAG,cAAc,GAAG,QAAQ,EAAE,CAAC,CAAC;IACpD,KAAK,MAAM,CAAC,GAAG,EAAE,KAAK,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,MAAM,CAAC,EAAE,CAAC;QAClD,IAAI,KAAK,KAAK,SAAS,EAAE,CAAC;YACxB,GAAG,CAAC,YAAY,CAAC,GAAG,CAAC,GAAG,EAAE,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC;QAC3C,CAAC;IACH,CAAC;IAED,MAAM,GAAG,GAAG,MAAM,KAAK,CAAC,GAAG,CAAC,QAAQ,EAAE,EAAE;QACtC,OAAO,EAAE;YACP,MAAM,EAAE,kBAAkB;YAC1B,iBAAiB,EAAE,MAAM;YACzB,sBAAsB,EAAE,MAAM;SAC/B;QACD,MAAM,EAAE,WAAW,CAAC,OAAO,CAAC,MAAM,CAAC;KACpC,CAAC,CAAC;IAEH,IAAI,CAAC,GAAG,CAAC,EAAE,EAAE,CAAC;QACZ,MAAM,IAAI,GAAG,MAAM,GAAG,CAAC,IAAI,EAAE,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC,eAAe,CAAC,CAAC;QAC3D,MAAM,IAAI,aAAa,CAAC,GAAG,CAAC,MAAM,EAAE,aAAa,GAAG,CAAC,MAAM,KAAK,IAAI,EAAE,CAAC,CAAC;IAC1E,CAAC;IAED,OAAO,GAAG,CAAC,IAAI,EAAE,CAAC;AACpB,CAAC"}
@@ -0,0 +1,21 @@
1
+ /**
2
+ * Config file management for imperium-crawl CLI.
3
+ *
4
+ * Saves API keys to ~/.imperium-crawl/config.json so users don't
5
+ * need to set environment variables manually.
6
+ *
7
+ * Priority: process.env (system) > config.json
8
+ * applyCliConfig() fills in env vars from config only if not already set.
9
+ */
10
+ export declare function getCliConfigPath(): string;
11
+ export declare function loadCliConfig(): Record<string, string>;
12
+ export declare function saveCliConfig(config: Record<string, string>): void;
13
+ /**
14
+ * Apply config.json values to process.env.
15
+ * System env vars take priority — config values are only applied
16
+ * when the key is not already set.
17
+ *
18
+ * Call this once at startup, before any tool initialization.
19
+ */
20
+ export declare function applyCliConfig(): void;
21
+ //# sourceMappingURL=config.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../../src/cli/config.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AASH,wBAAgB,gBAAgB,IAAI,MAAM,CAEzC;AAED,wBAAgB,aAAa,IAAI,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAWtD;AAED,wBAAgB,aAAa,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GAAG,IAAI,CAIlE;AAED;;;;;;GAMG;AACH,wBAAgB,cAAc,IAAI,IAAI,CAOrC"}
@@ -0,0 +1,51 @@
1
+ /**
2
+ * Config file management for imperium-crawl CLI.
3
+ *
4
+ * Saves API keys to ~/.imperium-crawl/config.json so users don't
5
+ * need to set environment variables manually.
6
+ *
7
+ * Priority: process.env (system) > config.json
8
+ * applyCliConfig() fills in env vars from config only if not already set.
9
+ */
10
+ import path from "node:path";
11
+ import os from "node:os";
12
+ import { readFileSync, writeFileSync, mkdirSync } from "node:fs";
13
+ import { SKILLS_DIR_NAME } from "../core/constants.js";
14
+ const CONFIG_FILENAME = "config.json";
15
+ export function getCliConfigPath() {
16
+ return path.join(os.homedir(), SKILLS_DIR_NAME, CONFIG_FILENAME);
17
+ }
18
+ export function loadCliConfig() {
19
+ try {
20
+ const content = readFileSync(getCliConfigPath(), "utf-8");
21
+ const parsed = JSON.parse(content);
22
+ if (parsed && typeof parsed === "object" && !Array.isArray(parsed)) {
23
+ return parsed;
24
+ }
25
+ }
26
+ catch {
27
+ // File doesn't exist or invalid JSON — return empty config
28
+ }
29
+ return {};
30
+ }
31
+ export function saveCliConfig(config) {
32
+ const configPath = getCliConfigPath();
33
+ mkdirSync(path.dirname(configPath), { recursive: true });
34
+ writeFileSync(configPath, JSON.stringify(config, null, 2) + "\n", "utf-8");
35
+ }
36
+ /**
37
+ * Apply config.json values to process.env.
38
+ * System env vars take priority — config values are only applied
39
+ * when the key is not already set.
40
+ *
41
+ * Call this once at startup, before any tool initialization.
42
+ */
43
+ export function applyCliConfig() {
44
+ const config = loadCliConfig();
45
+ for (const [key, value] of Object.entries(config)) {
46
+ if (typeof value === "string" && !process.env[key]) {
47
+ process.env[key] = value;
48
+ }
49
+ }
50
+ }
51
+ //# sourceMappingURL=config.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"config.js","sourceRoot":"","sources":["../../src/cli/config.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,IAAI,MAAM,WAAW,CAAC;AAC7B,OAAO,EAAE,MAAM,SAAS,CAAC;AACzB,OAAO,EAAE,YAAY,EAAE,aAAa,EAAE,SAAS,EAAE,MAAM,SAAS,CAAC;AACjE,OAAO,EAAE,eAAe,EAAE,MAAM,sBAAsB,CAAC;AAEvD,MAAM,eAAe,GAAG,aAAa,CAAC;AAEtC,MAAM,UAAU,gBAAgB;IAC9B,OAAO,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,OAAO,EAAE,EAAE,eAAe,EAAE,eAAe,CAAC,CAAC;AACnE,CAAC;AAED,MAAM,UAAU,aAAa;IAC3B,IAAI,CAAC;QACH,MAAM,OAAO,GAAG,YAAY,CAAC,gBAAgB,EAAE,EAAE,OAAO,CAAC,CAAC;QAC1D,MAAM,MAAM,GAAY,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC;QAC5C,IAAI,MAAM,IAAI,OAAO,MAAM,KAAK,QAAQ,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,MAAM,CAAC,EAAE,CAAC;YACnE,OAAO,MAAgC,CAAC;QAC1C,CAAC;IACH,CAAC;IAAC,MAAM,CAAC;QACP,2DAA2D;IAC7D,CAAC;IACD,OAAO,EAAE,CAAC;AACZ,CAAC;AAED,MAAM,UAAU,aAAa,CAAC,MAA8B;IAC1D,MAAM,UAAU,GAAG,gBAAgB,EAAE,CAAC;IACtC,SAAS,CAAC,IAAI,CAAC,OAAO,CAAC,UAAU,CAAC,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;IACzD,aAAa,CAAC,UAAU,EAAE,IAAI,CAAC,SAAS,CAAC,MAAM,EAAE,IAAI,EAAE,CAAC,CAAC,GAAG,IAAI,EAAE,OAAO,CAAC,CAAC;AAC7E,CAAC;AAED;;;;;;GAMG;AACH,MAAM,UAAU,cAAc;IAC5B,MAAM,MAAM,GAAG,aAAa,EAAE,CAAC;IAC/B,KAAK,MAAM,CAAC,GAAG,EAAE,KAAK,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,MAAM,CAAC,EAAE,CAAC;QAClD,IAAI,OAAO,KAAK,KAAK,QAAQ,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,GAAG,CAAC,EAAE,CAAC;YACnD,OAAO,CAAC,GAAG,CAAC,GAAG,CAAC,GAAG,KAAK,CAAC;QAC3B,CAAC;IACH,CAAC;AACH,CAAC"}
@@ -0,0 +1,17 @@
1
+ /**
2
+ * CLI engine for imperium-crawl.
3
+ *
4
+ * Dynamically maps each tool's Zod schema to Commander options,
5
+ * runs execute(), then formats output via formatters.ts.
6
+ *
7
+ * TTY-aware: spinners + colored output + tables in interactive mode;
8
+ * clean JSON to stdout when piped (non-TTY / CI / NO_COLOR).
9
+ *
10
+ * Lazy loading: only the requested tool's module is imported at startup.
11
+ * This keeps cold-start time fast even though some tools pull in heavy
12
+ * deps (cheerio, linkedom, playwright, stealth engine).
13
+ */
14
+ import { Command } from "commander";
15
+ export declare function buildCli(): Promise<Command>;
16
+ export declare function runCli(): Promise<void>;
17
+ //# sourceMappingURL=engine.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"engine.d.ts","sourceRoot":"","sources":["../../src/cli/engine.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAEH,OAAO,EAAE,OAAO,EAAU,MAAM,WAAW,CAAC;AA6U5C,wBAAsB,QAAQ,IAAI,OAAO,CAAC,OAAO,CAAC,CAwEjD;AA2ED,wBAAsB,MAAM,IAAI,OAAO,CAAC,IAAI,CAAC,CAG5C"}
@@ -0,0 +1,440 @@
1
+ /**
2
+ * CLI engine for imperium-crawl.
3
+ *
4
+ * Dynamically maps each tool's Zod schema to Commander options,
5
+ * runs execute(), then formats output via formatters.ts.
6
+ *
7
+ * TTY-aware: spinners + colored output + tables in interactive mode;
8
+ * clean JSON to stdout when piped (non-TTY / CI / NO_COLOR).
9
+ *
10
+ * Lazy loading: only the requested tool's module is imported at startup.
11
+ * This keeps cold-start time fast even though some tools pull in heavy
12
+ * deps (cheerio, linkedom, playwright, stealth engine).
13
+ */
14
+ import { Command, Option } from "commander";
15
+ import { z } from "zod";
16
+ import { writeFileSync } from "fs";
17
+ import { TOOL_MANIFEST } from "../tools/manifest.js";
18
+ import { formatOutput, parseToolOutput, parseImageOutput, } from "../core/formatters.js";
19
+ import { PACKAGE_VERSION } from "../core/constants.js";
20
+ import { isTTY, createSpinner, errorMsg, renderTable, colorUrl, } from "./ui.js";
21
+ /**
22
+ * Recursively unwrap ZodDefault, ZodOptional, ZodNullable
23
+ * to get the base type and metadata.
24
+ */
25
+ function unwrapZod(schema) {
26
+ let isOptional = false;
27
+ let hasDefault = false;
28
+ let defaultValue = undefined;
29
+ const description = schema.description;
30
+ let current = schema;
31
+ for (let i = 0; i < 10; i++) {
32
+ const typeName = current._def.typeName;
33
+ if (typeName === "ZodDefault") {
34
+ hasDefault = true;
35
+ defaultValue = current._def.defaultValue();
36
+ current = current._def.innerType;
37
+ }
38
+ else if (typeName === "ZodOptional") {
39
+ isOptional = true;
40
+ current = current._def.innerType;
41
+ }
42
+ else if (typeName === "ZodNullable") {
43
+ isOptional = true;
44
+ current = current._def.innerType;
45
+ }
46
+ else {
47
+ break;
48
+ }
49
+ }
50
+ return { base: current, isOptional, hasDefault, defaultValue, description };
51
+ }
52
+ function getZodTypeName(schema) {
53
+ return schema._def.typeName ?? "";
54
+ }
55
+ // ── Name Converters ──────────────────────────────────────────────────
56
+ /** snake_case → --kebab-case */
57
+ function snakeToKebab(key) {
58
+ return key.replace(/_/g, "-");
59
+ }
60
+ /** camelCase → snake_case (Commander's camelCase back to Zod's keys) */
61
+ function camelToSnake(key) {
62
+ return key.replace(/[A-Z]/g, (m) => "_" + m.toLowerCase());
63
+ }
64
+ // ── Zod-to-Commander Option Mapper ───────────────────────────────────
65
+ function addOptionsFromSchema(cmd, schema) {
66
+ if (getZodTypeName(schema) !== "ZodObject")
67
+ return;
68
+ const shape = schema.shape;
69
+ for (const [key, fieldSchema] of Object.entries(shape)) {
70
+ const { base, isOptional, hasDefault, defaultValue, description, } = unwrapZod(fieldSchema);
71
+ const flag = snakeToKebab(key);
72
+ const desc = description ?? key;
73
+ const typeName = getZodTypeName(base);
74
+ const required = !isOptional && !hasDefault;
75
+ if (typeName === "ZodString") {
76
+ if (required) {
77
+ cmd.requiredOption(`--${flag} <value>`, desc);
78
+ }
79
+ else {
80
+ const opt = new Option(`--${flag} <value>`, desc);
81
+ if (hasDefault)
82
+ opt.default(defaultValue);
83
+ cmd.addOption(opt);
84
+ }
85
+ }
86
+ else if (typeName === "ZodNumber") {
87
+ const parseNum = (v) => {
88
+ const n = parseFloat(v);
89
+ if (isNaN(n))
90
+ throw new Error(`Invalid number: ${v}`);
91
+ return n;
92
+ };
93
+ if (required) {
94
+ cmd.requiredOption(`--${flag} <n>`, desc, parseNum);
95
+ }
96
+ else {
97
+ const opt = new Option(`--${flag} <n>`, desc);
98
+ opt.argParser(parseNum);
99
+ if (hasDefault)
100
+ opt.default(defaultValue);
101
+ cmd.addOption(opt);
102
+ }
103
+ }
104
+ else if (typeName === "ZodBoolean") {
105
+ if (hasDefault && defaultValue === true) {
106
+ cmd.option(`--no-${flag}`, desc);
107
+ }
108
+ else {
109
+ cmd.option(`--${flag}`, desc, hasDefault ? defaultValue : false);
110
+ }
111
+ }
112
+ else if (typeName === "ZodEnum") {
113
+ const values = base._def.values;
114
+ const opt = new Option(`--${flag} <value>`, desc).choices(values);
115
+ if (hasDefault)
116
+ opt.default(defaultValue);
117
+ if (required)
118
+ opt.makeOptionMandatory(true);
119
+ cmd.addOption(opt);
120
+ }
121
+ else if (typeName === "ZodArray") {
122
+ const collect = (val, prev) => prev.concat(val);
123
+ const innerTypeName = getZodTypeName(unwrapZod(base._def.type).base);
124
+ if (innerTypeName === "ZodEnum") {
125
+ const values = unwrapZod(base._def.type).base._def
126
+ .values;
127
+ const opt = new Option(`--${flag} <value>`, desc)
128
+ .choices(values)
129
+ .argParser(collect)
130
+ .default(hasDefault ? defaultValue : []);
131
+ cmd.addOption(opt);
132
+ }
133
+ else if (innerTypeName === "ZodObject") {
134
+ // Parse as single JSON array string (e.g. --actions '[{...},{...}]')
135
+ const parseJsonArray = (val) => {
136
+ try {
137
+ const parsed = JSON.parse(val);
138
+ if (!Array.isArray(parsed))
139
+ throw new Error("Expected JSON array");
140
+ return parsed;
141
+ }
142
+ catch {
143
+ throw new Error(`Invalid JSON array for --${flag}: ${val}`);
144
+ }
145
+ };
146
+ if (required) {
147
+ cmd.requiredOption(`--${flag} <json>`, desc, parseJsonArray);
148
+ }
149
+ else {
150
+ const opt = new Option(`--${flag} <json>`, desc);
151
+ opt.argParser(parseJsonArray);
152
+ if (hasDefault)
153
+ opt.default(defaultValue);
154
+ cmd.addOption(opt);
155
+ }
156
+ }
157
+ else {
158
+ cmd.option(`--${flag} <value>`, desc, collect, (hasDefault ? defaultValue : []));
159
+ }
160
+ }
161
+ else if (typeName === "ZodRecord") {
162
+ const parseJson = (val) => {
163
+ try {
164
+ return JSON.parse(val);
165
+ }
166
+ catch {
167
+ throw new Error(`Invalid JSON for --${flag}: ${val}`);
168
+ }
169
+ };
170
+ if (required) {
171
+ cmd.requiredOption(`--${flag} <json>`, desc, parseJson);
172
+ }
173
+ else {
174
+ const opt = new Option(`--${flag} <json>`, desc);
175
+ opt.argParser(parseJson);
176
+ if (hasDefault)
177
+ opt.default(defaultValue);
178
+ cmd.addOption(opt);
179
+ }
180
+ }
181
+ else {
182
+ cmd.option(`--${flag} <value>`, desc);
183
+ }
184
+ }
185
+ }
186
+ // ── Reverse Key Mapper ───────────────────────────────────────────────
187
+ function optsToInput(opts, schema) {
188
+ if (getZodTypeName(schema) !== "ZodObject")
189
+ return {};
190
+ const shape = schema.shape;
191
+ const schemaKeys = new Set(Object.keys(shape));
192
+ const input = {};
193
+ for (const [camelKey, value] of Object.entries(opts)) {
194
+ if (value === undefined)
195
+ continue;
196
+ const snakeKey = camelToSnake(camelKey);
197
+ if (schemaKeys.has(snakeKey)) {
198
+ input[snakeKey] = value;
199
+ }
200
+ else if (schemaKeys.has(camelKey)) {
201
+ input[camelKey] = value;
202
+ }
203
+ }
204
+ return input;
205
+ }
206
+ // ── Lazy Tool Loader ─────────────────────────────────────────────────
207
+ /**
208
+ * Which tool command the user requested (from argv), or null for
209
+ * global commands (--help, setup, --version).
210
+ *
211
+ * Detected before Commander runs so we can load only that tool's module.
212
+ */
213
+ function getRequestedCmd() {
214
+ const arg = process.argv[2];
215
+ if (!arg || arg.startsWith("-"))
216
+ return null;
217
+ return arg; // e.g., "scrape", "list-jobs", "setup"
218
+ }
219
+ function positionalKeyForCmd(cmd) {
220
+ if (["run-flow", "inspect-flow", "validate-flow"].includes(cmd))
221
+ return "flow";
222
+ if (cmd === "serve-flow")
223
+ return "family";
224
+ return null;
225
+ }
226
+ // ── Table Renderers ──────────────────────────────────────────────────
227
+ function tryRenderTable(toolName, data) {
228
+ if (!isTTY)
229
+ return null;
230
+ if (!data || typeof data !== "object")
231
+ return null;
232
+ const obj = data;
233
+ if (toolName === "list_jobs") {
234
+ const jobs = Array.isArray(obj.jobs) ? obj.jobs : [];
235
+ if (jobs.length === 0)
236
+ return null;
237
+ const headers = ["Job ID", "Status", "URLs", "Progress"];
238
+ const rows = jobs.map((j) => {
239
+ const job = j;
240
+ const total = Number(job.urls_total ?? 0);
241
+ const done = Number(job.urls_completed ?? 0);
242
+ const failed = Number(job.urls_failed ?? 0);
243
+ const pct = total > 0 ? Math.round(((done + failed) / total) * 100) : 0;
244
+ return [String(job.id ?? ""), String(job.status ?? ""), `${done + failed}/${total}`, `${pct}%`];
245
+ });
246
+ return renderTable(headers, rows);
247
+ }
248
+ if (toolName === "list_skills") {
249
+ const skills = Array.isArray(obj.skills) ? obj.skills : [];
250
+ if (skills.length === 0)
251
+ return null;
252
+ const headers = ["Name", "Type", "URL", "Fields", "Created"];
253
+ const rows = skills.map((s) => {
254
+ const skill = s;
255
+ const fields = Array.isArray(skill.fields) ? skill.fields.join(", ") : "";
256
+ const created = typeof skill.created_at === "string" ? skill.created_at.split("T")[0] : "";
257
+ const toolType = String(skill.tool ?? "extract");
258
+ const typeLabel = skill.builtin ? `${toolType} (built-in)` : toolType;
259
+ return [String(skill.name ?? ""), typeLabel, colorUrl(String(skill.url ?? "")), fields, created];
260
+ });
261
+ return renderTable(headers, rows);
262
+ }
263
+ if (toolName === "knowledge") {
264
+ const domains = Array.isArray(obj.domains) ? obj.domains : [];
265
+ if (domains.length === 0)
266
+ return null;
267
+ const headers = ["Domain", "Level", "Success%", "Reqs", "Rate/RPM", "AntiBot", "Updated"];
268
+ const rows = domains.map((d) => {
269
+ const dom = d;
270
+ return [
271
+ String(dom.domain ?? ""),
272
+ `L${dom.optimal_level ?? 1}`,
273
+ String(dom.success_rate ?? "0%"),
274
+ String(dom.requests ?? 0),
275
+ String(dom.rate_limit_rpm ?? 60),
276
+ String(dom.antibot ?? "none"),
277
+ String(dom.last_updated ?? ""),
278
+ ];
279
+ });
280
+ return renderTable(headers, rows);
281
+ }
282
+ if (["search", "news_search", "image_search", "video_search"].includes(toolName)) {
283
+ const results = Array.isArray(obj.results) ? obj.results : [];
284
+ if (results.length === 0)
285
+ return null;
286
+ const headers = ["#", "Title", "URL"];
287
+ const rows = results.slice(0, 20).map((r, i) => {
288
+ const result = r;
289
+ const title = String(result.title ?? result.name ?? "").slice(0, 50);
290
+ const url = colorUrl(String(result.url ?? result.source ?? "").slice(0, 60));
291
+ return [String(i + 1), title, url];
292
+ });
293
+ return renderTable(headers, rows);
294
+ }
295
+ return null;
296
+ }
297
+ // ── Contextual Error Messages ────────────────────────────────────────
298
+ function handleToolError(toolName, msg) {
299
+ errorMsg(msg);
300
+ const needsKey = msg.includes("API_KEY") ||
301
+ msg.toLowerCase().includes("api key") ||
302
+ ["search", "news_search", "image_search", "video_search", "ai_extract"].includes(toolName);
303
+ if (needsKey) {
304
+ process.stderr.write("\n Run \x1b[1mimperiumcrawl setup\x1b[0m to configure API keys.\n\n");
305
+ }
306
+ }
307
+ // ── Program Builder ──────────────────────────────────────────────────
308
+ export async function buildCli() {
309
+ const program = new Command();
310
+ program
311
+ .name("imperiumcrawl")
312
+ .description("39-tool web scraping, crawling, search, media download, API discovery, and reusable browser workflow CLI.\nRun without arguments in TTY for interactive TUI.")
313
+ .version(PACKAGE_VERSION)
314
+ .addOption(new Option("--output-format <fmt>", "Output format")
315
+ .choices(["json", "jsonl", "csv", "markdown"])
316
+ .default("json"))
317
+ .option("--output <file>", "Write output to file instead of stdout")
318
+ .option("--pretty", "Pretty-print JSON output", false);
319
+ // ── Setup wizard ────────────────────────────────────────────────
320
+ program
321
+ .command("setup")
322
+ .description("Configure API keys and save them to ~/.imperium-crawl/config.json")
323
+ .action(async () => {
324
+ const { runSetup } = await import("./onboarding.js");
325
+ await runSetup();
326
+ });
327
+ // ── Explore REPL — interactive browser session with recording ──
328
+ program
329
+ .command("explore <url>")
330
+ .description("Open an interactive browser REPL. Navigate, click, type — every action is recorded. Run save-skill inside to export the session as a reusable skill.")
331
+ .option("--session-id <id>", "Restore and save cookies for this session ID")
332
+ .action(async (url, opts) => {
333
+ const { runExplore } = await import("./explore.js");
334
+ await runExplore(url, opts.sessionId);
335
+ });
336
+ // ── Tool commands — lazy loaded ──────────────────────────────────
337
+ const requestedCmd = getRequestedCmd();
338
+ for (const { cmd, description } of TOOL_MANIFEST) {
339
+ const sub = program.command(cmd).description(description);
340
+ const positionalKey = positionalKeyForCmd(cmd);
341
+ if (positionalKey === "flow")
342
+ sub.argument("[flow]", "Flow reference in '<family>/<variant>' form");
343
+ if (positionalKey === "family")
344
+ sub.argument("[family]", "Optional flow family to expose");
345
+ if (cmd === requestedCmd) {
346
+ // Load the full tool module for this command only.
347
+ // This is the only heavy import at startup — all others are skipped.
348
+ const toolModule = await import(`./tools/${cmd}.js`);
349
+ addOptionsFromSchema(sub, toolModule.schema);
350
+ sub.action(async (...args) => {
351
+ const last = args[args.length - 1];
352
+ const localOpts = typeof last.opts === "function"
353
+ ? last.opts()
354
+ : last;
355
+ if (positionalKey && typeof args[0] === "string")
356
+ localOpts[positionalKey] = args[0];
357
+ await runTool(toolModule, localOpts, program);
358
+ });
359
+ }
360
+ else {
361
+ // For all other commands: register with just name + description.
362
+ // Options are loaded lazily if the command is somehow invoked.
363
+ sub.action(async (...args) => {
364
+ const toolModule = await import(`./tools/${cmd}.js`);
365
+ // Re-parse options now that we have the schema
366
+ const rawOpts = sub.opts();
367
+ if (positionalKey && typeof args[0] === "string")
368
+ rawOpts[positionalKey] = args[0];
369
+ await runTool(toolModule, rawOpts, program);
370
+ });
371
+ }
372
+ }
373
+ return program;
374
+ }
375
+ async function runTool(tool, localOpts, program) {
376
+ const globalOpts = program.opts();
377
+ const input = optsToInput(localOpts, tool.schema);
378
+ let validated;
379
+ try {
380
+ validated = tool.schema.parse(input);
381
+ }
382
+ catch (err) {
383
+ if (err instanceof z.ZodError) {
384
+ const issues = err.issues
385
+ .map((i) => ` ${i.path.join(".")}: ${i.message}`)
386
+ .join("\n");
387
+ process.stderr.write(`Validation error:\n${issues}\n`);
388
+ process.exit(1);
389
+ }
390
+ throw err;
391
+ }
392
+ const spinner = createSpinner(`Running ${tool.name}...`);
393
+ const start = Date.now();
394
+ let result;
395
+ try {
396
+ result = await tool.execute(validated);
397
+ spinner.succeed(`Done in ${((Date.now() - start) / 1000).toFixed(1)}s`);
398
+ }
399
+ catch (err) {
400
+ const msg = err instanceof Error ? err.message : String(err);
401
+ spinner.fail(`Failed: ${msg}`);
402
+ handleToolError(tool.name, msg);
403
+ process.exit(1);
404
+ }
405
+ // Handle image responses (screenshot tool, interact with return_screenshot)
406
+ const imageResult = parseImageOutput(result);
407
+ if (imageResult) {
408
+ const imgPath = `screenshot-${Date.now()}.png`;
409
+ writeFileSync(imgPath, Buffer.from(imageResult.data, "base64"));
410
+ process.stderr.write(`Screenshot saved to ${imgPath}\n`);
411
+ }
412
+ const data = parseToolOutput(result);
413
+ // Image-only response (no text data) — we're done
414
+ if (imageResult && data === null)
415
+ return;
416
+ const table = tryRenderTable(tool.name, data);
417
+ if (table) {
418
+ process.stdout.write(table + "\n");
419
+ return;
420
+ }
421
+ const formatOptions = {
422
+ format: globalOpts.outputFormat,
423
+ pretty: globalOpts.pretty,
424
+ };
425
+ const output = formatOutput(data, formatOptions);
426
+ const outputFile = globalOpts.output;
427
+ if (outputFile) {
428
+ writeFileSync(outputFile, output + "\n", "utf-8");
429
+ process.stderr.write(`Written to ${outputFile}\n`);
430
+ }
431
+ else {
432
+ process.stdout.write(output + "\n");
433
+ }
434
+ }
435
+ // ── Entry Point ──────────────────────────────────────────────────────
436
+ export async function runCli() {
437
+ const program = await buildCli();
438
+ await program.parseAsync(process.argv);
439
+ }
440
+ //# sourceMappingURL=engine.js.map