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
|
@@ -0,0 +1,649 @@
|
|
|
1
|
+
import { z } from 'zod'
|
|
2
|
+
import { FeatureStateSchema, FeatureEventsSchema } from '../../schemas/base.js'
|
|
3
|
+
import { Feature } from "../feature.js";
|
|
4
|
+
import { NodeContainer } from "../container.js";
|
|
5
|
+
import { Server, Socket } from "net";
|
|
6
|
+
import { existsSync } from "fs";
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Zod schema for the IpcSocket feature state.
|
|
10
|
+
* Tracks the operational mode of the IPC socket (server or client).
|
|
11
|
+
*/
|
|
12
|
+
export const IpcStateSchema = FeatureStateSchema.extend({
|
|
13
|
+
/** The current mode of the IPC socket - either 'server' or 'client' */
|
|
14
|
+
mode: z.enum(['server', 'client']).optional().describe('The current mode of the IPC socket - either server or client'),
|
|
15
|
+
/** The socket path this instance is listening on or connected to */
|
|
16
|
+
socketPath: z.string().optional().describe('The socket path this instance is bound to'),
|
|
17
|
+
})
|
|
18
|
+
export type IpcState = z.infer<typeof IpcStateSchema>
|
|
19
|
+
|
|
20
|
+
export const IpcEventsSchema = FeatureEventsSchema.extend({
|
|
21
|
+
connection: z.tuple([
|
|
22
|
+
z.string().describe('The client ID assigned to the connection'),
|
|
23
|
+
z.any().describe('The connected net.Socket instance'),
|
|
24
|
+
]).describe('Emitted on the server when a new client connects (clientId, socket)'),
|
|
25
|
+
disconnection: z.tuple([
|
|
26
|
+
z.string().describe('The client ID that disconnected'),
|
|
27
|
+
]).describe('Emitted on the server when a client disconnects'),
|
|
28
|
+
message: z.tuple([
|
|
29
|
+
z.any().describe('The parsed JSON message object received over the socket'),
|
|
30
|
+
z.string().optional().describe('The client ID of the sender (server mode only)'),
|
|
31
|
+
]).describe('Emitted when a complete JSON message is received (data, clientId?)'),
|
|
32
|
+
})
|
|
33
|
+
|
|
34
|
+
/** Tracks a pending request awaiting a reply */
|
|
35
|
+
type PendingRequest = {
|
|
36
|
+
resolve: (value: any) => void
|
|
37
|
+
reject: (reason: any) => void
|
|
38
|
+
timer: ReturnType<typeof setTimeout>
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
/** Metadata for a connected client */
|
|
42
|
+
type ClientInfo = {
|
|
43
|
+
socket: Socket
|
|
44
|
+
id: string
|
|
45
|
+
name?: string
|
|
46
|
+
connectedAt: number
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* IpcSocket Feature - Inter-Process Communication via Unix Domain Sockets
|
|
51
|
+
*
|
|
52
|
+
* This feature provides robust IPC (Inter-Process Communication) capabilities using Unix domain sockets.
|
|
53
|
+
* It supports both server and client modes, allowing processes to communicate efficiently through
|
|
54
|
+
* file system-based socket connections.
|
|
55
|
+
*
|
|
56
|
+
* **Key Features:**
|
|
57
|
+
* - Hub-and-spoke: one server, many named clients with identity tracking
|
|
58
|
+
* - Targeted messaging: sendTo(clientId), broadcast(msg, excludeId)
|
|
59
|
+
* - Request/reply: ask() + reply() with timeout-based correlation
|
|
60
|
+
* - Auto-reconnect: clients reconnect with exponential backoff
|
|
61
|
+
* - Stale socket detection: probeSocket() before listen()
|
|
62
|
+
* - Clean shutdown: stopServer() removes socket file
|
|
63
|
+
*
|
|
64
|
+
* **Server (Hub):**
|
|
65
|
+
* ```typescript
|
|
66
|
+
* const ipc = container.feature('ipcSocket');
|
|
67
|
+
* await ipc.listen('/tmp/hub.sock', true);
|
|
68
|
+
*
|
|
69
|
+
* ipc.on('connection', (clientId, socket) => {
|
|
70
|
+
* console.log('Client joined:', clientId);
|
|
71
|
+
* });
|
|
72
|
+
*
|
|
73
|
+
* ipc.on('message', (data, clientId) => {
|
|
74
|
+
* console.log(`From ${clientId}:`, data);
|
|
75
|
+
* // Reply to sender, or ask and wait
|
|
76
|
+
* ipc.sendTo(clientId, { ack: true });
|
|
77
|
+
* });
|
|
78
|
+
* ```
|
|
79
|
+
*
|
|
80
|
+
* **Client (Spoke):**
|
|
81
|
+
* ```typescript
|
|
82
|
+
* const ipc = container.feature('ipcSocket');
|
|
83
|
+
* await ipc.connect('/tmp/hub.sock', { reconnect: true, name: 'worker-1' });
|
|
84
|
+
*
|
|
85
|
+
* // Fire and forget
|
|
86
|
+
* await ipc.send({ type: 'status', ready: true });
|
|
87
|
+
*
|
|
88
|
+
* // Request/reply
|
|
89
|
+
* ipc.on('message', (data) => {
|
|
90
|
+
* if (data.requestId) ipc.reply(data.requestId, { result: 42 });
|
|
91
|
+
* });
|
|
92
|
+
* ```
|
|
93
|
+
*
|
|
94
|
+
* @template T - The state type, defaults to IpcState
|
|
95
|
+
* @extends {Feature<T>}
|
|
96
|
+
*/
|
|
97
|
+
export class IpcSocket<T extends IpcState = IpcState> extends Feature<T> {
|
|
98
|
+
static { Feature.register(this, 'ipcSocket') }
|
|
99
|
+
/** The shortcut path for accessing this feature */
|
|
100
|
+
static override shortcut = "features.ipcSocket" as const
|
|
101
|
+
static override stateSchema = IpcStateSchema
|
|
102
|
+
static override eventsSchema = IpcEventsSchema
|
|
103
|
+
|
|
104
|
+
/** The Node.js net Server instance (when in server mode) */
|
|
105
|
+
server?: Server;
|
|
106
|
+
|
|
107
|
+
/** Connected clients keyed by client ID (server mode only) */
|
|
108
|
+
protected clients = new Map<string, ClientInfo>();
|
|
109
|
+
|
|
110
|
+
/** Reverse lookup: socket → clientId */
|
|
111
|
+
private _socketToClient = new WeakMap<Socket, string>();
|
|
112
|
+
|
|
113
|
+
/** Per-socket NDJSON read buffers for accumulating partial lines */
|
|
114
|
+
private _buffers = new WeakMap<Socket, string>();
|
|
115
|
+
|
|
116
|
+
/** Pending request/reply correlation map */
|
|
117
|
+
private _pending = new Map<string, PendingRequest>();
|
|
118
|
+
|
|
119
|
+
/** Default timeout for ask() calls in ms */
|
|
120
|
+
requestTimeoutMs = 10000;
|
|
121
|
+
|
|
122
|
+
/** Reconnection config (client mode) */
|
|
123
|
+
private _reconnect = { enabled: false, attempts: 0, maxAttempts: 10, delayMs: 1000, maxDelayMs: 30000, timer: null as ReturnType<typeof setTimeout> | null }
|
|
124
|
+
private _socketPath?: string;
|
|
125
|
+
|
|
126
|
+
/**
|
|
127
|
+
* Attaches the IpcSocket feature to a NodeContainer instance.
|
|
128
|
+
* Registers the feature and creates an auto-enabled instance.
|
|
129
|
+
*
|
|
130
|
+
* @param container - The NodeContainer to attach to
|
|
131
|
+
* @returns The container for method chaining
|
|
132
|
+
*/
|
|
133
|
+
static attach(container: NodeContainer & { ipcSocket?: IpcSocket }) {
|
|
134
|
+
container.ipcSocket = container.feature("ipcSocket", { enable: true });
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
/**
|
|
138
|
+
* Checks if the IPC socket is operating in client mode.
|
|
139
|
+
*
|
|
140
|
+
* @returns True if the socket is configured as a client
|
|
141
|
+
*/
|
|
142
|
+
get isClient(): boolean {
|
|
143
|
+
return this.state.get('mode') === 'client'
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
/**
|
|
147
|
+
* Checks if the IPC socket is operating in server mode.
|
|
148
|
+
*
|
|
149
|
+
* @returns True if the socket is configured as a server
|
|
150
|
+
*/
|
|
151
|
+
get isServer(): boolean {
|
|
152
|
+
return this.state.get('mode') === 'server'
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
/**
|
|
156
|
+
* Returns the number of currently connected clients (server mode).
|
|
157
|
+
*/
|
|
158
|
+
get clientCount(): number {
|
|
159
|
+
return this.clients.size
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
/**
|
|
163
|
+
* Returns info about all connected clients (server mode).
|
|
164
|
+
*/
|
|
165
|
+
get connectedClients(): Array<{ id: string; name?: string; connectedAt: number }> {
|
|
166
|
+
return Array.from(this.clients.values()).map(({ id, name, connectedAt }) => ({ id, name, connectedAt }))
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
/**
|
|
170
|
+
* Starts the IPC server listening on the specified socket path.
|
|
171
|
+
*
|
|
172
|
+
* This method sets up a Unix domain socket server that can accept multiple client connections.
|
|
173
|
+
* Each connected client is tracked, and the server automatically handles connection lifecycle
|
|
174
|
+
* events. Messages received from clients are JSON-parsed and emitted as 'message' events.
|
|
175
|
+
*
|
|
176
|
+
* **Server Behavior:**
|
|
177
|
+
* - Tracks all connected clients in the sockets Set
|
|
178
|
+
* - Automatically removes clients when they disconnect
|
|
179
|
+
* - JSON-parses incoming messages and emits 'message' events
|
|
180
|
+
* - Emits 'connection' events when clients connect
|
|
181
|
+
* - Prevents starting multiple servers on the same instance
|
|
182
|
+
*
|
|
183
|
+
* **Socket File Management:**
|
|
184
|
+
* - Resolves the socket path relative to the container's working directory
|
|
185
|
+
* - Optionally removes existing socket files to prevent "address in use" errors
|
|
186
|
+
* - Throws error if socket file exists and removeLock is false
|
|
187
|
+
*
|
|
188
|
+
* @param socketPath - The file system path for the Unix domain socket
|
|
189
|
+
* @param removeLock - Whether to remove existing socket file (default: false)
|
|
190
|
+
* @returns Promise resolving to the created Node.js Server instance
|
|
191
|
+
*
|
|
192
|
+
* @throws {Error} When already in client mode, server already running, or socket file exists
|
|
193
|
+
*
|
|
194
|
+
* @example
|
|
195
|
+
* ```typescript
|
|
196
|
+
* // Basic server setup
|
|
197
|
+
* const server = await ipc.listen('/tmp/myapp.sock');
|
|
198
|
+
*
|
|
199
|
+
* // With automatic lock removal
|
|
200
|
+
* const server = await ipc.listen('/tmp/myapp.sock', true);
|
|
201
|
+
*
|
|
202
|
+
* // Handle connections and messages
|
|
203
|
+
* ipc.on('connection', (socket) => {
|
|
204
|
+
* console.log('New client connected');
|
|
205
|
+
* });
|
|
206
|
+
*
|
|
207
|
+
* ipc.on('message', (data) => {
|
|
208
|
+
* console.log('Received message:', data);
|
|
209
|
+
* // Echo back to all clients
|
|
210
|
+
* ipc.broadcast({ echo: data });
|
|
211
|
+
* });
|
|
212
|
+
* ```
|
|
213
|
+
*/
|
|
214
|
+
async listen(socketPath: string, removeLock = false): Promise<Server> {
|
|
215
|
+
socketPath = this.container.paths.resolve(socketPath)
|
|
216
|
+
|
|
217
|
+
if (existsSync(socketPath)) {
|
|
218
|
+
if (removeLock) {
|
|
219
|
+
const alive = await this.probeSocket(socketPath)
|
|
220
|
+
if (alive) {
|
|
221
|
+
throw new Error(`Socket ${socketPath} is already in use by a live process`)
|
|
222
|
+
}
|
|
223
|
+
await this.container.fs.rm(socketPath)
|
|
224
|
+
} else {
|
|
225
|
+
throw new Error('Lock already exists')
|
|
226
|
+
}
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
if(this.isClient) {
|
|
230
|
+
throw new Error("Cannot listen on a client socket.");
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
this.state.set('mode', 'server')
|
|
234
|
+
this.state.set('socketPath', socketPath)
|
|
235
|
+
this._socketPath = socketPath
|
|
236
|
+
|
|
237
|
+
if (this.server) {
|
|
238
|
+
throw new Error("An IPC server is already running.");
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
this.server = new Server((socket) => {
|
|
242
|
+
const clientId = this.container.utils.uuid()
|
|
243
|
+
const clientInfo: ClientInfo = { socket, id: clientId, connectedAt: Date.now() }
|
|
244
|
+
this.clients.set(clientId, clientInfo)
|
|
245
|
+
this._socketToClient.set(socket, clientId)
|
|
246
|
+
this._buffers.set(socket, '');
|
|
247
|
+
|
|
248
|
+
// Send the client its assigned ID
|
|
249
|
+
socket.write(JSON.stringify({ type: '__ipc:welcome', clientId }) + '\n')
|
|
250
|
+
|
|
251
|
+
socket.on("close", () => {
|
|
252
|
+
this.clients.delete(clientId);
|
|
253
|
+
this.emit('disconnection', clientId)
|
|
254
|
+
});
|
|
255
|
+
|
|
256
|
+
socket.on('data', (chunk) => {
|
|
257
|
+
this._handleChunk(socket, chunk)
|
|
258
|
+
})
|
|
259
|
+
|
|
260
|
+
this.emit('connection', clientId, socket)
|
|
261
|
+
});
|
|
262
|
+
|
|
263
|
+
this.server.listen(socketPath);
|
|
264
|
+
|
|
265
|
+
return this.server;
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
/**
|
|
269
|
+
* Stops the IPC server and cleans up all connections.
|
|
270
|
+
*
|
|
271
|
+
* This method gracefully shuts down the server by:
|
|
272
|
+
* 1. Closing the server listener
|
|
273
|
+
* 2. Destroying all active client connections
|
|
274
|
+
* 3. Clearing the sockets tracking set
|
|
275
|
+
* 4. Resetting the server instance
|
|
276
|
+
*
|
|
277
|
+
* @returns Promise that resolves when the server is fully stopped
|
|
278
|
+
*
|
|
279
|
+
* @throws {Error} When no server is currently running
|
|
280
|
+
*
|
|
281
|
+
* @example
|
|
282
|
+
* ```typescript
|
|
283
|
+
* // Graceful shutdown
|
|
284
|
+
* try {
|
|
285
|
+
* await ipc.stopServer();
|
|
286
|
+
* console.log('IPC server stopped successfully');
|
|
287
|
+
* } catch (error) {
|
|
288
|
+
* console.error('Failed to stop server:', error.message);
|
|
289
|
+
* }
|
|
290
|
+
* ```
|
|
291
|
+
*/
|
|
292
|
+
stopServer(): Promise<void> {
|
|
293
|
+
return new Promise((resolve, reject) => {
|
|
294
|
+
if (!this.server) {
|
|
295
|
+
reject(new Error("No IPC server is running."));
|
|
296
|
+
return;
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
this.server.close((err) => {
|
|
300
|
+
// Clean up socket file
|
|
301
|
+
if (this._socketPath && existsSync(this._socketPath)) {
|
|
302
|
+
try { this.container.fs.rm(this._socketPath) } catch {}
|
|
303
|
+
}
|
|
304
|
+
if (err) {
|
|
305
|
+
reject(err);
|
|
306
|
+
} else {
|
|
307
|
+
resolve();
|
|
308
|
+
}
|
|
309
|
+
});
|
|
310
|
+
|
|
311
|
+
for (const { socket } of this.clients.values()) {
|
|
312
|
+
socket.destroy()
|
|
313
|
+
}
|
|
314
|
+
this.clients.clear();
|
|
315
|
+
this._pending.forEach(({ timer }) => clearTimeout(timer))
|
|
316
|
+
this._pending.clear()
|
|
317
|
+
this.server = undefined;
|
|
318
|
+
});
|
|
319
|
+
}
|
|
320
|
+
|
|
321
|
+
/** The client connection socket (client mode only) */
|
|
322
|
+
_connection?: Socket
|
|
323
|
+
|
|
324
|
+
/**
|
|
325
|
+
* Gets the current client connection socket.
|
|
326
|
+
*
|
|
327
|
+
* @returns The active Socket connection, or undefined if not connected
|
|
328
|
+
*/
|
|
329
|
+
get connection(): Socket | undefined {
|
|
330
|
+
return this._connection
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
/**
|
|
334
|
+
* Broadcasts a message to all connected clients (server mode only).
|
|
335
|
+
*
|
|
336
|
+
* @param message - The message object to broadcast
|
|
337
|
+
* @param exclude - Optional client ID to exclude from broadcast
|
|
338
|
+
* @returns This instance for method chaining
|
|
339
|
+
*/
|
|
340
|
+
broadcast(message: any, exclude?: string): this {
|
|
341
|
+
const envelope = JSON.stringify({
|
|
342
|
+
data: message,
|
|
343
|
+
id: this.container.utils.uuid()
|
|
344
|
+
}) + '\n'
|
|
345
|
+
|
|
346
|
+
for (const [clientId, { socket }] of this.clients) {
|
|
347
|
+
if (clientId === exclude) continue
|
|
348
|
+
if (!socket.writable) continue
|
|
349
|
+
socket.write(envelope)
|
|
350
|
+
}
|
|
351
|
+
|
|
352
|
+
return this
|
|
353
|
+
}
|
|
354
|
+
|
|
355
|
+
/**
|
|
356
|
+
* Sends a message to a specific client by ID (server mode only).
|
|
357
|
+
*
|
|
358
|
+
* @param clientId - The target client ID
|
|
359
|
+
* @param message - The message to send
|
|
360
|
+
* @returns True if the message was sent, false if client not found or not writable
|
|
361
|
+
*/
|
|
362
|
+
sendTo(clientId: string, message: any): boolean {
|
|
363
|
+
const client = this.clients.get(clientId)
|
|
364
|
+
if (!client || !client.socket.writable) return false
|
|
365
|
+
|
|
366
|
+
client.socket.write(JSON.stringify({
|
|
367
|
+
data: message,
|
|
368
|
+
id: this.container.utils.uuid()
|
|
369
|
+
}) + '\n')
|
|
370
|
+
|
|
371
|
+
return true
|
|
372
|
+
}
|
|
373
|
+
|
|
374
|
+
/**
|
|
375
|
+
* Fire-and-forget: sends a message to the server (client mode only).
|
|
376
|
+
* For server→client, use sendTo() or broadcast().
|
|
377
|
+
*
|
|
378
|
+
* @param message - The message to send
|
|
379
|
+
*/
|
|
380
|
+
async send(message: any): Promise<void> {
|
|
381
|
+
if(!this._connection) {
|
|
382
|
+
throw new Error("No connection.")
|
|
383
|
+
}
|
|
384
|
+
|
|
385
|
+
const id = this.container.utils.uuid()
|
|
386
|
+
this._connection.write(JSON.stringify({ id, data: message }) + '\n')
|
|
387
|
+
}
|
|
388
|
+
|
|
389
|
+
/**
|
|
390
|
+
* Sends a message and waits for a correlated reply.
|
|
391
|
+
* Works in both client and server mode.
|
|
392
|
+
*
|
|
393
|
+
* The recipient should call `reply(requestId, response)` to respond.
|
|
394
|
+
*
|
|
395
|
+
* @param message - The message to send
|
|
396
|
+
* @param options - Optional: clientId (server mode target), timeoutMs
|
|
397
|
+
* @returns The reply data
|
|
398
|
+
*/
|
|
399
|
+
async ask(message: any, options?: { clientId?: string; timeoutMs?: number }): Promise<any> {
|
|
400
|
+
const requestId = this.container.utils.uuid()
|
|
401
|
+
const timeoutMs = options?.timeoutMs ?? this.requestTimeoutMs
|
|
402
|
+
|
|
403
|
+
return new Promise((resolve, reject) => {
|
|
404
|
+
const timer = setTimeout(() => {
|
|
405
|
+
this._pending.delete(requestId)
|
|
406
|
+
reject(new Error(`IPC ask timed out after ${timeoutMs}ms`))
|
|
407
|
+
}, timeoutMs)
|
|
408
|
+
|
|
409
|
+
this._pending.set(requestId, { resolve, reject, timer })
|
|
410
|
+
|
|
411
|
+
const envelope = JSON.stringify({
|
|
412
|
+
id: requestId,
|
|
413
|
+
data: message,
|
|
414
|
+
requestId,
|
|
415
|
+
}) + '\n'
|
|
416
|
+
|
|
417
|
+
if (this.isServer) {
|
|
418
|
+
const clientId = options?.clientId
|
|
419
|
+
if (!clientId) {
|
|
420
|
+
clearTimeout(timer)
|
|
421
|
+
this._pending.delete(requestId)
|
|
422
|
+
reject(new Error('ask() in server mode requires options.clientId'))
|
|
423
|
+
return
|
|
424
|
+
}
|
|
425
|
+
const client = this.clients.get(clientId)
|
|
426
|
+
if (!client || !client.socket.writable) {
|
|
427
|
+
clearTimeout(timer)
|
|
428
|
+
this._pending.delete(requestId)
|
|
429
|
+
reject(new Error(`Client ${clientId} not found or not writable`))
|
|
430
|
+
return
|
|
431
|
+
}
|
|
432
|
+
client.socket.write(envelope)
|
|
433
|
+
} else {
|
|
434
|
+
if (!this._connection) {
|
|
435
|
+
clearTimeout(timer)
|
|
436
|
+
this._pending.delete(requestId)
|
|
437
|
+
reject(new Error('No connection'))
|
|
438
|
+
return
|
|
439
|
+
}
|
|
440
|
+
this._connection.write(envelope)
|
|
441
|
+
}
|
|
442
|
+
})
|
|
443
|
+
}
|
|
444
|
+
|
|
445
|
+
/**
|
|
446
|
+
* Sends a reply to a previous ask() call, correlated by requestId.
|
|
447
|
+
*
|
|
448
|
+
* @param requestId - The requestId from the incoming message
|
|
449
|
+
* @param data - The reply payload
|
|
450
|
+
* @param clientId - Target client (server mode; for client mode, omit)
|
|
451
|
+
*/
|
|
452
|
+
reply(requestId: string, data: any, clientId?: string): void {
|
|
453
|
+
const envelope = JSON.stringify({
|
|
454
|
+
id: this.container.utils.uuid(),
|
|
455
|
+
data,
|
|
456
|
+
replyTo: requestId,
|
|
457
|
+
}) + '\n'
|
|
458
|
+
|
|
459
|
+
if (this.isServer && clientId) {
|
|
460
|
+
const client = this.clients.get(clientId)
|
|
461
|
+
if (client?.socket.writable) {
|
|
462
|
+
client.socket.write(envelope)
|
|
463
|
+
}
|
|
464
|
+
} else if (this._connection) {
|
|
465
|
+
this._connection.write(envelope)
|
|
466
|
+
}
|
|
467
|
+
}
|
|
468
|
+
|
|
469
|
+
/** The server-assigned client ID (client mode only) */
|
|
470
|
+
clientId?: string;
|
|
471
|
+
|
|
472
|
+
/**
|
|
473
|
+
* Connects to an IPC server at the specified socket path (client mode).
|
|
474
|
+
*
|
|
475
|
+
* @param socketPath - Path to the server's Unix domain socket
|
|
476
|
+
* @param options - Optional: reconnect (enable auto-reconnect), name (identify this client)
|
|
477
|
+
* @returns The established Socket connection
|
|
478
|
+
*/
|
|
479
|
+
async connect(socketPath: string, options?: { reconnect?: boolean; name?: string }): Promise<Socket> {
|
|
480
|
+
if(this.isServer) {
|
|
481
|
+
throw new Error("Cannot connect on a server socket.")
|
|
482
|
+
}
|
|
483
|
+
|
|
484
|
+
if(this._connection) {
|
|
485
|
+
return this._connection
|
|
486
|
+
}
|
|
487
|
+
|
|
488
|
+
this._socketPath = socketPath
|
|
489
|
+
this.state.set('socketPath', socketPath)
|
|
490
|
+
|
|
491
|
+
if (options?.reconnect !== undefined) {
|
|
492
|
+
this._reconnect.enabled = options.reconnect
|
|
493
|
+
}
|
|
494
|
+
|
|
495
|
+
const connection: Socket = await this._doConnect(socketPath)
|
|
496
|
+
|
|
497
|
+
connection.on("close", () => {
|
|
498
|
+
this._connection = undefined
|
|
499
|
+
this.clientId = undefined
|
|
500
|
+
if (this._reconnect.enabled) {
|
|
501
|
+
this._scheduleReconnect()
|
|
502
|
+
}
|
|
503
|
+
})
|
|
504
|
+
|
|
505
|
+
this._buffers.set(connection, '');
|
|
506
|
+
connection.on("data", (chunk) => {
|
|
507
|
+
this._handleChunk(connection, chunk)
|
|
508
|
+
})
|
|
509
|
+
|
|
510
|
+
this._connection = connection
|
|
511
|
+
this._reconnect.attempts = 0
|
|
512
|
+
|
|
513
|
+
// Send identity if a name was provided
|
|
514
|
+
if (options?.name) {
|
|
515
|
+
connection.write(JSON.stringify({
|
|
516
|
+
id: this.container.utils.uuid(),
|
|
517
|
+
data: { type: '__ipc:identify', name: options.name }
|
|
518
|
+
}) + '\n')
|
|
519
|
+
}
|
|
520
|
+
|
|
521
|
+
return connection
|
|
522
|
+
}
|
|
523
|
+
|
|
524
|
+
private _doConnect(socketPath: string): Promise<Socket> {
|
|
525
|
+
return new Promise((resolve, reject) => {
|
|
526
|
+
const socket = new Socket();
|
|
527
|
+
socket.connect(socketPath, () => resolve(socket));
|
|
528
|
+
socket.on("error", (err) => reject(err));
|
|
529
|
+
})
|
|
530
|
+
}
|
|
531
|
+
|
|
532
|
+
private _scheduleReconnect() {
|
|
533
|
+
if (this._reconnect.timer) return
|
|
534
|
+
if (this._reconnect.attempts >= this._reconnect.maxAttempts) {
|
|
535
|
+
this.emit('message', { type: '__ipc:reconnect_failed', attempts: this._reconnect.attempts })
|
|
536
|
+
return
|
|
537
|
+
}
|
|
538
|
+
|
|
539
|
+
const delay = Math.min(
|
|
540
|
+
this._reconnect.delayMs * Math.pow(2, this._reconnect.attempts),
|
|
541
|
+
this._reconnect.maxDelayMs
|
|
542
|
+
)
|
|
543
|
+
this._reconnect.attempts++
|
|
544
|
+
|
|
545
|
+
this._reconnect.timer = setTimeout(async () => {
|
|
546
|
+
this._reconnect.timer = null
|
|
547
|
+
if (!this._socketPath) return
|
|
548
|
+
|
|
549
|
+
try {
|
|
550
|
+
await this.connect(this._socketPath)
|
|
551
|
+
} catch {
|
|
552
|
+
this._scheduleReconnect()
|
|
553
|
+
}
|
|
554
|
+
}, delay)
|
|
555
|
+
}
|
|
556
|
+
|
|
557
|
+
/**
|
|
558
|
+
* Disconnects the client and stops any reconnection attempts.
|
|
559
|
+
*/
|
|
560
|
+
disconnect(): void {
|
|
561
|
+
this._reconnect.enabled = false
|
|
562
|
+
if (this._reconnect.timer) {
|
|
563
|
+
clearTimeout(this._reconnect.timer)
|
|
564
|
+
this._reconnect.timer = null
|
|
565
|
+
}
|
|
566
|
+
if (this._connection) {
|
|
567
|
+
this._connection.destroy()
|
|
568
|
+
this._connection = undefined
|
|
569
|
+
}
|
|
570
|
+
this._pending.forEach(({ timer }) => clearTimeout(timer))
|
|
571
|
+
this._pending.clear()
|
|
572
|
+
}
|
|
573
|
+
|
|
574
|
+
/**
|
|
575
|
+
* Probe an existing socket to see if a live listener is behind it.
|
|
576
|
+
* Attempts a quick connect — if it succeeds, someone is listening.
|
|
577
|
+
*/
|
|
578
|
+
probeSocket(socketPath: string): Promise<boolean> {
|
|
579
|
+
if (!existsSync(socketPath)) return Promise.resolve(false)
|
|
580
|
+
return new Promise<boolean>((resolve) => {
|
|
581
|
+
const probe = new Socket()
|
|
582
|
+
const timer = setTimeout(() => {
|
|
583
|
+
probe.destroy()
|
|
584
|
+
resolve(false)
|
|
585
|
+
}, 500)
|
|
586
|
+
|
|
587
|
+
probe.once('connect', () => {
|
|
588
|
+
clearTimeout(timer)
|
|
589
|
+
probe.destroy()
|
|
590
|
+
resolve(true)
|
|
591
|
+
})
|
|
592
|
+
|
|
593
|
+
probe.once('error', () => {
|
|
594
|
+
clearTimeout(timer)
|
|
595
|
+
probe.destroy()
|
|
596
|
+
resolve(false)
|
|
597
|
+
})
|
|
598
|
+
|
|
599
|
+
probe.connect(socketPath)
|
|
600
|
+
})
|
|
601
|
+
}
|
|
602
|
+
|
|
603
|
+
private _handleChunk(socket: Socket, chunk: Buffer): void {
|
|
604
|
+
let buffer = (this._buffers.get(socket) || '') + chunk.toString()
|
|
605
|
+
const lines = buffer.split(/\r?\n/)
|
|
606
|
+
this._buffers.set(socket, lines.pop() || '')
|
|
607
|
+
|
|
608
|
+
for (const line of lines) {
|
|
609
|
+
if (!line.trim()) continue
|
|
610
|
+
try {
|
|
611
|
+
const parsed = JSON.parse(line)
|
|
612
|
+
|
|
613
|
+
// Handle protocol messages
|
|
614
|
+
if (parsed.type === '__ipc:welcome' && parsed.clientId) {
|
|
615
|
+
this.clientId = parsed.clientId
|
|
616
|
+
this.state.set('mode', 'client')
|
|
617
|
+
continue
|
|
618
|
+
}
|
|
619
|
+
|
|
620
|
+
if (parsed.data?.type === '__ipc:identify' && this.isServer) {
|
|
621
|
+
const clientId = this._socketToClient.get(socket)
|
|
622
|
+
if (clientId) {
|
|
623
|
+
const client = this.clients.get(clientId)
|
|
624
|
+
if (client) client.name = parsed.data.name
|
|
625
|
+
}
|
|
626
|
+
continue
|
|
627
|
+
}
|
|
628
|
+
|
|
629
|
+
// Handle reply correlation
|
|
630
|
+
const replyTo = parsed.replyTo
|
|
631
|
+
if (replyTo && this._pending.has(replyTo)) {
|
|
632
|
+
const pending = this._pending.get(replyTo)!
|
|
633
|
+
this._pending.delete(replyTo)
|
|
634
|
+
clearTimeout(pending.timer)
|
|
635
|
+
pending.resolve(parsed.data)
|
|
636
|
+
continue
|
|
637
|
+
}
|
|
638
|
+
|
|
639
|
+
// Regular message — include sender clientId in server mode
|
|
640
|
+
const clientId = this._socketToClient.get(socket)
|
|
641
|
+
this.emit('message', parsed.data ?? parsed, clientId)
|
|
642
|
+
} catch {
|
|
643
|
+
// Malformed JSON line — skip
|
|
644
|
+
}
|
|
645
|
+
}
|
|
646
|
+
}
|
|
647
|
+
}
|
|
648
|
+
|
|
649
|
+
export default IpcSocket
|