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,446 @@
1
+ import { z } from 'zod'
2
+ import Redis from 'ioredis'
3
+ import { Feature } from '../feature.js'
4
+ import { FeatureStateSchema, FeatureOptionsSchema, FeatureEventsSchema } from '../../schemas/base.js'
5
+ import type { ContainerContext } from '../../container.js'
6
+
7
+ export const RedisStateSchema = FeatureStateSchema.extend({
8
+ connected: z.boolean().default(false).describe('Whether the redis connection is currently open'),
9
+ url: z.string().default('').describe('Connection URL used for this redis feature instance'),
10
+ subscriberConnected: z.boolean().default(false).describe('Whether the dedicated subscriber connection is open'),
11
+ subscribedChannels: z.array(z.string()).default([]).describe('List of channels currently subscribed to'),
12
+ lastError: z.string().optional().describe('Most recent redis error message, if any'),
13
+ })
14
+
15
+ export const RedisOptionsSchema = FeatureOptionsSchema.extend({
16
+ url: z.string().optional().describe('Redis connection URL, e.g. redis://localhost:6379. Defaults to redis://localhost:6379'),
17
+ prefix: z.string().optional().describe('Key prefix applied to all get/set/del operations for namespace isolation'),
18
+ lazyConnect: z.boolean().default(false).describe('If true, connection is deferred until first command'),
19
+ })
20
+
21
+ export type RedisState = z.infer<typeof RedisStateSchema>
22
+ export type RedisOptions = z.infer<typeof RedisOptionsSchema>
23
+
24
+ export const RedisEventsSchema = FeatureEventsSchema.extend({
25
+ message: z.tuple([
26
+ z.string().describe('The channel name'),
27
+ z.string().describe('The message payload'),
28
+ ]).describe('When a message is received on a subscribed channel'),
29
+ subscribed: z.tuple([
30
+ z.string().describe('The channel name'),
31
+ ]).describe('When successfully subscribed to a channel'),
32
+ unsubscribed: z.tuple([
33
+ z.string().describe('The channel name'),
34
+ ]).describe('When unsubscribed from a channel'),
35
+ error: z.tuple([z.string().describe('The error message')]).describe('When a redis operation fails'),
36
+ closed: z.tuple([]).describe('When the redis connection is closed'),
37
+ }).describe('Redis events')
38
+
39
+ type MessageHandler = (channel: string, message: string) => void
40
+
41
+ /**
42
+ * Redis feature for shared state and pub/sub communication between container instances.
43
+ *
44
+ * Wraps ioredis with a focused API for the primitives that matter most:
45
+ * key/value state, pub/sub messaging, and cross-instance coordination.
46
+ *
47
+ * Uses a dedicated subscriber connection for pub/sub (ioredis requirement),
48
+ * created lazily on first subscribe call.
49
+ *
50
+ * @example
51
+ * ```typescript
52
+ * const redis = container.feature('redis', { url: 'redis://localhost:6379' })
53
+ *
54
+ * // Shared state
55
+ * await redis.set('worker:status', 'active')
56
+ * const status = await redis.get('worker:status')
57
+ *
58
+ * // Pub/sub between instances
59
+ * redis.on('message', (channel, msg) => console.log(`${channel}: ${msg}`))
60
+ * await redis.subscribe('tasks')
61
+ * await redis.publish('tasks', JSON.stringify({ type: 'ping' }))
62
+ *
63
+ * // JSON helpers
64
+ * await redis.setJSON('config', { workers: 4, debug: true })
65
+ * const config = await redis.getJSON<{ workers: number }>('config')
66
+ * ```
67
+ */
68
+ export class RedisFeature extends Feature<RedisState, RedisOptions> {
69
+ static override shortcut = 'features.redis' as const
70
+ static override stateSchema = RedisStateSchema
71
+ static override optionsSchema = RedisOptionsSchema
72
+ static override eventsSchema = RedisEventsSchema
73
+ static { Feature.register(this, 'redis') }
74
+
75
+ private _client: Redis
76
+ private _subscriber: Redis | null = null
77
+ private _prefix: string
78
+ private _messageHandlers: Map<string, Set<MessageHandler>> = new Map()
79
+
80
+ override get initialState(): RedisState {
81
+ return {
82
+ enabled: false,
83
+ connected: false,
84
+ url: '',
85
+ subscriberConnected: false,
86
+ subscribedChannels: [],
87
+ }
88
+ }
89
+
90
+ constructor(options: RedisOptions, context: ContainerContext) {
91
+ super(options, context)
92
+
93
+ const url = options.url || 'redis://localhost:6379'
94
+ this._prefix = options.prefix || ''
95
+
96
+ this._client = new Redis(url, {
97
+ lazyConnect: options.lazyConnect ?? false,
98
+ retryStrategy: (times: number) => Math.min(times * 200, 5000),
99
+ })
100
+
101
+ this.hide('_client')
102
+ this.hide('_subscriber')
103
+ this.hide('_messageHandlers')
104
+
105
+ this._client.on('connect', () => {
106
+ this.setState({ connected: true, url })
107
+ })
108
+
109
+ this._client.on('error', (err: Error) => {
110
+ this.setState({ lastError: err.message })
111
+ this.emit('error', err.message)
112
+ })
113
+
114
+ this._client.on('close', () => {
115
+ this.setState({ connected: false })
116
+ })
117
+
118
+ if (!options.lazyConnect) {
119
+ this.setState({ connected: true, url })
120
+ }
121
+ }
122
+
123
+ /** The underlying ioredis client for advanced operations. */
124
+ get client(): Redis {
125
+ return this._client
126
+ }
127
+
128
+ /** The dedicated subscriber connection, if pub/sub is active. */
129
+ get subscriber(): Redis | null {
130
+ return this._subscriber
131
+ }
132
+
133
+ // ── Key/Value Primitives ──────────────────────────────────────────
134
+
135
+ private _key(key: string): string {
136
+ return this._prefix ? `${this._prefix}:${key}` : key
137
+ }
138
+
139
+ /**
140
+ * Set a key to a string value with optional TTL.
141
+ *
142
+ * @param key - The key name
143
+ * @param value - The string value to store
144
+ * @param ttl - Optional time-to-live in seconds
145
+ */
146
+ async set(key: string, value: string, ttl?: number): Promise<void> {
147
+ if (ttl) {
148
+ await this._client.set(this._key(key), value, 'EX', ttl)
149
+ } else {
150
+ await this._client.set(this._key(key), value)
151
+ }
152
+ }
153
+
154
+ /**
155
+ * Get a key's value. Returns null if the key doesn't exist.
156
+ *
157
+ * @param key - The key name
158
+ * @returns The stored value, or null
159
+ */
160
+ async get(key: string): Promise<string | null> {
161
+ return this._client.get(this._key(key))
162
+ }
163
+
164
+ /**
165
+ * Delete one or more keys.
166
+ *
167
+ * @param keys - One or more key names to delete
168
+ * @returns Number of keys that were deleted
169
+ */
170
+ async del(...keys: string[]): Promise<number> {
171
+ return this._client.del(...keys.map(k => this._key(k)))
172
+ }
173
+
174
+ /**
175
+ * Check if a key exists.
176
+ *
177
+ * @param key - The key name
178
+ */
179
+ async exists(key: string): Promise<boolean> {
180
+ return (await this._client.exists(this._key(key))) === 1
181
+ }
182
+
183
+ /**
184
+ * Set a key's TTL in seconds.
185
+ *
186
+ * @param key - The key name
187
+ * @param seconds - TTL in seconds
188
+ */
189
+ async expire(key: string, seconds: number): Promise<boolean> {
190
+ return (await this._client.expire(this._key(key), seconds)) === 1
191
+ }
192
+
193
+ /**
194
+ * Find keys matching a glob pattern (respects prefix).
195
+ *
196
+ * @param pattern - Glob pattern, e.g. "worker:*"
197
+ * @returns Array of matching key names (with prefix stripped)
198
+ */
199
+ async keys(pattern: string = '*'): Promise<string[]> {
200
+ const results = await this._client.keys(this._key(pattern))
201
+ if (!this._prefix) return results
202
+ const strip = `${this._prefix}:`
203
+ return results.map(k => k.startsWith(strip) ? k.slice(strip.length) : k)
204
+ }
205
+
206
+ // ── JSON Helpers ──────────────────────────────────────────────────
207
+
208
+ /**
209
+ * Store a value as JSON.
210
+ *
211
+ * @param key - The key name
212
+ * @param value - Any JSON-serializable value
213
+ * @param ttl - Optional TTL in seconds
214
+ */
215
+ async setJSON(key: string, value: unknown, ttl?: number): Promise<void> {
216
+ await this.set(key, JSON.stringify(value), ttl)
217
+ }
218
+
219
+ /**
220
+ * Retrieve and parse a JSON value.
221
+ *
222
+ * @param key - The key name
223
+ * @returns The parsed value, or null if the key doesn't exist
224
+ */
225
+ async getJSON<T = unknown>(key: string): Promise<T | null> {
226
+ const raw = await this.get(key)
227
+ if (raw === null) return null
228
+ return JSON.parse(raw) as T
229
+ }
230
+
231
+ // ── Hash Helpers ──────────────────────────────────────────────────
232
+
233
+ /**
234
+ * Set fields on a hash.
235
+ *
236
+ * @param key - The hash key
237
+ * @param fields - Object of field/value pairs
238
+ */
239
+ async hset(key: string, fields: Record<string, string>): Promise<void> {
240
+ await this._client.hset(this._key(key), fields)
241
+ }
242
+
243
+ /**
244
+ * Get all fields from a hash.
245
+ *
246
+ * @param key - The hash key
247
+ * @returns Object of field/value pairs
248
+ */
249
+ async hgetall(key: string): Promise<Record<string, string>> {
250
+ return this._client.hgetall(this._key(key))
251
+ }
252
+
253
+ /**
254
+ * Get a single field from a hash.
255
+ *
256
+ * @param key - The hash key
257
+ * @param field - The field name
258
+ */
259
+ async hget(key: string, field: string): Promise<string | null> {
260
+ return this._client.hget(this._key(key), field)
261
+ }
262
+
263
+ // ── Pub/Sub ───────────────────────────────────────────────────────
264
+
265
+ /**
266
+ * Ensures the dedicated subscriber connection exists.
267
+ * ioredis requires a separate connection for subscriptions.
268
+ */
269
+ private _ensureSubscriber(): Redis {
270
+ if (this._subscriber) return this._subscriber
271
+
272
+ const url = this.state.get('url') || 'redis://localhost:6379'
273
+ this._subscriber = new Redis(url)
274
+
275
+ this._subscriber.on('message', (channel: string, message: string) => {
276
+ this.emit('message', channel, message)
277
+
278
+ const handlers = this._messageHandlers.get(channel)
279
+ if (handlers) {
280
+ for (const handler of handlers) {
281
+ handler(channel, message)
282
+ }
283
+ }
284
+ })
285
+
286
+ this._subscriber.on('connect', () => {
287
+ this.setState({ subscriberConnected: true })
288
+ })
289
+
290
+ this._subscriber.on('error', (err: Error) => {
291
+ this.setState({ lastError: err.message })
292
+ this.emit('error', err.message)
293
+ })
294
+
295
+ this._subscriber.on('close', () => {
296
+ this.setState({ subscriberConnected: false })
297
+ })
298
+
299
+ this.setState({ subscriberConnected: true })
300
+ return this._subscriber
301
+ }
302
+
303
+ /**
304
+ * Subscribe to one or more channels.
305
+ *
306
+ * Optionally pass a handler that fires only for these channels.
307
+ * The feature also emits a `message` event for all messages.
308
+ *
309
+ * @param channels - Channel name(s) to subscribe to
310
+ * @param handler - Optional per-channel message handler
311
+ *
312
+ * @example
313
+ * ```typescript
314
+ * await redis.subscribe('tasks', (channel, msg) => {
315
+ * console.log(`Got ${msg} on ${channel}`)
316
+ * })
317
+ * ```
318
+ */
319
+ async subscribe(channels: string | string[], handler?: MessageHandler): Promise<void> {
320
+ const sub = this._ensureSubscriber()
321
+ const list = Array.isArray(channels) ? channels : [channels]
322
+
323
+ await sub.subscribe(...list)
324
+
325
+ const current = this.state.get('subscribedChannels') || []
326
+ const next = [...new Set([...current, ...list])]
327
+ this.setState({ subscribedChannels: next })
328
+
329
+ if (handler) {
330
+ for (const ch of list) {
331
+ if (!this._messageHandlers.has(ch)) {
332
+ this._messageHandlers.set(ch, new Set())
333
+ }
334
+ this._messageHandlers.get(ch)!.add(handler)
335
+ }
336
+ }
337
+
338
+ for (const ch of list) {
339
+ this.emit('subscribed', ch)
340
+ }
341
+ }
342
+
343
+ /**
344
+ * Unsubscribe from one or more channels.
345
+ *
346
+ * @param channels - Channel name(s) to unsubscribe from
347
+ */
348
+ async unsubscribe(...channels: string[]): Promise<void> {
349
+ if (!this._subscriber) return
350
+
351
+ await this._subscriber.unsubscribe(...channels)
352
+
353
+ const current = this.state.get('subscribedChannels') || []
354
+ this.setState({
355
+ subscribedChannels: current.filter((ch: string) => !channels.includes(ch)),
356
+ })
357
+
358
+ for (const ch of channels) {
359
+ this._messageHandlers.delete(ch)
360
+ this.emit('unsubscribed', ch)
361
+ }
362
+ }
363
+
364
+ /**
365
+ * Publish a message to a channel.
366
+ *
367
+ * @param channel - The channel to publish to
368
+ * @param message - The message string (use JSON.stringify for objects)
369
+ * @returns Number of subscribers that received the message
370
+ */
371
+ async publish(channel: string, message: string): Promise<number> {
372
+ return this._client.publish(channel, message)
373
+ }
374
+
375
+ // ── Docker Convenience ──────────────────────────────────────────
376
+
377
+ /**
378
+ * Spin up a local Redis instance via Docker. Checks if a container with
379
+ * the given name already exists and starts it if stopped, or creates a
380
+ * new one from redis:alpine.
381
+ *
382
+ * Requires the docker feature to be available on the container.
383
+ *
384
+ * @param options - Container name and host port
385
+ * @returns The docker container ID
386
+ *
387
+ * @example
388
+ * ```typescript
389
+ * const redis = container.feature('redis', { url: 'redis://localhost:6379', lazyConnect: true })
390
+ * await redis.ensureLocalDocker()
391
+ * ```
392
+ */
393
+ async ensureLocalDocker(options: { name?: string; port?: number; image?: string } = {}): Promise<string> {
394
+ const { name = 'luca-redis', port = 6379, image = 'redis:alpine' } = options
395
+ const docker = this.container.feature('docker', { enable: true })
396
+
397
+ const containers = await docker.listContainers({ all: true })
398
+ const existing = containers.find((c: any) =>
399
+ c.names?.includes(name) || c.names?.includes(`/${name}`)
400
+ )
401
+
402
+ if (existing) {
403
+ if (existing.status !== 'running') {
404
+ await docker.startContainer(name)
405
+ }
406
+ return existing.id
407
+ }
408
+
409
+ return docker.runContainer(image, {
410
+ name,
411
+ ports: [`${port}:6379`],
412
+ detach: true,
413
+ restart: 'unless-stopped',
414
+ })
415
+ }
416
+
417
+ // ── Lifecycle ─────────────────────────────────────────────────────
418
+
419
+ /**
420
+ * Close all redis connections (main client + subscriber).
421
+ */
422
+ async close(): Promise<this> {
423
+ if (this._subscriber) {
424
+ this._subscriber.disconnect()
425
+ this._subscriber = null
426
+ }
427
+ this._client.disconnect()
428
+ this._messageHandlers.clear()
429
+ this.setState({
430
+ connected: false,
431
+ subscriberConnected: false,
432
+ subscribedChannels: [],
433
+ })
434
+ this.emit('closed')
435
+ return this
436
+ }
437
+ }
438
+
439
+ export { RedisFeature as Redis }
440
+ export default RedisFeature
441
+
442
+ declare module '../../feature.js' {
443
+ interface AvailableFeatures {
444
+ redis: typeof RedisFeature
445
+ }
446
+ }
@@ -0,0 +1,212 @@
1
+ import { z } from 'zod'
2
+ import { FeatureStateSchema, FeatureOptionsSchema } from '../../schemas/base.js'
3
+ import { Feature } from "../feature.js";
4
+ import vm from 'vm'
5
+ import readline from 'readline'
6
+ import { displayResult } from './display-result.js'
7
+
8
+ export const ReplStateSchema = FeatureStateSchema.extend({
9
+ started: z.boolean().optional().describe('Whether the REPL server has been started'),
10
+ })
11
+ export type ReplState = z.infer<typeof ReplStateSchema>
12
+
13
+ export const ReplOptionsSchema = FeatureOptionsSchema.extend({
14
+ prompt: z.string().optional().describe('The prompt string to display in the REPL (default: "> ")'),
15
+ historyPath: z.string().optional().describe('Path to the REPL history file for command persistence'),
16
+ })
17
+ export type ReplOptions = z.infer<typeof ReplOptionsSchema>
18
+
19
+ /**
20
+ * REPL feature — provides an interactive read-eval-print loop with tab completion and history.
21
+ *
22
+ * Launches a REPL session that evaluates JavaScript/TypeScript expressions in a sandboxed
23
+ * VM context populated with the container and its helpers. Supports tab completion for
24
+ * dot-notation property access, command history persistence, and async/await.
25
+ *
26
+ * @example
27
+ * ```typescript
28
+ * const repl = container.feature('repl', { enable: true })
29
+ * await repl.start({ context: { myVar: 42 } })
30
+ * ```
31
+ */
32
+ export class Repl<
33
+ T extends ReplState = ReplState,
34
+ K extends ReplOptions = ReplOptions
35
+ > extends Feature<T, K> {
36
+ static override shortcut = "features.repl" as const
37
+ static override stateSchema = ReplStateSchema
38
+ static override optionsSchema = ReplOptionsSchema
39
+ static { Feature.register(this, 'repl') }
40
+
41
+ /** Whether the REPL session is currently running. */
42
+ get isStarted() {
43
+ return !!this.state.get("started");
44
+ }
45
+
46
+ _rl?: readline.Interface
47
+ _vmContext?: vm.Context
48
+ _history: string[] = []
49
+ _historyPath?: string
50
+
51
+ /** The VM context object used for evaluating expressions in the REPL. */
52
+ get vmContext() {
53
+ return this._vmContext
54
+ }
55
+
56
+ /**
57
+ * Start the REPL session.
58
+ *
59
+ * Creates a VM context populated with the container and its helpers, sets up
60
+ * readline with tab completion and history, then enters the interactive loop.
61
+ * Type `.exit` or `exit` to quit. Supports top-level await.
62
+ *
63
+ * @param options - Configuration for the REPL session
64
+ * @param options.historyPath - Custom path for the history file (defaults to ~/.cache/luca/repl-{cwdHash}.history)
65
+ * @param options.context - Additional variables to inject into the VM context
66
+ * @returns The Repl instance
67
+ *
68
+ * @example
69
+ * ```typescript
70
+ * const repl = container.feature('repl', { enable: true })
71
+ * await repl.start({
72
+ * context: { db: myDatabase },
73
+ * historyPath: '.repl-history'
74
+ * })
75
+ * ```
76
+ */
77
+ async start(options: { historyPath?: string, context?: any } = {}) {
78
+ const { prompt = "> " } = this.options;
79
+
80
+ // If already started, resume with a fresh readline but reuse the VM context
81
+ if (this.isStarted) {
82
+ // Merge any new context into the existing VM context
83
+ if (options.context) {
84
+ for (const [k, v] of Object.entries(options.context)) {
85
+ this._vmContext![k] = v
86
+ }
87
+ }
88
+ return this._resume(prompt)
89
+ }
90
+
91
+ // Set up history file — per-project history keyed by cwd hash
92
+ const userHistoryPath = options.historyPath || this.options.historyPath
93
+ if (typeof userHistoryPath === 'string') {
94
+ this._historyPath = this.container.paths.resolve(userHistoryPath)
95
+ } else {
96
+ const cwdHash = this.container.utils.hashObject(this.container.cwd)
97
+ this._historyPath = this.container.paths.resolve(this.container.feature('os').cacheDir, `repl-${cwdHash}.history`)
98
+ }
99
+
100
+ this.container.fs.ensureFolder(this.container.paths.dirname(this._historyPath))
101
+
102
+ // Load existing history
103
+ try {
104
+ const content = this.container.fs.readFile(this._historyPath, 'utf-8') as string
105
+ this._history = content.split(/\r?\n/).filter(Boolean).reverse()
106
+ } catch {}
107
+
108
+ // Build VM context
109
+ this._vmContext = vm.createContext({
110
+ ...this.container.context,
111
+ ...options.context,
112
+ setTimeout, setInterval, process, clearInterval, clearTimeout, Buffer, URL, URLSearchParams,
113
+ // @ts-ignore
114
+ client: (...args: any[]) => this.container.client(...args),
115
+ })
116
+
117
+ this.state.set('started', true)
118
+
119
+ return this._resume(prompt)
120
+ }
121
+
122
+ /** Open a fresh readline and enter the REPL loop using the existing VM context. */
123
+ private _resume(prompt: string) {
124
+ const ctx = this._vmContext!
125
+
126
+ // Completer for tab autocomplete
127
+ const completer = (line: string): [string[], string] => {
128
+ // Dot-notation: e.g. container.fea<tab>
129
+ const dotMatch = line.match(/([a-zA-Z_$][\w$]*(?:\.[a-zA-Z_$][\w$]*)*)\.([a-zA-Z_$][\w$]*)?$/)
130
+ if (dotMatch) {
131
+ const objPath = dotMatch[1]!
132
+ const partial = dotMatch[2] || ''
133
+ try {
134
+ const obj = new vm.Script(objPath).runInContext(ctx)
135
+ if (obj != null && typeof obj === 'object') {
136
+ const own = Object.keys(obj)
137
+ const proto = Object.getOwnPropertyNames(Object.getPrototypeOf(obj) || {})
138
+ const all = [...new Set([...own, ...proto])]
139
+ .filter(p => p.startsWith(partial))
140
+ .sort()
141
+ .map(p => `${objPath}.${p}`)
142
+ return [all, dotMatch[0]!]
143
+ }
144
+ } catch {}
145
+ return [[], line]
146
+ }
147
+
148
+ // Top-level identifiers
149
+ const idMatch = line.match(/([a-zA-Z_$][\w$]*)$/)
150
+ const partial = idMatch ? idMatch[1]! : ''
151
+ const keys = Object.keys(ctx).filter(k => k.startsWith(partial)).sort()
152
+ return [keys, partial]
153
+ }
154
+
155
+ // Create readline interface
156
+ this._rl = readline.createInterface({
157
+ input: process.stdin,
158
+ output: process.stdout,
159
+ terminal: true,
160
+ history: this._history,
161
+ completer,
162
+ })
163
+
164
+ // REPL loop
165
+ let lastResult: any
166
+ const ask = (): void => {
167
+ this._rl!.question(prompt, async (input) => {
168
+ const trimmed = input.trim()
169
+ if (!trimmed) { ask(); return }
170
+ if (trimmed === '.exit' || trimmed === 'exit') {
171
+ this._saveHistory(input)
172
+ this._rl!.close()
173
+ return
174
+ }
175
+
176
+ this._saveHistory(input)
177
+
178
+ try {
179
+ const script = new vm.Script(trimmed)
180
+ let result = script.runInContext(ctx)
181
+
182
+ if (result && typeof result.then === 'function') {
183
+ result = await result
184
+ }
185
+
186
+ lastResult = result
187
+ ctx._ = lastResult
188
+
189
+ if (result !== undefined) {
190
+ displayResult(result)
191
+ }
192
+ } catch (err: any) {
193
+ console.log(`\x1b[31mError: ${err.message}\x1b[0m`)
194
+ }
195
+
196
+ ask()
197
+ })
198
+ }
199
+
200
+ ask()
201
+ return this;
202
+ }
203
+
204
+ private _saveHistory(line: string) {
205
+ if (!this._historyPath || !line.trim()) return
206
+ try {
207
+ this.container.fs.appendFile(this._historyPath, line + '\n')
208
+ } catch {}
209
+ }
210
+ }
211
+
212
+ export default Repl