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,735 @@
|
|
|
1
|
+
import { z } from 'zod'
|
|
2
|
+
import { FeatureStateSchema, FeatureOptionsSchema, FeatureEventsSchema } from '../../schemas/base.js'
|
|
3
|
+
import { Feature } from '../feature.js'
|
|
4
|
+
import { Feature as UniversalFeature } from '../../feature.js'
|
|
5
|
+
import { Client, clients } from '../../client.js'
|
|
6
|
+
import { RestClient } from '../../clients/rest.js'
|
|
7
|
+
import { GraphClient } from '../../clients/graph.js'
|
|
8
|
+
import { WebSocketClient } from '../../clients/websocket.js'
|
|
9
|
+
import { Server, servers } from '../../server.js'
|
|
10
|
+
import { ExpressServer } from '../../servers/express.js'
|
|
11
|
+
import { WebsocketServer } from '../../servers/socket.js'
|
|
12
|
+
import { Command, commands } from '../../command.js'
|
|
13
|
+
import { graftModule, isNativeHelperClass } from '../../graft.js'
|
|
14
|
+
import { endpoints } from '../../endpoint.js'
|
|
15
|
+
import { Selector, selectors } from '../../selector.js'
|
|
16
|
+
import type { Registry } from '../../registry.js'
|
|
17
|
+
import type { FileManager } from './file-manager.js'
|
|
18
|
+
import type { VM } from './vm.js'
|
|
19
|
+
import { resolve, parse } from 'path'
|
|
20
|
+
import { existsSync } from 'fs'
|
|
21
|
+
|
|
22
|
+
export const HelpersStateSchema = FeatureStateSchema.extend({
|
|
23
|
+
discovered: z.record(z.string(), z.boolean()).default({}).describe('Which registry types have been discovered'),
|
|
24
|
+
registered: z.array(z.string()).default([]).describe('Names of project-level helpers that were discovered (type.name)'),
|
|
25
|
+
})
|
|
26
|
+
|
|
27
|
+
export type HelpersState = z.infer<typeof HelpersStateSchema>
|
|
28
|
+
|
|
29
|
+
export const HelpersOptionsSchema = FeatureOptionsSchema.extend({
|
|
30
|
+
rootDir: z.string().optional().describe('Root directory to scan for helper folders. Defaults to container.cwd'),
|
|
31
|
+
})
|
|
32
|
+
|
|
33
|
+
export type HelpersOptions = z.infer<typeof HelpersOptionsSchema>
|
|
34
|
+
|
|
35
|
+
export const HelpersEventsSchema = FeatureEventsSchema.extend({
|
|
36
|
+
discovered: z.tuple([
|
|
37
|
+
z.string().describe('Registry type that was discovered'),
|
|
38
|
+
z.array(z.string()).describe('Names of newly registered helpers'),
|
|
39
|
+
]).describe('Emitted after a registry type has been discovered'),
|
|
40
|
+
registered: z.tuple([
|
|
41
|
+
z.string().describe('Registry type'),
|
|
42
|
+
z.string().describe('Helper name'),
|
|
43
|
+
z.any().describe('The helper class or module'),
|
|
44
|
+
]).describe('Emitted when a single helper is registered'),
|
|
45
|
+
})
|
|
46
|
+
|
|
47
|
+
type RegistryType = 'features' | 'clients' | 'servers' | 'commands' | 'endpoints' | 'selectors'
|
|
48
|
+
|
|
49
|
+
const CLASS_BASED: RegistryType[] = ['features', 'clients', 'servers']
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* The Helpers feature is a unified gateway for discovering and registering
|
|
53
|
+
* project-level helpers from conventional folder locations.
|
|
54
|
+
*
|
|
55
|
+
* It scans known folder names (features/, clients/, servers/, commands/, endpoints/, selectors/)
|
|
56
|
+
* and handles registration differently based on the helper type:
|
|
57
|
+
*
|
|
58
|
+
* - Class-based (features, clients, servers): Dynamic import, validate subclass, register
|
|
59
|
+
* - Config-based (commands, endpoints, selectors): Delegate to existing discovery mechanisms
|
|
60
|
+
*
|
|
61
|
+
* @example
|
|
62
|
+
* ```typescript
|
|
63
|
+
* const helpers = container.feature('helpers', { enable: true })
|
|
64
|
+
*
|
|
65
|
+
* // Discover all helper types
|
|
66
|
+
* await helpers.discoverAll()
|
|
67
|
+
*
|
|
68
|
+
* // Discover a specific type
|
|
69
|
+
* await helpers.discover('features')
|
|
70
|
+
*
|
|
71
|
+
* // Unified view of all available helpers
|
|
72
|
+
* console.log(helpers.available)
|
|
73
|
+
* ```
|
|
74
|
+
*/
|
|
75
|
+
export class Helpers extends Feature<HelpersState, HelpersOptions> {
|
|
76
|
+
static override shortcut = 'features.helpers' as const
|
|
77
|
+
static override description = 'Unified gateway for discovering and registering project-level helpers'
|
|
78
|
+
static override stateSchema = HelpersStateSchema
|
|
79
|
+
static override optionsSchema = HelpersOptionsSchema
|
|
80
|
+
static override eventsSchema = HelpersEventsSchema
|
|
81
|
+
static { Feature.register(this, 'helpers') }
|
|
82
|
+
|
|
83
|
+
/** In-flight or completed discovery promises, keyed by registry type */
|
|
84
|
+
private _discoveryPromises: Map<string, Promise<string[]>> = new Map()
|
|
85
|
+
/** Cached results from completed discoveries */
|
|
86
|
+
private _discoveryResults: Map<string, string[]> = new Map()
|
|
87
|
+
/** In-flight or completed discoverAll promise */
|
|
88
|
+
private _discoverAllPromise: Promise<Record<string, string[]>> | null = null
|
|
89
|
+
|
|
90
|
+
/**
|
|
91
|
+
* Returns a mapping from registry type name to its registry singleton, base class, and conventional folder candidates.
|
|
92
|
+
*/
|
|
93
|
+
private get registryMap(): Record<RegistryType, { registry: Registry<any>, baseClass: any, folders: string[] }> {
|
|
94
|
+
return {
|
|
95
|
+
features: { registry: this.container.features as any, baseClass: UniversalFeature, folders: ['features'] },
|
|
96
|
+
clients: { registry: clients, baseClass: Client, folders: ['clients'] },
|
|
97
|
+
servers: { registry: servers, baseClass: Server, folders: ['servers'] },
|
|
98
|
+
commands: { registry: commands, baseClass: null, folders: ['commands'] },
|
|
99
|
+
endpoints: { registry: endpoints, baseClass: null, folders: ['endpoints'] },
|
|
100
|
+
selectors: { registry: selectors, baseClass: null, folders: ['selectors'] },
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
/** The root directory to scan for helper folders. */
|
|
105
|
+
get rootDir(): string {
|
|
106
|
+
return this.options.rootDir || this.container.cwd
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
/**
|
|
110
|
+
* Whether to use native `import()` for loading project helpers.
|
|
111
|
+
* True only if `@soederpop/luca` is actually resolvable in `node_modules`.
|
|
112
|
+
* Warns when `node_modules` exists but the package is missing.
|
|
113
|
+
*/
|
|
114
|
+
get useNativeImport(): boolean {
|
|
115
|
+
const hasNodeModules = existsSync(resolve(this.rootDir, 'node_modules'))
|
|
116
|
+
const hasLuca = hasNodeModules && existsSync(resolve(this.rootDir, 'node_modules', '@soederpop', 'luca'))
|
|
117
|
+
|
|
118
|
+
// VM bundling handles missing @soederpop/luca gracefully — no warning needed
|
|
119
|
+
|
|
120
|
+
return hasLuca
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
|
|
124
|
+
/** Track whether we've seeded the VM with virtual modules */
|
|
125
|
+
private _vmSeeded = false
|
|
126
|
+
|
|
127
|
+
/**
|
|
128
|
+
* Seeds the VM feature with virtual modules so that project-level files
|
|
129
|
+
* can `import` / `require('@soederpop/luca')`, `zod`, etc. without
|
|
130
|
+
* needing them in `node_modules`.
|
|
131
|
+
*
|
|
132
|
+
* Called automatically when `useNativeImport` is false.
|
|
133
|
+
* Can also be called externally (e.g. from the CLI) to pre-seed before discovery.
|
|
134
|
+
*/
|
|
135
|
+
seedVirtualModules(): void {
|
|
136
|
+
if (this._vmSeeded) return
|
|
137
|
+
this._vmSeeded = true
|
|
138
|
+
|
|
139
|
+
const vm = this.container.feature('vm') as unknown as VM
|
|
140
|
+
|
|
141
|
+
// Provide the full @soederpop/luca barrel — everything node.ts exports
|
|
142
|
+
// We build the exports object from the already-loaded modules in memory
|
|
143
|
+
const lucaExports: Record<string, any> = {
|
|
144
|
+
// Core classes
|
|
145
|
+
Feature: UniversalFeature,
|
|
146
|
+
Container: this.container.constructor,
|
|
147
|
+
Helper: Object.getPrototypeOf(UniversalFeature.prototype).constructor,
|
|
148
|
+
Client,
|
|
149
|
+
Server,
|
|
150
|
+
Command,
|
|
151
|
+
Registry: Object.getPrototypeOf(this.container.features).constructor,
|
|
152
|
+
|
|
153
|
+
// Utilities
|
|
154
|
+
graftModule,
|
|
155
|
+
isNativeHelperClass,
|
|
156
|
+
|
|
157
|
+
// Registries
|
|
158
|
+
features: this.container.features,
|
|
159
|
+
clients,
|
|
160
|
+
servers,
|
|
161
|
+
commands,
|
|
162
|
+
endpoints,
|
|
163
|
+
selectors,
|
|
164
|
+
|
|
165
|
+
// Registry classes
|
|
166
|
+
ClientsRegistry: clients.constructor,
|
|
167
|
+
CommandsRegistry: commands.constructor,
|
|
168
|
+
EndpointsRegistry: endpoints.constructor,
|
|
169
|
+
ServersRegistry: servers.constructor,
|
|
170
|
+
SelectorsRegistry: selectors.constructor,
|
|
171
|
+
FeaturesRegistry: this.container.features.constructor,
|
|
172
|
+
|
|
173
|
+
// Helper subclasses
|
|
174
|
+
Selector,
|
|
175
|
+
RestClient,
|
|
176
|
+
GraphClient,
|
|
177
|
+
WebSocketClient,
|
|
178
|
+
ExpressServer,
|
|
179
|
+
WebsocketServer,
|
|
180
|
+
|
|
181
|
+
// The singleton container
|
|
182
|
+
default: this.container,
|
|
183
|
+
|
|
184
|
+
// Convenient feature instances
|
|
185
|
+
fs: this.container.feature('fs'),
|
|
186
|
+
ui: this.container.feature('ui'),
|
|
187
|
+
vm,
|
|
188
|
+
proc: this.container.feature('proc'),
|
|
189
|
+
|
|
190
|
+
// Zod re-export
|
|
191
|
+
z,
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
// Schemas
|
|
195
|
+
const schemasModule: Record<string, any> = { CommandOptionsSchema: commands.baseClass?.optionsSchema || z.object({}) }
|
|
196
|
+
try {
|
|
197
|
+
// Pull all base schemas from the already-loaded schemas/base module
|
|
198
|
+
const baseSchemas = require('../../schemas/base.js')
|
|
199
|
+
Object.assign(lucaExports, baseSchemas)
|
|
200
|
+
Object.assign(schemasModule, baseSchemas)
|
|
201
|
+
} catch {
|
|
202
|
+
// Fallback: provide the essentials
|
|
203
|
+
lucaExports.FeatureStateSchema = FeatureStateSchema
|
|
204
|
+
lucaExports.FeatureOptionsSchema = FeatureOptionsSchema
|
|
205
|
+
lucaExports.FeatureEventsSchema = FeatureEventsSchema
|
|
206
|
+
schemasModule.FeatureStateSchema = FeatureStateSchema
|
|
207
|
+
schemasModule.FeatureOptionsSchema = FeatureOptionsSchema
|
|
208
|
+
schemasModule.FeatureEventsSchema = FeatureEventsSchema
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
vm.defineModule('@soederpop/luca', lucaExports)
|
|
212
|
+
vm.defineModule('@soederpop/luca/schemas', schemasModule)
|
|
213
|
+
vm.defineModule('@soederpop/luca/node', lucaExports)
|
|
214
|
+
|
|
215
|
+
// Deep import paths AIs and developers might reach for
|
|
216
|
+
vm.defineModule('@soederpop/luca/client', { Client, ClientsRegistry: clients.constructor, default: Client })
|
|
217
|
+
vm.defineModule('@soederpop/luca/server', { Server, ServersRegistry: servers.constructor, default: Server })
|
|
218
|
+
vm.defineModule('@soederpop/luca/clients/rest', { RestClient, default: RestClient })
|
|
219
|
+
vm.defineModule('@soederpop/luca/clients/graph', { GraphClient, default: GraphClient })
|
|
220
|
+
vm.defineModule('@soederpop/luca/clients/websocket', { WebSocketClient, default: WebSocketClient })
|
|
221
|
+
vm.defineModule('@soederpop/luca/servers/express', { ExpressServer, default: ExpressServer })
|
|
222
|
+
vm.defineModule('@soederpop/luca/servers/socket', { WebsocketServer, default: WebsocketServer })
|
|
223
|
+
|
|
224
|
+
vm.defineModule('zod', { z, default: { z } })
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
/**
|
|
228
|
+
* Returns a unified view of all available helpers across all registries.
|
|
229
|
+
* Each key is a registry type, each value is the list of helper names in that registry.
|
|
230
|
+
*
|
|
231
|
+
* @example
|
|
232
|
+
* ```typescript
|
|
233
|
+
* container.helpers.available
|
|
234
|
+
* // { features: ['fs', 'git', ...], clients: ['rest', 'websocket'], ... }
|
|
235
|
+
* ```
|
|
236
|
+
*/
|
|
237
|
+
get available(): Record<string, string[]> {
|
|
238
|
+
const result: Record<string, string[]> = {}
|
|
239
|
+
for (const [type, { registry }] of Object.entries(this.registryMap)) {
|
|
240
|
+
result[type] = registry.available
|
|
241
|
+
}
|
|
242
|
+
return result
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
/**
|
|
246
|
+
* Ensures the fileManager feature is started before using it for discovery.
|
|
247
|
+
*
|
|
248
|
+
* @returns The started fileManager instance
|
|
249
|
+
*/
|
|
250
|
+
private async ensureFileManager(): Promise<FileManager> {
|
|
251
|
+
const fm = this.container.feature('fileManager', { enable: true }) as unknown as FileManager
|
|
252
|
+
if (!fm.isStarted) {
|
|
253
|
+
await fm.start()
|
|
254
|
+
}
|
|
255
|
+
return fm
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
/**
|
|
259
|
+
* Resolves which conventional folder path exists for a given registry type.
|
|
260
|
+
* Tries each candidate folder in order and returns the first one that exists.
|
|
261
|
+
*
|
|
262
|
+
* @param type - The registry type to resolve the folder for
|
|
263
|
+
* @returns Absolute path to the folder, or null if none exist
|
|
264
|
+
*/
|
|
265
|
+
private resolveFolderPath(type: RegistryType): string | null {
|
|
266
|
+
const { folders } = this.registryMap[type]
|
|
267
|
+
const { fs } = this.container
|
|
268
|
+
|
|
269
|
+
for (const candidate of folders) {
|
|
270
|
+
const dir = resolve(this.rootDir, candidate)
|
|
271
|
+
if (fs.exists(dir)) {
|
|
272
|
+
return dir
|
|
273
|
+
}
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
return null
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
/**
|
|
280
|
+
* Discover and register project-level helpers of the given type.
|
|
281
|
+
*
|
|
282
|
+
* Idempotent: the first caller triggers the actual scan. Subsequent callers
|
|
283
|
+
* receive the cached results. If discovery is in-flight, callers await the
|
|
284
|
+
* same promise — no duplicate work.
|
|
285
|
+
*
|
|
286
|
+
* @param type - Which type of helpers to discover
|
|
287
|
+
* @param options - Optional overrides
|
|
288
|
+
* @param options.directory - Override the directory to scan
|
|
289
|
+
* @returns Names of helpers that were discovered and registered
|
|
290
|
+
*
|
|
291
|
+
* @example
|
|
292
|
+
* ```typescript
|
|
293
|
+
* const names = await container.helpers.discover('features')
|
|
294
|
+
* console.log(names) // ['myCustomFeature']
|
|
295
|
+
* ```
|
|
296
|
+
*/
|
|
297
|
+
async discover(type: RegistryType, options: { directory?: string } = {}): Promise<string[]> {
|
|
298
|
+
// Key by type + resolved directory so that different directories
|
|
299
|
+
// (e.g. project commands/ vs ~/.luca/commands/) are discovered independently
|
|
300
|
+
// while concurrent calls to the same directory coalesce on one promise.
|
|
301
|
+
const dir = options.directory || this.resolveFolderPath(type)
|
|
302
|
+
const cacheKey = dir ? `${type}:${dir}` : type
|
|
303
|
+
|
|
304
|
+
// Return cached results if already completed
|
|
305
|
+
if (this._discoveryResults.has(cacheKey)) {
|
|
306
|
+
return this._discoveryResults.get(cacheKey)!
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
// If in-flight, await the same promise
|
|
310
|
+
if (this._discoveryPromises.has(cacheKey)) {
|
|
311
|
+
return this._discoveryPromises.get(cacheKey)!
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
// First caller — start the work and store the promise
|
|
315
|
+
const promise = this._doDiscover(type, { directory: dir || undefined })
|
|
316
|
+
this._discoveryPromises.set(cacheKey, promise)
|
|
317
|
+
|
|
318
|
+
const names = await promise
|
|
319
|
+
|
|
320
|
+
// Cache the final results
|
|
321
|
+
this._discoveryResults.set(cacheKey, names)
|
|
322
|
+
|
|
323
|
+
return names
|
|
324
|
+
}
|
|
325
|
+
|
|
326
|
+
/** Internal: performs the actual discovery work for a single type. */
|
|
327
|
+
private async _doDiscover(type: RegistryType, options: { directory?: string } = {}): Promise<string[]> {
|
|
328
|
+
const dir = options.directory || this.resolveFolderPath(type)
|
|
329
|
+
|
|
330
|
+
if (!dir) {
|
|
331
|
+
return []
|
|
332
|
+
}
|
|
333
|
+
|
|
334
|
+
let names: string[]
|
|
335
|
+
|
|
336
|
+
if (CLASS_BASED.includes(type)) {
|
|
337
|
+
names = await this.discoverClassBased(type, dir)
|
|
338
|
+
} else {
|
|
339
|
+
names = await this.discoverConfigBased(type, dir)
|
|
340
|
+
}
|
|
341
|
+
|
|
342
|
+
// Update state for observability
|
|
343
|
+
const discovered = this.state.get('discovered') || {}
|
|
344
|
+
this.state.set('discovered', { ...discovered, [type]: true })
|
|
345
|
+
|
|
346
|
+
const existing = this.state.get('registered') || []
|
|
347
|
+
this.state.set('registered', [...existing, ...names.map(n => `${type}.${n}`)])
|
|
348
|
+
|
|
349
|
+
this.emit('discovered' as any, type, names)
|
|
350
|
+
|
|
351
|
+
return names
|
|
352
|
+
}
|
|
353
|
+
|
|
354
|
+
/**
|
|
355
|
+
* Discover all helper types from their conventional folder locations.
|
|
356
|
+
*
|
|
357
|
+
* Idempotent: safe to call from multiple places (luca.cli.ts, commands, etc.).
|
|
358
|
+
* The first caller triggers discovery; all others receive the same results.
|
|
359
|
+
*
|
|
360
|
+
* @returns Map of registry type to discovered helper names
|
|
361
|
+
*
|
|
362
|
+
* @example
|
|
363
|
+
* ```typescript
|
|
364
|
+
* const results = await container.helpers.discoverAll()
|
|
365
|
+
* // { features: ['myFeature'], clients: [], servers: [], commands: ['deploy'], endpoints: [] }
|
|
366
|
+
* ```
|
|
367
|
+
*/
|
|
368
|
+
async discoverAll(): Promise<Record<string, string[]>> {
|
|
369
|
+
if (this._discoverAllPromise) {
|
|
370
|
+
return this._discoverAllPromise
|
|
371
|
+
}
|
|
372
|
+
|
|
373
|
+
this._discoverAllPromise = this._doDiscoverAll()
|
|
374
|
+
return this._discoverAllPromise
|
|
375
|
+
}
|
|
376
|
+
|
|
377
|
+
/** Internal: performs the actual discoverAll work. */
|
|
378
|
+
private async _doDiscoverAll(): Promise<Record<string, string[]>> {
|
|
379
|
+
const results: Record<string, string[]> = {}
|
|
380
|
+
|
|
381
|
+
for (const type of ['features', 'clients', 'servers', 'commands', 'endpoints', 'selectors'] as RegistryType[]) {
|
|
382
|
+
results[type] = await this.discover(type)
|
|
383
|
+
}
|
|
384
|
+
|
|
385
|
+
return results
|
|
386
|
+
}
|
|
387
|
+
|
|
388
|
+
/**
|
|
389
|
+
* Look up a helper class by type and name.
|
|
390
|
+
*
|
|
391
|
+
* @param type - The registry type (features, clients, servers, commands, endpoints)
|
|
392
|
+
* @param name - The helper name within that registry
|
|
393
|
+
* @returns The helper constructor
|
|
394
|
+
*
|
|
395
|
+
* @example
|
|
396
|
+
* ```typescript
|
|
397
|
+
* const FsClass = container.helpers.lookup('features', 'fs')
|
|
398
|
+
* ```
|
|
399
|
+
*/
|
|
400
|
+
lookup(type: RegistryType, name: string): any {
|
|
401
|
+
const { registry } = this.registryMap[type]
|
|
402
|
+
return registry.lookup(name)
|
|
403
|
+
}
|
|
404
|
+
|
|
405
|
+
/**
|
|
406
|
+
* Get the introspection description for a specific helper.
|
|
407
|
+
*
|
|
408
|
+
* @param type - The registry type
|
|
409
|
+
* @param name - The helper name
|
|
410
|
+
* @returns Markdown description of the helper's interface
|
|
411
|
+
*/
|
|
412
|
+
describe(type: RegistryType, name: string): string {
|
|
413
|
+
const { registry } = this.registryMap[type]
|
|
414
|
+
return registry.describe(name)
|
|
415
|
+
}
|
|
416
|
+
|
|
417
|
+
/**
|
|
418
|
+
* Load a module either via native `import()` or the VM's virtual module system.
|
|
419
|
+
* Uses the same `useNativeImport` check as discovery to decide the loading strategy.
|
|
420
|
+
*
|
|
421
|
+
* @param absPath - Absolute path to the module file
|
|
422
|
+
* @param options - Optional settings
|
|
423
|
+
* @param options.cacheBust - When true, appends a timestamp query to bypass the native import cache (useful for hot reload)
|
|
424
|
+
* @returns The module's exports
|
|
425
|
+
*/
|
|
426
|
+
async loadModuleExports(absPath: string, options?: { cacheBust?: boolean }): Promise<Record<string, any>> {
|
|
427
|
+
if (this.useNativeImport) {
|
|
428
|
+
const importPath = options?.cacheBust ? `${absPath}?t=${Date.now()}` : absPath
|
|
429
|
+
const mod = await import(importPath)
|
|
430
|
+
return mod
|
|
431
|
+
}
|
|
432
|
+
|
|
433
|
+
this.seedVirtualModules()
|
|
434
|
+
const vm = this.container.feature('vm') as unknown as VM
|
|
435
|
+
return vm.loadModule(absPath)
|
|
436
|
+
}
|
|
437
|
+
|
|
438
|
+
/**
|
|
439
|
+
* Discovers class-based helpers (features, clients, servers) from a directory.
|
|
440
|
+
* Uses fileManager when available (fast in git repos), falls back to Glob.
|
|
441
|
+
*/
|
|
442
|
+
private async discoverClassBased(type: RegistryType, dir: string): Promise<string[]> {
|
|
443
|
+
const { registry, baseClass } = this.registryMap[type]
|
|
444
|
+
const discovered: string[] = []
|
|
445
|
+
|
|
446
|
+
// Load build-time introspection data before importing helpers so that
|
|
447
|
+
// interceptRegistration() can merge JSDoc descriptions from the generated file.
|
|
448
|
+
const introspectionFile = resolve(dir, 'introspection.generated.ts')
|
|
449
|
+
try {
|
|
450
|
+
if (existsSync(introspectionFile)) {
|
|
451
|
+
await import(introspectionFile)
|
|
452
|
+
}
|
|
453
|
+
} catch {}
|
|
454
|
+
|
|
455
|
+
// Try fileManager first (faster in git repos, and now symlink-aware),
|
|
456
|
+
// fall back to fs.walk which also follows symlinks natively.
|
|
457
|
+
let files: string[] = []
|
|
458
|
+
try {
|
|
459
|
+
const fm = await this.ensureFileManager()
|
|
460
|
+
const absPatterns = [`${dir}/*.ts`, `${dir}/**/*.ts`]
|
|
461
|
+
const relPatterns = [`${type}/*.ts`, `${type}/**/*.ts`]
|
|
462
|
+
const matched = fm.match([...absPatterns, ...relPatterns])
|
|
463
|
+
files = matched.map((f: string) => f.startsWith('/') ? f : resolve(this.rootDir, f))
|
|
464
|
+
} catch {}
|
|
465
|
+
|
|
466
|
+
// Fall back to fs.walk if fileManager found nothing
|
|
467
|
+
if (files.length === 0) {
|
|
468
|
+
const { fs } = this.container
|
|
469
|
+
const walked = fs.walk(dir, { include: ['**/*.ts'] })
|
|
470
|
+
files = walked.files
|
|
471
|
+
}
|
|
472
|
+
|
|
473
|
+
for (const absPath of files) {
|
|
474
|
+
const { name: fileName } = parse(absPath)
|
|
475
|
+
|
|
476
|
+
if (fileName.includes('.test.') || fileName.includes('.spec.') || fileName.includes('.generated')) {
|
|
477
|
+
continue
|
|
478
|
+
}
|
|
479
|
+
try {
|
|
480
|
+
const mod = await this.loadModuleExports(absPath)
|
|
481
|
+
const ExportedClass = mod.default || mod
|
|
482
|
+
|
|
483
|
+
// Class-based: default export is a subclass of the base
|
|
484
|
+
if (typeof ExportedClass === 'function' && isNativeHelperClass(ExportedClass, baseClass)) {
|
|
485
|
+
const shortcut = ExportedClass.shortcut as string | undefined
|
|
486
|
+
const registryName = shortcut
|
|
487
|
+
? shortcut.replace(`${type}.`, '')
|
|
488
|
+
: this.fileNameToRegistryName(fileName)
|
|
489
|
+
|
|
490
|
+
discovered.push(registryName)
|
|
491
|
+
|
|
492
|
+
if (!registry.has(registryName)) {
|
|
493
|
+
registry.register(registryName, ExportedClass)
|
|
494
|
+
this.emit('registered' as any, type, registryName, ExportedClass)
|
|
495
|
+
}
|
|
496
|
+
} else {
|
|
497
|
+
// Module-based: graft exports onto a generated subclass
|
|
498
|
+
const moduleExports = mod.default && typeof mod.default === 'object' ? mod.default : mod
|
|
499
|
+
const isGraftable = (
|
|
500
|
+
moduleExports.description !== undefined ||
|
|
501
|
+
moduleExports.stateSchema !== undefined ||
|
|
502
|
+
moduleExports.optionsSchema !== undefined ||
|
|
503
|
+
typeof moduleExports.run === 'function' ||
|
|
504
|
+
typeof moduleExports.handler === 'function'
|
|
505
|
+
)
|
|
506
|
+
|
|
507
|
+
if (isGraftable) {
|
|
508
|
+
const registryName = this.fileNameToRegistryName(fileName)
|
|
509
|
+
const GraftedClass = graftModule(baseClass, moduleExports, registryName, type as any)
|
|
510
|
+
|
|
511
|
+
discovered.push(registryName)
|
|
512
|
+
|
|
513
|
+
if (!registry.has(registryName)) {
|
|
514
|
+
registry.register(registryName, GraftedClass as any)
|
|
515
|
+
this.emit('registered' as any, type, registryName, GraftedClass)
|
|
516
|
+
}
|
|
517
|
+
}
|
|
518
|
+
}
|
|
519
|
+
|
|
520
|
+
} catch (err: any) {
|
|
521
|
+
if (err.message?.includes('name collision')) {
|
|
522
|
+
throw err
|
|
523
|
+
}
|
|
524
|
+
console.warn(`Helpers gateway: failed to load ${type} from ${absPath}: ${err.message}`)
|
|
525
|
+
}
|
|
526
|
+
}
|
|
527
|
+
|
|
528
|
+
return discovered
|
|
529
|
+
}
|
|
530
|
+
|
|
531
|
+
/**
|
|
532
|
+
* Discovers config-based helpers (commands, endpoints) by delegating
|
|
533
|
+
* to existing discovery mechanisms.
|
|
534
|
+
*/
|
|
535
|
+
private async discoverConfigBased(type: RegistryType, dir: string): Promise<string[]> {
|
|
536
|
+
const { registry } = this.registryMap[type]
|
|
537
|
+
const beforeNames = new Set(registry.available)
|
|
538
|
+
|
|
539
|
+
if (type === 'commands') {
|
|
540
|
+
if (this.useNativeImport) {
|
|
541
|
+
await commands.discover({ directory: dir })
|
|
542
|
+
} else {
|
|
543
|
+
await this.discoverCommandsViaVM(dir)
|
|
544
|
+
}
|
|
545
|
+
} else if (type === 'endpoints') {
|
|
546
|
+
await this.discoverEndpoints(dir)
|
|
547
|
+
} else if (type === 'selectors') {
|
|
548
|
+
if (this.useNativeImport) {
|
|
549
|
+
await selectors.discover({ directory: dir })
|
|
550
|
+
} else {
|
|
551
|
+
await this.discoverSelectorsViaVM(dir)
|
|
552
|
+
}
|
|
553
|
+
}
|
|
554
|
+
|
|
555
|
+
const afterNames = new Set(registry.available)
|
|
556
|
+
return [...afterNames].filter(n => !beforeNames.has(n))
|
|
557
|
+
}
|
|
558
|
+
|
|
559
|
+
/**
|
|
560
|
+
* Discovers commands using the VM's virtual module system.
|
|
561
|
+
* Mirrors CommandsRegistry.discover() but uses vm.loadModule() instead of import().
|
|
562
|
+
*/
|
|
563
|
+
private async discoverCommandsViaVM(dir: string): Promise<void> {
|
|
564
|
+
this.seedVirtualModules()
|
|
565
|
+
const { fs } = this.container
|
|
566
|
+
// Commands are top-level only (not recursive) — walk and filter to *.ts in the immediate dir
|
|
567
|
+
const walked = fs.walk(dir, { include: ['*.ts'] })
|
|
568
|
+
const tsFiles = walked.files
|
|
569
|
+
.map(f => parse(f))
|
|
570
|
+
.filter(p => p.dir === dir) // top-level only
|
|
571
|
+
.map(p => p.base)
|
|
572
|
+
|
|
573
|
+
for (const file of tsFiles) {
|
|
574
|
+
if (file === 'index.ts') continue
|
|
575
|
+
|
|
576
|
+
const absPath = resolve(dir, file)
|
|
577
|
+
const name = file.replace(/\.ts$/, '')
|
|
578
|
+
|
|
579
|
+
if (commands.has(name)) continue
|
|
580
|
+
|
|
581
|
+
try {
|
|
582
|
+
const mod = await this.loadModuleExports(absPath)
|
|
583
|
+
|
|
584
|
+
// 1. Class-based: default export extends Command
|
|
585
|
+
if (isNativeHelperClass(mod.default, Command)) {
|
|
586
|
+
const ExportedClass = mod.default
|
|
587
|
+
if (!ExportedClass.shortcut || ExportedClass.shortcut === 'commands.base') {
|
|
588
|
+
ExportedClass.shortcut = `commands.${name}`
|
|
589
|
+
}
|
|
590
|
+
if (!ExportedClass.commandDescription && ExportedClass.description) {
|
|
591
|
+
ExportedClass.commandDescription = ExportedClass.description
|
|
592
|
+
}
|
|
593
|
+
commands.register(name, ExportedClass)
|
|
594
|
+
continue
|
|
595
|
+
}
|
|
596
|
+
|
|
597
|
+
const commandModule = mod.default || mod
|
|
598
|
+
|
|
599
|
+
// 2. Module-based with `run` export (new SimpleCommand pattern)
|
|
600
|
+
if (typeof commandModule.run === 'function') {
|
|
601
|
+
const Grafted = graftModule(Command as any, commandModule, name, 'commands')
|
|
602
|
+
commands.register(name, Grafted as any)
|
|
603
|
+
continue
|
|
604
|
+
}
|
|
605
|
+
|
|
606
|
+
// 3. Legacy: `handler` export
|
|
607
|
+
if (typeof commandModule.handler === 'function') {
|
|
608
|
+
const Grafted = graftModule(Command as any, {
|
|
609
|
+
description: commandModule.description,
|
|
610
|
+
argsSchema: commandModule.argsSchema,
|
|
611
|
+
handler: commandModule.handler,
|
|
612
|
+
}, name, 'commands')
|
|
613
|
+
commands.register(name, Grafted as any)
|
|
614
|
+
continue
|
|
615
|
+
}
|
|
616
|
+
|
|
617
|
+
// 4. Plain default-exported function: export default async function name(options, context)
|
|
618
|
+
if (typeof mod.default === 'function' && !isNativeHelperClass(mod.default, Command)) {
|
|
619
|
+
const Grafted = graftModule(Command as any, {
|
|
620
|
+
description: mod.description || '',
|
|
621
|
+
argsSchema: mod.argsSchema,
|
|
622
|
+
positionals: mod.positionals,
|
|
623
|
+
handler: mod.default,
|
|
624
|
+
}, name, 'commands')
|
|
625
|
+
commands.register(name, Grafted as any)
|
|
626
|
+
}
|
|
627
|
+
} catch (err: any) {
|
|
628
|
+
console.warn(`Helpers gateway: failed to load command from ${absPath}: ${err.message}`)
|
|
629
|
+
}
|
|
630
|
+
}
|
|
631
|
+
}
|
|
632
|
+
|
|
633
|
+
/**
|
|
634
|
+
* Discovers selectors using the VM's virtual module system.
|
|
635
|
+
* Mirrors discoverCommandsViaVM but uses selectors registry and Selector base class.
|
|
636
|
+
*/
|
|
637
|
+
private async discoverSelectorsViaVM(dir: string): Promise<void> {
|
|
638
|
+
this.seedVirtualModules()
|
|
639
|
+
const { fs } = this.container
|
|
640
|
+
const walked = fs.walk(dir, { include: ['*.ts'] })
|
|
641
|
+
const tsFiles = walked.files
|
|
642
|
+
.map(f => parse(f))
|
|
643
|
+
.filter(p => p.dir === dir)
|
|
644
|
+
.map(p => p.base)
|
|
645
|
+
|
|
646
|
+
for (const file of tsFiles) {
|
|
647
|
+
if (file === 'index.ts') continue
|
|
648
|
+
|
|
649
|
+
const absPath = resolve(dir, file)
|
|
650
|
+
const name = file.replace(/\.ts$/, '')
|
|
651
|
+
|
|
652
|
+
if (selectors.has(name)) continue
|
|
653
|
+
|
|
654
|
+
try {
|
|
655
|
+
const mod = await this.loadModuleExports(absPath)
|
|
656
|
+
|
|
657
|
+
// 1. Class-based: default export extends Selector
|
|
658
|
+
if (isNativeHelperClass(mod.default, Selector)) {
|
|
659
|
+
const ExportedClass = mod.default
|
|
660
|
+
if (!ExportedClass.shortcut || ExportedClass.shortcut === 'selectors.base') {
|
|
661
|
+
ExportedClass.shortcut = `selectors.${name}`
|
|
662
|
+
}
|
|
663
|
+
selectors.register(name, ExportedClass)
|
|
664
|
+
continue
|
|
665
|
+
}
|
|
666
|
+
|
|
667
|
+
const selectorModule = mod.default || mod
|
|
668
|
+
|
|
669
|
+
// 2. Module-based with `run` export
|
|
670
|
+
if (typeof selectorModule.run === 'function') {
|
|
671
|
+
const Grafted = graftModule(Selector as any, selectorModule, name, 'selectors')
|
|
672
|
+
selectors.register(name, Grafted as any)
|
|
673
|
+
}
|
|
674
|
+
} catch (err: any) {
|
|
675
|
+
console.warn(`Helpers gateway: failed to load selector from ${absPath}: ${err.message}`)
|
|
676
|
+
}
|
|
677
|
+
}
|
|
678
|
+
}
|
|
679
|
+
|
|
680
|
+
/**
|
|
681
|
+
* Discovers endpoints from a directory, registering them for discoverability.
|
|
682
|
+
* Actual mounting to an express server is handled separately by ExpressServer.useEndpoints().
|
|
683
|
+
*/
|
|
684
|
+
private async discoverEndpoints(dir: string): Promise<void> {
|
|
685
|
+
const { fs } = this.container
|
|
686
|
+
const walked = fs.walk(dir, { include: ['**/*.ts'] })
|
|
687
|
+
|
|
688
|
+
for (const file of walked.files) {
|
|
689
|
+
try {
|
|
690
|
+
const mod = await this.loadModuleExports(file)
|
|
691
|
+
const endpointModule = mod.default || mod
|
|
692
|
+
|
|
693
|
+
if (endpointModule.path && typeof endpointModule.path === 'string') {
|
|
694
|
+
const name = endpointModule.path.replace(/^\//, '').replace(/\//g, '_') || parse(file).name
|
|
695
|
+
|
|
696
|
+
if (!endpoints.has(name)) {
|
|
697
|
+
// Import the module so it's available, but don't mount it to a server
|
|
698
|
+
// The express server's useEndpoints() handles the actual mounting
|
|
699
|
+
}
|
|
700
|
+
}
|
|
701
|
+
} catch (err: any) {
|
|
702
|
+
console.warn(`Helpers gateway: failed to load endpoint from ${file}: ${err.message}`)
|
|
703
|
+
}
|
|
704
|
+
}
|
|
705
|
+
}
|
|
706
|
+
|
|
707
|
+
/**
|
|
708
|
+
* Check if a class is a subclass of a given base class by walking the prototype chain.
|
|
709
|
+
* Uses identity comparison first, then falls back to name comparison to handle
|
|
710
|
+
* cross-module boundaries (e.g. compiled binary vs dynamically imported modules
|
|
711
|
+
* that resolve to separate module instances of the same class).
|
|
712
|
+
*/
|
|
713
|
+
private isSubclassOf(candidate: any, base: any): boolean {
|
|
714
|
+
if (!candidate || !base) return false
|
|
715
|
+
if (candidate === base) return true
|
|
716
|
+
|
|
717
|
+
let proto = Object.getPrototypeOf(candidate)
|
|
718
|
+
while (proto) {
|
|
719
|
+
if (proto === base || (base.name && proto.name === base.name)) return true
|
|
720
|
+
proto = Object.getPrototypeOf(proto)
|
|
721
|
+
}
|
|
722
|
+
return false
|
|
723
|
+
}
|
|
724
|
+
|
|
725
|
+
/**
|
|
726
|
+
* Convert a kebab-case or snake_case filename to a camelCase registry name.
|
|
727
|
+
*/
|
|
728
|
+
private fileNameToRegistryName(fileName: string): string {
|
|
729
|
+
return fileName
|
|
730
|
+
.replace(/[-_](.)/g, (_, c: string) => c.toUpperCase())
|
|
731
|
+
.replace(/^(.)/, (_, c: string) => c.toLowerCase())
|
|
732
|
+
}
|
|
733
|
+
}
|
|
734
|
+
|
|
735
|
+
export default Helpers
|