luca 2.0.0 → 3.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.github/workflows/release.yaml +169 -0
- package/AGENTS.md +99 -0
- package/CLAUDE.md +115 -0
- package/CNAME +1 -0
- package/README.md +257 -9
- package/RUNME.md +56 -0
- package/assistants/codingAssistant/ABOUT.md +5 -0
- package/assistants/codingAssistant/CORE.md +28 -0
- package/assistants/codingAssistant/hooks.ts +21 -0
- package/assistants/codingAssistant/tools.ts +12 -0
- package/assistants/inkbot/ABOUT.md +16 -0
- package/assistants/inkbot/CORE.md +330 -0
- package/assistants/inkbot/hooks.ts +6 -0
- package/assistants/inkbot/tools.ts +53 -0
- package/assistants/researcher/ABOUT.md +5 -0
- package/assistants/researcher/CORE.md +46 -0
- package/assistants/researcher/hooks.ts +16 -0
- package/assistants/researcher/tools.ts +237 -0
- package/bun.lock +2769 -0
- package/bunfig.toml +3 -0
- package/commands/audit-docs.ts +740 -0
- package/commands/build-bootstrap.ts +118 -0
- package/commands/build-python-bridge.ts +43 -0
- package/commands/build-scaffolds.ts +176 -0
- package/commands/generate-api-docs.ts +114 -0
- package/commands/inkbot.ts +874 -0
- package/commands/release.ts +80 -0
- package/commands/try-all-challenges.ts +543 -0
- package/commands/try-challenge.ts +100 -0
- package/dist/agi/container.server.d.ts +63 -0
- package/dist/agi/container.server.d.ts.map +1 -0
- package/dist/agi/endpoints/ask.d.ts +20 -0
- package/dist/agi/endpoints/ask.d.ts.map +1 -0
- package/dist/agi/endpoints/conversations/[id].d.ts +27 -0
- package/dist/agi/endpoints/conversations/[id].d.ts.map +1 -0
- package/dist/agi/endpoints/conversations.d.ts +18 -0
- package/dist/agi/endpoints/conversations.d.ts.map +1 -0
- package/dist/agi/endpoints/experts.d.ts +8 -0
- package/dist/agi/endpoints/experts.d.ts.map +1 -0
- package/dist/agi/feature.d.ts +9 -0
- package/dist/agi/feature.d.ts.map +1 -0
- package/dist/agi/features/assistant.d.ts +509 -0
- package/dist/agi/features/assistant.d.ts.map +1 -0
- package/dist/agi/features/assistants-manager.d.ts +236 -0
- package/dist/agi/features/assistants-manager.d.ts.map +1 -0
- package/dist/agi/features/autonomous-assistant.d.ts +281 -0
- package/dist/agi/features/autonomous-assistant.d.ts.map +1 -0
- package/dist/agi/features/browser-use.d.ts +479 -0
- package/dist/agi/features/browser-use.d.ts.map +1 -0
- package/dist/agi/features/claude-code.d.ts +824 -0
- package/dist/agi/features/claude-code.d.ts.map +1 -0
- package/dist/agi/features/conversation-history.d.ts +245 -0
- package/dist/agi/features/conversation-history.d.ts.map +1 -0
- package/dist/agi/features/conversation.d.ts +464 -0
- package/dist/agi/features/conversation.d.ts.map +1 -0
- package/dist/agi/features/docs-reader.d.ts +72 -0
- package/dist/agi/features/docs-reader.d.ts.map +1 -0
- package/dist/agi/features/file-tools.d.ts +110 -0
- package/dist/agi/features/file-tools.d.ts.map +1 -0
- package/dist/agi/features/luca-coder.d.ts +323 -0
- package/dist/agi/features/luca-coder.d.ts.map +1 -0
- package/dist/agi/features/openai-codex.d.ts +381 -0
- package/dist/agi/features/openai-codex.d.ts.map +1 -0
- package/dist/agi/features/openapi.d.ts +200 -0
- package/dist/agi/features/openapi.d.ts.map +1 -0
- package/dist/agi/features/skills-library.d.ts +167 -0
- package/dist/agi/features/skills-library.d.ts.map +1 -0
- package/dist/agi/index.d.ts +5 -0
- package/dist/agi/index.d.ts.map +1 -0
- package/dist/agi/lib/interceptor-chain.d.ts +44 -0
- package/dist/agi/lib/interceptor-chain.d.ts.map +1 -0
- package/dist/agi/lib/token-counter.d.ts +13 -0
- package/dist/agi/lib/token-counter.d.ts.map +1 -0
- package/dist/bootstrap/generated.d.ts +5 -0
- package/dist/bootstrap/generated.d.ts.map +1 -0
- package/dist/browser.d.ts +12 -0
- package/dist/browser.d.ts.map +1 -0
- package/dist/bus.d.ts +29 -0
- package/dist/bus.d.ts.map +1 -0
- package/dist/cli/build-info.d.ts +4 -0
- package/dist/cli/build-info.d.ts.map +1 -0
- package/dist/cli/cli.d.ts +3 -12
- package/dist/cli/cli.d.ts.map +1 -0
- package/dist/client.d.ts +60 -0
- package/dist/client.d.ts.map +1 -0
- package/dist/clients/civitai/index.d.ts +472 -0
- package/dist/clients/civitai/index.d.ts.map +1 -0
- package/dist/clients/client-template.d.ts +30 -0
- package/dist/clients/client-template.d.ts.map +1 -0
- package/dist/clients/comfyui/index.d.ts +281 -0
- package/dist/clients/comfyui/index.d.ts.map +1 -0
- package/dist/clients/elevenlabs/index.d.ts +197 -0
- package/dist/clients/elevenlabs/index.d.ts.map +1 -0
- package/dist/clients/graph.d.ts +64 -0
- package/dist/clients/graph.d.ts.map +1 -0
- package/dist/clients/openai/index.d.ts +247 -0
- package/dist/clients/openai/index.d.ts.map +1 -0
- package/dist/clients/rest.d.ts +92 -0
- package/dist/clients/rest.d.ts.map +1 -0
- package/dist/clients/supabase/index.d.ts +176 -0
- package/dist/clients/supabase/index.d.ts.map +1 -0
- package/dist/clients/websocket.d.ts +127 -0
- package/dist/clients/websocket.d.ts.map +1 -0
- package/dist/command.d.ts +163 -0
- package/dist/command.d.ts.map +1 -0
- package/dist/commands/bootstrap.d.ts +20 -0
- package/dist/commands/bootstrap.d.ts.map +1 -0
- package/dist/commands/chat.d.ts +37 -0
- package/dist/commands/chat.d.ts.map +1 -0
- package/dist/commands/code.d.ts +28 -0
- package/dist/commands/code.d.ts.map +1 -0
- package/dist/commands/console.d.ts +22 -0
- package/dist/commands/console.d.ts.map +1 -0
- package/dist/commands/describe.d.ts +50 -0
- package/dist/commands/describe.d.ts.map +1 -0
- package/dist/commands/eval.d.ts +23 -0
- package/dist/commands/eval.d.ts.map +1 -0
- package/dist/commands/help.d.ts +25 -0
- package/dist/commands/help.d.ts.map +1 -0
- package/dist/commands/index.d.ts +18 -0
- package/dist/commands/index.d.ts.map +1 -0
- package/dist/commands/introspect.d.ts +24 -0
- package/dist/commands/introspect.d.ts.map +1 -0
- package/dist/commands/mcp.d.ts +35 -0
- package/dist/commands/mcp.d.ts.map +1 -0
- package/dist/commands/prompt.d.ts +38 -0
- package/dist/commands/prompt.d.ts.map +1 -0
- package/dist/commands/run.d.ts +24 -0
- package/dist/commands/run.d.ts.map +1 -0
- package/dist/commands/sandbox-mcp.d.ts +34 -0
- package/dist/commands/sandbox-mcp.d.ts.map +1 -0
- package/dist/commands/save-api-docs.d.ts +21 -0
- package/dist/commands/save-api-docs.d.ts.map +1 -0
- package/dist/commands/scaffold.d.ts +24 -0
- package/dist/commands/scaffold.d.ts.map +1 -0
- package/dist/commands/select.d.ts +22 -0
- package/dist/commands/select.d.ts.map +1 -0
- package/dist/commands/serve.d.ts +29 -0
- package/dist/commands/serve.d.ts.map +1 -0
- package/dist/container-describer.d.ts +144 -0
- package/dist/container-describer.d.ts.map +1 -0
- package/dist/container.d.ts +451 -0
- package/dist/container.d.ts.map +1 -0
- package/dist/endpoint.d.ts +113 -0
- package/dist/endpoint.d.ts.map +1 -0
- package/dist/feature.d.ts +47 -0
- package/dist/feature.d.ts.map +1 -0
- package/dist/graft.d.ts +29 -0
- package/dist/graft.d.ts.map +1 -0
- package/dist/hash-object.d.ts +8 -0
- package/dist/hash-object.d.ts.map +1 -0
- package/dist/helper.d.ts +209 -0
- package/dist/helper.d.ts.map +1 -0
- package/dist/introspection/generated.node.d.ts +44623 -0
- package/dist/introspection/generated.node.d.ts.map +1 -0
- package/dist/introspection/generated.web.d.ts +1412 -0
- package/dist/introspection/generated.web.d.ts.map +1 -0
- package/dist/introspection/index.d.ts +156 -0
- package/dist/introspection/index.d.ts.map +1 -0
- package/dist/introspection/scan.d.ts +147 -0
- package/dist/introspection/scan.d.ts.map +1 -0
- package/dist/node/container.d.ts +256 -0
- package/dist/node/container.d.ts.map +1 -0
- package/dist/node/feature.d.ts +9 -0
- package/dist/node/feature.d.ts.map +1 -0
- package/dist/node/features/container-link.d.ts +213 -0
- package/dist/node/features/container-link.d.ts.map +1 -0
- package/dist/node/features/content-db.d.ts +354 -0
- package/dist/node/features/content-db.d.ts.map +1 -0
- package/dist/node/features/disk-cache.d.ts +236 -0
- package/dist/node/features/disk-cache.d.ts.map +1 -0
- package/dist/node/features/dns.d.ts +511 -0
- package/dist/node/features/dns.d.ts.map +1 -0
- package/dist/node/features/docker.d.ts +485 -0
- package/dist/node/features/docker.d.ts.map +1 -0
- package/dist/node/features/downloader.d.ts +73 -0
- package/dist/node/features/downloader.d.ts.map +1 -0
- package/dist/node/features/figlet-fonts.d.ts +4 -0
- package/dist/node/features/figlet-fonts.d.ts.map +1 -0
- package/dist/node/features/file-manager.d.ts +177 -0
- package/dist/node/features/file-manager.d.ts.map +1 -0
- package/dist/node/features/fs.d.ts +635 -0
- package/dist/node/features/fs.d.ts.map +1 -0
- package/dist/node/features/git.d.ts +329 -0
- package/dist/node/features/git.d.ts.map +1 -0
- package/dist/node/features/google-auth.d.ts +200 -0
- package/dist/node/features/google-auth.d.ts.map +1 -0
- package/dist/node/features/google-calendar.d.ts +194 -0
- package/dist/node/features/google-calendar.d.ts.map +1 -0
- package/dist/node/features/google-docs.d.ts +138 -0
- package/dist/node/features/google-docs.d.ts.map +1 -0
- package/dist/node/features/google-drive.d.ts +202 -0
- package/dist/node/features/google-drive.d.ts.map +1 -0
- package/dist/node/features/google-mail.d.ts +221 -0
- package/dist/node/features/google-mail.d.ts.map +1 -0
- package/dist/node/features/google-sheets.d.ts +157 -0
- package/dist/node/features/google-sheets.d.ts.map +1 -0
- package/dist/node/features/grep.d.ts +207 -0
- package/dist/node/features/grep.d.ts.map +1 -0
- package/dist/node/features/helpers.d.ts +236 -0
- package/dist/node/features/helpers.d.ts.map +1 -0
- package/dist/node/features/ink.d.ts +332 -0
- package/dist/node/features/ink.d.ts.map +1 -0
- package/dist/node/features/ipc-socket.d.ts +298 -0
- package/dist/node/features/ipc-socket.d.ts.map +1 -0
- package/dist/node/features/json-tree.d.ts +140 -0
- package/dist/node/features/json-tree.d.ts.map +1 -0
- package/dist/node/features/networking.d.ts +373 -0
- package/dist/node/features/networking.d.ts.map +1 -0
- package/dist/node/features/nlp.d.ts +125 -0
- package/dist/node/features/nlp.d.ts.map +1 -0
- package/dist/node/features/opener.d.ts +93 -0
- package/dist/node/features/opener.d.ts.map +1 -0
- package/dist/node/features/os.d.ts +168 -0
- package/dist/node/features/os.d.ts.map +1 -0
- package/dist/node/features/package-finder.d.ts +419 -0
- package/dist/node/features/package-finder.d.ts.map +1 -0
- package/dist/node/features/postgres.d.ts +173 -0
- package/dist/node/features/postgres.d.ts.map +1 -0
- package/dist/node/features/proc.d.ts +285 -0
- package/dist/node/features/proc.d.ts.map +1 -0
- package/dist/node/features/process-manager.d.ts +427 -0
- package/dist/node/features/process-manager.d.ts.map +1 -0
- package/dist/node/features/python.d.ts +477 -0
- package/dist/node/features/python.d.ts.map +1 -0
- package/dist/node/features/redis.d.ts +247 -0
- package/dist/node/features/redis.d.ts.map +1 -0
- package/dist/node/features/repl.d.ts +84 -0
- package/dist/node/features/repl.d.ts.map +1 -0
- package/dist/node/features/runpod.d.ts +527 -0
- package/dist/node/features/runpod.d.ts.map +1 -0
- package/dist/node/features/secure-shell.d.ts +145 -0
- package/dist/node/features/secure-shell.d.ts.map +1 -0
- package/dist/node/features/semantic-search.d.ts +207 -0
- package/dist/node/features/semantic-search.d.ts.map +1 -0
- package/dist/node/features/sqlite.d.ts +180 -0
- package/dist/node/features/sqlite.d.ts.map +1 -0
- package/dist/node/features/telegram.d.ts +173 -0
- package/dist/node/features/telegram.d.ts.map +1 -0
- package/dist/node/features/transpiler.d.ts +51 -0
- package/dist/node/features/transpiler.d.ts.map +1 -0
- package/dist/node/features/tts.d.ts +108 -0
- package/dist/node/features/tts.d.ts.map +1 -0
- package/dist/node/features/ui.d.ts +562 -0
- package/dist/node/features/ui.d.ts.map +1 -0
- package/dist/node/features/vault.d.ts +90 -0
- package/dist/node/features/vault.d.ts.map +1 -0
- package/dist/node/features/vm.d.ts +285 -0
- package/dist/node/features/vm.d.ts.map +1 -0
- package/dist/node/features/yaml-tree.d.ts +118 -0
- package/dist/node/features/yaml-tree.d.ts.map +1 -0
- package/dist/node/features/yaml.d.ts +127 -0
- package/dist/node/features/yaml.d.ts.map +1 -0
- package/dist/node.d.ts +67 -0
- package/dist/node.d.ts.map +1 -0
- package/dist/python/generated.d.ts +2 -0
- package/dist/python/generated.d.ts.map +1 -0
- package/dist/react/index.d.ts +36 -0
- package/dist/react/index.d.ts.map +1 -0
- package/dist/registry.d.ts +97 -0
- package/dist/registry.d.ts.map +1 -0
- package/dist/scaffolds/generated.d.ts +13 -0
- package/dist/scaffolds/generated.d.ts.map +1 -0
- package/dist/scaffolds/template.d.ts +11 -0
- package/dist/scaffolds/template.d.ts.map +1 -0
- package/dist/schemas/base.d.ts +254 -0
- package/dist/schemas/base.d.ts.map +1 -0
- package/dist/selector.d.ts +130 -0
- package/dist/selector.d.ts.map +1 -0
- package/dist/server.d.ts +89 -0
- package/dist/server.d.ts.map +1 -0
- package/dist/servers/express.d.ts +104 -0
- package/dist/servers/express.d.ts.map +1 -0
- package/dist/servers/mcp.d.ts +201 -0
- package/dist/servers/mcp.d.ts.map +1 -0
- package/dist/servers/socket.d.ts +121 -0
- package/dist/servers/socket.d.ts.map +1 -0
- package/dist/state.d.ts +24 -0
- package/dist/state.d.ts.map +1 -0
- package/dist/web/clients/socket.d.ts +37 -0
- package/dist/web/clients/socket.d.ts.map +1 -0
- package/dist/web/container.d.ts +55 -0
- package/dist/web/container.d.ts.map +1 -0
- package/dist/web/extension.d.ts +4 -0
- package/dist/web/extension.d.ts.map +1 -0
- package/dist/web/feature.d.ts +8 -0
- package/dist/web/feature.d.ts.map +1 -0
- package/dist/web/features/asset-loader.d.ts +35 -0
- package/dist/web/features/asset-loader.d.ts.map +1 -0
- package/dist/web/features/container-link.d.ts +167 -0
- package/dist/web/features/container-link.d.ts.map +1 -0
- package/dist/web/features/esbuild.d.ts +51 -0
- package/dist/web/features/esbuild.d.ts.map +1 -0
- package/dist/web/features/helpers.d.ts +140 -0
- package/dist/web/features/helpers.d.ts.map +1 -0
- package/dist/web/features/network.d.ts +69 -0
- package/dist/web/features/network.d.ts.map +1 -0
- package/dist/web/features/speech.d.ts +71 -0
- package/dist/web/features/speech.d.ts.map +1 -0
- package/dist/web/features/vault.d.ts +62 -0
- package/dist/web/features/vault.d.ts.map +1 -0
- package/dist/web/features/vm.d.ts +48 -0
- package/dist/web/features/vm.d.ts.map +1 -0
- package/dist/web/features/voice-recognition.d.ts +96 -0
- package/dist/web/features/voice-recognition.d.ts.map +1 -0
- package/dist/web/shims/isomorphic-vm.d.ts +22 -0
- package/dist/web/shims/isomorphic-vm.d.ts.map +1 -0
- package/docs/CLI.md +335 -0
- package/docs/CNAME +1 -0
- package/docs/README.md +60 -0
- package/docs/TABLE-OF-CONTENTS.md +183 -0
- package/docs/apis/clients/elevenlabs.md +308 -0
- package/docs/apis/clients/graph.md +107 -0
- package/docs/apis/clients/openai.md +429 -0
- package/docs/apis/clients/rest.md +161 -0
- package/docs/apis/clients/websocket.md +174 -0
- package/docs/apis/features/agi/assistant.md +625 -0
- package/docs/apis/features/agi/assistants-manager.md +282 -0
- package/docs/apis/features/agi/auto-assistant.md +279 -0
- package/docs/apis/features/agi/browser-use.md +802 -0
- package/docs/apis/features/agi/claude-code.md +884 -0
- package/docs/apis/features/agi/conversation-history.md +364 -0
- package/docs/apis/features/agi/conversation.md +548 -0
- package/docs/apis/features/agi/docs-reader.md +99 -0
- package/docs/apis/features/agi/file-tools.md +163 -0
- package/docs/apis/features/agi/luca-coder.md +407 -0
- package/docs/apis/features/agi/openai-codex.md +396 -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 +239 -0
- package/docs/apis/features/node/container-link.md +192 -0
- package/docs/apis/features/node/content-db.md +450 -0
- package/docs/apis/features/node/disk-cache.md +379 -0
- package/docs/apis/features/node/dns.md +652 -0
- package/docs/apis/features/node/docker.md +706 -0
- package/docs/apis/features/node/downloader.md +81 -0
- package/docs/apis/features/node/esbuild.md +60 -0
- package/docs/apis/features/node/file-manager.md +191 -0
- package/docs/apis/features/node/fs.md +1217 -0
- package/docs/apis/features/node/git.md +371 -0
- package/docs/apis/features/node/google-auth.md +193 -0
- package/docs/apis/features/node/google-calendar.md +202 -0
- package/docs/apis/features/node/google-docs.md +173 -0
- package/docs/apis/features/node/google-drive.md +246 -0
- package/docs/apis/features/node/google-mail.md +214 -0
- package/docs/apis/features/node/google-sheets.md +194 -0
- package/docs/apis/features/node/grep.md +292 -0
- package/docs/apis/features/node/helpers.md +164 -0
- package/docs/apis/features/node/ink.md +334 -0
- package/docs/apis/features/node/ipc-socket.md +249 -0
- package/docs/apis/features/node/json-tree.md +86 -0
- package/docs/apis/features/node/networking.md +316 -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 +146 -0
- package/docs/apis/features/node/package-finder.md +392 -0
- package/docs/apis/features/node/postgres.md +234 -0
- package/docs/apis/features/node/proc.md +399 -0
- package/docs/apis/features/node/process-manager.md +305 -0
- package/docs/apis/features/node/python.md +604 -0
- package/docs/apis/features/node/redis.md +380 -0
- package/docs/apis/features/node/repl.md +88 -0
- package/docs/apis/features/node/runpod.md +674 -0
- package/docs/apis/features/node/secure-shell.md +176 -0
- package/docs/apis/features/node/semantic-search.md +408 -0
- package/docs/apis/features/node/sqlite.md +233 -0
- package/docs/apis/features/node/telegram.md +279 -0
- package/docs/apis/features/node/transpiler.md +74 -0
- package/docs/apis/features/node/tts.md +133 -0
- package/docs/apis/features/node/ui.md +701 -0
- package/docs/apis/features/node/vault.md +59 -0
- package/docs/apis/features/node/vm.md +75 -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 +59 -0
- package/docs/apis/features/web/container-link.md +192 -0
- package/docs/apis/features/web/esbuild.md +54 -0
- package/docs/apis/features/web/helpers.md +164 -0
- package/docs/apis/features/web/network.md +44 -0
- package/docs/apis/features/web/speech.md +69 -0
- package/docs/apis/features/web/vault.md +59 -0
- package/docs/apis/features/web/vm.md +75 -0
- package/docs/apis/features/web/voice.md +84 -0
- package/docs/apis/servers/express.md +171 -0
- package/docs/apis/servers/mcp.md +238 -0
- package/docs/apis/servers/websocket.md +170 -0
- package/docs/bootstrap/CLAUDE.md +101 -0
- package/docs/bootstrap/SKILL.md +341 -0
- package/docs/bootstrap/templates/about-command.ts +41 -0
- package/docs/bootstrap/templates/docs-models.ts +22 -0
- package/docs/bootstrap/templates/docs-readme.md +43 -0
- package/docs/bootstrap/templates/example-feature.ts +53 -0
- package/docs/bootstrap/templates/health-endpoint.ts +15 -0
- package/docs/bootstrap/templates/luca-cli.ts +30 -0
- package/docs/bootstrap/templates/runme.md +54 -0
- package/docs/challenges/caching-proxy.md +16 -0
- package/docs/challenges/content-db-round-trip.md +14 -0
- package/docs/challenges/custom-command.md +9 -0
- package/docs/challenges/file-watcher-pipeline.md +11 -0
- package/docs/challenges/grep-audit-report.md +15 -0
- package/docs/challenges/multi-feature-dashboard.md +14 -0
- package/docs/challenges/process-orchestrator.md +17 -0
- package/docs/challenges/rest-api-server-with-client.md +12 -0
- package/docs/challenges/script-runner-with-vm.md +11 -0
- package/docs/challenges/simple-rest-api.md +15 -0
- package/docs/challenges/websocket-serve-and-client.md +11 -0
- package/docs/challenges/yaml-config-system.md +14 -0
- package/docs/command-system-overhaul.md +94 -0
- package/docs/documentation-audit.md +134 -0
- package/docs/examples/assistant/CORE.md +18 -0
- package/docs/examples/assistant/hooks.ts +3 -0
- package/docs/examples/assistant/tools.ts +10 -0
- package/docs/examples/assistant-hooks-reference.ts +171 -0
- package/docs/examples/assistant-with-process-manager.md +84 -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/entity.md +124 -0
- package/docs/examples/esbuild.md +80 -0
- package/docs/examples/feature-as-tool-provider.md +143 -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/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/postgres.md +91 -0
- package/docs/examples/proc.md +81 -0
- package/docs/examples/process-manager.md +79 -0
- package/docs/examples/python.md +132 -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/structured-output-with-assistants.md +144 -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/websocket-ask-and-reply-example.md +128 -0
- package/docs/examples/yaml-tree.md +93 -0
- package/docs/examples/yaml.md +104 -0
- package/docs/ideas/assistant-factory-pattern.md +142 -0
- package/docs/in-memory-fs.md +4 -0
- package/docs/introspection-audit.md +49 -0
- package/docs/introspection.md +164 -0
- package/docs/mcp/readme.md +162 -0
- package/docs/models.ts +41 -0
- package/docs/philosophy.md +86 -0
- package/docs/principles.md +7 -0
- package/docs/prompts/audit-codebase-for-failures-to-use-the-container.md +34 -0
- package/docs/prompts/check-for-undocumented-features.md +27 -0
- package/docs/prompts/mcp-test-easy-command.md +27 -0
- package/docs/scaffolds/client.md +149 -0
- package/docs/scaffolds/command.md +120 -0
- package/docs/scaffolds/endpoint.md +171 -0
- package/docs/scaffolds/feature.md +158 -0
- package/docs/scaffolds/selector.md +91 -0
- package/docs/scaffolds/server.md +196 -0
- package/docs/selectors.md +115 -0
- package/docs/sessions/custom-command/attempt-log-2.md +195 -0
- package/docs/sessions/file-watcher-pipeline/attempt-log-1.md +728 -0
- package/docs/sessions/file-watcher-pipeline/attempt-log-2.md +555 -0
- package/docs/sessions/grep-audit-report/attempt-log-1.md +289 -0
- package/docs/sessions/multi-feature-dashboard/attempt-log-2.md +679 -0
- package/docs/sessions/rest-api-server-with-client/attempt-log-1.md +1 -0
- package/docs/sessions/rest-api-server-with-client/attempt-log-3.md +920 -0
- package/docs/sessions/simple-rest-api/attempt-log-1.md +593 -0
- package/docs/sessions/websocket-serve-and-client/attempt-log-2.md +995 -0
- package/docs/tutorials/00-bootstrap.md +166 -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 +252 -0
- package/docs/tutorials/09-clients.md +162 -0
- package/docs/tutorials/10-creating-features.md +203 -0
- package/docs/tutorials/11-contentbase.md +191 -0
- package/docs/tutorials/12-assistants.md +215 -0
- package/docs/tutorials/13-introspection.md +157 -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/docs/tutorials/19-python-sessions.md +401 -0
- package/docs/tutorials/20-browser-esm.md +234 -0
- package/index.html +1430 -0
- package/index.ts +1 -0
- package/install.sh +84 -0
- package/luca.cli.ts +16 -0
- package/luca.console.ts +9 -0
- package/main.py +6 -0
- package/package.json +219 -58
- package/public/index.html +1430 -0
- package/public/slides-ai-native.html +902 -0
- package/public/slides-intro.html +974 -0
- package/pyproject.toml +7 -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/using-assistant-with-mcp.ts +555 -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 +11 -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/scaffold.ts +391 -0
- package/scripts/scratch.ts +15 -0
- package/scripts/stamp-build.sh +12 -0
- package/scripts/test-assistant-hooks.ts +13 -0
- package/scripts/test-docs-reader.ts +10 -0
- package/scripts/test-linux-binary.sh +80 -0
- package/scripts/update-introspection-data.ts +58 -0
- package/src/agi/README.md +14 -0
- package/src/agi/container.server.ts +152 -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/feature.ts +13 -0
- package/src/agi/features/agent-memory.ts +694 -0
- package/src/agi/features/assistant.ts +1624 -0
- package/src/agi/features/assistants-manager.ts +418 -0
- package/src/agi/features/autonomous-assistant.ts +431 -0
- package/src/agi/features/browser-use.ts +653 -0
- package/src/agi/features/claude-code.ts +1538 -0
- package/src/agi/features/coding-tools.ts +175 -0
- package/src/agi/features/conversation-history.ts +495 -0
- package/src/agi/features/conversation.ts +1323 -0
- package/src/agi/features/docs-reader.ts +167 -0
- package/src/agi/features/file-tools.ts +293 -0
- package/src/agi/features/luca-coder.ts +639 -0
- package/src/agi/features/openai-codex.ts +651 -0
- package/src/agi/features/openapi.ts +445 -0
- package/src/agi/features/skills-library.ts +478 -0
- package/src/agi/index.ts +6 -0
- package/src/agi/lib/interceptor-chain.ts +89 -0
- package/src/agi/lib/token-counter.ts +122 -0
- package/src/bootstrap/generated.ts +9792 -0
- package/src/browser.ts +25 -0
- package/src/bus.ts +122 -0
- package/src/cli/build-info.ts +4 -0
- package/src/cli/cli.ts +355 -0
- package/src/client.ts +170 -0
- package/src/clients/civitai/index.ts +537 -0
- package/src/clients/client-template.ts +41 -0
- package/src/clients/comfyui/index.ts +604 -0
- package/src/clients/elevenlabs/index.ts +317 -0
- package/src/clients/graph.ts +87 -0
- package/src/clients/openai/index.ts +456 -0
- package/src/clients/rest.ts +207 -0
- package/src/clients/supabase/index.ts +357 -0
- package/src/clients/voicebox/index.ts +300 -0
- package/src/clients/websocket.ts +251 -0
- package/src/command.ts +505 -0
- package/src/commands/bootstrap.ts +244 -0
- package/src/commands/chat.ts +308 -0
- package/src/commands/code.ts +371 -0
- package/src/commands/console.ts +189 -0
- package/src/commands/describe.ts +243 -0
- package/src/commands/eval.ts +121 -0
- package/src/commands/help.ts +240 -0
- package/src/commands/index.ts +19 -0
- package/src/commands/introspect.ts +218 -0
- package/src/commands/mcp.ts +64 -0
- package/src/commands/prompt.ts +982 -0
- package/src/commands/run.ts +278 -0
- package/src/commands/sandbox-mcp.ts +343 -0
- package/src/commands/save-api-docs.ts +51 -0
- package/src/commands/scaffold.ts +225 -0
- package/src/commands/select.ts +99 -0
- package/src/commands/serve.ts +208 -0
- package/src/container-describer.ts +1084 -0
- package/src/container.ts +1186 -0
- package/src/endpoint.ts +365 -0
- package/src/entity.ts +173 -0
- package/src/feature.ts +118 -0
- package/src/graft.ts +181 -0
- package/src/hash-object.ts +97 -0
- package/src/helper.ts +849 -0
- package/src/introspection/generated.agi.ts +40208 -0
- package/src/introspection/generated.node.ts +28686 -0
- package/src/introspection/generated.web.ts +2251 -0
- package/src/introspection/index.ts +296 -0
- package/src/introspection/scan.ts +1131 -0
- package/src/node/container.ts +409 -0
- package/src/node/feature.ts +13 -0
- package/src/node/features/container-link.ts +559 -0
- package/src/node/features/content-db.ts +812 -0
- package/src/node/features/disk-cache.ts +388 -0
- package/src/node/features/dns.ts +669 -0
- package/src/node/features/docker.ts +921 -0
- package/src/node/features/downloader.ts +79 -0
- package/src/node/features/figlet-fonts.ts +600 -0
- package/src/node/features/file-manager.ts +535 -0
- package/src/node/features/fs.ts +1050 -0
- package/src/node/features/git.ts +592 -0
- package/src/node/features/google-auth.ts +504 -0
- package/src/node/features/google-calendar.ts +306 -0
- package/src/node/features/google-docs.ts +412 -0
- package/src/node/features/google-drive.ts +346 -0
- package/src/node/features/google-mail.ts +540 -0
- package/src/node/features/google-sheets.ts +286 -0
- package/src/node/features/grep.ts +427 -0
- package/src/node/features/helpers.ts +735 -0
- package/src/node/features/ink.ts +490 -0
- package/src/node/features/ipc-socket.ts +649 -0
- package/src/node/features/json-tree.ts +170 -0
- package/src/node/features/networking.ts +961 -0
- package/src/node/features/nlp.ts +212 -0
- package/src/node/features/opener.ts +180 -0
- package/src/node/features/os.ts +403 -0
- package/src/node/features/package-finder.ts +540 -0
- package/src/node/features/postgres.ts +289 -0
- package/src/node/features/proc.ts +503 -0
- package/src/node/features/process-manager.ts +844 -0
- package/src/node/features/python.ts +906 -0
- package/src/node/features/redis.ts +446 -0
- package/src/node/features/repl.ts +212 -0
- package/src/node/features/runpod.ts +811 -0
- package/src/node/features/secure-shell.ts +267 -0
- package/src/node/features/semantic-search.ts +935 -0
- package/src/node/features/sqlite.ts +289 -0
- package/src/node/features/telegram.ts +343 -0
- package/src/node/features/transpiler.ts +161 -0
- package/src/node/features/tts.ts +185 -0
- package/src/node/features/ui.ts +786 -0
- package/src/node/features/vault.ts +153 -0
- package/src/node/features/vm.ts +462 -0
- package/src/node/features/yaml-tree.ts +148 -0
- package/src/node/features/yaml.ts +133 -0
- package/src/node.ts +76 -0
- package/src/python/bridge.py +220 -0
- package/src/python/generated.ts +227 -0
- package/src/react/index.ts +175 -0
- package/src/registry.ts +210 -0
- package/src/scaffolds/generated.ts +1815 -0
- package/src/scaffolds/template.ts +46 -0
- package/src/schemas/base.ts +296 -0
- package/src/selector.ts +352 -0
- package/src/server.ts +229 -0
- package/src/servers/express.ts +283 -0
- package/src/servers/mcp.ts +802 -0
- package/src/servers/socket.ts +258 -0
- package/src/state.ts +101 -0
- package/src/web/clients/socket.ts +99 -0
- package/src/web/container.ts +75 -0
- package/src/web/extension.ts +30 -0
- package/src/web/feature.ts +12 -0
- package/src/web/features/asset-loader.ts +72 -0
- package/src/web/features/container-link.ts +382 -0
- package/src/web/features/esbuild.ts +93 -0
- package/src/web/features/helpers.ts +269 -0
- package/src/web/features/network.ts +85 -0
- package/src/web/features/speech.ts +104 -0
- package/src/web/features/vault.ts +207 -0
- package/src/web/features/vm.ts +85 -0
- package/src/web/features/voice-recognition.ts +161 -0
- package/src/web/shims/isomorphic-vm.ts +149 -0
- package/test/assistant-hooks.test.ts +306 -0
- package/test/assistant.test.ts +81 -0
- package/test/bus.test.ts +134 -0
- package/test/clients-servers.test.ts +217 -0
- package/test/command.test.ts +267 -0
- package/test/container-link.test.ts +274 -0
- package/test/conversation.test.ts +220 -0
- package/test/features.test.ts +160 -0
- package/test/fork-and-research.test.ts +450 -0
- package/test/integration.test.ts +787 -0
- package/test/interceptor-chain.test.ts +61 -0
- package/test/node-container.test.ts +121 -0
- package/test/python-session.test.ts +105 -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/vm-context.test.ts +146 -0
- package/test/vm-loadmodule.test.ts +213 -0
- package/test/websocket-ask.test.ts +101 -0
- package/test-integration/assistant.test.ts +138 -0
- package/test-integration/assistants-manager.test.ts +113 -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/memory.test.ts +204 -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/telegram.test.ts +46 -0
- package/tsconfig.build.json +12 -0
- package/tsconfig.json +58 -0
- package/uv.lock +8 -0
- package/LICENSE +0 -21
- package/dist/cli/cli.js +0 -48
- package/dist/cli/common.d.ts +0 -2
- package/dist/cli/common.js +0 -6
- package/dist/cli/index.d.ts +0 -2
- package/dist/cli/index.js +0 -5
- package/dist/cli/run.d.ts +0 -1
- package/dist/cli/run.js +0 -38
- package/dist/core/index.d.ts +0 -4
- package/dist/core/index.js +0 -32
- package/dist/core/read.d.ts +0 -2
- package/dist/core/read.js +0 -29
- package/dist/core/request.d.ts +0 -1
- package/dist/core/request.js +0 -2
- package/dist/core/write.d.ts +0 -2
- package/dist/core/write.js +0 -21
- package/dist/index.d.ts +0 -1
- package/dist/index.js +0 -5
- package/dist/utils/common.d.ts +0 -9
- package/dist/utils/common.js +0 -57
- package/dist/utils/consts.d.ts +0 -3
- package/dist/utils/consts.js +0 -11
- package/dist/utils/dict.d.ts +0 -1
- package/dist/utils/dict.js +0 -7
- package/dist/utils/index.d.ts +0 -5
- package/dist/utils/index.js +0 -21
- package/dist/utils/log.d.ts +0 -1
- package/dist/utils/log.js +0 -5
- package/dist/utils/types.d.ts +0 -1
- package/dist/utils/types.js +0 -2
- package/dist/utils/utils.test.d.ts +0 -1
- 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 '../../commands/eval.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
|