luca 2.0.0 → 3.0.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.github/workflows/release.yaml +170 -0
- package/AGENTS.md +99 -0
- package/CLAUDE.md +123 -0
- package/CNAME +1 -0
- package/README.md +275 -9
- package/RUNME.md +56 -0
- package/assistants/codingAssistant/ABOUT.md +5 -0
- package/assistants/codingAssistant/CORE.md +33 -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 +2667 -0
- package/bunfig.toml +3 -0
- package/commands/audit-docs.ts +740 -0
- package/commands/build-bootstrap.ts +117 -0
- package/commands/build-python-bridge.ts +42 -0
- package/commands/build-scaffolds.ts +175 -0
- package/commands/bundle-consumer-project.ts +521 -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/index.html +1457 -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 +1457 -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 +156 -0
- package/src/agi/feature.ts +13 -0
- package/src/agi/features/agent-memory.ts +694 -0
- package/src/agi/features/assistant.ts +1653 -0
- package/src/agi/features/assistants-manager.ts +534 -0
- package/src/agi/features/autonomous-assistant.ts +431 -0
- package/src/agi/features/browser-use.ts +672 -0
- package/src/agi/features/claude-code.ts +1584 -0
- package/src/agi/features/coding-tools.ts +175 -0
- package/src/agi/features/conversation-history.ts +672 -0
- package/src/agi/features/conversation.ts +1494 -0
- package/src/agi/features/docs-reader.ts +167 -0
- package/src/agi/features/file-tools.ts +340 -0
- package/src/agi/features/luca-coder.ts +641 -0
- package/src/agi/features/mcp-bridge.ts +532 -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 +557 -0
- package/src/agi/index.ts +6 -0
- package/src/agi/lib/interceptor-chain.ts +89 -0
- package/src/agi/lib/token-counter.ts +202 -0
- package/src/bootstrap/generated.ts +9791 -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 +506 -0
- package/src/commands/bootstrap.ts +244 -0
- package/src/commands/chat.ts +309 -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 +67 -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 +1014 -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 +1091 -0
- package/src/container.ts +1199 -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 +41200 -0
- package/src/introspection/generated.node.ts +28773 -0
- package/src/introspection/generated.web.ts +2272 -0
- package/src/introspection/index.ts +296 -0
- package/src/introspection/scan.ts +1136 -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 +849 -0
- package/src/node/features/disk-cache.ts +388 -0
- package/src/node/features/display-result.ts +57 -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 +762 -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 +912 -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 +261 -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 +160 -0
- package/src/node/features/tts.ts +185 -0
- package/src/node/features/ui.ts +791 -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 +226 -0
- package/src/react/index.ts +175 -0
- package/src/registry.ts +210 -0
- package/src/scaffolds/generated.ts +1814 -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 +291 -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/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
package/src/endpoint.ts
ADDED
|
@@ -0,0 +1,365 @@
|
|
|
1
|
+
import { Helper } from './helper.js'
|
|
2
|
+
import type { Container, ContainerContext } from './container.js'
|
|
3
|
+
import { Registry } from './registry.js'
|
|
4
|
+
import { z } from 'zod'
|
|
5
|
+
import { EndpointStateSchema, EndpointOptionsSchema, EndpointEventsSchema } from './schemas/base.js'
|
|
6
|
+
|
|
7
|
+
export interface AvailableEndpoints {}
|
|
8
|
+
|
|
9
|
+
export type EndpointState = z.infer<typeof EndpointStateSchema>
|
|
10
|
+
export type EndpointOptions = z.infer<typeof EndpointOptionsSchema>
|
|
11
|
+
|
|
12
|
+
export type EndpointHandler = (
|
|
13
|
+
parameters: Record<string, any>,
|
|
14
|
+
context: EndpointContext
|
|
15
|
+
) => Promise<any> | any
|
|
16
|
+
|
|
17
|
+
export type EndpointContext = {
|
|
18
|
+
container: Container<any>
|
|
19
|
+
request: any
|
|
20
|
+
response: any
|
|
21
|
+
query: Record<string, any>
|
|
22
|
+
body: Record<string, any>
|
|
23
|
+
params: Record<string, any>
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
export interface EndpointRateLimit {
|
|
27
|
+
/** Maximum requests allowed per window */
|
|
28
|
+
maxRequests: number
|
|
29
|
+
/** Window size in seconds (default: 1) */
|
|
30
|
+
windowSeconds?: number
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
export interface EndpointModule {
|
|
34
|
+
path: string
|
|
35
|
+
get?: EndpointHandler
|
|
36
|
+
post?: EndpointHandler
|
|
37
|
+
put?: EndpointHandler
|
|
38
|
+
patch?: EndpointHandler
|
|
39
|
+
delete?: EndpointHandler
|
|
40
|
+
getSchema?: z.ZodType
|
|
41
|
+
postSchema?: z.ZodType
|
|
42
|
+
putSchema?: z.ZodType
|
|
43
|
+
patchSchema?: z.ZodType
|
|
44
|
+
deleteSchema?: z.ZodType
|
|
45
|
+
/** Rate limit applied to all methods on this endpoint */
|
|
46
|
+
rateLimit?: EndpointRateLimit
|
|
47
|
+
/** Per-method rate limits (overrides the endpoint-level rateLimit) */
|
|
48
|
+
getRateLimit?: EndpointRateLimit
|
|
49
|
+
postRateLimit?: EndpointRateLimit
|
|
50
|
+
putRateLimit?: EndpointRateLimit
|
|
51
|
+
patchRateLimit?: EndpointRateLimit
|
|
52
|
+
deleteRateLimit?: EndpointRateLimit
|
|
53
|
+
description?: string
|
|
54
|
+
tags?: string[]
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
const HTTP_METHODS = ['get', 'post', 'put', 'patch', 'delete'] as const
|
|
58
|
+
|
|
59
|
+
/** All recognized exports on an endpoint module */
|
|
60
|
+
const KNOWN_EXPORTS = new Set([
|
|
61
|
+
'path', 'description', 'tags', 'default', 'rateLimit',
|
|
62
|
+
...HTTP_METHODS,
|
|
63
|
+
...HTTP_METHODS.map(m => `${m}Schema`),
|
|
64
|
+
...HTTP_METHODS.map(m => `${m}RateLimit`),
|
|
65
|
+
])
|
|
66
|
+
|
|
67
|
+
/**
|
|
68
|
+
* Sliding-window rate limiter keyed by IP address.
|
|
69
|
+
* Tracks timestamps of requests and prunes entries older than the window.
|
|
70
|
+
*/
|
|
71
|
+
class RateLimiter {
|
|
72
|
+
private _windows = new Map<string, number[]>()
|
|
73
|
+
|
|
74
|
+
/** Returns true if the request is allowed, false if rate-limited. */
|
|
75
|
+
allow(key: string, maxRequests: number, windowMs: number): boolean {
|
|
76
|
+
const now = Date.now()
|
|
77
|
+
let timestamps = this._windows.get(key)
|
|
78
|
+
|
|
79
|
+
if (!timestamps) {
|
|
80
|
+
timestamps = []
|
|
81
|
+
this._windows.set(key, timestamps)
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
// Prune timestamps outside the window
|
|
85
|
+
while (timestamps.length > 0 && (timestamps[0] ?? 0) <= now - windowMs) {
|
|
86
|
+
timestamps.shift()
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
if (timestamps.length >= maxRequests) {
|
|
90
|
+
return false
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
timestamps.push(now)
|
|
94
|
+
return true
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
/** Clear all tracking state */
|
|
98
|
+
reset(): void {
|
|
99
|
+
this._windows.clear()
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
export type EndpointFactory = <T extends keyof AvailableEndpoints>(
|
|
104
|
+
key: T,
|
|
105
|
+
options?: ConstructorParameters<AvailableEndpoints[T]>[0]
|
|
106
|
+
) => NonNullable<InstanceType<AvailableEndpoints[T]>>
|
|
107
|
+
|
|
108
|
+
export interface EndpointsInterface {
|
|
109
|
+
endpoints: EndpointsRegistry
|
|
110
|
+
endpoint: EndpointFactory
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
export class Endpoint<
|
|
114
|
+
T extends EndpointState = EndpointState,
|
|
115
|
+
K extends EndpointOptions = EndpointOptions
|
|
116
|
+
> extends Helper<T, K> {
|
|
117
|
+
static override shortcut = 'endpoints.base'
|
|
118
|
+
static override description = 'File-based HTTP endpoint with Remix-like DX'
|
|
119
|
+
static override stateSchema = EndpointStateSchema
|
|
120
|
+
static override optionsSchema = EndpointOptionsSchema
|
|
121
|
+
static override eventsSchema = EndpointEventsSchema
|
|
122
|
+
|
|
123
|
+
private _module: EndpointModule | null = null
|
|
124
|
+
private _rateLimiter = new RateLimiter()
|
|
125
|
+
|
|
126
|
+
static attach(container: Container & EndpointsInterface): any {
|
|
127
|
+
Object.assign(container, {
|
|
128
|
+
get endpoints() {
|
|
129
|
+
return endpoints
|
|
130
|
+
},
|
|
131
|
+
|
|
132
|
+
endpoint<T extends keyof AvailableEndpoints>(
|
|
133
|
+
id: T,
|
|
134
|
+
options?: ConstructorParameters<AvailableEndpoints[T]>[0]
|
|
135
|
+
): InstanceType<AvailableEndpoints[T]> {
|
|
136
|
+
const BaseClass = endpoints.lookup(id as string) as any
|
|
137
|
+
|
|
138
|
+
return container.createHelperInstance({
|
|
139
|
+
cache: helperCache,
|
|
140
|
+
type: 'endpoint',
|
|
141
|
+
id: String(id),
|
|
142
|
+
BaseClass,
|
|
143
|
+
options,
|
|
144
|
+
fallbackName: String(id),
|
|
145
|
+
}) as InstanceType<AvailableEndpoints[T]>
|
|
146
|
+
},
|
|
147
|
+
})
|
|
148
|
+
|
|
149
|
+
container.registerHelperType('endpoints', 'endpoint')
|
|
150
|
+
|
|
151
|
+
return container
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
override get initialState(): T {
|
|
155
|
+
return ({
|
|
156
|
+
mounted: false,
|
|
157
|
+
path: this.options.path || '',
|
|
158
|
+
methods: [],
|
|
159
|
+
requestCount: 0,
|
|
160
|
+
} as unknown) as T
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
get path() {
|
|
164
|
+
return this.options.path
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
get module() {
|
|
168
|
+
return this._module
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
get methods(): string[] {
|
|
172
|
+
if (!this._module) return []
|
|
173
|
+
return HTTP_METHODS.filter((m) => typeof (this._module as any)[m] === 'function')
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
get isMounted() {
|
|
177
|
+
return !!this.state.get('mounted')
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
async load(mod?: EndpointModule): Promise<this> {
|
|
181
|
+
if (mod) {
|
|
182
|
+
this._module = mod
|
|
183
|
+
} else if (this.options.filePath) {
|
|
184
|
+
const imported = await import(`${this.options.filePath}?t=${Date.now()}`)
|
|
185
|
+
this._module = imported.default || imported
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
// Note: DELETE handlers should be exported as `export { del as delete }`.
|
|
189
|
+
// We no longer remap `destroy` → `delete` because ESM namespace objects
|
|
190
|
+
// are frozen and the mutation throws on Bun.
|
|
191
|
+
|
|
192
|
+
this.state.set('methods', this.methods)
|
|
193
|
+
this.state.set('path', this.path)
|
|
194
|
+
this.emit('loaded', this._module)
|
|
195
|
+
return this
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
async reload(): Promise<this> {
|
|
199
|
+
this._module = null
|
|
200
|
+
if (this.options.filePath) {
|
|
201
|
+
const helpers = this.container.feature('helpers') as any
|
|
202
|
+
const mod = await helpers.loadModuleExports(this.options.filePath, { cacheBust: true })
|
|
203
|
+
const endpointModule: EndpointModule = mod.default || mod
|
|
204
|
+
return this.load(endpointModule)
|
|
205
|
+
}
|
|
206
|
+
return this.load()
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
handler(method: string): EndpointHandler | undefined {
|
|
210
|
+
return this._module?.[method as keyof EndpointModule] as EndpointHandler | undefined
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
schema(method: string): z.ZodType | undefined {
|
|
214
|
+
return this._module?.[`${method}Schema` as keyof EndpointModule] as z.ZodType | undefined
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
/** Returns the rate limit config for a given method, or undefined if none. */
|
|
218
|
+
rateLimitFor(method: string): EndpointRateLimit | undefined {
|
|
219
|
+
const perMethod = this._module?.[`${method}RateLimit` as keyof EndpointModule] as EndpointRateLimit | undefined
|
|
220
|
+
return perMethod || this._module?.rateLimit
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
/** Access the rate limiter instance (useful for testing or manual resets) */
|
|
224
|
+
get rateLimiter(): RateLimiter {
|
|
225
|
+
return this._rateLimiter
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
mount(app: any): this {
|
|
229
|
+
for (const method of this.methods) {
|
|
230
|
+
const endpoint = this
|
|
231
|
+
|
|
232
|
+
app[method](this.path, async (req: any, res: any) => {
|
|
233
|
+
try {
|
|
234
|
+
// Rate limit check
|
|
235
|
+
const limit = endpoint.rateLimitFor(method)
|
|
236
|
+
if (limit) {
|
|
237
|
+
const ip = req.ip || req.socket?.remoteAddress || 'unknown'
|
|
238
|
+
const key = `${method}:${ip}`
|
|
239
|
+
const windowMs = (limit.windowSeconds ?? 1) * 1000
|
|
240
|
+
if (!endpoint._rateLimiter.allow(key, limit.maxRequests, windowMs)) {
|
|
241
|
+
endpoint.emit('error', new Error(`Rate limit exceeded for ${method.toUpperCase()} ${endpoint.path}`))
|
|
242
|
+
res.status(429).json({ error: 'Too Many Requests' })
|
|
243
|
+
return
|
|
244
|
+
}
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
const currentHandler = endpoint.handler(method)
|
|
248
|
+
if (!currentHandler) {
|
|
249
|
+
res.status(404).json({ error: 'Not found' })
|
|
250
|
+
return
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
const parameters = { ...req.query, ...req.body, ...req.params }
|
|
254
|
+
const currentSchema = endpoint.schema(method)
|
|
255
|
+
const validated = currentSchema ? currentSchema.parse(parameters) : parameters
|
|
256
|
+
|
|
257
|
+
const ctx: EndpointContext = {
|
|
258
|
+
container: endpoint.container,
|
|
259
|
+
request: req,
|
|
260
|
+
response: res,
|
|
261
|
+
query: req.query || {},
|
|
262
|
+
body: req.body || {},
|
|
263
|
+
params: req.params || {},
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
const result = await currentHandler(validated, ctx)
|
|
267
|
+
endpoint.state.set('requestCount', (endpoint.state.get('requestCount') || 0) + 1)
|
|
268
|
+
endpoint.emit('request', method, endpoint.path, parameters)
|
|
269
|
+
|
|
270
|
+
if (!res.headersSent) {
|
|
271
|
+
res.json(result)
|
|
272
|
+
}
|
|
273
|
+
} catch (err: any) {
|
|
274
|
+
endpoint.emit('error', err)
|
|
275
|
+
if (!res.headersSent) {
|
|
276
|
+
if (err.name === 'ZodError') {
|
|
277
|
+
const issues = err.issues || err.errors || []
|
|
278
|
+
const details = issues.map((e: any) => `${(e.path || []).join('.')}: ${e.message}`).join(', ')
|
|
279
|
+
console.error(`[${method.toUpperCase()} ${endpoint.path}] Validation failed: ${details}`)
|
|
280
|
+
res.status(400).json({ error: `Validation failed: ${details}`, details: issues })
|
|
281
|
+
} else {
|
|
282
|
+
console.error(`[${method.toUpperCase()} ${endpoint.path}] ${err.message}`)
|
|
283
|
+
res.status(500).json({ error: err.message })
|
|
284
|
+
}
|
|
285
|
+
}
|
|
286
|
+
}
|
|
287
|
+
})
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
this.state.set('mounted', true)
|
|
291
|
+
this.emit('mounted', this.path)
|
|
292
|
+
return this
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
toOpenAPIPathItem(): Record<string, any> {
|
|
296
|
+
const pathItem: Record<string, any> = {}
|
|
297
|
+
|
|
298
|
+
for (const method of this.methods) {
|
|
299
|
+
const methodSchema = this.schema(method)
|
|
300
|
+
const operationId = `${method}_${this.path.replace(/\//g, '_').replace(/^_/, '')}`
|
|
301
|
+
|
|
302
|
+
const operation: Record<string, any> = {
|
|
303
|
+
operationId,
|
|
304
|
+
summary: this._module?.description || `${method.toUpperCase()} ${this.path}`,
|
|
305
|
+
tags: this._module?.tags || [],
|
|
306
|
+
responses: {
|
|
307
|
+
'200': {
|
|
308
|
+
description: 'Successful response',
|
|
309
|
+
content: { 'application/json': { schema: { type: 'object' } } },
|
|
310
|
+
},
|
|
311
|
+
...(this.rateLimitFor(method) ? { '429': { description: 'Rate limit exceeded' } } : {}),
|
|
312
|
+
'400': { description: 'Validation error' },
|
|
313
|
+
'500': { description: 'Server error' },
|
|
314
|
+
},
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
if (methodSchema) {
|
|
318
|
+
try {
|
|
319
|
+
const jsonSchema = (methodSchema as any).toJSONSchema()
|
|
320
|
+
|
|
321
|
+
if (method === 'get' || method === 'delete') {
|
|
322
|
+
operation.parameters = Object.entries((jsonSchema as any).properties || {}).map(
|
|
323
|
+
([name, prop]: [string, any]) => ({
|
|
324
|
+
name,
|
|
325
|
+
in: 'query',
|
|
326
|
+
required: (jsonSchema as any).required?.includes(name) || false,
|
|
327
|
+
schema: prop,
|
|
328
|
+
description: prop.description || '',
|
|
329
|
+
})
|
|
330
|
+
)
|
|
331
|
+
} else {
|
|
332
|
+
operation.requestBody = {
|
|
333
|
+
required: true,
|
|
334
|
+
content: { 'application/json': { schema: jsonSchema } },
|
|
335
|
+
}
|
|
336
|
+
}
|
|
337
|
+
} catch {
|
|
338
|
+
// Schema conversion failed, serve without parameter docs
|
|
339
|
+
}
|
|
340
|
+
}
|
|
341
|
+
|
|
342
|
+
pathItem[method] = operation
|
|
343
|
+
}
|
|
344
|
+
|
|
345
|
+
return pathItem
|
|
346
|
+
}
|
|
347
|
+
}
|
|
348
|
+
|
|
349
|
+
export function warnUnknownExports(mod: Record<string, any>, filePath: string): void {
|
|
350
|
+
const unknown = Object.keys(mod).filter(k => !k.startsWith('__') && !KNOWN_EXPORTS.has(k))
|
|
351
|
+
if (unknown.length > 0) {
|
|
352
|
+
console.warn(`[endpoint] ${filePath}: unknown exports: ${unknown.join(', ')}`)
|
|
353
|
+
}
|
|
354
|
+
}
|
|
355
|
+
|
|
356
|
+
export class EndpointsRegistry extends Registry<Endpoint<any>> {
|
|
357
|
+
override scope = 'endpoints'
|
|
358
|
+
override baseClass = Endpoint
|
|
359
|
+
}
|
|
360
|
+
|
|
361
|
+
export const endpoints = new EndpointsRegistry()
|
|
362
|
+
|
|
363
|
+
export const helperCache = new Map()
|
|
364
|
+
|
|
365
|
+
export default Endpoint
|
package/src/entity.ts
ADDED
|
@@ -0,0 +1,173 @@
|
|
|
1
|
+
import { Bus, type EventMap } from './bus.js'
|
|
2
|
+
import { State, type SetStateValue } from './state.js'
|
|
3
|
+
import type { Container } from './container.js'
|
|
4
|
+
import uuid from 'node-uuid'
|
|
5
|
+
import { z } from 'zod'
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* An Entity is a lightweight, composable object with observable state, a typed event bus,
|
|
9
|
+
* and a reference to the container. Unlike Helpers, Entities are not class-based — they
|
|
10
|
+
* are plain objects created directly via `container.entity()` and extended via `.extend()`.
|
|
11
|
+
*
|
|
12
|
+
* Same id + options always returns the same underlying state/bus instance (cached).
|
|
13
|
+
*
|
|
14
|
+
* @template TState - Shape of the entity's observable state
|
|
15
|
+
* @template TOptions - Shape of the options passed at construction
|
|
16
|
+
* @template TEvents - Event map for the typed event bus
|
|
17
|
+
*/
|
|
18
|
+
export type Entity<
|
|
19
|
+
TState extends Record<string, any> = Record<string, any>,
|
|
20
|
+
TOptions extends Record<string, any> = Record<string, any>,
|
|
21
|
+
TEvents extends EventMap = EventMap,
|
|
22
|
+
> = {
|
|
23
|
+
/** The id passed to container.entity() */
|
|
24
|
+
readonly id: string
|
|
25
|
+
/** Per-instance UUID */
|
|
26
|
+
readonly uuid: string
|
|
27
|
+
/** The options the entity was created with */
|
|
28
|
+
readonly options: TOptions
|
|
29
|
+
/** Observable state */
|
|
30
|
+
readonly state: State<TState>
|
|
31
|
+
/** The container this entity belongs to */
|
|
32
|
+
readonly container: Container<any>
|
|
33
|
+
|
|
34
|
+
on<E extends keyof TEvents>(event: E, listener: (...args: TEvents[E]) => void): void
|
|
35
|
+
off<E extends keyof TEvents>(event: E, listener?: (...args: TEvents[E]) => void): void
|
|
36
|
+
once<E extends keyof TEvents>(event: E, listener: (...args: TEvents[E]) => void): void
|
|
37
|
+
emit<E extends keyof TEvents>(event: E, ...args: TEvents[E]): void
|
|
38
|
+
waitFor<E extends keyof TEvents>(event: E): Promise<TEvents[E]>
|
|
39
|
+
setState(value: SetStateValue<TState>): void
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* Graft an object of functions and getters onto this entity, returning a new type-safe
|
|
43
|
+
* object. The base entity (state, events, container, options) is accessible via `this`
|
|
44
|
+
* in all grafted methods and getters. Chaining `.extend()` on the result works correctly —
|
|
45
|
+
* each layer has access to everything in the layers below it.
|
|
46
|
+
*
|
|
47
|
+
* @example
|
|
48
|
+
* ```ts
|
|
49
|
+
* const session = container.entity('session:abc', { userId: '42' })
|
|
50
|
+
* const rich = session.extend({
|
|
51
|
+
* greet() { return `Hello user ${this.options.userId}` },
|
|
52
|
+
* get label() { return `Session ${this.id}` },
|
|
53
|
+
* })
|
|
54
|
+
* rich.greet() // "Hello user 42"
|
|
55
|
+
* ```
|
|
56
|
+
*/
|
|
57
|
+
extend<Ext extends Record<string, any>>(
|
|
58
|
+
extensions: Ext & ThisType<Entity<TState, TOptions, TEvents> & Ext>
|
|
59
|
+
): Entity<TState, TOptions, TEvents> & Ext
|
|
60
|
+
|
|
61
|
+
/**
|
|
62
|
+
* Register a method on this entity as an AI tool with the given Zod schema.
|
|
63
|
+
* Chainable — returns `this` so you can stack multiple `.expose()` calls.
|
|
64
|
+
*
|
|
65
|
+
* @example
|
|
66
|
+
* ```ts
|
|
67
|
+
* const agent = container.entity('agent', {})
|
|
68
|
+
* .extend({ search({ query }: { query: string }) { ... } })
|
|
69
|
+
* .expose('search', z.object({ query: z.string() }))
|
|
70
|
+
*
|
|
71
|
+
* assistant.addTools(agent)
|
|
72
|
+
* ```
|
|
73
|
+
*/
|
|
74
|
+
expose(this: Entity<TState, TOptions, TEvents>, functionName: string, schema: z.ZodType): Entity<TState, TOptions, TEvents>
|
|
75
|
+
|
|
76
|
+
/**
|
|
77
|
+
* Returns `{ schemas, handlers }` for all tools registered via `.expose()`.
|
|
78
|
+
* Compatible with `assistant.addTools()` and the standard `toTools` contract.
|
|
79
|
+
*/
|
|
80
|
+
toTools(): { schemas: Record<string, z.ZodType>; handlers: Record<string, Function> }
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
/** @internal */
|
|
84
|
+
export function createEntityObject<
|
|
85
|
+
TState extends Record<string, any> = Record<string, any>,
|
|
86
|
+
TOptions extends Record<string, any> = Record<string, any>,
|
|
87
|
+
TEvents extends EventMap = EventMap,
|
|
88
|
+
>(
|
|
89
|
+
id: string,
|
|
90
|
+
container: Container<any>,
|
|
91
|
+
options: TOptions,
|
|
92
|
+
): Entity<TState, TOptions, TEvents> {
|
|
93
|
+
const _events = new Bus<TEvents>()
|
|
94
|
+
const _state = new State<TState>()
|
|
95
|
+
|
|
96
|
+
const _exposed = new Map<string, z.ZodType>()
|
|
97
|
+
|
|
98
|
+
const entity: Entity<TState, TOptions, TEvents> = {
|
|
99
|
+
id,
|
|
100
|
+
uuid: (uuid as any).v4(),
|
|
101
|
+
options,
|
|
102
|
+
state: _state,
|
|
103
|
+
container,
|
|
104
|
+
on(event: any, listener: any) { _events.on(event, listener) },
|
|
105
|
+
off(event: any, listener?: any) { _events.off(event, listener) },
|
|
106
|
+
once(event: any, listener: any) { _events.once(event, listener) },
|
|
107
|
+
emit(event: any, ...args: any[]) { _events.emit(event, ...args as any) },
|
|
108
|
+
waitFor(event: any) { return _events.waitFor(event) },
|
|
109
|
+
setState(value: any) { _state.setState(value) },
|
|
110
|
+
extend(extensions: any) { return applyExtensions(this as any, extensions) },
|
|
111
|
+
expose(functionName: string, schema: z.ZodType) {
|
|
112
|
+
// Each prototype layer gets its own _exposed map (lazy-created on write)
|
|
113
|
+
if (!Object.prototype.hasOwnProperty.call(this, '_exposed')) {
|
|
114
|
+
;(this as any)._exposed = new Map<string, z.ZodType>()
|
|
115
|
+
}
|
|
116
|
+
;(this as any)._exposed.set(functionName, schema)
|
|
117
|
+
return this
|
|
118
|
+
},
|
|
119
|
+
toTools() {
|
|
120
|
+
const schemas: Record<string, z.ZodType> = {}
|
|
121
|
+
const handlers: Record<string, Function> = {}
|
|
122
|
+
|
|
123
|
+
// Walk the prototype chain collecting _exposed maps; shallower layers win
|
|
124
|
+
let obj: any = this
|
|
125
|
+
while (obj !== null) {
|
|
126
|
+
if (Object.prototype.hasOwnProperty.call(obj, '_exposed')) {
|
|
127
|
+
for (const [name, schema] of (obj._exposed as Map<string, z.ZodType>)) {
|
|
128
|
+
if (!(name in schemas)) {
|
|
129
|
+
schemas[name] = schema
|
|
130
|
+
handlers[name] = (args: any) => (this as any)[name](args)
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
obj = Object.getPrototypeOf(obj)
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
return { schemas, handlers }
|
|
138
|
+
},
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
;(entity as any)._exposed = _exposed
|
|
142
|
+
|
|
143
|
+
return entity
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
/**
|
|
147
|
+
* Graft extensions onto an entity via prototype delegation.
|
|
148
|
+
*
|
|
149
|
+
* The extended object's prototype is `base`, so all base properties (state, container,
|
|
150
|
+
* on/off/emit, etc.) are reachable through `this` in any grafted method or getter.
|
|
151
|
+
* Calling `.extend()` on the result correctly uses the extended object as the new base.
|
|
152
|
+
*/
|
|
153
|
+
function applyExtensions<
|
|
154
|
+
TState extends Record<string, any>,
|
|
155
|
+
TOptions extends Record<string, any>,
|
|
156
|
+
TEvents extends EventMap,
|
|
157
|
+
Ext extends Record<string, any>,
|
|
158
|
+
>(
|
|
159
|
+
base: Entity<TState, TOptions, TEvents>,
|
|
160
|
+
extensions: Ext,
|
|
161
|
+
): Entity<TState, TOptions, TEvents> & Ext {
|
|
162
|
+
const extended = Object.create(base) as Entity<TState, TOptions, TEvents> & Ext
|
|
163
|
+
|
|
164
|
+
for (const key of Object.getOwnPropertyNames(extensions)) {
|
|
165
|
+
const descriptor = Object.getOwnPropertyDescriptor(extensions, key)!
|
|
166
|
+
Object.defineProperty(extended, key, {
|
|
167
|
+
...descriptor,
|
|
168
|
+
configurable: true,
|
|
169
|
+
})
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
return extended
|
|
173
|
+
}
|
package/src/feature.ts
ADDED
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
import { Helper } from './helper.js';
|
|
2
|
+
import { Registry } from './registry.js'
|
|
3
|
+
import type { ContainerContext } from './container.js'
|
|
4
|
+
import { kebabCase, camelCase } from 'lodash-es'
|
|
5
|
+
import type { YAML } from './node/features/yaml.js';
|
|
6
|
+
import { z } from 'zod'
|
|
7
|
+
import { FeatureStateSchema, FeatureOptionsSchema, FeatureEventsSchema } from './schemas/base.js'
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Use module augmentation to register features, the same way you would register
|
|
11
|
+
* them at runtime. This will help developers get autocomplete etc.
|
|
12
|
+
*/
|
|
13
|
+
export interface AvailableFeatures {
|
|
14
|
+
yaml: typeof YAML
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export type FeatureOptions = z.infer<typeof FeatureOptionsSchema>
|
|
18
|
+
export type FeatureState = z.infer<typeof FeatureStateSchema>
|
|
19
|
+
|
|
20
|
+
export abstract class Feature<T extends FeatureState = FeatureState, K extends FeatureOptions = FeatureOptions> extends Helper<T, K> {
|
|
21
|
+
static override stateSchema = FeatureStateSchema
|
|
22
|
+
static override optionsSchema = FeatureOptionsSchema
|
|
23
|
+
static override eventsSchema = FeatureEventsSchema
|
|
24
|
+
|
|
25
|
+
/** Self-register a Feature subclass from a static initialization block. */
|
|
26
|
+
static register: (SubClass: abstract new (options: any, context: any) => Feature, id?: string) => abstract new (options: any, context: any) => Feature
|
|
27
|
+
|
|
28
|
+
override get shortcut() {
|
|
29
|
+
return (this.constructor as any).shortcut as string
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
get isEnabled() {
|
|
33
|
+
return this.state.get('enabled')
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
constructor(options: K, context: ContainerContext) {
|
|
37
|
+
super(options, context)
|
|
38
|
+
|
|
39
|
+
if(typeof context.container !== 'object') {
|
|
40
|
+
console.error(this, options, context)
|
|
41
|
+
throw new Error('You should not instantiate a feature directly. Use container.feature() instead.')
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
if(options?.enable) {
|
|
46
|
+
this.enable()
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* For features where there only needs to be a single instance, you
|
|
52
|
+
* can use this method to attach the feature to the container.
|
|
53
|
+
*/
|
|
54
|
+
protected attachToContainer() {
|
|
55
|
+
Object.defineProperty(this.container, this.shortcut.split('.').pop()!, {
|
|
56
|
+
get: () => this,
|
|
57
|
+
configurable: true,
|
|
58
|
+
enumerable: true,
|
|
59
|
+
})
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
async enable(options: any = {}) : Promise<this> {
|
|
63
|
+
this.attachToContainer()
|
|
64
|
+
this.emit('enabled')
|
|
65
|
+
this.state.set('enabled', true)
|
|
66
|
+
|
|
67
|
+
this.container.emit('featureEnabled', this.shortcut, this)
|
|
68
|
+
|
|
69
|
+
return this
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
export class FeaturesRegistry extends Registry<Feature<any, any>> {
|
|
74
|
+
override scope = "features"
|
|
75
|
+
override baseClass = Feature as any
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
export const features = new FeaturesRegistry()
|
|
79
|
+
|
|
80
|
+
/**
|
|
81
|
+
* Self-register a Feature subclass from a static initialization block.
|
|
82
|
+
* IMPORTANT: Place the static block AFTER all static override declarations
|
|
83
|
+
* so schemas, envVars, and other metadata are set before interceptRegistration fires.
|
|
84
|
+
*
|
|
85
|
+
* @example
|
|
86
|
+
* ```typescript
|
|
87
|
+
* export default class DNS extends Feature<DnsState, DnsOptions> {
|
|
88
|
+
* static override stateSchema = DnsStateSchema
|
|
89
|
+
* static override optionsSchema = DnsOptionsSchema
|
|
90
|
+
* static { Feature.register(this, 'dns') } // must come last
|
|
91
|
+
* }
|
|
92
|
+
* ```
|
|
93
|
+
*/
|
|
94
|
+
Feature.register = function registerFeature(
|
|
95
|
+
SubClass: abstract new (options: any, context: any) => Feature,
|
|
96
|
+
id?: string,
|
|
97
|
+
) {
|
|
98
|
+
const registryId = id ?? SubClass.name[0]!.toLowerCase() + SubClass.name.slice(1)
|
|
99
|
+
|
|
100
|
+
// Auto-set shortcut if not explicitly overridden on this class
|
|
101
|
+
if (!Object.getOwnPropertyDescriptor(SubClass, 'shortcut')?.value ||
|
|
102
|
+
(SubClass as any).shortcut === 'unspecified') {
|
|
103
|
+
;(SubClass as any).shortcut = `features.${registryId}` as const
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
// Register in the features registry (interceptRegistration sees all statics above)
|
|
107
|
+
features.register(registryId, SubClass as any)
|
|
108
|
+
|
|
109
|
+
// Generate default attach() if not explicitly overridden on this class
|
|
110
|
+
if (!Object.getOwnPropertyDescriptor(SubClass, 'attach')) {
|
|
111
|
+
;(SubClass as any).attach = (container: any) => {
|
|
112
|
+
features.register(registryId, SubClass as any)
|
|
113
|
+
return container
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
return SubClass
|
|
118
|
+
}
|