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,811 @@
|
|
|
1
|
+
import { z } from 'zod'
|
|
2
|
+
import { FeatureStateSchema, FeatureOptionsSchema } from '../../schemas/base.js'
|
|
3
|
+
import { Feature } from '../feature'
|
|
4
|
+
import axios from 'axios'
|
|
5
|
+
|
|
6
|
+
export const RunpodStateSchema = FeatureStateSchema.extend({})
|
|
7
|
+
export type RunpodState = z.infer<typeof RunpodStateSchema>
|
|
8
|
+
|
|
9
|
+
export const RunpodOptionsSchema = FeatureOptionsSchema.extend({
|
|
10
|
+
apiKey: z.string().optional().describe('RunPod API key (falls back to RUNPOD_API_KEY env var)'),
|
|
11
|
+
dataCenterId: z.string().optional().describe('Preferred data center ID (default: US-TX-3)'),
|
|
12
|
+
})
|
|
13
|
+
export type RunpodOptions = z.infer<typeof RunpodOptionsSchema>
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* RunPod feature — manage GPU cloud pods, templates, volumes, and SSH connections via the RunPod REST API.
|
|
17
|
+
*
|
|
18
|
+
* Provides a complete interface for provisioning and managing RunPod GPU instances.
|
|
19
|
+
* Supports creating pods from templates, managing network storage volumes, SSH access
|
|
20
|
+
* via the SecureShell feature, file transfers, and polling for pod readiness.
|
|
21
|
+
*
|
|
22
|
+
* @extends Feature
|
|
23
|
+
*
|
|
24
|
+
* @example
|
|
25
|
+
* ```typescript
|
|
26
|
+
* const runpod = container.feature('runpod', { enable: true })
|
|
27
|
+
* const pod = await runpod.createPod({ gpuTypeId: 'NVIDIA RTX 4090', templateId: 'abc123' })
|
|
28
|
+
* const ready = await runpod.waitForPod(pod.id)
|
|
29
|
+
* const shell = await runpod.getShell(pod.id)
|
|
30
|
+
* await shell.exec('nvidia-smi')
|
|
31
|
+
* ```
|
|
32
|
+
*/
|
|
33
|
+
export class Runpod extends Feature<RunpodState, RunpodOptions> {
|
|
34
|
+
static override shortcut = 'features.runpod' as const
|
|
35
|
+
static override envVars = ['RUNPOD_API_KEY']
|
|
36
|
+
static override stateSchema = RunpodStateSchema
|
|
37
|
+
static override optionsSchema = RunpodOptionsSchema
|
|
38
|
+
static { Feature.register(this, 'runpod') }
|
|
39
|
+
|
|
40
|
+
private _resolvedRunpodctlPath: string | null = null
|
|
41
|
+
|
|
42
|
+
/** The proc feature used for executing CLI commands like runpodctl. */
|
|
43
|
+
get proc() {
|
|
44
|
+
return this.container.feature('proc')
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
/** Resolve the runpodctl binary path, caching the result. */
|
|
48
|
+
get runpodctlPath(): string {
|
|
49
|
+
if (this._resolvedRunpodctlPath) return this._resolvedRunpodctlPath
|
|
50
|
+
this._resolvedRunpodctlPath = this.container.feature('os').whichCommand('runpodctl')
|
|
51
|
+
return this._resolvedRunpodctlPath
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
/** RunPod API key from options or the RUNPOD_API_KEY environment variable. */
|
|
55
|
+
get apiKey() {
|
|
56
|
+
return this.options.apiKey || process.env.RUNPOD_API_KEY || ''
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
/** Preferred data center ID, defaults to 'US-TX-3'. */
|
|
60
|
+
get dataCenterId() {
|
|
61
|
+
return this.options.dataCenterId || 'US-TX-3'
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
private api(path: string, options: any = {}) {
|
|
65
|
+
return axios({
|
|
66
|
+
baseURL: 'https://rest.runpod.io/v1',
|
|
67
|
+
url: path,
|
|
68
|
+
headers: {
|
|
69
|
+
Authorization: `Bearer ${this.apiKey}`,
|
|
70
|
+
'Content-Type': 'application/json',
|
|
71
|
+
},
|
|
72
|
+
...options,
|
|
73
|
+
}).then(r => r.data)
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
/**
|
|
77
|
+
* List available pod templates.
|
|
78
|
+
*
|
|
79
|
+
* @param options - Filter options for templates
|
|
80
|
+
* @param options.includePublic - Include public community templates (default: false)
|
|
81
|
+
* @param options.includeRunpod - Include RunPod official templates (default: true)
|
|
82
|
+
* @returns Array of template info objects
|
|
83
|
+
*
|
|
84
|
+
* @example
|
|
85
|
+
* ```typescript
|
|
86
|
+
* const templates = await runpod.listTemplates({ includeRunpod: true })
|
|
87
|
+
* console.log(templates.map(t => t.name))
|
|
88
|
+
* ```
|
|
89
|
+
*/
|
|
90
|
+
async listTemplates(options: { includePublic?: boolean, includeRunpod?: boolean } = {}): Promise<TemplateInfo[]> {
|
|
91
|
+
return this.api('/templates', {
|
|
92
|
+
params: {
|
|
93
|
+
includePublicTemplates: options.includePublic ?? false,
|
|
94
|
+
includeRunpodTemplates: options.includeRunpod ?? true,
|
|
95
|
+
}
|
|
96
|
+
})
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
/**
|
|
100
|
+
* Get details for a specific template by ID.
|
|
101
|
+
*
|
|
102
|
+
* @param templateId - The template ID to look up
|
|
103
|
+
* @returns Template info object
|
|
104
|
+
*
|
|
105
|
+
* @example
|
|
106
|
+
* ```typescript
|
|
107
|
+
* const template = await runpod.getTemplate('abc123')
|
|
108
|
+
* console.log(template.imageName)
|
|
109
|
+
* ```
|
|
110
|
+
*/
|
|
111
|
+
async getTemplate(templateId: string): Promise<TemplateInfo> {
|
|
112
|
+
return this.api(`/templates/${templateId}`)
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
/**
|
|
116
|
+
* Create a new GPU pod on RunPod.
|
|
117
|
+
*
|
|
118
|
+
* @param options - Pod configuration options
|
|
119
|
+
* @returns The created pod info
|
|
120
|
+
*
|
|
121
|
+
* @example
|
|
122
|
+
* ```typescript
|
|
123
|
+
* const pod = await runpod.createPod({
|
|
124
|
+
* gpuTypeId: 'NVIDIA RTX 4090',
|
|
125
|
+
* templateId: 'abc123',
|
|
126
|
+
* volumeInGb: 50,
|
|
127
|
+
* })
|
|
128
|
+
* console.log(`Pod ${pod.id} created`)
|
|
129
|
+
* ```
|
|
130
|
+
*/
|
|
131
|
+
async createPod(options: CreatePodOptions): Promise<PodInfo> {
|
|
132
|
+
return this.api('/pods', {
|
|
133
|
+
method: 'POST',
|
|
134
|
+
data: {
|
|
135
|
+
name: options.name ?? 'luca-pod',
|
|
136
|
+
imageName: options.imageName,
|
|
137
|
+
gpuTypeIds: Array.isArray(options.gpuTypeId) ? options.gpuTypeId : [options.gpuTypeId],
|
|
138
|
+
gpuCount: options.gpuCount ?? 1,
|
|
139
|
+
templateId: options.templateId,
|
|
140
|
+
cloudType: options.cloudType ?? 'SECURE',
|
|
141
|
+
containerDiskInGb: options.containerDiskInGb ?? 50,
|
|
142
|
+
volumeInGb: options.volumeInGb ?? 20,
|
|
143
|
+
volumeMountPath: options.volumeMountPath ?? '/workspace',
|
|
144
|
+
...(options.ports ? { ports: options.ports } : !options.templateId ? { ports: ['8888/http', '22/tcp'] } : {}),
|
|
145
|
+
env: options.env,
|
|
146
|
+
interruptible: options.interruptible ?? false,
|
|
147
|
+
networkVolumeId: options.networkVolumeId,
|
|
148
|
+
minRAMPerGPU: options.minRAMPerGPU,
|
|
149
|
+
}
|
|
150
|
+
})
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
/**
|
|
154
|
+
* Stop a running pod.
|
|
155
|
+
*
|
|
156
|
+
* @param podId - The pod ID to stop
|
|
157
|
+
* @returns API response
|
|
158
|
+
*
|
|
159
|
+
* @example
|
|
160
|
+
* ```typescript
|
|
161
|
+
* await runpod.stopPod('pod-abc123')
|
|
162
|
+
* ```
|
|
163
|
+
*/
|
|
164
|
+
async stopPod(podId: string) {
|
|
165
|
+
return this.api(`/pods/${podId}/stop`, { method: 'POST' })
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
/**
|
|
169
|
+
* Start a stopped pod.
|
|
170
|
+
*
|
|
171
|
+
* @param podId - The pod ID to start
|
|
172
|
+
* @returns API response
|
|
173
|
+
*
|
|
174
|
+
* @example
|
|
175
|
+
* ```typescript
|
|
176
|
+
* await runpod.startPod('pod-abc123')
|
|
177
|
+
* ```
|
|
178
|
+
*/
|
|
179
|
+
async startPod(podId: string) {
|
|
180
|
+
return this.api(`/pods/${podId}/start`, { method: 'POST' })
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
/**
|
|
184
|
+
* Permanently delete a pod.
|
|
185
|
+
*
|
|
186
|
+
* @param podId - The pod ID to remove
|
|
187
|
+
* @returns API response
|
|
188
|
+
*
|
|
189
|
+
* @example
|
|
190
|
+
* ```typescript
|
|
191
|
+
* await runpod.removePod('pod-abc123')
|
|
192
|
+
* ```
|
|
193
|
+
*/
|
|
194
|
+
async removePod(podId: string) {
|
|
195
|
+
return this.api(`/pods/${podId}`, { method: 'DELETE' })
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
/**
|
|
199
|
+
* Get all pods via the REST API.
|
|
200
|
+
*
|
|
201
|
+
* @param filters - Optional filters for name, image, or status
|
|
202
|
+
* @param filters.name - Filter by pod name
|
|
203
|
+
* @param filters.imageName - Filter by Docker image name
|
|
204
|
+
* @param filters.desiredStatus - Filter by status (RUNNING, EXITED, TERMINATED)
|
|
205
|
+
* @returns Array of pod info objects
|
|
206
|
+
*
|
|
207
|
+
* @example
|
|
208
|
+
* ```typescript
|
|
209
|
+
* const pods = await runpod.getpods({ desiredStatus: 'RUNNING' })
|
|
210
|
+
* console.log(pods.map(p => `${p.name}: ${p.desiredStatus}`))
|
|
211
|
+
* ```
|
|
212
|
+
*/
|
|
213
|
+
async getpods(filters: { name?: string; imageName?: string; desiredStatus?: string } = {}): Promise<RestPodInfo[]> {
|
|
214
|
+
return this.api('/pods', { params: filters })
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
/**
|
|
218
|
+
* Get detailed pod info via the REST API.
|
|
219
|
+
*
|
|
220
|
+
* Returns richer data than the CLI-based `getPodInfo`, including port mappings and public IP.
|
|
221
|
+
*
|
|
222
|
+
* @param podId - The pod ID to look up
|
|
223
|
+
* @returns Detailed pod info with port mappings, costs, and GPU details
|
|
224
|
+
*
|
|
225
|
+
* @example
|
|
226
|
+
* ```typescript
|
|
227
|
+
* const pod = await runpod.getPod('pod-abc123')
|
|
228
|
+
* console.log(`${pod.name} - ${pod.desiredStatus} - $${pod.costPerHr}/hr`)
|
|
229
|
+
* ```
|
|
230
|
+
*/
|
|
231
|
+
async getPod(podId: string): Promise<RestPodInfo> {
|
|
232
|
+
return this.api(`/pods/${podId}`)
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
/**
|
|
236
|
+
* Poll until a pod reaches a desired status.
|
|
237
|
+
*
|
|
238
|
+
* @param podId - The pod ID to monitor
|
|
239
|
+
* @param status - Target status to wait for (default: 'RUNNING')
|
|
240
|
+
* @param options - Polling configuration
|
|
241
|
+
* @param options.interval - Polling interval in ms (default: 5000)
|
|
242
|
+
* @param options.timeout - Max wait time in ms (default: 300000)
|
|
243
|
+
* @returns The pod info once it reaches the target status
|
|
244
|
+
* @throws If the pod does not reach the target status within the timeout
|
|
245
|
+
*
|
|
246
|
+
* @example
|
|
247
|
+
* ```typescript
|
|
248
|
+
* const pod = await runpod.createPod({ gpuTypeId: 'NVIDIA RTX 4090', templateId: 'abc' })
|
|
249
|
+
* const ready = await runpod.waitForPod(pod.id, 'RUNNING', { timeout: 120000 })
|
|
250
|
+
* ```
|
|
251
|
+
*/
|
|
252
|
+
async waitForPod(podId: string, status: string = 'RUNNING', { interval = 5000, timeout = 300000 } = {}): Promise<RestPodInfo> {
|
|
253
|
+
const start = Date.now()
|
|
254
|
+
while (Date.now() - start < timeout) {
|
|
255
|
+
const pod = await this.getPod(podId)
|
|
256
|
+
if (pod.desiredStatus === status && pod.portMappings) {
|
|
257
|
+
return pod
|
|
258
|
+
}
|
|
259
|
+
await new Promise(r => setTimeout(r, interval))
|
|
260
|
+
}
|
|
261
|
+
throw new Error(`Pod ${podId} did not reach status ${status} within ${timeout / 1000}s`)
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
/**
|
|
265
|
+
* List all network storage volumes on your account.
|
|
266
|
+
*
|
|
267
|
+
* @returns Array of volume info objects
|
|
268
|
+
*
|
|
269
|
+
* @example
|
|
270
|
+
* ```typescript
|
|
271
|
+
* const volumes = await runpod.listVolumes()
|
|
272
|
+
* console.log(volumes.map(v => `${v.name}: ${v.size}GB`))
|
|
273
|
+
* ```
|
|
274
|
+
*/
|
|
275
|
+
async listVolumes(): Promise<VolumeInfo[]> {
|
|
276
|
+
return this.api('/networkvolumes')
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
/**
|
|
280
|
+
* Get details for a specific network volume.
|
|
281
|
+
*
|
|
282
|
+
* @param volumeId - The volume ID to look up
|
|
283
|
+
* @returns Volume info object
|
|
284
|
+
*
|
|
285
|
+
* @example
|
|
286
|
+
* ```typescript
|
|
287
|
+
* const vol = await runpod.getVolume('vol-abc123')
|
|
288
|
+
* console.log(`${vol.name}: ${vol.size}GB in ${vol.dataCenterId}`)
|
|
289
|
+
* ```
|
|
290
|
+
*/
|
|
291
|
+
async getVolume(volumeId: string): Promise<VolumeInfo> {
|
|
292
|
+
return this.api(`/networkvolumes/${volumeId}`)
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
/**
|
|
296
|
+
* Create a new network storage volume.
|
|
297
|
+
*
|
|
298
|
+
* @param options - Volume configuration
|
|
299
|
+
* @returns The created volume info
|
|
300
|
+
*
|
|
301
|
+
* @example
|
|
302
|
+
* ```typescript
|
|
303
|
+
* const vol = await runpod.createVolume({ name: 'my-models', size: 100 })
|
|
304
|
+
* console.log(`Created volume ${vol.id}`)
|
|
305
|
+
* ```
|
|
306
|
+
*/
|
|
307
|
+
async createVolume(options: CreateVolumeOptions): Promise<VolumeInfo> {
|
|
308
|
+
return this.api('/networkvolumes', {
|
|
309
|
+
method: 'POST',
|
|
310
|
+
data: {
|
|
311
|
+
name: options.name,
|
|
312
|
+
size: options.size,
|
|
313
|
+
dataCenterId: options.dataCenterId ?? this.dataCenterId,
|
|
314
|
+
}
|
|
315
|
+
})
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
/**
|
|
319
|
+
* Delete a network storage volume.
|
|
320
|
+
*
|
|
321
|
+
* @param volumeId - The volume ID to delete
|
|
322
|
+
* @returns API response
|
|
323
|
+
*
|
|
324
|
+
* @example
|
|
325
|
+
* ```typescript
|
|
326
|
+
* await runpod.removeVolume('vol-abc123')
|
|
327
|
+
* ```
|
|
328
|
+
*/
|
|
329
|
+
async removeVolume(volumeId: string) {
|
|
330
|
+
return this.api(`/networkvolumes/${volumeId}`, { method: 'DELETE' })
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
/**
|
|
334
|
+
* Create an SSH connection to a pod using the runpodctl CLI.
|
|
335
|
+
*
|
|
336
|
+
* Prefer `getShell()` which uses the REST API and is more reliable.
|
|
337
|
+
*
|
|
338
|
+
* @param podId - The pod ID to connect to
|
|
339
|
+
* @returns A SecureShell feature instance connected to the pod
|
|
340
|
+
*
|
|
341
|
+
* @example
|
|
342
|
+
* ```typescript
|
|
343
|
+
* const shell = await runpod.createRemoteShell('pod-abc123')
|
|
344
|
+
* const output = await shell.exec('nvidia-smi')
|
|
345
|
+
* ```
|
|
346
|
+
*/
|
|
347
|
+
async createRemoteShell(podId: string) {
|
|
348
|
+
const podInfo = await this.getPodInfo(podId)!
|
|
349
|
+
const sshService = podInfo.ports.find((p:any) => p.serviceType == 'tcp' && p.external == '22')
|
|
350
|
+
|
|
351
|
+
if (!sshService) {
|
|
352
|
+
throw new Error('No SSH service found')
|
|
353
|
+
}
|
|
354
|
+
|
|
355
|
+
return this.container.feature('secureShell', {
|
|
356
|
+
host: sshService.ip,
|
|
357
|
+
port: sshService.internal,
|
|
358
|
+
key: '~/.ssh/id_ed25519',
|
|
359
|
+
username: 'root'
|
|
360
|
+
})
|
|
361
|
+
}
|
|
362
|
+
|
|
363
|
+
/**
|
|
364
|
+
* Get an SSH connection to a pod using the REST API.
|
|
365
|
+
*
|
|
366
|
+
* Uses port mappings and public IP from the REST API, which is more reliable
|
|
367
|
+
* than the CLI-based `createRemoteShell`.
|
|
368
|
+
*
|
|
369
|
+
* @param podId - The pod ID to connect to
|
|
370
|
+
* @returns A SecureShell feature instance connected to the pod
|
|
371
|
+
* @throws If no SSH port mapping or public IP is found
|
|
372
|
+
*
|
|
373
|
+
* @example
|
|
374
|
+
* ```typescript
|
|
375
|
+
* const shell = await runpod.getShell('pod-abc123')
|
|
376
|
+
* const output = await shell.exec('ls /workspace')
|
|
377
|
+
* ```
|
|
378
|
+
*/
|
|
379
|
+
async getShell(podId: string) {
|
|
380
|
+
const pod = await this.getPod(podId)
|
|
381
|
+
|
|
382
|
+
const sshPort = pod.portMappings?.['22']
|
|
383
|
+
if (!sshPort) {
|
|
384
|
+
throw new Error(`No SSH port mapping found for pod ${podId}. Is SSH (22/tcp) exposed?`)
|
|
385
|
+
}
|
|
386
|
+
|
|
387
|
+
if (!pod.publicIp) {
|
|
388
|
+
throw new Error(`No public IP found for pod ${podId}. Is the pod running?`)
|
|
389
|
+
}
|
|
390
|
+
|
|
391
|
+
return this.container.feature('secureShell', {
|
|
392
|
+
host: pod.publicIp,
|
|
393
|
+
port: sshPort,
|
|
394
|
+
key: '~/.ssh/id_ed25519',
|
|
395
|
+
username: 'root',
|
|
396
|
+
})
|
|
397
|
+
}
|
|
398
|
+
|
|
399
|
+
/**
|
|
400
|
+
* Ensure a file exists on a pod's filesystem. If missing, kicks off a background
|
|
401
|
+
* download via a helper script and polls until the file appears.
|
|
402
|
+
*
|
|
403
|
+
* @param podId - The pod ID
|
|
404
|
+
* @param remotePath - Absolute path on the pod where the file should exist
|
|
405
|
+
* @param fallbackUrl - URL to download from (inside the pod) if the file doesn't exist
|
|
406
|
+
* @param options.pollInterval - How often to check in ms (default 5000)
|
|
407
|
+
* @param options.timeout - Max time to wait for download in ms (default 600000 / 10 min)
|
|
408
|
+
* @param options.onProgress - Called each poll with current file size in bytes
|
|
409
|
+
* @returns Object with `existed` (was already there) and `path`
|
|
410
|
+
*
|
|
411
|
+
* @example
|
|
412
|
+
* ```ts
|
|
413
|
+
* await runpod.ensureFileExists(
|
|
414
|
+
* podId,
|
|
415
|
+
* '/workspace/ComfyUI/models/checkpoints/juggernaut_xl.safetensors',
|
|
416
|
+
* 'https://civitai.com/api/download/models/456789',
|
|
417
|
+
* { onProgress: (bytes) => console.log(`${(bytes / 1e9).toFixed(2)} GB downloaded`) }
|
|
418
|
+
* )
|
|
419
|
+
* ```
|
|
420
|
+
*/
|
|
421
|
+
async ensureFileExists(
|
|
422
|
+
podId: string,
|
|
423
|
+
remotePath: string,
|
|
424
|
+
fallbackUrl: string,
|
|
425
|
+
options: {
|
|
426
|
+
pollInterval?: number
|
|
427
|
+
timeout?: number
|
|
428
|
+
onProgress?: (bytes: number) => void
|
|
429
|
+
} = {}
|
|
430
|
+
): Promise<{ existed: boolean; path: string }> {
|
|
431
|
+
const { pollInterval = 5000, timeout = 600_000, onProgress } = options
|
|
432
|
+
const shell = await this.getShell(podId)
|
|
433
|
+
|
|
434
|
+
// Check if file already exists
|
|
435
|
+
const check = await shell.exec(`test -f ${esc(remotePath)} && echo EXISTS || echo MISSING`)
|
|
436
|
+
|
|
437
|
+
if (check.trim() === 'EXISTS') {
|
|
438
|
+
return { existed: true, path: remotePath }
|
|
439
|
+
}
|
|
440
|
+
|
|
441
|
+
// Ensure parent directory exists
|
|
442
|
+
const dir = remotePath.substring(0, remotePath.lastIndexOf('/'))
|
|
443
|
+
await shell.exec(`mkdir -p ${esc(dir)}`)
|
|
444
|
+
|
|
445
|
+
// Encode the download command as base64 and decode+exec on the pod.
|
|
446
|
+
// This completely sidesteps quoting issues with nohup/& through SSH.
|
|
447
|
+
const partial = `${remotePath}.partial`
|
|
448
|
+
const downloadCmd = `wget -q -O '${partial}' '${fallbackUrl}' && mv '${partial}' '${remotePath}'`
|
|
449
|
+
const b64 = Buffer.from(downloadCmd).toString('base64')
|
|
450
|
+
|
|
451
|
+
await shell.exec(`echo ${b64} | base64 -d | nohup bash >/dev/null 2>&1 &`)
|
|
452
|
+
|
|
453
|
+
// Poll until the final file appears
|
|
454
|
+
const start = Date.now()
|
|
455
|
+
|
|
456
|
+
while (Date.now() - start < timeout) {
|
|
457
|
+
await new Promise(r => setTimeout(r, pollInterval))
|
|
458
|
+
|
|
459
|
+
// Check if the final file landed (mv from .partial succeeded)
|
|
460
|
+
const result = await shell.exec(
|
|
461
|
+
`if test -f ${esc(remotePath)}; then echo DONE; elif test -f ${esc(partial)}; then stat -c%s ${esc(partial)} 2>/dev/null || stat -f%z ${esc(partial)} 2>/dev/null; else echo MISSING; fi`
|
|
462
|
+
)
|
|
463
|
+
|
|
464
|
+
const trimmed = result.trim()
|
|
465
|
+
|
|
466
|
+
if (trimmed === 'DONE') {
|
|
467
|
+
return { existed: false, path: remotePath }
|
|
468
|
+
}
|
|
469
|
+
|
|
470
|
+
if (trimmed === 'MISSING') {
|
|
471
|
+
// wget hasn't started writing yet, or it failed before creating the file.
|
|
472
|
+
// Give it a moment — if we're early in the poll loop, keep waiting.
|
|
473
|
+
if (Date.now() - start > 30_000) {
|
|
474
|
+
throw new Error(`Download failed: neither ${remotePath} nor ${partial} found after 30s`)
|
|
475
|
+
}
|
|
476
|
+
continue
|
|
477
|
+
}
|
|
478
|
+
|
|
479
|
+
// It's a number — file size of the .partial
|
|
480
|
+
const bytes = parseInt(trimmed, 10)
|
|
481
|
+
if (!isNaN(bytes) && onProgress) {
|
|
482
|
+
onProgress(bytes)
|
|
483
|
+
}
|
|
484
|
+
}
|
|
485
|
+
|
|
486
|
+
throw new Error(`Timed out waiting for download of ${remotePath} after ${timeout / 1000}s`)
|
|
487
|
+
}
|
|
488
|
+
|
|
489
|
+
/**
|
|
490
|
+
* Get the public HTTP proxy URLs for a pod's exposed HTTP ports.
|
|
491
|
+
*
|
|
492
|
+
* @param podId - The pod ID
|
|
493
|
+
* @returns Array of public proxy URLs
|
|
494
|
+
*
|
|
495
|
+
* @example
|
|
496
|
+
* ```typescript
|
|
497
|
+
* const urls = await runpod.getPodHttpURLs('pod-abc123')
|
|
498
|
+
* // ['https://pod-abc123-8888.proxy.runpod.net']
|
|
499
|
+
* ```
|
|
500
|
+
*/
|
|
501
|
+
async getPodHttpURLs(podId: string) {
|
|
502
|
+
const podInfo = await this.getPodInfo(podId)!
|
|
503
|
+
const httpServices = podInfo.ports.filter(p => p.serviceType == 'http')
|
|
504
|
+
return httpServices.map(p => `https://${podInfo.id}-${p.external}.proxy.runpod.net`)
|
|
505
|
+
}
|
|
506
|
+
|
|
507
|
+
/**
|
|
508
|
+
* List all pods using the runpodctl CLI.
|
|
509
|
+
*
|
|
510
|
+
* Parses the tabular output from `runpodctl get pod`. For richer data, use `getpods()`.
|
|
511
|
+
*
|
|
512
|
+
* @param detailed - Reserved for future use
|
|
513
|
+
* @returns Array of pod info objects
|
|
514
|
+
*
|
|
515
|
+
* @example
|
|
516
|
+
* ```typescript
|
|
517
|
+
* const pods = await runpod.listPods()
|
|
518
|
+
* pods.forEach(p => console.log(`${p.name} (${p.gpu}): ${p.status}`))
|
|
519
|
+
* ```
|
|
520
|
+
*/
|
|
521
|
+
async listPods(detailed = false): Promise<PodInfo[]> {
|
|
522
|
+
const { stdout: output } = await this.proc.spawnAndCapture(this.runpodctlPath, ['get', 'pod', '-a'])
|
|
523
|
+
const pods = output
|
|
524
|
+
.trim()
|
|
525
|
+
.split(/\r?\n/)
|
|
526
|
+
.slice(1)
|
|
527
|
+
.map(line => line.trim().split("\t"))
|
|
528
|
+
.map((fields) => fields.map(f => f.trim()))
|
|
529
|
+
.map((fields) => ({
|
|
530
|
+
id: fields[0],
|
|
531
|
+
name: fields[1],
|
|
532
|
+
gpu: fields[2],
|
|
533
|
+
imageName: fields[3],
|
|
534
|
+
status: fields[4],
|
|
535
|
+
podType: fields[5],
|
|
536
|
+
cpu: fields[6],
|
|
537
|
+
memory: fields[7],
|
|
538
|
+
containerDisk: fields[8],
|
|
539
|
+
volumeDisk: fields[9],
|
|
540
|
+
price: fields[10],
|
|
541
|
+
ports: parsePortInfo(fields[11] || ''),
|
|
542
|
+
}))
|
|
543
|
+
|
|
544
|
+
return pods as unknown as PodInfo[]
|
|
545
|
+
}
|
|
546
|
+
|
|
547
|
+
/**
|
|
548
|
+
* Get pod info using the runpodctl CLI.
|
|
549
|
+
*
|
|
550
|
+
* For richer data including port mappings and public IP, use `getPod()`.
|
|
551
|
+
*
|
|
552
|
+
* @param podId - The pod ID to look up
|
|
553
|
+
* @returns Pod info parsed from CLI output
|
|
554
|
+
*
|
|
555
|
+
* @example
|
|
556
|
+
* ```typescript
|
|
557
|
+
* const info = await runpod.getPodInfo('pod-abc123')
|
|
558
|
+
* console.log(`${info.name}: ${info.status}`)
|
|
559
|
+
* ```
|
|
560
|
+
*/
|
|
561
|
+
async getPodInfo(podId: string): Promise<PodInfo> {
|
|
562
|
+
const { stdout: output } = await this.proc.spawnAndCapture(this.runpodctlPath, ['get', 'pod', podId, '-a'])
|
|
563
|
+
|
|
564
|
+
return output
|
|
565
|
+
.trim()
|
|
566
|
+
.split(/\r?\n/)
|
|
567
|
+
.slice(1)
|
|
568
|
+
.map(line => line.trim().split("\t"))
|
|
569
|
+
.map((fields) => fields.map(f => f.trim()))
|
|
570
|
+
.map((fields) => ({
|
|
571
|
+
id: fields[0],
|
|
572
|
+
name: fields[1],
|
|
573
|
+
gpu: fields[2],
|
|
574
|
+
imageName: fields[3],
|
|
575
|
+
status: fields[4],
|
|
576
|
+
podType: fields[5],
|
|
577
|
+
cpu: fields[6],
|
|
578
|
+
memory: fields[7],
|
|
579
|
+
containerDisk: fields[8],
|
|
580
|
+
volumeDisk: fields[9],
|
|
581
|
+
spotPrice: fields[10],
|
|
582
|
+
ports: parsePortInfo(fields[11] || ''),
|
|
583
|
+
}))[0] as PodInfo
|
|
584
|
+
}
|
|
585
|
+
|
|
586
|
+
/**
|
|
587
|
+
* List available secure GPU types with pricing.
|
|
588
|
+
*
|
|
589
|
+
* Uses the runpodctl CLI to query available secure cloud GPUs, filtering out reserved instances.
|
|
590
|
+
*
|
|
591
|
+
* @returns Array of GPU info with type, memory, CPU count, and pricing
|
|
592
|
+
*
|
|
593
|
+
* @example
|
|
594
|
+
* ```typescript
|
|
595
|
+
* const gpus = await runpod.listSecureGPUs()
|
|
596
|
+
* gpus.forEach(g => console.log(`${g.gpuType}: $${g.ondemandPrice}/hr`))
|
|
597
|
+
* ```
|
|
598
|
+
*/
|
|
599
|
+
async listSecureGPUs() {
|
|
600
|
+
const { stdout: output } = await this.proc.spawnAndCapture(this.runpodctlPath, ['get', 'cloud', '--secure'])
|
|
601
|
+
|
|
602
|
+
return output
|
|
603
|
+
.split(/\r?\n/)
|
|
604
|
+
.filter((line) => !line.includes("Reserved"))
|
|
605
|
+
.map(line => line.trim().split("\t"))
|
|
606
|
+
.filter(list => list.length > 3)
|
|
607
|
+
.map((fields) => fields.map(f => f.trim()))
|
|
608
|
+
.map((fields) => ({
|
|
609
|
+
gpuType: fields[0],
|
|
610
|
+
memory: parseInt(fields[1]!),
|
|
611
|
+
cpuCount: parseInt(fields[2]!),
|
|
612
|
+
spotPrice: parseFloat(fields[3]!),
|
|
613
|
+
ondemandPrice: parseFloat(fields[4]!),
|
|
614
|
+
}))
|
|
615
|
+
|
|
616
|
+
}
|
|
617
|
+
|
|
618
|
+
}
|
|
619
|
+
|
|
620
|
+
export default Runpod
|
|
621
|
+
/** Shell-escape a string for safe use in SSH commands */
|
|
622
|
+
function esc(s: string): string {
|
|
623
|
+
return `'${s.replace(/'/g, "'\\''")}'`
|
|
624
|
+
}
|
|
625
|
+
|
|
626
|
+
function parsePortInfo(portsString: string) {
|
|
627
|
+
const portsInfo = portsString.trim().split(')')
|
|
628
|
+
.map((p) => p.trim().replace('(', '').replace(/^,/,''))
|
|
629
|
+
.filter((v) => v.length > 0)
|
|
630
|
+
.map((line) => line.split(/\s+/))
|
|
631
|
+
|
|
632
|
+
return portsInfo.map((p: string[]) => {
|
|
633
|
+
let [mappings,info] = p
|
|
634
|
+
const mappingsInfo = (mappings || '').split(':')
|
|
635
|
+
|
|
636
|
+
info = info || ''
|
|
637
|
+
|
|
638
|
+
const ip = mappingsInfo[0]
|
|
639
|
+
const portMappings = mappingsInfo[1]?.split(/\W+/) || ['0', '0']
|
|
640
|
+
const internal = parseInt(portMappings![0]!)
|
|
641
|
+
const external = parseInt(portMappings![1]!)
|
|
642
|
+
const serviceType = info.split(',').pop()
|
|
643
|
+
const isPublic = info.includes('pub')
|
|
644
|
+
|
|
645
|
+
return {
|
|
646
|
+
ip,
|
|
647
|
+
internal,
|
|
648
|
+
external,
|
|
649
|
+
serviceType,
|
|
650
|
+
isPublic
|
|
651
|
+
}
|
|
652
|
+
}).filter((info) => {
|
|
653
|
+
if (info.serviceType == 'http' && info.external > 10000) {
|
|
654
|
+
return false
|
|
655
|
+
}
|
|
656
|
+
|
|
657
|
+
return true
|
|
658
|
+
})
|
|
659
|
+
}
|
|
660
|
+
|
|
661
|
+
type PortInfo = {
|
|
662
|
+
ip: string
|
|
663
|
+
internal: number
|
|
664
|
+
external: number
|
|
665
|
+
serviceType: string
|
|
666
|
+
isPublic: boolean
|
|
667
|
+
}
|
|
668
|
+
|
|
669
|
+
/**
|
|
670
|
+
* {
|
|
671
|
+
"id": "vv32fbi9y21cxz",
|
|
672
|
+
"name": "RunPod Stable Diffusion",
|
|
673
|
+
"gpu": "1 RTX 4090",
|
|
674
|
+
"imageName": "runpod/stable-diffusion:web-ui-10.2.1",
|
|
675
|
+
"status": "RUNNING",
|
|
676
|
+
"podType": "RESERVED",
|
|
677
|
+
"cpu": "21",
|
|
678
|
+
"memory": "41",
|
|
679
|
+
"containerDisk": "10",
|
|
680
|
+
"volumeDisk": "0",
|
|
681
|
+
"spotPrice": "0.690",
|
|
682
|
+
"ports": [
|
|
683
|
+
{
|
|
684
|
+
"ip": "100.65.22.197",
|
|
685
|
+
"internal": "60014",
|
|
686
|
+
"external": "8888",
|
|
687
|
+
"serviceType": "http",
|
|
688
|
+
"isPublic": false
|
|
689
|
+
},
|
|
690
|
+
{
|
|
691
|
+
"ip": "100.65.22.197",
|
|
692
|
+
"internal": "60013",
|
|
693
|
+
"external": "19123",
|
|
694
|
+
"serviceType": "http",
|
|
695
|
+
"isPublic": false
|
|
696
|
+
},
|
|
697
|
+
{
|
|
698
|
+
"ip": "203.57.40.89",
|
|
699
|
+
"internal": "10020",
|
|
700
|
+
"external": "22",
|
|
701
|
+
"serviceType": "tcp",
|
|
702
|
+
"isPublic": true
|
|
703
|
+
},
|
|
704
|
+
{
|
|
705
|
+
"ip": "100.65.22.197",
|
|
706
|
+
"internal": "60015",
|
|
707
|
+
"external": "3001",
|
|
708
|
+
"serviceType": "http",
|
|
709
|
+
"isPublic": false
|
|
710
|
+
}
|
|
711
|
+
]
|
|
712
|
+
}
|
|
713
|
+
*/
|
|
714
|
+
|
|
715
|
+
type PodInfo = {
|
|
716
|
+
id: string
|
|
717
|
+
name: string
|
|
718
|
+
gpu: string
|
|
719
|
+
imageName: string
|
|
720
|
+
status: string
|
|
721
|
+
podType: string
|
|
722
|
+
cpu: string
|
|
723
|
+
memory: string
|
|
724
|
+
containerDisk: string
|
|
725
|
+
volumeDisk: string
|
|
726
|
+
spotPrice: string
|
|
727
|
+
ports: PortInfo[]
|
|
728
|
+
}
|
|
729
|
+
|
|
730
|
+
type TemplateInfo = {
|
|
731
|
+
id: string
|
|
732
|
+
name: string
|
|
733
|
+
category: string
|
|
734
|
+
imageName: string
|
|
735
|
+
isPublic: boolean
|
|
736
|
+
isRunpod: boolean
|
|
737
|
+
isServerless: boolean
|
|
738
|
+
containerDiskInGb: number
|
|
739
|
+
volumeInGb: number
|
|
740
|
+
volumeMountPath: string
|
|
741
|
+
ports: string[]
|
|
742
|
+
env: Record<string, string>
|
|
743
|
+
readme: string
|
|
744
|
+
}
|
|
745
|
+
|
|
746
|
+
type VolumeInfo = {
|
|
747
|
+
id: string
|
|
748
|
+
name: string
|
|
749
|
+
size: number
|
|
750
|
+
dataCenterId: string
|
|
751
|
+
}
|
|
752
|
+
|
|
753
|
+
type CreateVolumeOptions = {
|
|
754
|
+
/** Display name for the volume */
|
|
755
|
+
name: string
|
|
756
|
+
/** Size in GB */
|
|
757
|
+
size: number
|
|
758
|
+
/** Data center to create in (defaults to feature's dataCenterId) */
|
|
759
|
+
dataCenterId?: string
|
|
760
|
+
}
|
|
761
|
+
|
|
762
|
+
type RestPodInfo = {
|
|
763
|
+
id: string
|
|
764
|
+
name: string
|
|
765
|
+
desiredStatus: 'RUNNING' | 'EXITED' | 'TERMINATED'
|
|
766
|
+
costPerHr: number
|
|
767
|
+
adjustedCostPerHr: number
|
|
768
|
+
gpu: { id: string; count: number; displayName: string }
|
|
769
|
+
machine: { dataCenterId: string; location: string; gpuTypeId: string }
|
|
770
|
+
/** Public IP for SSH/TCP connections */
|
|
771
|
+
publicIp: string | null
|
|
772
|
+
ports: string[]
|
|
773
|
+
/** Maps internal port (e.g. "22") to external port number (e.g. 22122) */
|
|
774
|
+
portMappings: Record<string, number> | null
|
|
775
|
+
containerDiskInGb: number
|
|
776
|
+
volumeInGb: number
|
|
777
|
+
memoryInGb: number
|
|
778
|
+
vcpuCount: number
|
|
779
|
+
image: string
|
|
780
|
+
}
|
|
781
|
+
|
|
782
|
+
type CreatePodOptions = {
|
|
783
|
+
/** Pod display name (default: 'luca-pod') */
|
|
784
|
+
name?: string
|
|
785
|
+
/** Docker image name to run */
|
|
786
|
+
imageName?: string
|
|
787
|
+
/** GPU type ID or array of acceptable GPU types */
|
|
788
|
+
gpuTypeId: string | string[]
|
|
789
|
+
/** Number of GPUs to allocate (default: 1) */
|
|
790
|
+
gpuCount?: number
|
|
791
|
+
/** Template ID to use for pod configuration */
|
|
792
|
+
templateId?: string
|
|
793
|
+
/** Cloud type: 'SECURE' for dedicated or 'COMMUNITY' for shared (default: 'SECURE') */
|
|
794
|
+
cloudType?: 'SECURE' | 'COMMUNITY'
|
|
795
|
+
/** Container disk size in GB (default: 50) */
|
|
796
|
+
containerDiskInGb?: number
|
|
797
|
+
/** Persistent volume size in GB (default: 20) */
|
|
798
|
+
volumeInGb?: number
|
|
799
|
+
/** Mount path for the volume (default: '/workspace') */
|
|
800
|
+
volumeMountPath?: string
|
|
801
|
+
/** Port mappings like ['8888/http', '22/tcp'] */
|
|
802
|
+
ports?: string[]
|
|
803
|
+
/** Environment variables to set in the container */
|
|
804
|
+
env?: Record<string, string>
|
|
805
|
+
/** Whether the pod can be preempted for spot pricing */
|
|
806
|
+
interruptible?: boolean
|
|
807
|
+
/** ID of an existing network volume to attach */
|
|
808
|
+
networkVolumeId?: string
|
|
809
|
+
/** Minimum RAM per GPU in GB */
|
|
810
|
+
minRAMPerGPU?: number
|
|
811
|
+
}
|