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.
Files changed (532) hide show
  1. package/.github/workflows/release.yaml +170 -0
  2. package/AGENTS.md +99 -0
  3. package/CLAUDE.md +123 -0
  4. package/CNAME +1 -0
  5. package/README.md +275 -9
  6. package/RUNME.md +56 -0
  7. package/assistants/codingAssistant/ABOUT.md +5 -0
  8. package/assistants/codingAssistant/CORE.md +33 -0
  9. package/assistants/codingAssistant/hooks.ts +21 -0
  10. package/assistants/codingAssistant/tools.ts +12 -0
  11. package/assistants/inkbot/ABOUT.md +16 -0
  12. package/assistants/inkbot/CORE.md +330 -0
  13. package/assistants/inkbot/hooks.ts +6 -0
  14. package/assistants/inkbot/tools.ts +53 -0
  15. package/assistants/researcher/ABOUT.md +5 -0
  16. package/assistants/researcher/CORE.md +46 -0
  17. package/assistants/researcher/hooks.ts +16 -0
  18. package/assistants/researcher/tools.ts +237 -0
  19. package/bun.lock +2667 -0
  20. package/bunfig.toml +3 -0
  21. package/commands/audit-docs.ts +740 -0
  22. package/commands/build-bootstrap.ts +117 -0
  23. package/commands/build-python-bridge.ts +42 -0
  24. package/commands/build-scaffolds.ts +175 -0
  25. package/commands/bundle-consumer-project.ts +521 -0
  26. package/commands/generate-api-docs.ts +114 -0
  27. package/commands/inkbot.ts +874 -0
  28. package/commands/release.ts +80 -0
  29. package/commands/try-all-challenges.ts +543 -0
  30. package/commands/try-challenge.ts +100 -0
  31. package/dist/agi/container.server.d.ts +63 -0
  32. package/dist/agi/container.server.d.ts.map +1 -0
  33. package/dist/agi/endpoints/ask.d.ts +20 -0
  34. package/dist/agi/endpoints/ask.d.ts.map +1 -0
  35. package/dist/agi/endpoints/conversations/[id].d.ts +27 -0
  36. package/dist/agi/endpoints/conversations/[id].d.ts.map +1 -0
  37. package/dist/agi/endpoints/conversations.d.ts +18 -0
  38. package/dist/agi/endpoints/conversations.d.ts.map +1 -0
  39. package/dist/agi/endpoints/experts.d.ts +8 -0
  40. package/dist/agi/endpoints/experts.d.ts.map +1 -0
  41. package/dist/agi/feature.d.ts +9 -0
  42. package/dist/agi/feature.d.ts.map +1 -0
  43. package/dist/agi/features/assistant.d.ts +509 -0
  44. package/dist/agi/features/assistant.d.ts.map +1 -0
  45. package/dist/agi/features/assistants-manager.d.ts +236 -0
  46. package/dist/agi/features/assistants-manager.d.ts.map +1 -0
  47. package/dist/agi/features/autonomous-assistant.d.ts +281 -0
  48. package/dist/agi/features/autonomous-assistant.d.ts.map +1 -0
  49. package/dist/agi/features/browser-use.d.ts +479 -0
  50. package/dist/agi/features/browser-use.d.ts.map +1 -0
  51. package/dist/agi/features/claude-code.d.ts +824 -0
  52. package/dist/agi/features/claude-code.d.ts.map +1 -0
  53. package/dist/agi/features/conversation-history.d.ts +245 -0
  54. package/dist/agi/features/conversation-history.d.ts.map +1 -0
  55. package/dist/agi/features/conversation.d.ts +464 -0
  56. package/dist/agi/features/conversation.d.ts.map +1 -0
  57. package/dist/agi/features/docs-reader.d.ts +72 -0
  58. package/dist/agi/features/docs-reader.d.ts.map +1 -0
  59. package/dist/agi/features/file-tools.d.ts +110 -0
  60. package/dist/agi/features/file-tools.d.ts.map +1 -0
  61. package/dist/agi/features/luca-coder.d.ts +323 -0
  62. package/dist/agi/features/luca-coder.d.ts.map +1 -0
  63. package/dist/agi/features/openai-codex.d.ts +381 -0
  64. package/dist/agi/features/openai-codex.d.ts.map +1 -0
  65. package/dist/agi/features/openapi.d.ts +200 -0
  66. package/dist/agi/features/openapi.d.ts.map +1 -0
  67. package/dist/agi/features/skills-library.d.ts +167 -0
  68. package/dist/agi/features/skills-library.d.ts.map +1 -0
  69. package/dist/agi/index.d.ts +5 -0
  70. package/dist/agi/index.d.ts.map +1 -0
  71. package/dist/agi/lib/interceptor-chain.d.ts +44 -0
  72. package/dist/agi/lib/interceptor-chain.d.ts.map +1 -0
  73. package/dist/agi/lib/token-counter.d.ts +13 -0
  74. package/dist/agi/lib/token-counter.d.ts.map +1 -0
  75. package/dist/bootstrap/generated.d.ts +5 -0
  76. package/dist/bootstrap/generated.d.ts.map +1 -0
  77. package/dist/browser.d.ts +12 -0
  78. package/dist/browser.d.ts.map +1 -0
  79. package/dist/bus.d.ts +29 -0
  80. package/dist/bus.d.ts.map +1 -0
  81. package/dist/cli/build-info.d.ts +4 -0
  82. package/dist/cli/build-info.d.ts.map +1 -0
  83. package/dist/cli/cli.d.ts +3 -12
  84. package/dist/cli/cli.d.ts.map +1 -0
  85. package/dist/client.d.ts +60 -0
  86. package/dist/client.d.ts.map +1 -0
  87. package/dist/clients/civitai/index.d.ts +472 -0
  88. package/dist/clients/civitai/index.d.ts.map +1 -0
  89. package/dist/clients/client-template.d.ts +30 -0
  90. package/dist/clients/client-template.d.ts.map +1 -0
  91. package/dist/clients/comfyui/index.d.ts +281 -0
  92. package/dist/clients/comfyui/index.d.ts.map +1 -0
  93. package/dist/clients/elevenlabs/index.d.ts +197 -0
  94. package/dist/clients/elevenlabs/index.d.ts.map +1 -0
  95. package/dist/clients/graph.d.ts +64 -0
  96. package/dist/clients/graph.d.ts.map +1 -0
  97. package/dist/clients/openai/index.d.ts +247 -0
  98. package/dist/clients/openai/index.d.ts.map +1 -0
  99. package/dist/clients/rest.d.ts +92 -0
  100. package/dist/clients/rest.d.ts.map +1 -0
  101. package/dist/clients/supabase/index.d.ts +176 -0
  102. package/dist/clients/supabase/index.d.ts.map +1 -0
  103. package/dist/clients/websocket.d.ts +127 -0
  104. package/dist/clients/websocket.d.ts.map +1 -0
  105. package/dist/command.d.ts +163 -0
  106. package/dist/command.d.ts.map +1 -0
  107. package/dist/commands/bootstrap.d.ts +20 -0
  108. package/dist/commands/bootstrap.d.ts.map +1 -0
  109. package/dist/commands/chat.d.ts +37 -0
  110. package/dist/commands/chat.d.ts.map +1 -0
  111. package/dist/commands/code.d.ts +28 -0
  112. package/dist/commands/code.d.ts.map +1 -0
  113. package/dist/commands/console.d.ts +22 -0
  114. package/dist/commands/console.d.ts.map +1 -0
  115. package/dist/commands/describe.d.ts +50 -0
  116. package/dist/commands/describe.d.ts.map +1 -0
  117. package/dist/commands/eval.d.ts +23 -0
  118. package/dist/commands/eval.d.ts.map +1 -0
  119. package/dist/commands/help.d.ts +25 -0
  120. package/dist/commands/help.d.ts.map +1 -0
  121. package/dist/commands/index.d.ts +18 -0
  122. package/dist/commands/index.d.ts.map +1 -0
  123. package/dist/commands/introspect.d.ts +24 -0
  124. package/dist/commands/introspect.d.ts.map +1 -0
  125. package/dist/commands/mcp.d.ts +35 -0
  126. package/dist/commands/mcp.d.ts.map +1 -0
  127. package/dist/commands/prompt.d.ts +38 -0
  128. package/dist/commands/prompt.d.ts.map +1 -0
  129. package/dist/commands/run.d.ts +24 -0
  130. package/dist/commands/run.d.ts.map +1 -0
  131. package/dist/commands/sandbox-mcp.d.ts +34 -0
  132. package/dist/commands/sandbox-mcp.d.ts.map +1 -0
  133. package/dist/commands/save-api-docs.d.ts +21 -0
  134. package/dist/commands/save-api-docs.d.ts.map +1 -0
  135. package/dist/commands/scaffold.d.ts +24 -0
  136. package/dist/commands/scaffold.d.ts.map +1 -0
  137. package/dist/commands/select.d.ts +22 -0
  138. package/dist/commands/select.d.ts.map +1 -0
  139. package/dist/commands/serve.d.ts +29 -0
  140. package/dist/commands/serve.d.ts.map +1 -0
  141. package/dist/container-describer.d.ts +144 -0
  142. package/dist/container-describer.d.ts.map +1 -0
  143. package/dist/container.d.ts +451 -0
  144. package/dist/container.d.ts.map +1 -0
  145. package/dist/endpoint.d.ts +113 -0
  146. package/dist/endpoint.d.ts.map +1 -0
  147. package/dist/feature.d.ts +47 -0
  148. package/dist/feature.d.ts.map +1 -0
  149. package/dist/graft.d.ts +29 -0
  150. package/dist/graft.d.ts.map +1 -0
  151. package/dist/hash-object.d.ts +8 -0
  152. package/dist/hash-object.d.ts.map +1 -0
  153. package/dist/helper.d.ts +209 -0
  154. package/dist/helper.d.ts.map +1 -0
  155. package/dist/introspection/generated.node.d.ts +44623 -0
  156. package/dist/introspection/generated.node.d.ts.map +1 -0
  157. package/dist/introspection/generated.web.d.ts +1412 -0
  158. package/dist/introspection/generated.web.d.ts.map +1 -0
  159. package/dist/introspection/index.d.ts +156 -0
  160. package/dist/introspection/index.d.ts.map +1 -0
  161. package/dist/introspection/scan.d.ts +147 -0
  162. package/dist/introspection/scan.d.ts.map +1 -0
  163. package/dist/node/container.d.ts +256 -0
  164. package/dist/node/container.d.ts.map +1 -0
  165. package/dist/node/feature.d.ts +9 -0
  166. package/dist/node/feature.d.ts.map +1 -0
  167. package/dist/node/features/container-link.d.ts +213 -0
  168. package/dist/node/features/container-link.d.ts.map +1 -0
  169. package/dist/node/features/content-db.d.ts +354 -0
  170. package/dist/node/features/content-db.d.ts.map +1 -0
  171. package/dist/node/features/disk-cache.d.ts +236 -0
  172. package/dist/node/features/disk-cache.d.ts.map +1 -0
  173. package/dist/node/features/dns.d.ts +511 -0
  174. package/dist/node/features/dns.d.ts.map +1 -0
  175. package/dist/node/features/docker.d.ts +485 -0
  176. package/dist/node/features/docker.d.ts.map +1 -0
  177. package/dist/node/features/downloader.d.ts +73 -0
  178. package/dist/node/features/downloader.d.ts.map +1 -0
  179. package/dist/node/features/figlet-fonts.d.ts +4 -0
  180. package/dist/node/features/figlet-fonts.d.ts.map +1 -0
  181. package/dist/node/features/file-manager.d.ts +177 -0
  182. package/dist/node/features/file-manager.d.ts.map +1 -0
  183. package/dist/node/features/fs.d.ts +635 -0
  184. package/dist/node/features/fs.d.ts.map +1 -0
  185. package/dist/node/features/git.d.ts +329 -0
  186. package/dist/node/features/git.d.ts.map +1 -0
  187. package/dist/node/features/google-auth.d.ts +200 -0
  188. package/dist/node/features/google-auth.d.ts.map +1 -0
  189. package/dist/node/features/google-calendar.d.ts +194 -0
  190. package/dist/node/features/google-calendar.d.ts.map +1 -0
  191. package/dist/node/features/google-docs.d.ts +138 -0
  192. package/dist/node/features/google-docs.d.ts.map +1 -0
  193. package/dist/node/features/google-drive.d.ts +202 -0
  194. package/dist/node/features/google-drive.d.ts.map +1 -0
  195. package/dist/node/features/google-mail.d.ts +221 -0
  196. package/dist/node/features/google-mail.d.ts.map +1 -0
  197. package/dist/node/features/google-sheets.d.ts +157 -0
  198. package/dist/node/features/google-sheets.d.ts.map +1 -0
  199. package/dist/node/features/grep.d.ts +207 -0
  200. package/dist/node/features/grep.d.ts.map +1 -0
  201. package/dist/node/features/helpers.d.ts +236 -0
  202. package/dist/node/features/helpers.d.ts.map +1 -0
  203. package/dist/node/features/ink.d.ts +332 -0
  204. package/dist/node/features/ink.d.ts.map +1 -0
  205. package/dist/node/features/ipc-socket.d.ts +298 -0
  206. package/dist/node/features/ipc-socket.d.ts.map +1 -0
  207. package/dist/node/features/json-tree.d.ts +140 -0
  208. package/dist/node/features/json-tree.d.ts.map +1 -0
  209. package/dist/node/features/networking.d.ts +373 -0
  210. package/dist/node/features/networking.d.ts.map +1 -0
  211. package/dist/node/features/nlp.d.ts +125 -0
  212. package/dist/node/features/nlp.d.ts.map +1 -0
  213. package/dist/node/features/opener.d.ts +93 -0
  214. package/dist/node/features/opener.d.ts.map +1 -0
  215. package/dist/node/features/os.d.ts +168 -0
  216. package/dist/node/features/os.d.ts.map +1 -0
  217. package/dist/node/features/package-finder.d.ts +419 -0
  218. package/dist/node/features/package-finder.d.ts.map +1 -0
  219. package/dist/node/features/postgres.d.ts +173 -0
  220. package/dist/node/features/postgres.d.ts.map +1 -0
  221. package/dist/node/features/proc.d.ts +285 -0
  222. package/dist/node/features/proc.d.ts.map +1 -0
  223. package/dist/node/features/process-manager.d.ts +427 -0
  224. package/dist/node/features/process-manager.d.ts.map +1 -0
  225. package/dist/node/features/python.d.ts +477 -0
  226. package/dist/node/features/python.d.ts.map +1 -0
  227. package/dist/node/features/redis.d.ts +247 -0
  228. package/dist/node/features/redis.d.ts.map +1 -0
  229. package/dist/node/features/repl.d.ts +84 -0
  230. package/dist/node/features/repl.d.ts.map +1 -0
  231. package/dist/node/features/runpod.d.ts +527 -0
  232. package/dist/node/features/runpod.d.ts.map +1 -0
  233. package/dist/node/features/secure-shell.d.ts +145 -0
  234. package/dist/node/features/secure-shell.d.ts.map +1 -0
  235. package/dist/node/features/semantic-search.d.ts +207 -0
  236. package/dist/node/features/semantic-search.d.ts.map +1 -0
  237. package/dist/node/features/sqlite.d.ts +180 -0
  238. package/dist/node/features/sqlite.d.ts.map +1 -0
  239. package/dist/node/features/telegram.d.ts +173 -0
  240. package/dist/node/features/telegram.d.ts.map +1 -0
  241. package/dist/node/features/transpiler.d.ts +51 -0
  242. package/dist/node/features/transpiler.d.ts.map +1 -0
  243. package/dist/node/features/tts.d.ts +108 -0
  244. package/dist/node/features/tts.d.ts.map +1 -0
  245. package/dist/node/features/ui.d.ts +562 -0
  246. package/dist/node/features/ui.d.ts.map +1 -0
  247. package/dist/node/features/vault.d.ts +90 -0
  248. package/dist/node/features/vault.d.ts.map +1 -0
  249. package/dist/node/features/vm.d.ts +285 -0
  250. package/dist/node/features/vm.d.ts.map +1 -0
  251. package/dist/node/features/yaml-tree.d.ts +118 -0
  252. package/dist/node/features/yaml-tree.d.ts.map +1 -0
  253. package/dist/node/features/yaml.d.ts +127 -0
  254. package/dist/node/features/yaml.d.ts.map +1 -0
  255. package/dist/node.d.ts +67 -0
  256. package/dist/node.d.ts.map +1 -0
  257. package/dist/python/generated.d.ts +2 -0
  258. package/dist/python/generated.d.ts.map +1 -0
  259. package/dist/react/index.d.ts +36 -0
  260. package/dist/react/index.d.ts.map +1 -0
  261. package/dist/registry.d.ts +97 -0
  262. package/dist/registry.d.ts.map +1 -0
  263. package/dist/scaffolds/generated.d.ts +13 -0
  264. package/dist/scaffolds/generated.d.ts.map +1 -0
  265. package/dist/scaffolds/template.d.ts +11 -0
  266. package/dist/scaffolds/template.d.ts.map +1 -0
  267. package/dist/schemas/base.d.ts +254 -0
  268. package/dist/schemas/base.d.ts.map +1 -0
  269. package/dist/selector.d.ts +130 -0
  270. package/dist/selector.d.ts.map +1 -0
  271. package/dist/server.d.ts +89 -0
  272. package/dist/server.d.ts.map +1 -0
  273. package/dist/servers/express.d.ts +104 -0
  274. package/dist/servers/express.d.ts.map +1 -0
  275. package/dist/servers/mcp.d.ts +201 -0
  276. package/dist/servers/mcp.d.ts.map +1 -0
  277. package/dist/servers/socket.d.ts +121 -0
  278. package/dist/servers/socket.d.ts.map +1 -0
  279. package/dist/state.d.ts +24 -0
  280. package/dist/state.d.ts.map +1 -0
  281. package/dist/web/clients/socket.d.ts +37 -0
  282. package/dist/web/clients/socket.d.ts.map +1 -0
  283. package/dist/web/container.d.ts +55 -0
  284. package/dist/web/container.d.ts.map +1 -0
  285. package/dist/web/extension.d.ts +4 -0
  286. package/dist/web/extension.d.ts.map +1 -0
  287. package/dist/web/feature.d.ts +8 -0
  288. package/dist/web/feature.d.ts.map +1 -0
  289. package/dist/web/features/asset-loader.d.ts +35 -0
  290. package/dist/web/features/asset-loader.d.ts.map +1 -0
  291. package/dist/web/features/container-link.d.ts +167 -0
  292. package/dist/web/features/container-link.d.ts.map +1 -0
  293. package/dist/web/features/esbuild.d.ts +51 -0
  294. package/dist/web/features/esbuild.d.ts.map +1 -0
  295. package/dist/web/features/helpers.d.ts +140 -0
  296. package/dist/web/features/helpers.d.ts.map +1 -0
  297. package/dist/web/features/network.d.ts +69 -0
  298. package/dist/web/features/network.d.ts.map +1 -0
  299. package/dist/web/features/speech.d.ts +71 -0
  300. package/dist/web/features/speech.d.ts.map +1 -0
  301. package/dist/web/features/vault.d.ts +62 -0
  302. package/dist/web/features/vault.d.ts.map +1 -0
  303. package/dist/web/features/vm.d.ts +48 -0
  304. package/dist/web/features/vm.d.ts.map +1 -0
  305. package/dist/web/features/voice-recognition.d.ts +96 -0
  306. package/dist/web/features/voice-recognition.d.ts.map +1 -0
  307. package/dist/web/shims/isomorphic-vm.d.ts +22 -0
  308. package/dist/web/shims/isomorphic-vm.d.ts.map +1 -0
  309. package/index.html +1457 -0
  310. package/index.ts +1 -0
  311. package/install.sh +84 -0
  312. package/luca.cli.ts +16 -0
  313. package/luca.console.ts +9 -0
  314. package/main.py +6 -0
  315. package/package.json +219 -58
  316. package/public/index.html +1457 -0
  317. package/public/slides-ai-native.html +902 -0
  318. package/public/slides-intro.html +974 -0
  319. package/pyproject.toml +7 -0
  320. package/scripts/build-web.ts +28 -0
  321. package/scripts/examples/ask-luca-expert.ts +42 -0
  322. package/scripts/examples/assistant-questions.ts +12 -0
  323. package/scripts/examples/excalidraw-expert.ts +75 -0
  324. package/scripts/examples/expert-chat.ts +0 -0
  325. package/scripts/examples/file-manager.ts +14 -0
  326. package/scripts/examples/ideas.ts +12 -0
  327. package/scripts/examples/interactive-chat.ts +20 -0
  328. package/scripts/examples/openai-tool-calls.ts +113 -0
  329. package/scripts/examples/opening-a-web-browser.ts +5 -0
  330. package/scripts/examples/telegram-bot.ts +79 -0
  331. package/scripts/examples/using-assistant-with-mcp.ts +555 -0
  332. package/scripts/examples/using-claude-code.ts +10 -0
  333. package/scripts/examples/using-contentdb.ts +35 -0
  334. package/scripts/examples/using-conversations.ts +35 -0
  335. package/scripts/examples/using-disk-cache.ts +10 -0
  336. package/scripts/examples/using-docker-shell.ts +75 -0
  337. package/scripts/examples/using-elevenlabs.ts +25 -0
  338. package/scripts/examples/using-google-calendar.ts +57 -0
  339. package/scripts/examples/using-google-docs.ts +74 -0
  340. package/scripts/examples/using-google-drive.ts +74 -0
  341. package/scripts/examples/using-google-sheets.ts +89 -0
  342. package/scripts/examples/using-nlp.ts +55 -0
  343. package/scripts/examples/using-ollama.ts +11 -0
  344. package/scripts/examples/using-postgres.ts +55 -0
  345. package/scripts/examples/using-runpod.ts +32 -0
  346. package/scripts/examples/using-tts.ts +40 -0
  347. package/scripts/scaffold.ts +391 -0
  348. package/scripts/scratch.ts +15 -0
  349. package/scripts/stamp-build.sh +12 -0
  350. package/scripts/test-assistant-hooks.ts +13 -0
  351. package/scripts/test-docs-reader.ts +10 -0
  352. package/scripts/test-linux-binary.sh +80 -0
  353. package/scripts/update-introspection-data.ts +58 -0
  354. package/src/agi/README.md +14 -0
  355. package/src/agi/container.server.ts +156 -0
  356. package/src/agi/feature.ts +13 -0
  357. package/src/agi/features/agent-memory.ts +694 -0
  358. package/src/agi/features/assistant.ts +1653 -0
  359. package/src/agi/features/assistants-manager.ts +534 -0
  360. package/src/agi/features/autonomous-assistant.ts +431 -0
  361. package/src/agi/features/browser-use.ts +672 -0
  362. package/src/agi/features/claude-code.ts +1584 -0
  363. package/src/agi/features/coding-tools.ts +175 -0
  364. package/src/agi/features/conversation-history.ts +672 -0
  365. package/src/agi/features/conversation.ts +1494 -0
  366. package/src/agi/features/docs-reader.ts +167 -0
  367. package/src/agi/features/file-tools.ts +340 -0
  368. package/src/agi/features/luca-coder.ts +641 -0
  369. package/src/agi/features/mcp-bridge.ts +532 -0
  370. package/src/agi/features/openai-codex.ts +651 -0
  371. package/src/agi/features/openapi.ts +445 -0
  372. package/src/agi/features/skills-library.ts +557 -0
  373. package/src/agi/index.ts +6 -0
  374. package/src/agi/lib/interceptor-chain.ts +89 -0
  375. package/src/agi/lib/token-counter.ts +202 -0
  376. package/src/bootstrap/generated.ts +9791 -0
  377. package/src/browser.ts +25 -0
  378. package/src/bus.ts +122 -0
  379. package/src/cli/build-info.ts +4 -0
  380. package/src/cli/cli.ts +355 -0
  381. package/src/client.ts +170 -0
  382. package/src/clients/civitai/index.ts +537 -0
  383. package/src/clients/client-template.ts +41 -0
  384. package/src/clients/comfyui/index.ts +604 -0
  385. package/src/clients/elevenlabs/index.ts +317 -0
  386. package/src/clients/graph.ts +87 -0
  387. package/src/clients/openai/index.ts +456 -0
  388. package/src/clients/rest.ts +207 -0
  389. package/src/clients/supabase/index.ts +357 -0
  390. package/src/clients/voicebox/index.ts +300 -0
  391. package/src/clients/websocket.ts +251 -0
  392. package/src/command.ts +506 -0
  393. package/src/commands/bootstrap.ts +244 -0
  394. package/src/commands/chat.ts +309 -0
  395. package/src/commands/code.ts +371 -0
  396. package/src/commands/console.ts +189 -0
  397. package/src/commands/describe.ts +243 -0
  398. package/src/commands/eval.ts +67 -0
  399. package/src/commands/help.ts +240 -0
  400. package/src/commands/index.ts +19 -0
  401. package/src/commands/introspect.ts +218 -0
  402. package/src/commands/mcp.ts +64 -0
  403. package/src/commands/prompt.ts +1014 -0
  404. package/src/commands/run.ts +278 -0
  405. package/src/commands/sandbox-mcp.ts +343 -0
  406. package/src/commands/save-api-docs.ts +51 -0
  407. package/src/commands/scaffold.ts +225 -0
  408. package/src/commands/select.ts +99 -0
  409. package/src/commands/serve.ts +208 -0
  410. package/src/container-describer.ts +1091 -0
  411. package/src/container.ts +1199 -0
  412. package/src/endpoint.ts +365 -0
  413. package/src/entity.ts +173 -0
  414. package/src/feature.ts +118 -0
  415. package/src/graft.ts +181 -0
  416. package/src/hash-object.ts +97 -0
  417. package/src/helper.ts +849 -0
  418. package/src/introspection/generated.agi.ts +41200 -0
  419. package/src/introspection/generated.node.ts +28773 -0
  420. package/src/introspection/generated.web.ts +2272 -0
  421. package/src/introspection/index.ts +296 -0
  422. package/src/introspection/scan.ts +1136 -0
  423. package/src/node/container.ts +409 -0
  424. package/src/node/feature.ts +13 -0
  425. package/src/node/features/container-link.ts +559 -0
  426. package/src/node/features/content-db.ts +849 -0
  427. package/src/node/features/disk-cache.ts +388 -0
  428. package/src/node/features/display-result.ts +57 -0
  429. package/src/node/features/dns.ts +669 -0
  430. package/src/node/features/docker.ts +921 -0
  431. package/src/node/features/downloader.ts +79 -0
  432. package/src/node/features/figlet-fonts.ts +600 -0
  433. package/src/node/features/file-manager.ts +535 -0
  434. package/src/node/features/fs.ts +1050 -0
  435. package/src/node/features/git.ts +592 -0
  436. package/src/node/features/google-auth.ts +504 -0
  437. package/src/node/features/google-calendar.ts +306 -0
  438. package/src/node/features/google-docs.ts +412 -0
  439. package/src/node/features/google-drive.ts +346 -0
  440. package/src/node/features/google-mail.ts +540 -0
  441. package/src/node/features/google-sheets.ts +286 -0
  442. package/src/node/features/grep.ts +427 -0
  443. package/src/node/features/helpers.ts +762 -0
  444. package/src/node/features/ink.ts +490 -0
  445. package/src/node/features/ipc-socket.ts +649 -0
  446. package/src/node/features/json-tree.ts +170 -0
  447. package/src/node/features/networking.ts +961 -0
  448. package/src/node/features/nlp.ts +212 -0
  449. package/src/node/features/opener.ts +180 -0
  450. package/src/node/features/os.ts +403 -0
  451. package/src/node/features/package-finder.ts +540 -0
  452. package/src/node/features/postgres.ts +289 -0
  453. package/src/node/features/proc.ts +503 -0
  454. package/src/node/features/process-manager.ts +844 -0
  455. package/src/node/features/python.ts +912 -0
  456. package/src/node/features/redis.ts +446 -0
  457. package/src/node/features/repl.ts +212 -0
  458. package/src/node/features/runpod.ts +811 -0
  459. package/src/node/features/secure-shell.ts +261 -0
  460. package/src/node/features/semantic-search.ts +935 -0
  461. package/src/node/features/sqlite.ts +289 -0
  462. package/src/node/features/telegram.ts +343 -0
  463. package/src/node/features/transpiler.ts +160 -0
  464. package/src/node/features/tts.ts +185 -0
  465. package/src/node/features/ui.ts +791 -0
  466. package/src/node/features/vault.ts +153 -0
  467. package/src/node/features/vm.ts +462 -0
  468. package/src/node/features/yaml-tree.ts +148 -0
  469. package/src/node/features/yaml.ts +133 -0
  470. package/src/node.ts +76 -0
  471. package/src/python/bridge.py +220 -0
  472. package/src/python/generated.ts +226 -0
  473. package/src/react/index.ts +175 -0
  474. package/src/registry.ts +210 -0
  475. package/src/scaffolds/generated.ts +1814 -0
  476. package/src/scaffolds/template.ts +46 -0
  477. package/src/schemas/base.ts +296 -0
  478. package/src/selector.ts +352 -0
  479. package/src/server.ts +229 -0
  480. package/src/servers/express.ts +283 -0
  481. package/src/servers/mcp.ts +802 -0
  482. package/src/servers/socket.ts +258 -0
  483. package/src/state.ts +101 -0
  484. package/src/web/clients/socket.ts +99 -0
  485. package/src/web/container.ts +75 -0
  486. package/src/web/extension.ts +30 -0
  487. package/src/web/feature.ts +12 -0
  488. package/src/web/features/asset-loader.ts +72 -0
  489. package/src/web/features/container-link.ts +382 -0
  490. package/src/web/features/esbuild.ts +93 -0
  491. package/src/web/features/helpers.ts +291 -0
  492. package/src/web/features/network.ts +85 -0
  493. package/src/web/features/speech.ts +104 -0
  494. package/src/web/features/vault.ts +207 -0
  495. package/src/web/features/vm.ts +85 -0
  496. package/src/web/features/voice-recognition.ts +161 -0
  497. package/src/web/shims/isomorphic-vm.ts +149 -0
  498. package/tsconfig.build.json +12 -0
  499. package/tsconfig.json +58 -0
  500. package/uv.lock +8 -0
  501. package/LICENSE +0 -21
  502. package/dist/cli/cli.js +0 -48
  503. package/dist/cli/common.d.ts +0 -2
  504. package/dist/cli/common.js +0 -6
  505. package/dist/cli/index.d.ts +0 -2
  506. package/dist/cli/index.js +0 -5
  507. package/dist/cli/run.d.ts +0 -1
  508. package/dist/cli/run.js +0 -38
  509. package/dist/core/index.d.ts +0 -4
  510. package/dist/core/index.js +0 -32
  511. package/dist/core/read.d.ts +0 -2
  512. package/dist/core/read.js +0 -29
  513. package/dist/core/request.d.ts +0 -1
  514. package/dist/core/request.js +0 -2
  515. package/dist/core/write.d.ts +0 -2
  516. package/dist/core/write.js +0 -21
  517. package/dist/index.d.ts +0 -1
  518. package/dist/index.js +0 -5
  519. package/dist/utils/common.d.ts +0 -9
  520. package/dist/utils/common.js +0 -57
  521. package/dist/utils/consts.d.ts +0 -3
  522. package/dist/utils/consts.js +0 -11
  523. package/dist/utils/dict.d.ts +0 -1
  524. package/dist/utils/dict.js +0 -7
  525. package/dist/utils/index.d.ts +0 -5
  526. package/dist/utils/index.js +0 -21
  527. package/dist/utils/log.d.ts +0 -1
  528. package/dist/utils/log.js +0 -5
  529. package/dist/utils/types.d.ts +0 -1
  530. package/dist/utils/types.js +0 -2
  531. package/dist/utils/utils.test.d.ts +0 -1
  532. 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
+ }