luca 2.0.0 → 3.0.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (532) hide show
  1. package/.github/workflows/release.yaml +170 -0
  2. package/AGENTS.md +99 -0
  3. package/CLAUDE.md +123 -0
  4. package/CNAME +1 -0
  5. package/README.md +275 -9
  6. package/RUNME.md +56 -0
  7. package/assistants/codingAssistant/ABOUT.md +5 -0
  8. package/assistants/codingAssistant/CORE.md +33 -0
  9. package/assistants/codingAssistant/hooks.ts +21 -0
  10. package/assistants/codingAssistant/tools.ts +12 -0
  11. package/assistants/inkbot/ABOUT.md +16 -0
  12. package/assistants/inkbot/CORE.md +330 -0
  13. package/assistants/inkbot/hooks.ts +6 -0
  14. package/assistants/inkbot/tools.ts +53 -0
  15. package/assistants/researcher/ABOUT.md +5 -0
  16. package/assistants/researcher/CORE.md +46 -0
  17. package/assistants/researcher/hooks.ts +16 -0
  18. package/assistants/researcher/tools.ts +237 -0
  19. package/bun.lock +2667 -0
  20. package/bunfig.toml +3 -0
  21. package/commands/audit-docs.ts +740 -0
  22. package/commands/build-bootstrap.ts +117 -0
  23. package/commands/build-python-bridge.ts +42 -0
  24. package/commands/build-scaffolds.ts +175 -0
  25. package/commands/bundle-consumer-project.ts +521 -0
  26. package/commands/generate-api-docs.ts +114 -0
  27. package/commands/inkbot.ts +874 -0
  28. package/commands/release.ts +80 -0
  29. package/commands/try-all-challenges.ts +543 -0
  30. package/commands/try-challenge.ts +100 -0
  31. package/dist/agi/container.server.d.ts +63 -0
  32. package/dist/agi/container.server.d.ts.map +1 -0
  33. package/dist/agi/endpoints/ask.d.ts +20 -0
  34. package/dist/agi/endpoints/ask.d.ts.map +1 -0
  35. package/dist/agi/endpoints/conversations/[id].d.ts +27 -0
  36. package/dist/agi/endpoints/conversations/[id].d.ts.map +1 -0
  37. package/dist/agi/endpoints/conversations.d.ts +18 -0
  38. package/dist/agi/endpoints/conversations.d.ts.map +1 -0
  39. package/dist/agi/endpoints/experts.d.ts +8 -0
  40. package/dist/agi/endpoints/experts.d.ts.map +1 -0
  41. package/dist/agi/feature.d.ts +9 -0
  42. package/dist/agi/feature.d.ts.map +1 -0
  43. package/dist/agi/features/assistant.d.ts +509 -0
  44. package/dist/agi/features/assistant.d.ts.map +1 -0
  45. package/dist/agi/features/assistants-manager.d.ts +236 -0
  46. package/dist/agi/features/assistants-manager.d.ts.map +1 -0
  47. package/dist/agi/features/autonomous-assistant.d.ts +281 -0
  48. package/dist/agi/features/autonomous-assistant.d.ts.map +1 -0
  49. package/dist/agi/features/browser-use.d.ts +479 -0
  50. package/dist/agi/features/browser-use.d.ts.map +1 -0
  51. package/dist/agi/features/claude-code.d.ts +824 -0
  52. package/dist/agi/features/claude-code.d.ts.map +1 -0
  53. package/dist/agi/features/conversation-history.d.ts +245 -0
  54. package/dist/agi/features/conversation-history.d.ts.map +1 -0
  55. package/dist/agi/features/conversation.d.ts +464 -0
  56. package/dist/agi/features/conversation.d.ts.map +1 -0
  57. package/dist/agi/features/docs-reader.d.ts +72 -0
  58. package/dist/agi/features/docs-reader.d.ts.map +1 -0
  59. package/dist/agi/features/file-tools.d.ts +110 -0
  60. package/dist/agi/features/file-tools.d.ts.map +1 -0
  61. package/dist/agi/features/luca-coder.d.ts +323 -0
  62. package/dist/agi/features/luca-coder.d.ts.map +1 -0
  63. package/dist/agi/features/openai-codex.d.ts +381 -0
  64. package/dist/agi/features/openai-codex.d.ts.map +1 -0
  65. package/dist/agi/features/openapi.d.ts +200 -0
  66. package/dist/agi/features/openapi.d.ts.map +1 -0
  67. package/dist/agi/features/skills-library.d.ts +167 -0
  68. package/dist/agi/features/skills-library.d.ts.map +1 -0
  69. package/dist/agi/index.d.ts +5 -0
  70. package/dist/agi/index.d.ts.map +1 -0
  71. package/dist/agi/lib/interceptor-chain.d.ts +44 -0
  72. package/dist/agi/lib/interceptor-chain.d.ts.map +1 -0
  73. package/dist/agi/lib/token-counter.d.ts +13 -0
  74. package/dist/agi/lib/token-counter.d.ts.map +1 -0
  75. package/dist/bootstrap/generated.d.ts +5 -0
  76. package/dist/bootstrap/generated.d.ts.map +1 -0
  77. package/dist/browser.d.ts +12 -0
  78. package/dist/browser.d.ts.map +1 -0
  79. package/dist/bus.d.ts +29 -0
  80. package/dist/bus.d.ts.map +1 -0
  81. package/dist/cli/build-info.d.ts +4 -0
  82. package/dist/cli/build-info.d.ts.map +1 -0
  83. package/dist/cli/cli.d.ts +3 -12
  84. package/dist/cli/cli.d.ts.map +1 -0
  85. package/dist/client.d.ts +60 -0
  86. package/dist/client.d.ts.map +1 -0
  87. package/dist/clients/civitai/index.d.ts +472 -0
  88. package/dist/clients/civitai/index.d.ts.map +1 -0
  89. package/dist/clients/client-template.d.ts +30 -0
  90. package/dist/clients/client-template.d.ts.map +1 -0
  91. package/dist/clients/comfyui/index.d.ts +281 -0
  92. package/dist/clients/comfyui/index.d.ts.map +1 -0
  93. package/dist/clients/elevenlabs/index.d.ts +197 -0
  94. package/dist/clients/elevenlabs/index.d.ts.map +1 -0
  95. package/dist/clients/graph.d.ts +64 -0
  96. package/dist/clients/graph.d.ts.map +1 -0
  97. package/dist/clients/openai/index.d.ts +247 -0
  98. package/dist/clients/openai/index.d.ts.map +1 -0
  99. package/dist/clients/rest.d.ts +92 -0
  100. package/dist/clients/rest.d.ts.map +1 -0
  101. package/dist/clients/supabase/index.d.ts +176 -0
  102. package/dist/clients/supabase/index.d.ts.map +1 -0
  103. package/dist/clients/websocket.d.ts +127 -0
  104. package/dist/clients/websocket.d.ts.map +1 -0
  105. package/dist/command.d.ts +163 -0
  106. package/dist/command.d.ts.map +1 -0
  107. package/dist/commands/bootstrap.d.ts +20 -0
  108. package/dist/commands/bootstrap.d.ts.map +1 -0
  109. package/dist/commands/chat.d.ts +37 -0
  110. package/dist/commands/chat.d.ts.map +1 -0
  111. package/dist/commands/code.d.ts +28 -0
  112. package/dist/commands/code.d.ts.map +1 -0
  113. package/dist/commands/console.d.ts +22 -0
  114. package/dist/commands/console.d.ts.map +1 -0
  115. package/dist/commands/describe.d.ts +50 -0
  116. package/dist/commands/describe.d.ts.map +1 -0
  117. package/dist/commands/eval.d.ts +23 -0
  118. package/dist/commands/eval.d.ts.map +1 -0
  119. package/dist/commands/help.d.ts +25 -0
  120. package/dist/commands/help.d.ts.map +1 -0
  121. package/dist/commands/index.d.ts +18 -0
  122. package/dist/commands/index.d.ts.map +1 -0
  123. package/dist/commands/introspect.d.ts +24 -0
  124. package/dist/commands/introspect.d.ts.map +1 -0
  125. package/dist/commands/mcp.d.ts +35 -0
  126. package/dist/commands/mcp.d.ts.map +1 -0
  127. package/dist/commands/prompt.d.ts +38 -0
  128. package/dist/commands/prompt.d.ts.map +1 -0
  129. package/dist/commands/run.d.ts +24 -0
  130. package/dist/commands/run.d.ts.map +1 -0
  131. package/dist/commands/sandbox-mcp.d.ts +34 -0
  132. package/dist/commands/sandbox-mcp.d.ts.map +1 -0
  133. package/dist/commands/save-api-docs.d.ts +21 -0
  134. package/dist/commands/save-api-docs.d.ts.map +1 -0
  135. package/dist/commands/scaffold.d.ts +24 -0
  136. package/dist/commands/scaffold.d.ts.map +1 -0
  137. package/dist/commands/select.d.ts +22 -0
  138. package/dist/commands/select.d.ts.map +1 -0
  139. package/dist/commands/serve.d.ts +29 -0
  140. package/dist/commands/serve.d.ts.map +1 -0
  141. package/dist/container-describer.d.ts +144 -0
  142. package/dist/container-describer.d.ts.map +1 -0
  143. package/dist/container.d.ts +451 -0
  144. package/dist/container.d.ts.map +1 -0
  145. package/dist/endpoint.d.ts +113 -0
  146. package/dist/endpoint.d.ts.map +1 -0
  147. package/dist/feature.d.ts +47 -0
  148. package/dist/feature.d.ts.map +1 -0
  149. package/dist/graft.d.ts +29 -0
  150. package/dist/graft.d.ts.map +1 -0
  151. package/dist/hash-object.d.ts +8 -0
  152. package/dist/hash-object.d.ts.map +1 -0
  153. package/dist/helper.d.ts +209 -0
  154. package/dist/helper.d.ts.map +1 -0
  155. package/dist/introspection/generated.node.d.ts +44623 -0
  156. package/dist/introspection/generated.node.d.ts.map +1 -0
  157. package/dist/introspection/generated.web.d.ts +1412 -0
  158. package/dist/introspection/generated.web.d.ts.map +1 -0
  159. package/dist/introspection/index.d.ts +156 -0
  160. package/dist/introspection/index.d.ts.map +1 -0
  161. package/dist/introspection/scan.d.ts +147 -0
  162. package/dist/introspection/scan.d.ts.map +1 -0
  163. package/dist/node/container.d.ts +256 -0
  164. package/dist/node/container.d.ts.map +1 -0
  165. package/dist/node/feature.d.ts +9 -0
  166. package/dist/node/feature.d.ts.map +1 -0
  167. package/dist/node/features/container-link.d.ts +213 -0
  168. package/dist/node/features/container-link.d.ts.map +1 -0
  169. package/dist/node/features/content-db.d.ts +354 -0
  170. package/dist/node/features/content-db.d.ts.map +1 -0
  171. package/dist/node/features/disk-cache.d.ts +236 -0
  172. package/dist/node/features/disk-cache.d.ts.map +1 -0
  173. package/dist/node/features/dns.d.ts +511 -0
  174. package/dist/node/features/dns.d.ts.map +1 -0
  175. package/dist/node/features/docker.d.ts +485 -0
  176. package/dist/node/features/docker.d.ts.map +1 -0
  177. package/dist/node/features/downloader.d.ts +73 -0
  178. package/dist/node/features/downloader.d.ts.map +1 -0
  179. package/dist/node/features/figlet-fonts.d.ts +4 -0
  180. package/dist/node/features/figlet-fonts.d.ts.map +1 -0
  181. package/dist/node/features/file-manager.d.ts +177 -0
  182. package/dist/node/features/file-manager.d.ts.map +1 -0
  183. package/dist/node/features/fs.d.ts +635 -0
  184. package/dist/node/features/fs.d.ts.map +1 -0
  185. package/dist/node/features/git.d.ts +329 -0
  186. package/dist/node/features/git.d.ts.map +1 -0
  187. package/dist/node/features/google-auth.d.ts +200 -0
  188. package/dist/node/features/google-auth.d.ts.map +1 -0
  189. package/dist/node/features/google-calendar.d.ts +194 -0
  190. package/dist/node/features/google-calendar.d.ts.map +1 -0
  191. package/dist/node/features/google-docs.d.ts +138 -0
  192. package/dist/node/features/google-docs.d.ts.map +1 -0
  193. package/dist/node/features/google-drive.d.ts +202 -0
  194. package/dist/node/features/google-drive.d.ts.map +1 -0
  195. package/dist/node/features/google-mail.d.ts +221 -0
  196. package/dist/node/features/google-mail.d.ts.map +1 -0
  197. package/dist/node/features/google-sheets.d.ts +157 -0
  198. package/dist/node/features/google-sheets.d.ts.map +1 -0
  199. package/dist/node/features/grep.d.ts +207 -0
  200. package/dist/node/features/grep.d.ts.map +1 -0
  201. package/dist/node/features/helpers.d.ts +236 -0
  202. package/dist/node/features/helpers.d.ts.map +1 -0
  203. package/dist/node/features/ink.d.ts +332 -0
  204. package/dist/node/features/ink.d.ts.map +1 -0
  205. package/dist/node/features/ipc-socket.d.ts +298 -0
  206. package/dist/node/features/ipc-socket.d.ts.map +1 -0
  207. package/dist/node/features/json-tree.d.ts +140 -0
  208. package/dist/node/features/json-tree.d.ts.map +1 -0
  209. package/dist/node/features/networking.d.ts +373 -0
  210. package/dist/node/features/networking.d.ts.map +1 -0
  211. package/dist/node/features/nlp.d.ts +125 -0
  212. package/dist/node/features/nlp.d.ts.map +1 -0
  213. package/dist/node/features/opener.d.ts +93 -0
  214. package/dist/node/features/opener.d.ts.map +1 -0
  215. package/dist/node/features/os.d.ts +168 -0
  216. package/dist/node/features/os.d.ts.map +1 -0
  217. package/dist/node/features/package-finder.d.ts +419 -0
  218. package/dist/node/features/package-finder.d.ts.map +1 -0
  219. package/dist/node/features/postgres.d.ts +173 -0
  220. package/dist/node/features/postgres.d.ts.map +1 -0
  221. package/dist/node/features/proc.d.ts +285 -0
  222. package/dist/node/features/proc.d.ts.map +1 -0
  223. package/dist/node/features/process-manager.d.ts +427 -0
  224. package/dist/node/features/process-manager.d.ts.map +1 -0
  225. package/dist/node/features/python.d.ts +477 -0
  226. package/dist/node/features/python.d.ts.map +1 -0
  227. package/dist/node/features/redis.d.ts +247 -0
  228. package/dist/node/features/redis.d.ts.map +1 -0
  229. package/dist/node/features/repl.d.ts +84 -0
  230. package/dist/node/features/repl.d.ts.map +1 -0
  231. package/dist/node/features/runpod.d.ts +527 -0
  232. package/dist/node/features/runpod.d.ts.map +1 -0
  233. package/dist/node/features/secure-shell.d.ts +145 -0
  234. package/dist/node/features/secure-shell.d.ts.map +1 -0
  235. package/dist/node/features/semantic-search.d.ts +207 -0
  236. package/dist/node/features/semantic-search.d.ts.map +1 -0
  237. package/dist/node/features/sqlite.d.ts +180 -0
  238. package/dist/node/features/sqlite.d.ts.map +1 -0
  239. package/dist/node/features/telegram.d.ts +173 -0
  240. package/dist/node/features/telegram.d.ts.map +1 -0
  241. package/dist/node/features/transpiler.d.ts +51 -0
  242. package/dist/node/features/transpiler.d.ts.map +1 -0
  243. package/dist/node/features/tts.d.ts +108 -0
  244. package/dist/node/features/tts.d.ts.map +1 -0
  245. package/dist/node/features/ui.d.ts +562 -0
  246. package/dist/node/features/ui.d.ts.map +1 -0
  247. package/dist/node/features/vault.d.ts +90 -0
  248. package/dist/node/features/vault.d.ts.map +1 -0
  249. package/dist/node/features/vm.d.ts +285 -0
  250. package/dist/node/features/vm.d.ts.map +1 -0
  251. package/dist/node/features/yaml-tree.d.ts +118 -0
  252. package/dist/node/features/yaml-tree.d.ts.map +1 -0
  253. package/dist/node/features/yaml.d.ts +127 -0
  254. package/dist/node/features/yaml.d.ts.map +1 -0
  255. package/dist/node.d.ts +67 -0
  256. package/dist/node.d.ts.map +1 -0
  257. package/dist/python/generated.d.ts +2 -0
  258. package/dist/python/generated.d.ts.map +1 -0
  259. package/dist/react/index.d.ts +36 -0
  260. package/dist/react/index.d.ts.map +1 -0
  261. package/dist/registry.d.ts +97 -0
  262. package/dist/registry.d.ts.map +1 -0
  263. package/dist/scaffolds/generated.d.ts +13 -0
  264. package/dist/scaffolds/generated.d.ts.map +1 -0
  265. package/dist/scaffolds/template.d.ts +11 -0
  266. package/dist/scaffolds/template.d.ts.map +1 -0
  267. package/dist/schemas/base.d.ts +254 -0
  268. package/dist/schemas/base.d.ts.map +1 -0
  269. package/dist/selector.d.ts +130 -0
  270. package/dist/selector.d.ts.map +1 -0
  271. package/dist/server.d.ts +89 -0
  272. package/dist/server.d.ts.map +1 -0
  273. package/dist/servers/express.d.ts +104 -0
  274. package/dist/servers/express.d.ts.map +1 -0
  275. package/dist/servers/mcp.d.ts +201 -0
  276. package/dist/servers/mcp.d.ts.map +1 -0
  277. package/dist/servers/socket.d.ts +121 -0
  278. package/dist/servers/socket.d.ts.map +1 -0
  279. package/dist/state.d.ts +24 -0
  280. package/dist/state.d.ts.map +1 -0
  281. package/dist/web/clients/socket.d.ts +37 -0
  282. package/dist/web/clients/socket.d.ts.map +1 -0
  283. package/dist/web/container.d.ts +55 -0
  284. package/dist/web/container.d.ts.map +1 -0
  285. package/dist/web/extension.d.ts +4 -0
  286. package/dist/web/extension.d.ts.map +1 -0
  287. package/dist/web/feature.d.ts +8 -0
  288. package/dist/web/feature.d.ts.map +1 -0
  289. package/dist/web/features/asset-loader.d.ts +35 -0
  290. package/dist/web/features/asset-loader.d.ts.map +1 -0
  291. package/dist/web/features/container-link.d.ts +167 -0
  292. package/dist/web/features/container-link.d.ts.map +1 -0
  293. package/dist/web/features/esbuild.d.ts +51 -0
  294. package/dist/web/features/esbuild.d.ts.map +1 -0
  295. package/dist/web/features/helpers.d.ts +140 -0
  296. package/dist/web/features/helpers.d.ts.map +1 -0
  297. package/dist/web/features/network.d.ts +69 -0
  298. package/dist/web/features/network.d.ts.map +1 -0
  299. package/dist/web/features/speech.d.ts +71 -0
  300. package/dist/web/features/speech.d.ts.map +1 -0
  301. package/dist/web/features/vault.d.ts +62 -0
  302. package/dist/web/features/vault.d.ts.map +1 -0
  303. package/dist/web/features/vm.d.ts +48 -0
  304. package/dist/web/features/vm.d.ts.map +1 -0
  305. package/dist/web/features/voice-recognition.d.ts +96 -0
  306. package/dist/web/features/voice-recognition.d.ts.map +1 -0
  307. package/dist/web/shims/isomorphic-vm.d.ts +22 -0
  308. package/dist/web/shims/isomorphic-vm.d.ts.map +1 -0
  309. package/index.html +1457 -0
  310. package/index.ts +1 -0
  311. package/install.sh +84 -0
  312. package/luca.cli.ts +16 -0
  313. package/luca.console.ts +9 -0
  314. package/main.py +6 -0
  315. package/package.json +219 -58
  316. package/public/index.html +1457 -0
  317. package/public/slides-ai-native.html +902 -0
  318. package/public/slides-intro.html +974 -0
  319. package/pyproject.toml +7 -0
  320. package/scripts/build-web.ts +28 -0
  321. package/scripts/examples/ask-luca-expert.ts +42 -0
  322. package/scripts/examples/assistant-questions.ts +12 -0
  323. package/scripts/examples/excalidraw-expert.ts +75 -0
  324. package/scripts/examples/expert-chat.ts +0 -0
  325. package/scripts/examples/file-manager.ts +14 -0
  326. package/scripts/examples/ideas.ts +12 -0
  327. package/scripts/examples/interactive-chat.ts +20 -0
  328. package/scripts/examples/openai-tool-calls.ts +113 -0
  329. package/scripts/examples/opening-a-web-browser.ts +5 -0
  330. package/scripts/examples/telegram-bot.ts +79 -0
  331. package/scripts/examples/using-assistant-with-mcp.ts +555 -0
  332. package/scripts/examples/using-claude-code.ts +10 -0
  333. package/scripts/examples/using-contentdb.ts +35 -0
  334. package/scripts/examples/using-conversations.ts +35 -0
  335. package/scripts/examples/using-disk-cache.ts +10 -0
  336. package/scripts/examples/using-docker-shell.ts +75 -0
  337. package/scripts/examples/using-elevenlabs.ts +25 -0
  338. package/scripts/examples/using-google-calendar.ts +57 -0
  339. package/scripts/examples/using-google-docs.ts +74 -0
  340. package/scripts/examples/using-google-drive.ts +74 -0
  341. package/scripts/examples/using-google-sheets.ts +89 -0
  342. package/scripts/examples/using-nlp.ts +55 -0
  343. package/scripts/examples/using-ollama.ts +11 -0
  344. package/scripts/examples/using-postgres.ts +55 -0
  345. package/scripts/examples/using-runpod.ts +32 -0
  346. package/scripts/examples/using-tts.ts +40 -0
  347. package/scripts/scaffold.ts +391 -0
  348. package/scripts/scratch.ts +15 -0
  349. package/scripts/stamp-build.sh +12 -0
  350. package/scripts/test-assistant-hooks.ts +13 -0
  351. package/scripts/test-docs-reader.ts +10 -0
  352. package/scripts/test-linux-binary.sh +80 -0
  353. package/scripts/update-introspection-data.ts +58 -0
  354. package/src/agi/README.md +14 -0
  355. package/src/agi/container.server.ts +156 -0
  356. package/src/agi/feature.ts +13 -0
  357. package/src/agi/features/agent-memory.ts +694 -0
  358. package/src/agi/features/assistant.ts +1653 -0
  359. package/src/agi/features/assistants-manager.ts +534 -0
  360. package/src/agi/features/autonomous-assistant.ts +431 -0
  361. package/src/agi/features/browser-use.ts +672 -0
  362. package/src/agi/features/claude-code.ts +1584 -0
  363. package/src/agi/features/coding-tools.ts +175 -0
  364. package/src/agi/features/conversation-history.ts +672 -0
  365. package/src/agi/features/conversation.ts +1494 -0
  366. package/src/agi/features/docs-reader.ts +167 -0
  367. package/src/agi/features/file-tools.ts +340 -0
  368. package/src/agi/features/luca-coder.ts +641 -0
  369. package/src/agi/features/mcp-bridge.ts +532 -0
  370. package/src/agi/features/openai-codex.ts +651 -0
  371. package/src/agi/features/openapi.ts +445 -0
  372. package/src/agi/features/skills-library.ts +557 -0
  373. package/src/agi/index.ts +6 -0
  374. package/src/agi/lib/interceptor-chain.ts +89 -0
  375. package/src/agi/lib/token-counter.ts +202 -0
  376. package/src/bootstrap/generated.ts +9791 -0
  377. package/src/browser.ts +25 -0
  378. package/src/bus.ts +122 -0
  379. package/src/cli/build-info.ts +4 -0
  380. package/src/cli/cli.ts +355 -0
  381. package/src/client.ts +170 -0
  382. package/src/clients/civitai/index.ts +537 -0
  383. package/src/clients/client-template.ts +41 -0
  384. package/src/clients/comfyui/index.ts +604 -0
  385. package/src/clients/elevenlabs/index.ts +317 -0
  386. package/src/clients/graph.ts +87 -0
  387. package/src/clients/openai/index.ts +456 -0
  388. package/src/clients/rest.ts +207 -0
  389. package/src/clients/supabase/index.ts +357 -0
  390. package/src/clients/voicebox/index.ts +300 -0
  391. package/src/clients/websocket.ts +251 -0
  392. package/src/command.ts +506 -0
  393. package/src/commands/bootstrap.ts +244 -0
  394. package/src/commands/chat.ts +309 -0
  395. package/src/commands/code.ts +371 -0
  396. package/src/commands/console.ts +189 -0
  397. package/src/commands/describe.ts +243 -0
  398. package/src/commands/eval.ts +67 -0
  399. package/src/commands/help.ts +240 -0
  400. package/src/commands/index.ts +19 -0
  401. package/src/commands/introspect.ts +218 -0
  402. package/src/commands/mcp.ts +64 -0
  403. package/src/commands/prompt.ts +1014 -0
  404. package/src/commands/run.ts +278 -0
  405. package/src/commands/sandbox-mcp.ts +343 -0
  406. package/src/commands/save-api-docs.ts +51 -0
  407. package/src/commands/scaffold.ts +225 -0
  408. package/src/commands/select.ts +99 -0
  409. package/src/commands/serve.ts +208 -0
  410. package/src/container-describer.ts +1091 -0
  411. package/src/container.ts +1199 -0
  412. package/src/endpoint.ts +365 -0
  413. package/src/entity.ts +173 -0
  414. package/src/feature.ts +118 -0
  415. package/src/graft.ts +181 -0
  416. package/src/hash-object.ts +97 -0
  417. package/src/helper.ts +849 -0
  418. package/src/introspection/generated.agi.ts +41200 -0
  419. package/src/introspection/generated.node.ts +28773 -0
  420. package/src/introspection/generated.web.ts +2272 -0
  421. package/src/introspection/index.ts +296 -0
  422. package/src/introspection/scan.ts +1136 -0
  423. package/src/node/container.ts +409 -0
  424. package/src/node/feature.ts +13 -0
  425. package/src/node/features/container-link.ts +559 -0
  426. package/src/node/features/content-db.ts +849 -0
  427. package/src/node/features/disk-cache.ts +388 -0
  428. package/src/node/features/display-result.ts +57 -0
  429. package/src/node/features/dns.ts +669 -0
  430. package/src/node/features/docker.ts +921 -0
  431. package/src/node/features/downloader.ts +79 -0
  432. package/src/node/features/figlet-fonts.ts +600 -0
  433. package/src/node/features/file-manager.ts +535 -0
  434. package/src/node/features/fs.ts +1050 -0
  435. package/src/node/features/git.ts +592 -0
  436. package/src/node/features/google-auth.ts +504 -0
  437. package/src/node/features/google-calendar.ts +306 -0
  438. package/src/node/features/google-docs.ts +412 -0
  439. package/src/node/features/google-drive.ts +346 -0
  440. package/src/node/features/google-mail.ts +540 -0
  441. package/src/node/features/google-sheets.ts +286 -0
  442. package/src/node/features/grep.ts +427 -0
  443. package/src/node/features/helpers.ts +762 -0
  444. package/src/node/features/ink.ts +490 -0
  445. package/src/node/features/ipc-socket.ts +649 -0
  446. package/src/node/features/json-tree.ts +170 -0
  447. package/src/node/features/networking.ts +961 -0
  448. package/src/node/features/nlp.ts +212 -0
  449. package/src/node/features/opener.ts +180 -0
  450. package/src/node/features/os.ts +403 -0
  451. package/src/node/features/package-finder.ts +540 -0
  452. package/src/node/features/postgres.ts +289 -0
  453. package/src/node/features/proc.ts +503 -0
  454. package/src/node/features/process-manager.ts +844 -0
  455. package/src/node/features/python.ts +912 -0
  456. package/src/node/features/redis.ts +446 -0
  457. package/src/node/features/repl.ts +212 -0
  458. package/src/node/features/runpod.ts +811 -0
  459. package/src/node/features/secure-shell.ts +261 -0
  460. package/src/node/features/semantic-search.ts +935 -0
  461. package/src/node/features/sqlite.ts +289 -0
  462. package/src/node/features/telegram.ts +343 -0
  463. package/src/node/features/transpiler.ts +160 -0
  464. package/src/node/features/tts.ts +185 -0
  465. package/src/node/features/ui.ts +791 -0
  466. package/src/node/features/vault.ts +153 -0
  467. package/src/node/features/vm.ts +462 -0
  468. package/src/node/features/yaml-tree.ts +148 -0
  469. package/src/node/features/yaml.ts +133 -0
  470. package/src/node.ts +76 -0
  471. package/src/python/bridge.py +220 -0
  472. package/src/python/generated.ts +226 -0
  473. package/src/react/index.ts +175 -0
  474. package/src/registry.ts +210 -0
  475. package/src/scaffolds/generated.ts +1814 -0
  476. package/src/scaffolds/template.ts +46 -0
  477. package/src/schemas/base.ts +296 -0
  478. package/src/selector.ts +352 -0
  479. package/src/server.ts +229 -0
  480. package/src/servers/express.ts +283 -0
  481. package/src/servers/mcp.ts +802 -0
  482. package/src/servers/socket.ts +258 -0
  483. package/src/state.ts +101 -0
  484. package/src/web/clients/socket.ts +99 -0
  485. package/src/web/container.ts +75 -0
  486. package/src/web/extension.ts +30 -0
  487. package/src/web/feature.ts +12 -0
  488. package/src/web/features/asset-loader.ts +72 -0
  489. package/src/web/features/container-link.ts +382 -0
  490. package/src/web/features/esbuild.ts +93 -0
  491. package/src/web/features/helpers.ts +291 -0
  492. package/src/web/features/network.ts +85 -0
  493. package/src/web/features/speech.ts +104 -0
  494. package/src/web/features/vault.ts +207 -0
  495. package/src/web/features/vm.ts +85 -0
  496. package/src/web/features/voice-recognition.ts +161 -0
  497. package/src/web/shims/isomorphic-vm.ts +149 -0
  498. package/tsconfig.build.json +12 -0
  499. package/tsconfig.json +58 -0
  500. package/uv.lock +8 -0
  501. package/LICENSE +0 -21
  502. package/dist/cli/cli.js +0 -48
  503. package/dist/cli/common.d.ts +0 -2
  504. package/dist/cli/common.js +0 -6
  505. package/dist/cli/index.d.ts +0 -2
  506. package/dist/cli/index.js +0 -5
  507. package/dist/cli/run.d.ts +0 -1
  508. package/dist/cli/run.js +0 -38
  509. package/dist/core/index.d.ts +0 -4
  510. package/dist/core/index.js +0 -32
  511. package/dist/core/read.d.ts +0 -2
  512. package/dist/core/read.js +0 -29
  513. package/dist/core/request.d.ts +0 -1
  514. package/dist/core/request.js +0 -2
  515. package/dist/core/write.d.ts +0 -2
  516. package/dist/core/write.js +0 -21
  517. package/dist/index.d.ts +0 -1
  518. package/dist/index.js +0 -5
  519. package/dist/utils/common.d.ts +0 -9
  520. package/dist/utils/common.js +0 -57
  521. package/dist/utils/consts.d.ts +0 -3
  522. package/dist/utils/consts.js +0 -11
  523. package/dist/utils/dict.d.ts +0 -1
  524. package/dist/utils/dict.js +0 -7
  525. package/dist/utils/index.d.ts +0 -5
  526. package/dist/utils/index.js +0 -21
  527. package/dist/utils/log.d.ts +0 -1
  528. package/dist/utils/log.js +0 -5
  529. package/dist/utils/types.d.ts +0 -1
  530. package/dist/utils/types.js +0 -2
  531. package/dist/utils/utils.test.d.ts +0 -1
  532. package/dist/utils/utils.test.js +0 -7
@@ -0,0 +1,961 @@
1
+ import { z } from 'zod'
2
+ import net from 'net'
3
+ import detectPort from 'detect-port'
4
+ import { Feature } from '../feature.js'
5
+ import { FeatureStateSchema, FeatureOptionsSchema, FeatureEventsSchema } from '../../schemas/base.js'
6
+
7
+ const MAX_CIDR_HOSTS = 65536
8
+
9
+ const PORT_SERVICE_MAP: Record<number, string> = {
10
+ 20: 'ftp-data',
11
+ 21: 'ftp',
12
+ 22: 'ssh',
13
+ 23: 'telnet',
14
+ 25: 'smtp',
15
+ 53: 'dns',
16
+ 67: 'dhcp',
17
+ 68: 'dhcp',
18
+ 69: 'tftp',
19
+ 80: 'http',
20
+ 110: 'pop3',
21
+ 111: 'rpcbind',
22
+ 119: 'nntp',
23
+ 123: 'ntp',
24
+ 135: 'msrpc',
25
+ 137: 'netbios-ns',
26
+ 138: 'netbios-dgm',
27
+ 139: 'netbios-ssn',
28
+ 143: 'imap',
29
+ 161: 'snmp',
30
+ 389: 'ldap',
31
+ 443: 'https',
32
+ 445: 'microsoft-ds',
33
+ 465: 'smtps',
34
+ 514: 'syslog',
35
+ 515: 'printer',
36
+ 543: 'kerberos',
37
+ 587: 'submission',
38
+ 631: 'ipp',
39
+ 636: 'ldaps',
40
+ 873: 'rsync',
41
+ 993: 'imaps',
42
+ 995: 'pop3s',
43
+ 1080: 'socks',
44
+ 1194: 'openvpn',
45
+ 1433: 'ms-sql',
46
+ 1521: 'oracle',
47
+ 1723: 'pptp',
48
+ 1883: 'mqtt',
49
+ 2049: 'nfs',
50
+ 2375: 'docker',
51
+ 2376: 'docker-tls',
52
+ 3000: 'http-alt',
53
+ 3306: 'mysql',
54
+ 3389: 'rdp',
55
+ 4000: 'http-alt',
56
+ 5000: 'http-alt',
57
+ 5432: 'postgresql',
58
+ 5672: 'amqp',
59
+ 5900: 'vnc',
60
+ 5985: 'winrm',
61
+ 5986: 'winrm-https',
62
+ 6379: 'redis',
63
+ 7001: 'http-alt',
64
+ 8080: 'http-proxy',
65
+ 8081: 'http-alt',
66
+ 8443: 'https-alt',
67
+ 9000: 'http-alt',
68
+ 9092: 'kafka',
69
+ 9200: 'elasticsearch',
70
+ 11211: 'memcached',
71
+ 27017: 'mongodb',
72
+ }
73
+
74
+ const DEFAULT_PORTS = [22, 80, 443, 3000, 3306, 5432, 6379, 8080, 8443]
75
+
76
+ export const LocalNetworkSchema = z.object({
77
+ interface: z.string().describe('Network interface name'),
78
+ address: z.string().describe('IPv4 address for this interface'),
79
+ netmask: z.string().describe('IPv4 netmask'),
80
+ cidr: z.string().describe('Derived CIDR range for this interface'),
81
+ mac: z.string().optional().describe('MAC address for this interface'),
82
+ })
83
+ export type LocalNetwork = z.infer<typeof LocalNetworkSchema>
84
+
85
+ export const ArpEntrySchema = z.object({
86
+ ip: z.string().describe('IP address from ARP table'),
87
+ mac: z.string().optional().describe('MAC address from ARP table'),
88
+ interface: z.string().optional().describe('Interface name if available'),
89
+ })
90
+ export type ArpEntry = z.infer<typeof ArpEntrySchema>
91
+
92
+ export const DiscoverHostSchema = z.object({
93
+ ip: z.string().describe('Host IP address'),
94
+ mac: z.string().optional().describe('MAC address when available'),
95
+ reachable: z.boolean().describe('Whether host appears reachable'),
96
+ method: z.enum(['arp', 'tcp']).describe('Discovery method used for this host'),
97
+ })
98
+ export type DiscoverHost = z.infer<typeof DiscoverHostSchema>
99
+
100
+ export const PortScanResultSchema = z.object({
101
+ port: z.number().int().min(1).max(65535).describe('Port number'),
102
+ status: z.enum(['open', 'closed', 'filtered']).describe('TCP connect scan status'),
103
+ service: z.string().optional().describe('Best-effort service guess by port'),
104
+ banner: z.string().optional().describe('Banner text when captured'),
105
+ })
106
+ export type PortScanResult = z.infer<typeof PortScanResultSchema>
107
+
108
+ export const LocalNetworkScanHostSchema = z.object({
109
+ ip: z.string().describe('Host IP address'),
110
+ mac: z.string().optional().describe('MAC address when available'),
111
+ hostname: z.string().optional().describe('Hostname when available'),
112
+ openPorts: z.array(PortScanResultSchema).describe('Open ports discovered for this host'),
113
+ })
114
+ export type LocalNetworkScanHost = z.infer<typeof LocalNetworkScanHostSchema>
115
+
116
+ export const NetworkSnapshotSchema = z.object({
117
+ cidr: z.string().describe('Scanned CIDR range'),
118
+ hosts: z.array(LocalNetworkScanHostSchema).describe('Hosts discovered in this CIDR'),
119
+ })
120
+ export type NetworkSnapshot = z.infer<typeof NetworkSnapshotSchema>
121
+
122
+ export const NetworkingEventsSchema = FeatureEventsSchema.extend({
123
+ 'host:discovered': z.tuple([DiscoverHostSchema.describe('The discovered host')]).describe('When a host is found during network scanning'),
124
+ 'port:open': z.tuple([z.object({
125
+ host: z.string().describe('Host IP address'),
126
+ port: z.number().describe('Open port number'),
127
+ service: z.string().optional().describe('Best-effort service name'),
128
+ banner: z.string().optional().describe('Banner text when captured'),
129
+ }).describe('Open port details')]).describe('When an open port is detected on a host'),
130
+ 'scan:start': z.tuple([z.object({
131
+ target: z.string().describe('Scan target identifier'),
132
+ type: z.string().describe('Scan type identifier'),
133
+ }).describe('Scan start details')]).describe('When a network scan begins'),
134
+ 'scan:complete': z.tuple([z.object({
135
+ target: z.string().describe('Scan target identifier'),
136
+ type: z.string().describe('Scan type identifier'),
137
+ duration: z.number().describe('Scan duration in milliseconds'),
138
+ hostsFound: z.number().describe('Number of hosts discovered'),
139
+ portsFound: z.number().describe('Number of open ports discovered'),
140
+ }).describe('Scan completion details')]).describe('When a network scan finishes'),
141
+ }).describe('Networking events')
142
+
143
+ export const NetworkingStateSchema = FeatureStateSchema.extend({
144
+ lastScan: z.object({
145
+ timestamp: z.number().describe('Unix epoch timestamp in ms'),
146
+ target: z.string().describe('Primary scan target identifier'),
147
+ type: z.string().describe('Scan type identifier'),
148
+ networks: z.array(NetworkSnapshotSchema).describe('Last known scan results'),
149
+ }).optional().describe('The most recent network scan result'),
150
+ })
151
+ export type NetworkingState = z.infer<typeof NetworkingStateSchema>
152
+
153
+ export const NetworkingOptionsSchema = FeatureOptionsSchema.extend({
154
+ timeout: z.number().optional().describe('Default timeout in milliseconds for probing'),
155
+ concurrency: z.number().optional().describe('Default concurrency for scanning operations'),
156
+ })
157
+ export type NetworkingOptions = z.infer<typeof NetworkingOptionsSchema>
158
+
159
+ type ScanPortsOptions = {
160
+ ports?: string | number[]
161
+ timeout?: number
162
+ concurrency?: number
163
+ banner?: boolean
164
+ includeClosed?: boolean
165
+ }
166
+
167
+ type DiscoverHostsOptions = {
168
+ timeout?: number
169
+ concurrency?: number
170
+ ports?: number[]
171
+ }
172
+
173
+ type ReachableHostOptions = {
174
+ timeout?: number
175
+ ports?: number[]
176
+ }
177
+
178
+ type ScanLocalNetworksOptions = {
179
+ ports?: string | number[]
180
+ timeout?: number
181
+ concurrency?: number
182
+ hostConcurrency?: number
183
+ banner?: boolean
184
+ }
185
+
186
+ type NmapPort = {
187
+ port: number
188
+ state: string
189
+ protocol: string
190
+ service?: string
191
+ }
192
+
193
+ type NmapHost = {
194
+ ip: string
195
+ hostname?: string
196
+ status?: string
197
+ mac?: string
198
+ ports: NmapPort[]
199
+ }
200
+
201
+ /**
202
+ * The Networking feature provides utilities for network-related operations.
203
+ *
204
+ * This feature includes utilities for port detection and availability checking,
205
+ * which are commonly needed when setting up servers or network services.
206
+ *
207
+ * @example
208
+ * ```typescript
209
+ * const networking = container.feature('networking')
210
+ *
211
+ * // Find an available port starting from 3000
212
+ * const port = await networking.findOpenPort(3000)
213
+ * console.log(`Available port: ${port}`)
214
+ *
215
+ * // Check if a specific port is available
216
+ * const isAvailable = await networking.isPortOpen(8080)
217
+ * if (isAvailable) {
218
+ * console.log('Port 8080 is available')
219
+ * }
220
+ * ```
221
+ *
222
+ * @extends Feature
223
+ */
224
+ export class Networking extends Feature<NetworkingState, NetworkingOptions> {
225
+ static override shortcut = 'features.networking' as const
226
+ static override stateSchema = NetworkingStateSchema
227
+ static override optionsSchema = NetworkingOptionsSchema
228
+ static override eventsSchema = NetworkingEventsSchema
229
+ static { Feature.register(this, 'networking') }
230
+
231
+ override get initialState(): NetworkingState {
232
+ return {
233
+ ...super.initialState,
234
+ enabled: false,
235
+ lastScan: undefined,
236
+ }
237
+ }
238
+
239
+ private _binCache: Record<string, string> = {}
240
+
241
+ /** Resolve a binary path via `which`, caching the result. */
242
+ private resolveBin(name: string): string {
243
+ if (this._binCache[name]) return this._binCache[name]
244
+ try {
245
+ this._binCache[name] = this.proc.exec(`which ${name}`).trim()
246
+ } catch {
247
+ this._binCache[name] = name
248
+ }
249
+ return this._binCache[name]
250
+ }
251
+
252
+ get proc(): ReturnType<typeof this.container.feature<'proc'>> {
253
+ return this.container.feature('proc')
254
+ }
255
+
256
+ get os(): ReturnType<typeof this.container.feature<'os'>> {
257
+ return this.container.feature('os')
258
+ }
259
+
260
+ /**
261
+ * Finds the next available port starting from the specified port number.
262
+ *
263
+ * This method will search for the first available port starting from the given
264
+ * port number. If the specified port is available, it returns that port.
265
+ * Otherwise, it returns the next available port.
266
+ *
267
+ * @param {number} [startAt=0] - The port number to start searching from (0 means system will choose)
268
+ * @returns {Promise<number>} Promise that resolves to an available port number
269
+ *
270
+ * @example
271
+ * ```typescript
272
+ * // Find any available port
273
+ * const anyPort = await networking.findOpenPort()
274
+ *
275
+ * // Find an available port starting from 3000
276
+ * const port = await networking.findOpenPort(3000)
277
+ * console.log(`Server can use port: ${port}`)
278
+ * ```
279
+ */
280
+ async findOpenPort(startAt = 0) {
281
+ const nextPort = await detectPort(Number(startAt))
282
+ return nextPort
283
+ }
284
+
285
+ /**
286
+ * Checks if a specific port is available for use.
287
+ *
288
+ * This method attempts to detect if the specified port is available.
289
+ * It returns true if the port is available, false if it's already in use.
290
+ *
291
+ * @param {number} [checkPort=0] - The port number to check for availability
292
+ * @returns {Promise<boolean>} Promise that resolves to true if the port is available, false otherwise
293
+ *
294
+ * @example
295
+ * ```typescript
296
+ * // Check if port 8080 is available
297
+ * const isAvailable = await networking.isPortOpen(8080)
298
+ * if (isAvailable) {
299
+ * console.log('Port 8080 is free to use')
300
+ * } else {
301
+ * console.log('Port 8080 is already in use')
302
+ * }
303
+ * ```
304
+ */
305
+ async isPortOpen(checkPort = 0) {
306
+ const nextPort = await detectPort(Number(checkPort))
307
+ return nextPort && nextPort === Number(checkPort)
308
+ }
309
+
310
+ /**
311
+ * Returns local external IPv4 interfaces and their CIDR ranges.
312
+ */
313
+ getLocalNetworks(): LocalNetwork[] {
314
+ const interfaces = this.os.networkInterfaces as Record<string, Array<{
315
+ address: string
316
+ netmask: string
317
+ family: string | number
318
+ mac?: string
319
+ internal: boolean
320
+ }> | undefined>
321
+ const networks: LocalNetwork[] = []
322
+
323
+ for (const [interfaceName, details] of Object.entries(interfaces)) {
324
+ if (!details || details.length === 0) {
325
+ continue
326
+ }
327
+
328
+ for (const detail of details) {
329
+ if (!detail) {
330
+ continue
331
+ }
332
+
333
+ const family = typeof detail.family === 'string' ? detail.family : detail.family === 4 ? 'IPv4' : 'IPv6'
334
+ if (detail.internal || family !== 'IPv4') {
335
+ continue
336
+ }
337
+
338
+ const cidr = this.computeCidr(detail.address, detail.netmask)
339
+ networks.push({
340
+ interface: interfaceName,
341
+ address: detail.address,
342
+ netmask: detail.netmask,
343
+ cidr,
344
+ mac: detail.mac,
345
+ })
346
+ }
347
+ }
348
+
349
+ return networks
350
+ }
351
+
352
+ /**
353
+ * Expands a CIDR block to host IP addresses.
354
+ * For /31 and /32, all addresses are returned. For all others, network/broadcast are excluded.
355
+ */
356
+ expandCidr(cidr: string): string[] {
357
+ const [ipPart, maskPart] = cidr.split('/')
358
+ if (!ipPart || !maskPart) {
359
+ throw new Error(`Invalid CIDR: ${cidr}`)
360
+ }
361
+
362
+ const maskBits = Number(maskPart)
363
+ if (!Number.isInteger(maskBits) || maskBits < 0 || maskBits > 32) {
364
+ throw new Error(`Invalid CIDR mask: ${cidr}`)
365
+ }
366
+
367
+ const baseIpInt = this.ipToInt(ipPart)
368
+ const networkMask = maskBits === 0 ? 0 : (0xffffffff << (32 - maskBits)) >>> 0
369
+ const network = baseIpInt & networkMask
370
+ const broadcast = (network | (~networkMask >>> 0)) >>> 0
371
+ const totalAddresses = broadcast - network + 1
372
+
373
+ if (totalAddresses > MAX_CIDR_HOSTS) {
374
+ throw new Error(`CIDR ${cidr} is too large to expand safely (${totalAddresses} addresses)`)
375
+ }
376
+
377
+ const includeAll = maskBits >= 31
378
+ const start = includeAll ? network : network + 1
379
+ const end = includeAll ? broadcast : broadcast - 1
380
+ const hosts: string[] = []
381
+
382
+ if (end < start) {
383
+ return []
384
+ }
385
+
386
+ for (let current = start; current <= end; current += 1) {
387
+ hosts.push(this.intToIp(current >>> 0))
388
+ }
389
+
390
+ return hosts
391
+ }
392
+
393
+ /**
394
+ * Reads and parses the system ARP cache.
395
+ */
396
+ async getArpTable(): Promise<ArpEntry[]> {
397
+ const output = await this.proc.execAndCapture(`${this.resolveBin('arp')} -a`)
398
+ if (output.exitCode !== 0) {
399
+ return []
400
+ }
401
+
402
+ return this.parseArpOutput(output.stdout)
403
+ }
404
+
405
+ /**
406
+ * Performs a lightweight TCP reachability probe.
407
+ */
408
+ async isHostReachable(host: string, options: ReachableHostOptions = {}): Promise<boolean> {
409
+ const timeout = options.timeout ?? this.options.timeout ?? 1000
410
+ const ports = options.ports?.length ? options.ports : [80, 443]
411
+
412
+ for (const port of ports) {
413
+ const probe = await this.probeTcpPort(host, port, timeout, false)
414
+ if (probe.status === 'open') {
415
+ return true
416
+ }
417
+ }
418
+
419
+ return false
420
+ }
421
+
422
+ /**
423
+ * Discovers hosts in a CIDR range by combining ARP cache and TCP probes.
424
+ */
425
+ async discoverHosts(cidr: string, options: DiscoverHostsOptions = {}): Promise<DiscoverHost[]> {
426
+ const timeout = options.timeout ?? this.options.timeout ?? 1000
427
+ const concurrency = Math.max(1, options.concurrency ?? this.options.concurrency ?? 200)
428
+ const probePorts = options.ports?.length ? options.ports : [80, 443]
429
+ const allIps = this.expandCidr(cidr)
430
+ const arp = await this.getArpTable()
431
+ const arpByIp = new Map<string, ArpEntry>(arp.map(entry => [entry.ip, entry]))
432
+
433
+ this.emit('scan:start', { target: cidr, type: 'discoverHosts' })
434
+ const startTime = Date.now()
435
+
436
+ const discovered = await this.mapWithConcurrency(allIps, concurrency, async (ip): Promise<DiscoverHost | null> => {
437
+ const arpHit = arpByIp.get(ip)
438
+ if (arpHit) {
439
+ const host: DiscoverHost = {
440
+ ip,
441
+ mac: arpHit.mac,
442
+ reachable: true,
443
+ method: 'arp',
444
+ }
445
+ this.emit('host:discovered', host)
446
+ return host
447
+ }
448
+
449
+ const reachable = await this.isHostReachable(ip, { timeout, ports: probePorts })
450
+ if (!reachable) {
451
+ return null
452
+ }
453
+
454
+ const host: DiscoverHost = {
455
+ ip,
456
+ reachable: true,
457
+ method: 'tcp',
458
+ }
459
+ this.emit('host:discovered', host)
460
+ return host
461
+ })
462
+
463
+ const hosts = discovered.filter((value): value is DiscoverHost => value !== null)
464
+
465
+ this.setState({
466
+ lastScan: {
467
+ timestamp: Date.now(),
468
+ target: cidr,
469
+ type: 'discoverHosts',
470
+ networks: [
471
+ {
472
+ cidr,
473
+ hosts: hosts.map(host => ({
474
+ ip: host.ip,
475
+ mac: host.mac,
476
+ openPorts: [],
477
+ })),
478
+ },
479
+ ],
480
+ },
481
+ })
482
+
483
+ this.emit('scan:complete', {
484
+ target: cidr,
485
+ type: 'discoverHosts',
486
+ duration: Date.now() - startTime,
487
+ hostsFound: hosts.length,
488
+ portsFound: 0,
489
+ })
490
+
491
+ return hosts
492
+ }
493
+
494
+ /**
495
+ * TCP connect scan for a host. By default only returns open ports.
496
+ */
497
+ async scanPorts(host: string, options: ScanPortsOptions = {}): Promise<PortScanResult[]> {
498
+ const timeout = options.timeout ?? this.options.timeout ?? 2000
499
+ const concurrency = Math.max(1, options.concurrency ?? this.options.concurrency ?? 200)
500
+ const includeBanner = !!options.banner
501
+ const includeClosed = !!options.includeClosed
502
+ const ports = this.parsePortsOption(options.ports)
503
+
504
+ this.emit('scan:start', { target: host, type: 'scanPorts' })
505
+ const startTime = Date.now()
506
+
507
+ const scanned = await this.mapWithConcurrency(ports, concurrency, async (port): Promise<PortScanResult> => {
508
+ const probe = await this.probeTcpPort(host, port, timeout, includeBanner)
509
+ const result: PortScanResult = {
510
+ port,
511
+ status: probe.status,
512
+ }
513
+
514
+ if (probe.status === 'open') {
515
+ result.service = PORT_SERVICE_MAP[port] || 'unknown'
516
+ if (probe.banner) {
517
+ result.banner = probe.banner
518
+ }
519
+
520
+ this.emit('port:open', {
521
+ host,
522
+ port,
523
+ service: result.service,
524
+ banner: result.banner,
525
+ })
526
+ }
527
+
528
+ return result
529
+ })
530
+
531
+ const results = includeClosed ? scanned : scanned.filter(result => result.status === 'open')
532
+
533
+ this.emit('scan:complete', {
534
+ target: host,
535
+ type: 'scanPorts',
536
+ duration: Date.now() - startTime,
537
+ hostsFound: 1,
538
+ portsFound: results.filter(result => result.status === 'open').length,
539
+ })
540
+
541
+ return results
542
+ }
543
+
544
+ /**
545
+ * Convenience method: discover and port-scan hosts across all local networks.
546
+ */
547
+ async scanLocalNetworks(options: ScanLocalNetworksOptions = {}): Promise<LocalNetworkScanHost[]> {
548
+ const networks = this.getLocalNetworks()
549
+ const timeout = options.timeout ?? this.options.timeout ?? 1500
550
+ const concurrency = Math.max(1, options.concurrency ?? this.options.concurrency ?? 200)
551
+ const hostConcurrency = Math.max(1, options.hostConcurrency ?? 20)
552
+ const targetLabel = networks.map(network => network.cidr).join(', ')
553
+ const startTime = Date.now()
554
+
555
+ this.emit('scan:start', { target: targetLabel, type: 'scanLocalNetworks' })
556
+
557
+ const snapshots: NetworkSnapshot[] = []
558
+ const mergedHosts = new Map<string, LocalNetworkScanHost>()
559
+
560
+ for (const network of networks) {
561
+ const discovered = await this.discoverHosts(network.cidr, { timeout, concurrency })
562
+ const hosts = await this.mapWithConcurrency(discovered, hostConcurrency, async (host): Promise<LocalNetworkScanHost> => {
563
+ const scan = await this.scanPorts(host.ip, {
564
+ ports: options.ports,
565
+ timeout,
566
+ concurrency,
567
+ banner: options.banner,
568
+ includeClosed: false,
569
+ })
570
+
571
+ return {
572
+ ip: host.ip,
573
+ mac: host.mac,
574
+ openPorts: scan.filter(port => port.status === 'open'),
575
+ }
576
+ })
577
+
578
+ snapshots.push({
579
+ cidr: network.cidr,
580
+ hosts,
581
+ })
582
+
583
+ for (const host of hosts) {
584
+ const existing = mergedHosts.get(host.ip)
585
+ if (!existing) {
586
+ mergedHosts.set(host.ip, host)
587
+ continue
588
+ }
589
+
590
+ const portsByNumber = new Map<number, PortScanResult>()
591
+ for (const port of existing.openPorts) {
592
+ portsByNumber.set(port.port, port)
593
+ }
594
+ for (const port of host.openPorts) {
595
+ portsByNumber.set(port.port, port)
596
+ }
597
+
598
+ mergedHosts.set(host.ip, {
599
+ ip: host.ip,
600
+ mac: host.mac || existing.mac,
601
+ hostname: host.hostname || existing.hostname,
602
+ openPorts: Array.from(portsByNumber.values()).sort((a, b) => a.port - b.port),
603
+ })
604
+ }
605
+ }
606
+
607
+ const results = Array.from(mergedHosts.values()).sort((a, b) => a.ip.localeCompare(b.ip))
608
+ const portsFound = results.reduce((sum, host) => sum + host.openPorts.length, 0)
609
+
610
+ this.setState({
611
+ lastScan: {
612
+ timestamp: Date.now(),
613
+ target: targetLabel,
614
+ type: 'scanLocalNetworks',
615
+ networks: snapshots,
616
+ },
617
+ })
618
+
619
+ this.emit('scan:complete', {
620
+ target: targetLabel,
621
+ type: 'scanLocalNetworks',
622
+ duration: Date.now() - startTime,
623
+ hostsFound: results.length,
624
+ portsFound,
625
+ })
626
+
627
+ return results
628
+ }
629
+
630
+ /**
631
+ * Optional nmap wrapper for users that already have nmap installed.
632
+ */
633
+ get nmap(): { isAvailable: () => Promise<boolean>; scan: (target: string, args?: string[]) => Promise<{ hosts: NmapHost[]; raw: string }>; quickScan: (cidr: string) => Promise<{ hosts: NmapHost[]; raw: string }>; fullScan: (target: string) => Promise<{ hosts: NmapHost[]; raw: string }> } {
634
+ return {
635
+ isAvailable: async () => this.isNmapAvailable(),
636
+ scan: async (target: string, args: string[] = []) => this.runNmapScan(target, args),
637
+ quickScan: async (cidr: string) => this.runNmapScan(cidr, ['-sn']),
638
+ fullScan: async (target: string) => this.runNmapScan(target, ['-sV', '-O']),
639
+ }
640
+ }
641
+
642
+ private async isNmapAvailable(): Promise<boolean> {
643
+ const result = await this.proc.spawnAndCapture(this.resolveBin('nmap'), ['--version'])
644
+ return result.exitCode === 0
645
+ }
646
+
647
+ private async runNmapScan(target: string, args: string[] = []) {
648
+ const available = await this.isNmapAvailable()
649
+ if (!available) {
650
+ throw new Error('nmap binary not found in PATH')
651
+ }
652
+
653
+ this.emit('scan:start', { target, type: 'nmap' })
654
+ const startTime = Date.now()
655
+
656
+ const cmdArgs = [...args, '-oG', '-', target]
657
+ const result = await this.proc.spawnAndCapture(this.resolveBin('nmap'), cmdArgs)
658
+
659
+ if (result.exitCode !== 0) {
660
+ throw new Error(result.stderr || 'nmap scan failed')
661
+ }
662
+
663
+ const hosts = this.parseNmapGrepable(result.stdout)
664
+ const portsFound = hosts.reduce((sum, host) => sum + host.ports.filter(port => port.state === 'open').length, 0)
665
+
666
+ this.emit('scan:complete', {
667
+ target,
668
+ type: 'nmap',
669
+ duration: Date.now() - startTime,
670
+ hostsFound: hosts.length,
671
+ portsFound,
672
+ })
673
+
674
+ return {
675
+ hosts,
676
+ raw: result.stdout,
677
+ }
678
+ }
679
+
680
+ private parseNmapGrepable(output: string): NmapHost[] {
681
+ const hostMap = new Map<string, NmapHost>()
682
+ const lines = output.split(/\r?\n/).map(line => line.trim()).filter(Boolean)
683
+
684
+ for (const line of lines) {
685
+ if (!line.startsWith('Host: ')) {
686
+ continue
687
+ }
688
+
689
+ const hostMatch = line.match(/^Host:\s+([0-9]+\.[0-9]+\.[0-9]+\.[0-9]+)\s+\(([^)]*)\)/)
690
+ if (!hostMatch) {
691
+ continue
692
+ }
693
+
694
+ const ip = hostMatch[1]
695
+ if (!ip) {
696
+ continue
697
+ }
698
+
699
+ const hostname = hostMatch[2] || undefined
700
+ const current: NmapHost = hostMap.get(ip) || { ip, hostname, ports: [] }
701
+
702
+ const statusMatch = line.match(/Status:\s+([A-Za-z]+)/)
703
+ if (statusMatch?.[1]) {
704
+ current.status = statusMatch[1]
705
+ }
706
+
707
+ const macMatch = line.match(/MAC Address:\s+([0-9A-Fa-f:]+)/)
708
+ if (macMatch?.[1]) {
709
+ current.mac = macMatch[1].toLowerCase()
710
+ }
711
+
712
+ const portsMatch = line.match(/Ports:\s+(.+)$/)
713
+ if (portsMatch?.[1]) {
714
+ const parts = portsMatch[1].split(',').map(portSpec => portSpec.trim()).filter(Boolean)
715
+ const ports: NmapPort[] = []
716
+
717
+ for (const part of parts) {
718
+ const portBits = part.split('/')
719
+ const parsedPort = Number(portBits[0])
720
+ if (!Number.isInteger(parsedPort) || parsedPort <= 0) {
721
+ continue
722
+ }
723
+
724
+ ports.push({
725
+ port: parsedPort,
726
+ state: portBits[1] ?? 'unknown',
727
+ protocol: portBits[2] ?? 'tcp',
728
+ service: portBits[4] || undefined,
729
+ })
730
+ }
731
+
732
+ current.ports = ports
733
+ }
734
+
735
+ hostMap.set(ip, current)
736
+ }
737
+
738
+ return Array.from(hostMap.values())
739
+ }
740
+
741
+ private parseArpOutput(output: string): ArpEntry[] {
742
+ const entries = new Map<string, ArpEntry>()
743
+ const lines = output.split(/\r?\n/).map(line => line.trim()).filter(Boolean)
744
+ let currentInterface: string | undefined
745
+
746
+ for (const line of lines) {
747
+ const ifaceMatch = line.match(/^Interface:\s+([0-9]+\.[0-9]+\.[0-9]+\.[0-9]+)/i)
748
+ if (ifaceMatch?.[1]) {
749
+ currentInterface = ifaceMatch[1]
750
+ continue
751
+ }
752
+
753
+ const unixMatch = line.match(/^\?\s+\(([0-9]+\.[0-9]+\.[0-9]+\.[0-9]+)\)\s+at\s+([0-9a-fA-F:-]+|\(incomplete\))\s+on\s+([a-zA-Z0-9_.:-]+)/)
754
+ if (unixMatch?.[1] && unixMatch?.[2] && unixMatch?.[3]) {
755
+ const ip = unixMatch[1]
756
+ const rawMac = unixMatch[2]
757
+ const iface = unixMatch[3]
758
+ const mac = rawMac === '(incomplete)' ? undefined : this.normalizeMac(rawMac)
759
+
760
+ entries.set(ip, { ip, mac, interface: iface })
761
+ continue
762
+ }
763
+
764
+ const winMatch = line.match(/^([0-9]+\.[0-9]+\.[0-9]+\.[0-9]+)\s+([0-9a-fA-F-]{17})\s+\w+/)
765
+ if (winMatch?.[1] && winMatch?.[2]) {
766
+ const ip = winMatch[1]
767
+ entries.set(ip, {
768
+ ip,
769
+ mac: this.normalizeMac(winMatch[2]),
770
+ interface: currentInterface,
771
+ })
772
+ }
773
+ }
774
+
775
+ return Array.from(entries.values())
776
+ }
777
+
778
+ private normalizeMac(mac: string): string {
779
+ return mac.replaceAll('-', ':').toLowerCase()
780
+ }
781
+
782
+ private parsePortsOption(ports?: string | number[]): number[] {
783
+ if (!ports) {
784
+ return [...DEFAULT_PORTS]
785
+ }
786
+
787
+ if (Array.isArray(ports)) {
788
+ const unique = Array.from(new Set(ports.map(port => Number(port)).filter(port => Number.isInteger(port) && port >= 1 && port <= 65535)))
789
+ return unique.sort((a, b) => a - b)
790
+ }
791
+
792
+ const parsed = new Set<number>()
793
+ for (const segment of ports.split(',').map(part => part.trim()).filter(Boolean)) {
794
+ if (segment.includes('-')) {
795
+ const [startRaw, endRaw] = segment.split('-')
796
+ const start = Number(startRaw)
797
+ const end = Number(endRaw)
798
+
799
+ if (!Number.isInteger(start) || !Number.isInteger(end) || start < 1 || end > 65535 || start > end) {
800
+ continue
801
+ }
802
+
803
+ for (let current = start; current <= end; current += 1) {
804
+ parsed.add(current)
805
+ }
806
+ } else {
807
+ const value = Number(segment)
808
+ if (Number.isInteger(value) && value >= 1 && value <= 65535) {
809
+ parsed.add(value)
810
+ }
811
+ }
812
+ }
813
+
814
+ const values = Array.from(parsed.values()).sort((a, b) => a - b)
815
+ return values.length ? values : [...DEFAULT_PORTS]
816
+ }
817
+
818
+ private async probeTcpPort(host: string, port: number, timeout: number, captureBanner: boolean): Promise<{ status: 'open' | 'closed' | 'filtered'; banner?: string }> {
819
+ return new Promise((resolve) => {
820
+ const socket = new net.Socket()
821
+ let settled = false
822
+ let connected = false
823
+ let banner = ''
824
+
825
+ const done = (status: 'open' | 'closed' | 'filtered') => {
826
+ if (settled) {
827
+ return
828
+ }
829
+ settled = true
830
+
831
+ try {
832
+ socket.destroy()
833
+ } catch {
834
+ // no-op
835
+ }
836
+
837
+ resolve({
838
+ status,
839
+ banner: banner.length > 0 ? banner : undefined,
840
+ })
841
+ }
842
+
843
+ socket.setTimeout(timeout)
844
+
845
+ socket.on('connect', () => {
846
+ connected = true
847
+
848
+ if (!captureBanner) {
849
+ done('open')
850
+ return
851
+ }
852
+
853
+ if ([80, 3000, 4000, 5000, 8080, 8443, 9000].includes(port)) {
854
+ socket.write('HEAD / HTTP/1.0\r\n\r\n')
855
+ }
856
+
857
+ setTimeout(() => done('open'), 300)
858
+ })
859
+
860
+ socket.on('data', (chunk: Buffer) => {
861
+ if (!captureBanner) {
862
+ return
863
+ }
864
+
865
+ banner = `${banner}${chunk.toString('utf8')}`.slice(0, 256).trim()
866
+ done('open')
867
+ })
868
+
869
+ socket.on('timeout', () => {
870
+ done(connected ? 'open' : 'filtered')
871
+ })
872
+
873
+ socket.on('error', (error: any) => {
874
+ const code = String(error?.code || '')
875
+ if (code === 'ECONNREFUSED' || code === 'ECONNRESET' || code === 'EHOSTUNREACH' || code === 'ENETUNREACH') {
876
+ done('closed')
877
+ return
878
+ }
879
+
880
+ done('filtered')
881
+ })
882
+
883
+ socket.on('close', () => {
884
+ if (!settled) {
885
+ done(connected ? 'open' : 'filtered')
886
+ }
887
+ })
888
+
889
+ socket.connect(port, host)
890
+ })
891
+ }
892
+
893
+ private computeCidr(address: string, netmask: string): string {
894
+ const addressInt = this.ipToInt(address)
895
+ const netmaskInt = this.ipToInt(netmask)
896
+ const prefixLength = this.countMaskBits(netmaskInt)
897
+ const network = addressInt & netmaskInt
898
+
899
+ return `${this.intToIp(network >>> 0)}/${prefixLength}`
900
+ }
901
+
902
+ private ipToInt(ip: string): number {
903
+ const parts = ip.split('.').map(part => Number(part))
904
+ if (parts.length !== 4 || parts.some(part => !Number.isInteger(part) || part < 0 || part > 255)) {
905
+ throw new Error(`Invalid IPv4 address: ${ip}`)
906
+ }
907
+
908
+ const a = parts[0]!
909
+ const b = parts[1]!
910
+ const c = parts[2]!
911
+ const d = parts[3]!
912
+ return (((a << 24) >>> 0) + (b << 16) + (c << 8) + d) >>> 0
913
+ }
914
+
915
+ private intToIp(value: number): string {
916
+ const normalized = value >>> 0
917
+ return [
918
+ (normalized >>> 24) & 255,
919
+ (normalized >>> 16) & 255,
920
+ (normalized >>> 8) & 255,
921
+ normalized & 255,
922
+ ].join('.')
923
+ }
924
+
925
+ private countMaskBits(mask: number): number {
926
+ let bits = 0
927
+ let current = mask >>> 0
928
+ while (current > 0) {
929
+ bits += current & 1
930
+ current >>>= 1
931
+ }
932
+ return bits
933
+ }
934
+
935
+ private async mapWithConcurrency<T, R>(items: T[], concurrency: number, worker: (item: T, index: number) => Promise<R>): Promise<R[]> {
936
+ if (items.length === 0) {
937
+ return []
938
+ }
939
+
940
+ const safeConcurrency = Math.min(Math.max(1, concurrency), items.length)
941
+ const results = new Array<R>(items.length)
942
+ let nextIndex = 0
943
+
944
+ const runWorker = async () => {
945
+ while (true) {
946
+ const index = nextIndex
947
+ nextIndex += 1
948
+ if (index >= items.length) {
949
+ return
950
+ }
951
+ const item = items[index] as T
952
+ results[index] = await worker(item, index)
953
+ }
954
+ }
955
+
956
+ await Promise.all(Array.from({ length: safeConcurrency }, () => runWorker()))
957
+ return results
958
+ }
959
+ }
960
+
961
+ export default Networking