@soederpop/luca 0.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 (358) hide show
  1. package/CLAUDE.md +71 -0
  2. package/README.md +78 -0
  3. package/bun.lock +2928 -0
  4. package/bunfig.toml +3 -0
  5. package/commands/audit-docs.ts +740 -0
  6. package/commands/build-scaffolds.ts +154 -0
  7. package/commands/generate-api-docs.ts +114 -0
  8. package/commands/update-introspection.ts +67 -0
  9. package/docs/CLI.md +335 -0
  10. package/docs/README.md +88 -0
  11. package/docs/TABLE-OF-CONTENTS.md +157 -0
  12. package/docs/apis/clients/elevenlabs.md +84 -0
  13. package/docs/apis/clients/graph.md +56 -0
  14. package/docs/apis/clients/openai.md +69 -0
  15. package/docs/apis/clients/rest.md +41 -0
  16. package/docs/apis/clients/websocket.md +107 -0
  17. package/docs/apis/features/agi/assistant.md +471 -0
  18. package/docs/apis/features/agi/assistants-manager.md +154 -0
  19. package/docs/apis/features/agi/claude-code.md +602 -0
  20. package/docs/apis/features/agi/conversation-history.md +352 -0
  21. package/docs/apis/features/agi/conversation.md +333 -0
  22. package/docs/apis/features/agi/docs-reader.md +121 -0
  23. package/docs/apis/features/agi/openai-codex.md +318 -0
  24. package/docs/apis/features/agi/openapi.md +138 -0
  25. package/docs/apis/features/agi/semantic-search.md +387 -0
  26. package/docs/apis/features/agi/skills-library.md +216 -0
  27. package/docs/apis/features/node/container-link.md +133 -0
  28. package/docs/apis/features/node/content-db.md +313 -0
  29. package/docs/apis/features/node/disk-cache.md +379 -0
  30. package/docs/apis/features/node/dns.md +651 -0
  31. package/docs/apis/features/node/docker.md +705 -0
  32. package/docs/apis/features/node/downloader.md +81 -0
  33. package/docs/apis/features/node/esbuild.md +59 -0
  34. package/docs/apis/features/node/file-manager.md +182 -0
  35. package/docs/apis/features/node/fs.md +581 -0
  36. package/docs/apis/features/node/git.md +330 -0
  37. package/docs/apis/features/node/google-auth.md +174 -0
  38. package/docs/apis/features/node/google-calendar.md +187 -0
  39. package/docs/apis/features/node/google-docs.md +151 -0
  40. package/docs/apis/features/node/google-drive.md +225 -0
  41. package/docs/apis/features/node/google-sheets.md +179 -0
  42. package/docs/apis/features/node/grep.md +290 -0
  43. package/docs/apis/features/node/helpers.md +135 -0
  44. package/docs/apis/features/node/ink.md +334 -0
  45. package/docs/apis/features/node/ipc-socket.md +260 -0
  46. package/docs/apis/features/node/json-tree.md +86 -0
  47. package/docs/apis/features/node/launcher-app-command-listener.md +145 -0
  48. package/docs/apis/features/node/networking.md +281 -0
  49. package/docs/apis/features/node/nlp.md +133 -0
  50. package/docs/apis/features/node/opener.md +97 -0
  51. package/docs/apis/features/node/os.md +118 -0
  52. package/docs/apis/features/node/package-finder.md +402 -0
  53. package/docs/apis/features/node/postgres.md +212 -0
  54. package/docs/apis/features/node/proc.md +430 -0
  55. package/docs/apis/features/node/process-manager.md +210 -0
  56. package/docs/apis/features/node/python.md +278 -0
  57. package/docs/apis/features/node/repl.md +88 -0
  58. package/docs/apis/features/node/runpod.md +673 -0
  59. package/docs/apis/features/node/secure-shell.md +169 -0
  60. package/docs/apis/features/node/semantic-search.md +401 -0
  61. package/docs/apis/features/node/sqlite.md +211 -0
  62. package/docs/apis/features/node/telegram.md +254 -0
  63. package/docs/apis/features/node/tts.md +118 -0
  64. package/docs/apis/features/node/ui.md +703 -0
  65. package/docs/apis/features/node/vault.md +64 -0
  66. package/docs/apis/features/node/vm.md +84 -0
  67. package/docs/apis/features/node/window-manager.md +337 -0
  68. package/docs/apis/features/node/yaml-tree.md +85 -0
  69. package/docs/apis/features/node/yaml.md +176 -0
  70. package/docs/apis/features/web/asset-loader.md +47 -0
  71. package/docs/apis/features/web/container-link.md +133 -0
  72. package/docs/apis/features/web/esbuild.md +59 -0
  73. package/docs/apis/features/web/helpers.md +135 -0
  74. package/docs/apis/features/web/network.md +30 -0
  75. package/docs/apis/features/web/speech.md +55 -0
  76. package/docs/apis/features/web/vault.md +64 -0
  77. package/docs/apis/features/web/vm.md +84 -0
  78. package/docs/apis/features/web/voice.md +67 -0
  79. package/docs/apis/servers/express.md +127 -0
  80. package/docs/apis/servers/mcp.md +213 -0
  81. package/docs/apis/servers/websocket.md +99 -0
  82. package/docs/documentation-audit.md +134 -0
  83. package/docs/examples/content-db.md +77 -0
  84. package/docs/examples/disk-cache.md +83 -0
  85. package/docs/examples/docker.md +101 -0
  86. package/docs/examples/downloader.md +70 -0
  87. package/docs/examples/esbuild.md +80 -0
  88. package/docs/examples/file-manager.md +82 -0
  89. package/docs/examples/fs.md +83 -0
  90. package/docs/examples/git.md +85 -0
  91. package/docs/examples/google-auth.md +88 -0
  92. package/docs/examples/google-calendar.md +94 -0
  93. package/docs/examples/google-docs.md +82 -0
  94. package/docs/examples/google-drive.md +96 -0
  95. package/docs/examples/google-sheets.md +95 -0
  96. package/docs/examples/grep.md +85 -0
  97. package/docs/examples/ink-blocks.md +75 -0
  98. package/docs/examples/ink-renderer.md +41 -0
  99. package/docs/examples/ink.md +103 -0
  100. package/docs/examples/ipc-socket.md +103 -0
  101. package/docs/examples/json-tree.md +91 -0
  102. package/docs/examples/launcher-app-command-listener.md +120 -0
  103. package/docs/examples/networking.md +58 -0
  104. package/docs/examples/nlp.md +91 -0
  105. package/docs/examples/opener.md +78 -0
  106. package/docs/examples/os.md +72 -0
  107. package/docs/examples/package-finder.md +89 -0
  108. package/docs/examples/port-exposer.md +89 -0
  109. package/docs/examples/postgres.md +91 -0
  110. package/docs/examples/proc.md +81 -0
  111. package/docs/examples/process-manager.md +79 -0
  112. package/docs/examples/python.md +91 -0
  113. package/docs/examples/repl.md +93 -0
  114. package/docs/examples/runpod.md +119 -0
  115. package/docs/examples/secure-shell.md +92 -0
  116. package/docs/examples/sqlite.md +86 -0
  117. package/docs/examples/telegram.md +77 -0
  118. package/docs/examples/tts.md +86 -0
  119. package/docs/examples/ui.md +80 -0
  120. package/docs/examples/vault.md +70 -0
  121. package/docs/examples/vm.md +86 -0
  122. package/docs/examples/window-manager.md +125 -0
  123. package/docs/examples/yaml-tree.md +93 -0
  124. package/docs/examples/yaml.md +104 -0
  125. package/docs/ideas/class-registration-refactor-possibilities.md +197 -0
  126. package/docs/ideas/container-use-api.md +9 -0
  127. package/docs/ideas/easy-auth-for-express-servers-and-luca-serve.md +0 -0
  128. package/docs/ideas/feature-stacks.md +22 -0
  129. package/docs/ideas/luca-cli-self-sufficiency-demo.md +23 -0
  130. package/docs/ideas/mcp-design.md +9 -0
  131. package/docs/ideas/web-container-debugging-feature.md +13 -0
  132. package/docs/introspection-audit.md +49 -0
  133. package/docs/introspection.md +154 -0
  134. package/docs/mcp/readme.md +162 -0
  135. package/docs/models.ts +38 -0
  136. package/docs/philosophy.md +85 -0
  137. package/docs/principles.md +7 -0
  138. package/docs/prompts/audit-codebase-for-failures-to-use-the-container.md +34 -0
  139. package/docs/prompts/mcp-test-easy-command.md +27 -0
  140. package/docs/reports/assistant-bugs.md +38 -0
  141. package/docs/reports/attach-pattern-usage.md +18 -0
  142. package/docs/reports/code-audit-results.md +391 -0
  143. package/docs/reports/introspection-audit-tasks.md +378 -0
  144. package/docs/reports/luca-mcp-improvements.md +128 -0
  145. package/docs/scaffolds/client.md +140 -0
  146. package/docs/scaffolds/command.md +106 -0
  147. package/docs/scaffolds/endpoint.md +176 -0
  148. package/docs/scaffolds/feature.md +148 -0
  149. package/docs/scaffolds/server.md +187 -0
  150. package/docs/tasks/web-container-helper-discovery.md +71 -0
  151. package/docs/todos.md +1 -0
  152. package/docs/tutorials/01-getting-started.md +106 -0
  153. package/docs/tutorials/02-container.md +210 -0
  154. package/docs/tutorials/03-scripts.md +194 -0
  155. package/docs/tutorials/04-features-overview.md +196 -0
  156. package/docs/tutorials/05-state-and-events.md +171 -0
  157. package/docs/tutorials/06-servers.md +157 -0
  158. package/docs/tutorials/07-endpoints.md +198 -0
  159. package/docs/tutorials/08-commands.md +171 -0
  160. package/docs/tutorials/09-clients.md +162 -0
  161. package/docs/tutorials/10-creating-features.md +198 -0
  162. package/docs/tutorials/11-contentbase.md +191 -0
  163. package/docs/tutorials/12-assistants.md +215 -0
  164. package/docs/tutorials/13-introspection.md +147 -0
  165. package/docs/tutorials/14-type-system.md +174 -0
  166. package/docs/tutorials/15-project-patterns.md +222 -0
  167. package/docs/tutorials/16-google-features.md +534 -0
  168. package/docs/tutorials/17-tui-blocks.md +530 -0
  169. package/docs/tutorials/18-semantic-search.md +334 -0
  170. package/index.ts +1 -0
  171. package/luca.console.ts +9 -0
  172. package/main.py +6 -0
  173. package/package.json +154 -0
  174. package/pyproject.toml +7 -0
  175. package/scripts/animations/chrome-glitch.ts +55 -0
  176. package/scripts/animations/index.ts +16 -0
  177. package/scripts/animations/neon-pulse.ts +64 -0
  178. package/scripts/animations/types.ts +6 -0
  179. package/scripts/build-web.ts +28 -0
  180. package/scripts/examples/ask-luca-expert.ts +42 -0
  181. package/scripts/examples/assistant-questions.ts +12 -0
  182. package/scripts/examples/excalidraw-expert.ts +75 -0
  183. package/scripts/examples/expert-chat.ts +0 -0
  184. package/scripts/examples/file-manager.ts +14 -0
  185. package/scripts/examples/ideas.ts +12 -0
  186. package/scripts/examples/interactive-chat.ts +20 -0
  187. package/scripts/examples/openai-tool-calls.ts +113 -0
  188. package/scripts/examples/opening-a-web-browser.ts +5 -0
  189. package/scripts/examples/telegram-bot.ts +79 -0
  190. package/scripts/examples/telegram-ink-ui.ts +302 -0
  191. package/scripts/examples/using-assistant-with-mcp.ts +560 -0
  192. package/scripts/examples/using-claude-code.ts +10 -0
  193. package/scripts/examples/using-contentdb.ts +35 -0
  194. package/scripts/examples/using-conversations.ts +35 -0
  195. package/scripts/examples/using-disk-cache.ts +10 -0
  196. package/scripts/examples/using-docker-shell.ts +75 -0
  197. package/scripts/examples/using-elevenlabs.ts +25 -0
  198. package/scripts/examples/using-google-calendar.ts +57 -0
  199. package/scripts/examples/using-google-docs.ts +74 -0
  200. package/scripts/examples/using-google-drive.ts +74 -0
  201. package/scripts/examples/using-google-sheets.ts +89 -0
  202. package/scripts/examples/using-nlp.ts +55 -0
  203. package/scripts/examples/using-ollama.ts +10 -0
  204. package/scripts/examples/using-openai-codex.ts +23 -0
  205. package/scripts/examples/using-postgres.ts +55 -0
  206. package/scripts/examples/using-runpod.ts +32 -0
  207. package/scripts/examples/using-tts.ts +40 -0
  208. package/scripts/examples/vm-loading-esm-modules.ts +16 -0
  209. package/scripts/scaffold.ts +391 -0
  210. package/scripts/scratch.ts +15 -0
  211. package/scripts/test-command-listener.ts +123 -0
  212. package/scripts/test-window-manager-lifecycle.ts +86 -0
  213. package/scripts/test-window-manager.ts +43 -0
  214. package/scripts/update-introspection-data.ts +58 -0
  215. package/src/agi/README.md +14 -0
  216. package/src/agi/container.server.ts +114 -0
  217. package/src/agi/endpoints/ask.ts +60 -0
  218. package/src/agi/endpoints/conversations/[id].ts +45 -0
  219. package/src/agi/endpoints/conversations.ts +31 -0
  220. package/src/agi/endpoints/experts.ts +37 -0
  221. package/src/agi/features/assistant.ts +767 -0
  222. package/src/agi/features/assistants-manager.ts +260 -0
  223. package/src/agi/features/claude-code.ts +1111 -0
  224. package/src/agi/features/conversation-history.ts +497 -0
  225. package/src/agi/features/conversation.ts +799 -0
  226. package/src/agi/features/openai-codex.ts +631 -0
  227. package/src/agi/features/openapi.ts +438 -0
  228. package/src/agi/features/skills-library.ts +425 -0
  229. package/src/agi/index.ts +6 -0
  230. package/src/agi/lib/token-counter.ts +122 -0
  231. package/src/browser.ts +25 -0
  232. package/src/bus.ts +100 -0
  233. package/src/cli/cli.ts +70 -0
  234. package/src/client.ts +461 -0
  235. package/src/clients/civitai/index.ts +541 -0
  236. package/src/clients/client-template.ts +41 -0
  237. package/src/clients/comfyui/index.ts +597 -0
  238. package/src/clients/elevenlabs/index.ts +291 -0
  239. package/src/clients/openai/index.ts +451 -0
  240. package/src/clients/supabase/index.ts +366 -0
  241. package/src/command.ts +164 -0
  242. package/src/commands/chat.ts +182 -0
  243. package/src/commands/console.ts +192 -0
  244. package/src/commands/describe.ts +433 -0
  245. package/src/commands/eval.ts +116 -0
  246. package/src/commands/help.ts +214 -0
  247. package/src/commands/index.ts +14 -0
  248. package/src/commands/mcp.ts +64 -0
  249. package/src/commands/prompt.ts +807 -0
  250. package/src/commands/run.ts +257 -0
  251. package/src/commands/sandbox-mcp.ts +439 -0
  252. package/src/commands/scaffold.ts +79 -0
  253. package/src/commands/serve.ts +172 -0
  254. package/src/container.ts +781 -0
  255. package/src/endpoint.ts +340 -0
  256. package/src/feature.ts +75 -0
  257. package/src/hash-object.ts +97 -0
  258. package/src/helper.ts +543 -0
  259. package/src/introspection/generated.agi.ts +23388 -0
  260. package/src/introspection/generated.node.ts +18899 -0
  261. package/src/introspection/generated.web.ts +2021 -0
  262. package/src/introspection/index.ts +256 -0
  263. package/src/introspection/scan.ts +912 -0
  264. package/src/node/container.ts +354 -0
  265. package/src/node/feature.ts +13 -0
  266. package/src/node/features/container-link.ts +558 -0
  267. package/src/node/features/content-db.ts +475 -0
  268. package/src/node/features/disk-cache.ts +382 -0
  269. package/src/node/features/dns.ts +655 -0
  270. package/src/node/features/docker.ts +912 -0
  271. package/src/node/features/downloader.ts +92 -0
  272. package/src/node/features/esbuild.ts +68 -0
  273. package/src/node/features/file-manager.ts +357 -0
  274. package/src/node/features/fs.ts +534 -0
  275. package/src/node/features/git.ts +492 -0
  276. package/src/node/features/google-auth.ts +502 -0
  277. package/src/node/features/google-calendar.ts +300 -0
  278. package/src/node/features/google-docs.ts +404 -0
  279. package/src/node/features/google-drive.ts +339 -0
  280. package/src/node/features/google-sheets.ts +279 -0
  281. package/src/node/features/grep.ts +406 -0
  282. package/src/node/features/helpers.ts +374 -0
  283. package/src/node/features/ink.ts +490 -0
  284. package/src/node/features/ipc-socket.ts +459 -0
  285. package/src/node/features/json-tree.ts +188 -0
  286. package/src/node/features/launcher-app-command-listener.ts +388 -0
  287. package/src/node/features/networking.ts +925 -0
  288. package/src/node/features/nlp.ts +211 -0
  289. package/src/node/features/opener.ts +166 -0
  290. package/src/node/features/os.ts +157 -0
  291. package/src/node/features/package-finder.ts +539 -0
  292. package/src/node/features/port-exposer.ts +342 -0
  293. package/src/node/features/postgres.ts +273 -0
  294. package/src/node/features/proc.ts +502 -0
  295. package/src/node/features/process-manager.ts +542 -0
  296. package/src/node/features/python.ts +444 -0
  297. package/src/node/features/repl.ts +194 -0
  298. package/src/node/features/runpod.ts +802 -0
  299. package/src/node/features/secure-shell.ts +248 -0
  300. package/src/node/features/semantic-search.ts +924 -0
  301. package/src/node/features/sqlite.ts +289 -0
  302. package/src/node/features/telegram.ts +342 -0
  303. package/src/node/features/tts.ts +184 -0
  304. package/src/node/features/ui.ts +857 -0
  305. package/src/node/features/vault.ts +164 -0
  306. package/src/node/features/vm.ts +312 -0
  307. package/src/node/features/window-manager.ts +804 -0
  308. package/src/node/features/yaml-tree.ts +149 -0
  309. package/src/node/features/yaml.ts +132 -0
  310. package/src/node.ts +70 -0
  311. package/src/react/index.ts +175 -0
  312. package/src/registry.ts +199 -0
  313. package/src/scaffolds/generated.ts +1613 -0
  314. package/src/scaffolds/template.ts +37 -0
  315. package/src/schemas/base.ts +255 -0
  316. package/src/server.ts +135 -0
  317. package/src/servers/express.ts +209 -0
  318. package/src/servers/mcp.ts +805 -0
  319. package/src/servers/socket.ts +120 -0
  320. package/src/state.ts +101 -0
  321. package/src/web/clients/socket.ts +82 -0
  322. package/src/web/container.ts +74 -0
  323. package/src/web/extension.ts +30 -0
  324. package/src/web/feature.ts +12 -0
  325. package/src/web/features/asset-loader.ts +64 -0
  326. package/src/web/features/container-link.ts +385 -0
  327. package/src/web/features/esbuild.ts +79 -0
  328. package/src/web/features/helpers.ts +267 -0
  329. package/src/web/features/network.ts +61 -0
  330. package/src/web/features/speech.ts +87 -0
  331. package/src/web/features/vault.ts +189 -0
  332. package/src/web/features/vm.ts +78 -0
  333. package/src/web/features/voice-recognition.ts +129 -0
  334. package/src/web/shims/isomorphic-vm.ts +149 -0
  335. package/test/bus.test.ts +134 -0
  336. package/test/clients-servers.test.ts +216 -0
  337. package/test/container-link.test.ts +274 -0
  338. package/test/features.test.ts +160 -0
  339. package/test/integration.test.ts +787 -0
  340. package/test/node-container.test.ts +121 -0
  341. package/test/rate-limit.test.ts +272 -0
  342. package/test/semantic-search.test.ts +550 -0
  343. package/test/state.test.ts +121 -0
  344. package/test-integration/assistant.test.ts +138 -0
  345. package/test-integration/assistants-manager.test.ts +123 -0
  346. package/test-integration/claude-code.test.ts +98 -0
  347. package/test-integration/conversation-history.test.ts +205 -0
  348. package/test-integration/conversation.test.ts +137 -0
  349. package/test-integration/elevenlabs.test.ts +55 -0
  350. package/test-integration/google-services.test.ts +80 -0
  351. package/test-integration/helpers.ts +89 -0
  352. package/test-integration/openai-codex.test.ts +93 -0
  353. package/test-integration/runpod.test.ts +58 -0
  354. package/test-integration/server-endpoints.test.ts +97 -0
  355. package/test-integration/skills-library.test.ts +157 -0
  356. package/test-integration/telegram.test.ts +46 -0
  357. package/tsconfig.json +58 -0
  358. package/uv.lock +8 -0
@@ -0,0 +1,164 @@
1
+ import { z } from 'zod'
2
+ import { FeatureStateSchema, FeatureOptionsSchema } from '../../schemas/base.js'
3
+ import crypto from 'node:crypto'
4
+ import { Feature, features } from '../feature.js'
5
+ import { type NodeContainer } from '../container.js'
6
+ import { type ContainerContext } from '../../container.js'
7
+
8
+ export const VaultStateSchema = FeatureStateSchema.extend({
9
+ /** Secret key buffer used for encryption/decryption */
10
+ secret: z.custom<Buffer>().optional().describe('Secret key buffer used for encryption/decryption'),
11
+ })
12
+ export type VaultState = z.infer<typeof VaultStateSchema>
13
+
14
+ export const VaultOptionsSchema = FeatureOptionsSchema.extend({
15
+ /** Secret key as Buffer or base64 string for encryption */
16
+ secret: z.union([z.custom<Buffer>(), z.string()]).optional().describe('Secret key as Buffer or base64 string for encryption'),
17
+ })
18
+ export type VaultOptions = z.infer<typeof VaultOptionsSchema>
19
+
20
+ /**
21
+ * The Vault feature provides encryption and decryption capabilities using AES-256-GCM.
22
+ *
23
+ * This feature allows you to securely encrypt and decrypt sensitive data using
24
+ * industry-standard encryption. It manages secret keys and provides a simple
25
+ * interface for cryptographic operations.
26
+ *
27
+ * @example
28
+ * ```typescript
29
+ * const vault = container.feature('vault')
30
+ *
31
+ * // Encrypt sensitive data
32
+ * const encrypted = vault.encrypt('sensitive information')
33
+ * console.log(encrypted) // Base64 encoded encrypted data
34
+ *
35
+ * // Decrypt the data
36
+ * const decrypted = vault.decrypt(encrypted)
37
+ * console.log(decrypted) // 'sensitive information'
38
+ * ```
39
+ *
40
+ * @extends Feature
41
+ */
42
+ export class Vault extends Feature<VaultState, VaultOptions> {
43
+ static override shortcut = 'features.vault' as const
44
+ static override stateSchema = VaultStateSchema
45
+ static override optionsSchema = VaultOptionsSchema
46
+
47
+ /**
48
+ * Attach hook for the Vault feature. Currently a no-op placeholder
49
+ * for future container-level initialization logic.
50
+ *
51
+ * @param c - The node container instance
52
+ */
53
+ static attach(c: NodeContainer) {
54
+
55
+ }
56
+
57
+ constructor(options: VaultOptions, context: ContainerContext) {
58
+ let secret = options.secret
59
+
60
+ if (typeof secret === 'string') {
61
+ secret = Buffer.from(secret, 'base64')
62
+ }
63
+
64
+ super({ ...options, secret }, context)
65
+
66
+ this.state.set('secret', secret)
67
+ }
68
+
69
+ /**
70
+ * Gets the secret key as a base64-encoded string.
71
+ *
72
+ * @returns {string | undefined} The secret key encoded as base64, or undefined if no secret is set
73
+ */
74
+ get secretText() {
75
+ return this.state.get('secret')!?.toString('base64')
76
+ }
77
+
78
+ /**
79
+ * Gets or generates a secret key for encryption operations.
80
+ *
81
+ * @param {object} [options={}] - Options for secret key handling
82
+ * @param {boolean} [options.refresh=false] - Whether to generate a new secret key
83
+ * @param {boolean} [options.set=true] - Whether to store the generated key in state
84
+ * @returns {Buffer} The secret key as a Buffer
85
+ */
86
+ secret({ refresh = false, set = true } = {}) : Buffer {
87
+ if (!refresh && this.state.get('secret')) {
88
+ return this.state.get('secret')!
89
+ }
90
+
91
+ const val = generateSecretKey()
92
+
93
+ if(set && !this.state.get('secret')) {
94
+ this.state.set('secret', val)
95
+ }
96
+
97
+ return val
98
+ }
99
+
100
+ /**
101
+ * Decrypts an encrypted payload that was created by the encrypt method.
102
+ *
103
+ * @param {string} payload - The encrypted payload to decrypt (base64 encoded with delimiters)
104
+ * @returns {string} The decrypted plaintext
105
+ * @throws {Error} Throws an error if decryption fails or the payload is malformed
106
+ */
107
+ decrypt(payload: string) {
108
+ const [iv, ciphertext, authTag] = payload.split('\n------\n').map((v) => Buffer.from(v, 'base64'))
109
+ return this._decrypt(ciphertext!, iv!, authTag!)
110
+ }
111
+
112
+ /**
113
+ * Encrypts a plaintext string using AES-256-GCM encryption.
114
+ *
115
+ * @param {string} payload - The plaintext string to encrypt
116
+ * @returns {string} The encrypted payload as a base64 encoded string with delimiters
117
+ */
118
+ encrypt(payload: string) {
119
+ const { iv, ciphertext, authTag } = this._encrypt(payload)
120
+
121
+ return [
122
+ iv.toString('base64'),
123
+ ciphertext.toString('base64'),
124
+ authTag.toString('base64')
125
+ ].join('\n------\n')
126
+ }
127
+
128
+ private _encrypt(payload: string) {
129
+ const secret = this.secret()
130
+ const { iv, ciphertext, authTag } = encrypt(payload, secret)
131
+ return { iv, ciphertext, authTag }
132
+ }
133
+
134
+ private _decrypt(cipher: Buffer, iv: Buffer, authTag: Buffer) {
135
+ return decrypt(cipher, this.secret(), iv, authTag)
136
+ }
137
+ }
138
+
139
+ export default features.register('vault', Vault)
140
+
141
+ function generateSecretKey(): Buffer {
142
+ return crypto.randomBytes(32);
143
+ }
144
+
145
+ type EncryptionResult = {
146
+ iv: Buffer;
147
+ ciphertext: Buffer;
148
+ authTag: Buffer;
149
+ };
150
+
151
+ function encrypt(plaintext: string, secretKey: Buffer): EncryptionResult {
152
+ const iv = crypto.randomBytes(12);
153
+ const cipher = crypto.createCipheriv("aes-256-gcm", secretKey, iv);
154
+ const ciphertext = Buffer.concat([cipher.update(plaintext, "utf8"), cipher.final()]);
155
+ const authTag = cipher.getAuthTag();
156
+ return { iv, ciphertext, authTag };
157
+ }
158
+
159
+ function decrypt(ciphertext: Buffer, secretKey: Buffer, iv: Buffer, authTag: Buffer): string {
160
+ const decipher = crypto.createDecipheriv("aes-256-gcm", secretKey, iv);
161
+ decipher.setAuthTag(authTag);
162
+ const plaintext = Buffer.concat([decipher.update(ciphertext), decipher.final()]).toString("utf8");
163
+ return plaintext;
164
+ }
@@ -0,0 +1,312 @@
1
+ import { z } from 'zod'
2
+ import { createRequire } from 'module'
3
+ import { FeatureStateSchema, FeatureOptionsSchema } from '../../schemas/base.js'
4
+ import vm from 'vm'
5
+ import { Feature, features } from "../feature.js";
6
+
7
+ export const VMStateSchema = FeatureStateSchema.extend({})
8
+ export type VMState = z.infer<typeof VMStateSchema>
9
+
10
+ export const VMOptionsSchema = FeatureOptionsSchema.extend({
11
+ /** Default context object to inject into the VM execution environment */
12
+ context: z.any().describe('Default context object to inject into the VM execution environment'),
13
+ })
14
+ export type VMOptions = z.infer<typeof VMOptionsSchema>
15
+
16
+ /**
17
+ * The VM feature provides Node.js virtual machine capabilities for executing JavaScript code.
18
+ *
19
+ * This feature wraps Node.js's built-in `vm` module to provide secure code execution
20
+ * in isolated contexts. It's useful for running untrusted code, creating sandboxed
21
+ * environments, or dynamically executing code with controlled access to variables and modules.
22
+ *
23
+ * @example
24
+ * ```typescript
25
+ * const vm = container.feature('vm')
26
+ *
27
+ * // Execute simple code
28
+ * const result = vm.run('1 + 2 + 3')
29
+ * console.log(result) // 6
30
+ *
31
+ * // Execute code with custom context
32
+ * const result2 = vm.run('greeting + " " + name', {
33
+ * greeting: 'Hello',
34
+ * name: 'World'
35
+ * })
36
+ * console.log(result2) // 'Hello World'
37
+ * ```
38
+ *
39
+ * @extends Feature
40
+ */
41
+ export class VM<
42
+ T extends VMState = VMState,
43
+ K extends VMOptions = VMOptions
44
+ > extends Feature<T, K> {
45
+ static override shortcut = "features.vm" as const
46
+ static override stateSchema = VMStateSchema
47
+ static override optionsSchema = VMOptionsSchema
48
+
49
+ /** Map of virtual module IDs to their exports, consulted before Node's native require */
50
+ modules: Map<string, any> = new Map()
51
+
52
+ /**
53
+ * Register a virtual module that will be available to `require()` inside VM-executed code.
54
+ * Modules registered here take precedence over Node's native resolution.
55
+ *
56
+ * @param id - The module specifier (e.g. `'@soederpop/luca'`, `'zod'`)
57
+ * @param exports - The module's exports object
58
+ *
59
+ * @example
60
+ * ```typescript
61
+ * const vm = container.feature('vm')
62
+ * vm.defineModule('@soederpop/luca', { Container, Feature, fs, proc })
63
+ * vm.defineModule('zod', { z })
64
+ *
65
+ * // Now loadModule can resolve these in user code:
66
+ * // import { Container } from '@soederpop/luca' → works
67
+ * ```
68
+ */
69
+ defineModule(id: string, exports: any) {
70
+ this.modules.set(id, exports)
71
+ }
72
+
73
+ /**
74
+ * Build a require function that resolves from the virtual modules map first,
75
+ * falling back to Node's native `createRequire` for everything else.
76
+ *
77
+ * @param filePath - The file path to scope native require resolution to
78
+ * @returns A require function with `.resolve` preserved from the native require
79
+ */
80
+ createRequireFor(filePath: string) {
81
+ const nodeRequire = createRequire(filePath)
82
+ const modules = this.modules
83
+
84
+ const customRequire = (id: string) => {
85
+ if (modules.has(id)) return modules.get(id)
86
+ return nodeRequire(id)
87
+ }
88
+
89
+ customRequire.resolve = nodeRequire.resolve.bind(nodeRequire)
90
+ return customRequire
91
+ }
92
+
93
+ /**
94
+ * Creates a new VM script from the provided code.
95
+ *
96
+ * This method compiles JavaScript code into a VM script that can be executed
97
+ * multiple times in different contexts. The script is pre-compiled for better
98
+ * performance when executing the same code repeatedly.
99
+ *
100
+ * @param {string} code - The JavaScript code to compile into a script
101
+ * @param {vm.ScriptOptions} [options] - Options for script compilation
102
+ * @returns {vm.Script} A compiled VM script ready for execution
103
+ *
104
+ * @example
105
+ * ```typescript
106
+ * const script = vm.createScript('Math.max(a, b)')
107
+ *
108
+ * // Execute the script multiple times with different contexts
109
+ * const result1 = script.runInContext(vm.createContext({ a: 5, b: 3 }))
110
+ * const result2 = script.runInContext(vm.createContext({ a: 10, b: 20 }))
111
+ * ```
112
+ */
113
+ createScript(code: string, options?: vm.ScriptOptions) {
114
+ return new vm.Script(code, {
115
+ ...options
116
+ })
117
+ }
118
+
119
+ /**
120
+ * Check whether an object has already been contextified by `vm.createContext()`.
121
+ *
122
+ * Useful to avoid double-contextifying when you're not sure if the caller
123
+ * passed a plain object or an existing context.
124
+ *
125
+ * @param ctx - The object to check
126
+ * @returns True if the object is a VM context
127
+ *
128
+ * @example
129
+ * ```typescript
130
+ * const ctx = vm.createContext({ x: 1 })
131
+ * vm.isContext(ctx) // true
132
+ * vm.isContext({ x: 1 }) // false
133
+ * ```
134
+ */
135
+ isContext(ctx: unknown): ctx is vm.Context {
136
+ return typeof ctx === 'object' && ctx !== null && vm.isContext(ctx as vm.Context)
137
+ }
138
+
139
+ /**
140
+ * Create an isolated JavaScript execution context.
141
+ *
142
+ * Combines the container's context with any additional variables provided.
143
+ * If the input is already a VM context, it is returned as-is.
144
+ *
145
+ * @param ctx - Additional context variables to include
146
+ * @returns A VM context ready for script execution
147
+ *
148
+ * @example
149
+ * ```typescript
150
+ * const context = vm.createContext({ user: { name: 'John' } })
151
+ * const result = vm.runSync('user.name', context)
152
+ * ```
153
+ */
154
+ createContext(ctx: any = {}) {
155
+ if (this.isContext(ctx)) return ctx
156
+ return vm.createContext({
157
+ ...this.container.context,
158
+ ...ctx
159
+ })
160
+ }
161
+
162
+ /**
163
+ * Executes JavaScript code in a controlled environment.
164
+ *
165
+ * This method creates a script from the provided code, sets up an execution context
166
+ * with the specified variables, and runs the code safely. It handles errors gracefully
167
+ * and returns either the result or the error object.
168
+ *
169
+ * @param {string} code - The JavaScript code to execute
170
+ * @param {any} [ctx={}] - Context variables to make available to the executing code
171
+ * @returns {any} The result of the code execution, or an Error object if execution failed
172
+ *
173
+ * @example
174
+ * ```typescript
175
+ * // Simple calculation
176
+ * const result = vm.run('2 + 3 * 4')
177
+ * console.log(result) // 14
178
+ *
179
+ * // Using context variables
180
+ * const greeting = vm.run('`Hello ${name}!`', { name: 'Alice' })
181
+ * console.log(greeting) // 'Hello Alice!'
182
+ *
183
+ * // Array operations
184
+ * const sum = vm.run('numbers.reduce((a, b) => a + b, 0)', {
185
+ * numbers: [1, 2, 3, 4, 5]
186
+ * })
187
+ * console.log(sum) // 15
188
+ *
189
+ * // Error handling
190
+ * const error = vm.run('invalidFunction()')
191
+ * if (error instanceof Error) {
192
+ * console.log('Execution failed:', error.message)
193
+ * }
194
+ * ```
195
+ */
196
+ async run<T extends any>(code: string, ctx: any = {}): Promise<T> {
197
+ const script = this.createScript(code)
198
+ const context = this.isContext(ctx) ? ctx : this.createContext(ctx)
199
+
200
+ return (await script.runInContext(context)) as T
201
+ }
202
+
203
+ /**
204
+ * Execute JavaScript code synchronously in a controlled environment.
205
+ *
206
+ * @param code - The JavaScript code to execute
207
+ * @param ctx - Context variables to make available to the executing code
208
+ * @returns The result of the code execution
209
+ *
210
+ * @example
211
+ * ```typescript
212
+ * const sum = vm.runSync('a + b', { a: 2, b: 3 })
213
+ * console.log(sum) // 5
214
+ * ```
215
+ */
216
+ runSync<T extends any = any>(code: string, ctx: any = {}): T {
217
+ const script = this.createScript(code)
218
+ const context = this.isContext(ctx) ? ctx : this.createContext(ctx)
219
+
220
+ return script.runInContext(context) as T
221
+ }
222
+
223
+ /**
224
+ * Execute code asynchronously and return both the result and the execution context.
225
+ *
226
+ * Unlike `run`, this method also returns the context object, allowing you to inspect
227
+ * variables set during execution.
228
+ *
229
+ * @param code - The JavaScript code to execute
230
+ * @param ctx - Context variables to make available to the executing code
231
+ * @returns The execution result and the context object
232
+ *
233
+ * @example
234
+ * ```typescript
235
+ * const { result, context } = await vm.perform('x = 42; x * 2', { x: 0 })
236
+ * console.log(result) // 84
237
+ * console.log(context.x) // 42
238
+ * ```
239
+ */
240
+ async perform<T extends any>(code: string, ctx: any = {}): Promise<{ result: T, context: vm.Context }> {
241
+ const script = this.createScript(code)
242
+ const context = this.isContext(ctx) ? ctx : this.createContext(ctx)
243
+
244
+ return { result: (await script.runInContext(context)) as T, context }
245
+ }
246
+
247
+ /**
248
+ * Executes JavaScript code synchronously and returns both the result and the execution context.
249
+ *
250
+ * Unlike `runSync`, this method also returns the context object, allowing you to inspect
251
+ * variables set during execution (e.g. `module.exports`). This is the synchronous equivalent
252
+ * of `perform()`.
253
+ *
254
+ * @param {string} code - The JavaScript code to execute
255
+ * @param {any} [ctx={}] - Context variables to make available to the executing code
256
+ * @returns {{ result: T, context: vm.Context }} The execution result and the context object
257
+ *
258
+ * @example
259
+ * ```typescript
260
+ * const { result, context } = vm.performSync(code, {
261
+ * exports: {},
262
+ * module: { exports: {} },
263
+ * })
264
+ * const moduleExports = context.module?.exports || context.exports
265
+ * ```
266
+ */
267
+ performSync<T extends any = any>(code: string, ctx: any = {}): { result: T, context: vm.Context } {
268
+ const script = this.createScript(code)
269
+ const context = this.isContext(ctx) ? ctx : this.createContext(ctx)
270
+
271
+ return { result: script.runInContext(context) as T, context }
272
+ }
273
+
274
+ /**
275
+ * Synchronously loads a JavaScript/TypeScript module from a file path, executing it
276
+ * in an isolated VM context and returning its exports. The module gets `require`,
277
+ * `exports`, and `module` globals automatically, plus any additional context you provide.
278
+ *
279
+ * @param {string} filePath - Absolute path to the module file to load
280
+ * @param {any} [ctx={}] - Additional context variables to inject into the module's execution environment
281
+ * @returns {Record<string, any>} The module's exports (from `module.exports` or `exports`)
282
+ *
283
+ * @example
284
+ * ```typescript
285
+ * const vm = container.feature('vm')
286
+ *
287
+ * // Load a tools module, injecting the container
288
+ * const tools = vm.loadModule('/path/to/tools.ts', { container, me: assistant })
289
+ * // tools.myFunction, tools.schemas, etc.
290
+ * ```
291
+ */
292
+ loadModule(filePath: string, ctx: any = {}): Record<string, any> {
293
+ const { fs } = this.container
294
+
295
+ if (!fs.exists(filePath)) return {}
296
+
297
+ const raw = fs.readFile(filePath)
298
+ const { code } = this.container.feature('esbuild').transformSync(raw, { format: 'cjs' })
299
+
300
+ const { context } = this.performSync(code, {
301
+ require: this.createRequireFor(filePath),
302
+ exports: {},
303
+ module: { exports: {} },
304
+ console,
305
+ ...ctx,
306
+ })
307
+
308
+ return context.module?.exports || context.exports || {}
309
+ }
310
+ }
311
+
312
+ export default features.register("vm", VM);