@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.
- package/CLAUDE.md +71 -0
- package/README.md +78 -0
- package/bun.lock +2928 -0
- package/bunfig.toml +3 -0
- package/commands/audit-docs.ts +740 -0
- package/commands/build-scaffolds.ts +154 -0
- package/commands/generate-api-docs.ts +114 -0
- package/commands/update-introspection.ts +67 -0
- package/docs/CLI.md +335 -0
- package/docs/README.md +88 -0
- package/docs/TABLE-OF-CONTENTS.md +157 -0
- package/docs/apis/clients/elevenlabs.md +84 -0
- package/docs/apis/clients/graph.md +56 -0
- package/docs/apis/clients/openai.md +69 -0
- package/docs/apis/clients/rest.md +41 -0
- package/docs/apis/clients/websocket.md +107 -0
- package/docs/apis/features/agi/assistant.md +471 -0
- package/docs/apis/features/agi/assistants-manager.md +154 -0
- package/docs/apis/features/agi/claude-code.md +602 -0
- package/docs/apis/features/agi/conversation-history.md +352 -0
- package/docs/apis/features/agi/conversation.md +333 -0
- package/docs/apis/features/agi/docs-reader.md +121 -0
- package/docs/apis/features/agi/openai-codex.md +318 -0
- package/docs/apis/features/agi/openapi.md +138 -0
- package/docs/apis/features/agi/semantic-search.md +387 -0
- package/docs/apis/features/agi/skills-library.md +216 -0
- package/docs/apis/features/node/container-link.md +133 -0
- package/docs/apis/features/node/content-db.md +313 -0
- package/docs/apis/features/node/disk-cache.md +379 -0
- package/docs/apis/features/node/dns.md +651 -0
- package/docs/apis/features/node/docker.md +705 -0
- package/docs/apis/features/node/downloader.md +81 -0
- package/docs/apis/features/node/esbuild.md +59 -0
- package/docs/apis/features/node/file-manager.md +182 -0
- package/docs/apis/features/node/fs.md +581 -0
- package/docs/apis/features/node/git.md +330 -0
- package/docs/apis/features/node/google-auth.md +174 -0
- package/docs/apis/features/node/google-calendar.md +187 -0
- package/docs/apis/features/node/google-docs.md +151 -0
- package/docs/apis/features/node/google-drive.md +225 -0
- package/docs/apis/features/node/google-sheets.md +179 -0
- package/docs/apis/features/node/grep.md +290 -0
- package/docs/apis/features/node/helpers.md +135 -0
- package/docs/apis/features/node/ink.md +334 -0
- package/docs/apis/features/node/ipc-socket.md +260 -0
- package/docs/apis/features/node/json-tree.md +86 -0
- package/docs/apis/features/node/launcher-app-command-listener.md +145 -0
- package/docs/apis/features/node/networking.md +281 -0
- package/docs/apis/features/node/nlp.md +133 -0
- package/docs/apis/features/node/opener.md +97 -0
- package/docs/apis/features/node/os.md +118 -0
- package/docs/apis/features/node/package-finder.md +402 -0
- package/docs/apis/features/node/postgres.md +212 -0
- package/docs/apis/features/node/proc.md +430 -0
- package/docs/apis/features/node/process-manager.md +210 -0
- package/docs/apis/features/node/python.md +278 -0
- package/docs/apis/features/node/repl.md +88 -0
- package/docs/apis/features/node/runpod.md +673 -0
- package/docs/apis/features/node/secure-shell.md +169 -0
- package/docs/apis/features/node/semantic-search.md +401 -0
- package/docs/apis/features/node/sqlite.md +211 -0
- package/docs/apis/features/node/telegram.md +254 -0
- package/docs/apis/features/node/tts.md +118 -0
- package/docs/apis/features/node/ui.md +703 -0
- package/docs/apis/features/node/vault.md +64 -0
- package/docs/apis/features/node/vm.md +84 -0
- package/docs/apis/features/node/window-manager.md +337 -0
- package/docs/apis/features/node/yaml-tree.md +85 -0
- package/docs/apis/features/node/yaml.md +176 -0
- package/docs/apis/features/web/asset-loader.md +47 -0
- package/docs/apis/features/web/container-link.md +133 -0
- package/docs/apis/features/web/esbuild.md +59 -0
- package/docs/apis/features/web/helpers.md +135 -0
- package/docs/apis/features/web/network.md +30 -0
- package/docs/apis/features/web/speech.md +55 -0
- package/docs/apis/features/web/vault.md +64 -0
- package/docs/apis/features/web/vm.md +84 -0
- package/docs/apis/features/web/voice.md +67 -0
- package/docs/apis/servers/express.md +127 -0
- package/docs/apis/servers/mcp.md +213 -0
- package/docs/apis/servers/websocket.md +99 -0
- package/docs/documentation-audit.md +134 -0
- package/docs/examples/content-db.md +77 -0
- package/docs/examples/disk-cache.md +83 -0
- package/docs/examples/docker.md +101 -0
- package/docs/examples/downloader.md +70 -0
- package/docs/examples/esbuild.md +80 -0
- package/docs/examples/file-manager.md +82 -0
- package/docs/examples/fs.md +83 -0
- package/docs/examples/git.md +85 -0
- package/docs/examples/google-auth.md +88 -0
- package/docs/examples/google-calendar.md +94 -0
- package/docs/examples/google-docs.md +82 -0
- package/docs/examples/google-drive.md +96 -0
- package/docs/examples/google-sheets.md +95 -0
- package/docs/examples/grep.md +85 -0
- package/docs/examples/ink-blocks.md +75 -0
- package/docs/examples/ink-renderer.md +41 -0
- package/docs/examples/ink.md +103 -0
- package/docs/examples/ipc-socket.md +103 -0
- package/docs/examples/json-tree.md +91 -0
- package/docs/examples/launcher-app-command-listener.md +120 -0
- package/docs/examples/networking.md +58 -0
- package/docs/examples/nlp.md +91 -0
- package/docs/examples/opener.md +78 -0
- package/docs/examples/os.md +72 -0
- package/docs/examples/package-finder.md +89 -0
- package/docs/examples/port-exposer.md +89 -0
- package/docs/examples/postgres.md +91 -0
- package/docs/examples/proc.md +81 -0
- package/docs/examples/process-manager.md +79 -0
- package/docs/examples/python.md +91 -0
- package/docs/examples/repl.md +93 -0
- package/docs/examples/runpod.md +119 -0
- package/docs/examples/secure-shell.md +92 -0
- package/docs/examples/sqlite.md +86 -0
- package/docs/examples/telegram.md +77 -0
- package/docs/examples/tts.md +86 -0
- package/docs/examples/ui.md +80 -0
- package/docs/examples/vault.md +70 -0
- package/docs/examples/vm.md +86 -0
- package/docs/examples/window-manager.md +125 -0
- package/docs/examples/yaml-tree.md +93 -0
- package/docs/examples/yaml.md +104 -0
- package/docs/ideas/class-registration-refactor-possibilities.md +197 -0
- package/docs/ideas/container-use-api.md +9 -0
- package/docs/ideas/easy-auth-for-express-servers-and-luca-serve.md +0 -0
- package/docs/ideas/feature-stacks.md +22 -0
- package/docs/ideas/luca-cli-self-sufficiency-demo.md +23 -0
- package/docs/ideas/mcp-design.md +9 -0
- package/docs/ideas/web-container-debugging-feature.md +13 -0
- package/docs/introspection-audit.md +49 -0
- package/docs/introspection.md +154 -0
- package/docs/mcp/readme.md +162 -0
- package/docs/models.ts +38 -0
- package/docs/philosophy.md +85 -0
- package/docs/principles.md +7 -0
- package/docs/prompts/audit-codebase-for-failures-to-use-the-container.md +34 -0
- package/docs/prompts/mcp-test-easy-command.md +27 -0
- package/docs/reports/assistant-bugs.md +38 -0
- package/docs/reports/attach-pattern-usage.md +18 -0
- package/docs/reports/code-audit-results.md +391 -0
- package/docs/reports/introspection-audit-tasks.md +378 -0
- package/docs/reports/luca-mcp-improvements.md +128 -0
- package/docs/scaffolds/client.md +140 -0
- package/docs/scaffolds/command.md +106 -0
- package/docs/scaffolds/endpoint.md +176 -0
- package/docs/scaffolds/feature.md +148 -0
- package/docs/scaffolds/server.md +187 -0
- package/docs/tasks/web-container-helper-discovery.md +71 -0
- package/docs/todos.md +1 -0
- package/docs/tutorials/01-getting-started.md +106 -0
- package/docs/tutorials/02-container.md +210 -0
- package/docs/tutorials/03-scripts.md +194 -0
- package/docs/tutorials/04-features-overview.md +196 -0
- package/docs/tutorials/05-state-and-events.md +171 -0
- package/docs/tutorials/06-servers.md +157 -0
- package/docs/tutorials/07-endpoints.md +198 -0
- package/docs/tutorials/08-commands.md +171 -0
- package/docs/tutorials/09-clients.md +162 -0
- package/docs/tutorials/10-creating-features.md +198 -0
- package/docs/tutorials/11-contentbase.md +191 -0
- package/docs/tutorials/12-assistants.md +215 -0
- package/docs/tutorials/13-introspection.md +147 -0
- package/docs/tutorials/14-type-system.md +174 -0
- package/docs/tutorials/15-project-patterns.md +222 -0
- package/docs/tutorials/16-google-features.md +534 -0
- package/docs/tutorials/17-tui-blocks.md +530 -0
- package/docs/tutorials/18-semantic-search.md +334 -0
- package/index.ts +1 -0
- package/luca.console.ts +9 -0
- package/main.py +6 -0
- package/package.json +154 -0
- package/pyproject.toml +7 -0
- package/scripts/animations/chrome-glitch.ts +55 -0
- package/scripts/animations/index.ts +16 -0
- package/scripts/animations/neon-pulse.ts +64 -0
- package/scripts/animations/types.ts +6 -0
- package/scripts/build-web.ts +28 -0
- package/scripts/examples/ask-luca-expert.ts +42 -0
- package/scripts/examples/assistant-questions.ts +12 -0
- package/scripts/examples/excalidraw-expert.ts +75 -0
- package/scripts/examples/expert-chat.ts +0 -0
- package/scripts/examples/file-manager.ts +14 -0
- package/scripts/examples/ideas.ts +12 -0
- package/scripts/examples/interactive-chat.ts +20 -0
- package/scripts/examples/openai-tool-calls.ts +113 -0
- package/scripts/examples/opening-a-web-browser.ts +5 -0
- package/scripts/examples/telegram-bot.ts +79 -0
- package/scripts/examples/telegram-ink-ui.ts +302 -0
- package/scripts/examples/using-assistant-with-mcp.ts +560 -0
- package/scripts/examples/using-claude-code.ts +10 -0
- package/scripts/examples/using-contentdb.ts +35 -0
- package/scripts/examples/using-conversations.ts +35 -0
- package/scripts/examples/using-disk-cache.ts +10 -0
- package/scripts/examples/using-docker-shell.ts +75 -0
- package/scripts/examples/using-elevenlabs.ts +25 -0
- package/scripts/examples/using-google-calendar.ts +57 -0
- package/scripts/examples/using-google-docs.ts +74 -0
- package/scripts/examples/using-google-drive.ts +74 -0
- package/scripts/examples/using-google-sheets.ts +89 -0
- package/scripts/examples/using-nlp.ts +55 -0
- package/scripts/examples/using-ollama.ts +10 -0
- package/scripts/examples/using-openai-codex.ts +23 -0
- package/scripts/examples/using-postgres.ts +55 -0
- package/scripts/examples/using-runpod.ts +32 -0
- package/scripts/examples/using-tts.ts +40 -0
- package/scripts/examples/vm-loading-esm-modules.ts +16 -0
- package/scripts/scaffold.ts +391 -0
- package/scripts/scratch.ts +15 -0
- package/scripts/test-command-listener.ts +123 -0
- package/scripts/test-window-manager-lifecycle.ts +86 -0
- package/scripts/test-window-manager.ts +43 -0
- package/scripts/update-introspection-data.ts +58 -0
- package/src/agi/README.md +14 -0
- package/src/agi/container.server.ts +114 -0
- package/src/agi/endpoints/ask.ts +60 -0
- package/src/agi/endpoints/conversations/[id].ts +45 -0
- package/src/agi/endpoints/conversations.ts +31 -0
- package/src/agi/endpoints/experts.ts +37 -0
- package/src/agi/features/assistant.ts +767 -0
- package/src/agi/features/assistants-manager.ts +260 -0
- package/src/agi/features/claude-code.ts +1111 -0
- package/src/agi/features/conversation-history.ts +497 -0
- package/src/agi/features/conversation.ts +799 -0
- package/src/agi/features/openai-codex.ts +631 -0
- package/src/agi/features/openapi.ts +438 -0
- package/src/agi/features/skills-library.ts +425 -0
- package/src/agi/index.ts +6 -0
- package/src/agi/lib/token-counter.ts +122 -0
- package/src/browser.ts +25 -0
- package/src/bus.ts +100 -0
- package/src/cli/cli.ts +70 -0
- package/src/client.ts +461 -0
- package/src/clients/civitai/index.ts +541 -0
- package/src/clients/client-template.ts +41 -0
- package/src/clients/comfyui/index.ts +597 -0
- package/src/clients/elevenlabs/index.ts +291 -0
- package/src/clients/openai/index.ts +451 -0
- package/src/clients/supabase/index.ts +366 -0
- package/src/command.ts +164 -0
- package/src/commands/chat.ts +182 -0
- package/src/commands/console.ts +192 -0
- package/src/commands/describe.ts +433 -0
- package/src/commands/eval.ts +116 -0
- package/src/commands/help.ts +214 -0
- package/src/commands/index.ts +14 -0
- package/src/commands/mcp.ts +64 -0
- package/src/commands/prompt.ts +807 -0
- package/src/commands/run.ts +257 -0
- package/src/commands/sandbox-mcp.ts +439 -0
- package/src/commands/scaffold.ts +79 -0
- package/src/commands/serve.ts +172 -0
- package/src/container.ts +781 -0
- package/src/endpoint.ts +340 -0
- package/src/feature.ts +75 -0
- package/src/hash-object.ts +97 -0
- package/src/helper.ts +543 -0
- package/src/introspection/generated.agi.ts +23388 -0
- package/src/introspection/generated.node.ts +18899 -0
- package/src/introspection/generated.web.ts +2021 -0
- package/src/introspection/index.ts +256 -0
- package/src/introspection/scan.ts +912 -0
- package/src/node/container.ts +354 -0
- package/src/node/feature.ts +13 -0
- package/src/node/features/container-link.ts +558 -0
- package/src/node/features/content-db.ts +475 -0
- package/src/node/features/disk-cache.ts +382 -0
- package/src/node/features/dns.ts +655 -0
- package/src/node/features/docker.ts +912 -0
- package/src/node/features/downloader.ts +92 -0
- package/src/node/features/esbuild.ts +68 -0
- package/src/node/features/file-manager.ts +357 -0
- package/src/node/features/fs.ts +534 -0
- package/src/node/features/git.ts +492 -0
- package/src/node/features/google-auth.ts +502 -0
- package/src/node/features/google-calendar.ts +300 -0
- package/src/node/features/google-docs.ts +404 -0
- package/src/node/features/google-drive.ts +339 -0
- package/src/node/features/google-sheets.ts +279 -0
- package/src/node/features/grep.ts +406 -0
- package/src/node/features/helpers.ts +374 -0
- package/src/node/features/ink.ts +490 -0
- package/src/node/features/ipc-socket.ts +459 -0
- package/src/node/features/json-tree.ts +188 -0
- package/src/node/features/launcher-app-command-listener.ts +388 -0
- package/src/node/features/networking.ts +925 -0
- package/src/node/features/nlp.ts +211 -0
- package/src/node/features/opener.ts +166 -0
- package/src/node/features/os.ts +157 -0
- package/src/node/features/package-finder.ts +539 -0
- package/src/node/features/port-exposer.ts +342 -0
- package/src/node/features/postgres.ts +273 -0
- package/src/node/features/proc.ts +502 -0
- package/src/node/features/process-manager.ts +542 -0
- package/src/node/features/python.ts +444 -0
- package/src/node/features/repl.ts +194 -0
- package/src/node/features/runpod.ts +802 -0
- package/src/node/features/secure-shell.ts +248 -0
- package/src/node/features/semantic-search.ts +924 -0
- package/src/node/features/sqlite.ts +289 -0
- package/src/node/features/telegram.ts +342 -0
- package/src/node/features/tts.ts +184 -0
- package/src/node/features/ui.ts +857 -0
- package/src/node/features/vault.ts +164 -0
- package/src/node/features/vm.ts +312 -0
- package/src/node/features/window-manager.ts +804 -0
- package/src/node/features/yaml-tree.ts +149 -0
- package/src/node/features/yaml.ts +132 -0
- package/src/node.ts +70 -0
- package/src/react/index.ts +175 -0
- package/src/registry.ts +199 -0
- package/src/scaffolds/generated.ts +1613 -0
- package/src/scaffolds/template.ts +37 -0
- package/src/schemas/base.ts +255 -0
- package/src/server.ts +135 -0
- package/src/servers/express.ts +209 -0
- package/src/servers/mcp.ts +805 -0
- package/src/servers/socket.ts +120 -0
- package/src/state.ts +101 -0
- package/src/web/clients/socket.ts +82 -0
- package/src/web/container.ts +74 -0
- package/src/web/extension.ts +30 -0
- package/src/web/feature.ts +12 -0
- package/src/web/features/asset-loader.ts +64 -0
- package/src/web/features/container-link.ts +385 -0
- package/src/web/features/esbuild.ts +79 -0
- package/src/web/features/helpers.ts +267 -0
- package/src/web/features/network.ts +61 -0
- package/src/web/features/speech.ts +87 -0
- package/src/web/features/vault.ts +189 -0
- package/src/web/features/vm.ts +78 -0
- package/src/web/features/voice-recognition.ts +129 -0
- package/src/web/shims/isomorphic-vm.ts +149 -0
- package/test/bus.test.ts +134 -0
- package/test/clients-servers.test.ts +216 -0
- package/test/container-link.test.ts +274 -0
- package/test/features.test.ts +160 -0
- package/test/integration.test.ts +787 -0
- package/test/node-container.test.ts +121 -0
- package/test/rate-limit.test.ts +272 -0
- package/test/semantic-search.test.ts +550 -0
- package/test/state.test.ts +121 -0
- package/test-integration/assistant.test.ts +138 -0
- package/test-integration/assistants-manager.test.ts +123 -0
- package/test-integration/claude-code.test.ts +98 -0
- package/test-integration/conversation-history.test.ts +205 -0
- package/test-integration/conversation.test.ts +137 -0
- package/test-integration/elevenlabs.test.ts +55 -0
- package/test-integration/google-services.test.ts +80 -0
- package/test-integration/helpers.ts +89 -0
- package/test-integration/openai-codex.test.ts +93 -0
- package/test-integration/runpod.test.ts +58 -0
- package/test-integration/server-endpoints.test.ts +97 -0
- package/test-integration/skills-library.test.ts +157 -0
- package/test-integration/telegram.test.ts +46 -0
- package/tsconfig.json +58 -0
- 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);
|