dstack 0.0.9__py3-none-any.whl → 0.20.7__py3-none-any.whl

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 (879) hide show
  1. dstack/_internal/cli/commands/__init__.py +80 -0
  2. dstack/_internal/cli/commands/apply.py +100 -0
  3. dstack/_internal/cli/commands/attach.py +161 -0
  4. dstack/_internal/cli/commands/completion.py +22 -0
  5. dstack/_internal/cli/commands/delete.py +44 -0
  6. dstack/_internal/cli/commands/event.py +168 -0
  7. dstack/_internal/cli/commands/fleet.py +161 -0
  8. dstack/_internal/cli/commands/gateway.py +159 -0
  9. dstack/_internal/cli/commands/init.py +64 -0
  10. dstack/_internal/cli/commands/login.py +352 -0
  11. dstack/_internal/cli/commands/logs.py +62 -0
  12. dstack/_internal/cli/commands/metrics.py +153 -0
  13. dstack/_internal/cli/commands/offer.py +146 -0
  14. dstack/_internal/cli/commands/project.py +259 -0
  15. dstack/_internal/cli/commands/ps.py +81 -0
  16. dstack/_internal/cli/commands/run.py +69 -0
  17. dstack/_internal/cli/commands/secrets.py +92 -0
  18. dstack/_internal/cli/commands/server.py +96 -0
  19. dstack/_internal/cli/commands/stop.py +26 -0
  20. dstack/_internal/cli/commands/volume.py +117 -0
  21. dstack/_internal/cli/main.py +101 -0
  22. dstack/_internal/cli/models/gateways.py +16 -0
  23. dstack/_internal/cli/models/offers.py +47 -0
  24. dstack/_internal/cli/models/runs.py +16 -0
  25. dstack/_internal/cli/services/args.py +31 -0
  26. dstack/_internal/cli/services/completion.py +91 -0
  27. dstack/_internal/cli/services/configurators/__init__.py +86 -0
  28. dstack/_internal/cli/services/configurators/base.py +103 -0
  29. dstack/_internal/cli/services/configurators/fleet.py +475 -0
  30. dstack/_internal/cli/services/configurators/gateway.py +231 -0
  31. dstack/_internal/cli/services/configurators/run.py +882 -0
  32. dstack/_internal/cli/services/configurators/volume.py +222 -0
  33. dstack/_internal/cli/services/events.py +68 -0
  34. dstack/_internal/cli/services/profile.py +182 -0
  35. dstack/_internal/cli/services/repos.py +71 -0
  36. dstack/_internal/cli/services/resources.py +54 -0
  37. dstack/_internal/cli/utils/common.py +159 -0
  38. dstack/_internal/cli/utils/fleet.py +106 -0
  39. dstack/_internal/cli/utils/gateway.py +56 -0
  40. dstack/_internal/cli/utils/gpu.py +178 -0
  41. dstack/_internal/cli/utils/rich.py +156 -0
  42. dstack/_internal/cli/utils/run.py +517 -0
  43. dstack/_internal/cli/utils/secrets.py +25 -0
  44. dstack/_internal/cli/utils/updates.py +98 -0
  45. dstack/_internal/cli/utils/volume.py +58 -0
  46. dstack/_internal/compat.py +3 -0
  47. dstack/_internal/core/backends/amddevcloud/__init__.py +1 -0
  48. dstack/_internal/core/backends/amddevcloud/backend.py +16 -0
  49. dstack/_internal/core/backends/amddevcloud/compute.py +5 -0
  50. dstack/_internal/core/backends/amddevcloud/configurator.py +29 -0
  51. dstack/_internal/core/backends/aws/auth.py +30 -0
  52. dstack/_internal/core/backends/aws/backend.py +31 -0
  53. dstack/_internal/core/backends/aws/compute.py +1153 -0
  54. dstack/_internal/core/backends/aws/configurator.py +191 -0
  55. dstack/_internal/core/backends/aws/models.py +135 -0
  56. dstack/_internal/core/backends/aws/resources.py +700 -0
  57. dstack/_internal/core/backends/azure/auth.py +39 -0
  58. dstack/_internal/core/backends/azure/backend.py +21 -0
  59. dstack/_internal/core/backends/azure/compute.py +676 -0
  60. dstack/_internal/core/backends/azure/configurator.py +472 -0
  61. dstack/_internal/core/backends/azure/models.py +98 -0
  62. dstack/_internal/core/backends/azure/resources.py +116 -0
  63. dstack/_internal/core/backends/azure/utils.py +42 -0
  64. dstack/_internal/core/backends/base/backend.py +18 -0
  65. dstack/_internal/core/backends/base/compute.py +1101 -0
  66. dstack/_internal/core/backends/base/configurator.py +117 -0
  67. dstack/_internal/core/backends/base/models.py +24 -0
  68. dstack/_internal/core/backends/base/offers.py +232 -0
  69. dstack/_internal/core/backends/cloudrift/api_client.py +220 -0
  70. dstack/_internal/core/backends/cloudrift/backend.py +16 -0
  71. dstack/_internal/core/backends/cloudrift/compute.py +138 -0
  72. dstack/_internal/core/backends/cloudrift/configurator.py +72 -0
  73. dstack/_internal/core/backends/cloudrift/models.py +40 -0
  74. dstack/_internal/core/backends/configurators.py +181 -0
  75. dstack/_internal/core/backends/cudo/__init__.py +0 -0
  76. dstack/_internal/core/backends/cudo/api_client.py +111 -0
  77. dstack/_internal/core/backends/cudo/backend.py +16 -0
  78. dstack/_internal/core/backends/cudo/compute.py +174 -0
  79. dstack/_internal/core/backends/cudo/configurator.py +63 -0
  80. dstack/_internal/core/backends/cudo/models.py +37 -0
  81. dstack/_internal/core/backends/datacrunch/__init__.py +1 -0
  82. dstack/_internal/core/backends/datacrunch/backend.py +18 -0
  83. dstack/_internal/core/backends/datacrunch/compute.py +8 -0
  84. dstack/_internal/core/backends/datacrunch/configurator.py +17 -0
  85. dstack/_internal/core/backends/digitalocean/__init__.py +1 -0
  86. dstack/_internal/core/backends/digitalocean/backend.py +16 -0
  87. dstack/_internal/core/backends/digitalocean/compute.py +5 -0
  88. dstack/_internal/core/backends/digitalocean/configurator.py +31 -0
  89. dstack/_internal/core/backends/digitalocean_base/__init__.py +1 -0
  90. dstack/_internal/core/backends/digitalocean_base/api_client.py +104 -0
  91. dstack/_internal/core/backends/digitalocean_base/backend.py +5 -0
  92. dstack/_internal/core/backends/digitalocean_base/compute.py +174 -0
  93. dstack/_internal/core/backends/digitalocean_base/configurator.py +57 -0
  94. dstack/_internal/core/backends/digitalocean_base/models.py +43 -0
  95. dstack/_internal/core/backends/dstack/__init__.py +0 -0
  96. dstack/_internal/core/backends/dstack/models.py +26 -0
  97. dstack/_internal/core/backends/features.py +74 -0
  98. dstack/_internal/core/backends/gcp/__init__.py +0 -0
  99. dstack/_internal/core/backends/gcp/auth.py +57 -0
  100. dstack/_internal/core/backends/gcp/backend.py +17 -0
  101. dstack/_internal/core/backends/gcp/compute.py +1257 -0
  102. dstack/_internal/core/backends/gcp/configurator.py +206 -0
  103. dstack/_internal/core/backends/gcp/features/__init__.py +0 -0
  104. dstack/_internal/core/backends/gcp/features/tcpx.py +65 -0
  105. dstack/_internal/core/backends/gcp/models.py +160 -0
  106. dstack/_internal/core/backends/gcp/resources.py +585 -0
  107. dstack/_internal/core/backends/hotaisle/__init__.py +1 -0
  108. dstack/_internal/core/backends/hotaisle/api_client.py +101 -0
  109. dstack/_internal/core/backends/hotaisle/backend.py +16 -0
  110. dstack/_internal/core/backends/hotaisle/compute.py +188 -0
  111. dstack/_internal/core/backends/hotaisle/configurator.py +66 -0
  112. dstack/_internal/core/backends/hotaisle/models.py +45 -0
  113. dstack/_internal/core/backends/kubernetes/__init__.py +0 -0
  114. dstack/_internal/core/backends/kubernetes/backend.py +16 -0
  115. dstack/_internal/core/backends/kubernetes/compute.py +1077 -0
  116. dstack/_internal/core/backends/kubernetes/configurator.py +61 -0
  117. dstack/_internal/core/backends/kubernetes/models.py +71 -0
  118. dstack/_internal/core/backends/kubernetes/utils.py +81 -0
  119. dstack/_internal/core/backends/lambdalabs/__init__.py +0 -0
  120. dstack/_internal/core/backends/lambdalabs/api_client.py +87 -0
  121. dstack/_internal/core/backends/lambdalabs/backend.py +17 -0
  122. dstack/_internal/core/backends/lambdalabs/compute.py +233 -0
  123. dstack/_internal/core/backends/lambdalabs/configurator.py +65 -0
  124. dstack/_internal/core/backends/lambdalabs/models.py +37 -0
  125. dstack/_internal/core/backends/local/__init__.py +0 -0
  126. dstack/_internal/core/backends/local/backend.py +14 -0
  127. dstack/_internal/core/backends/local/compute.py +130 -0
  128. dstack/_internal/core/backends/models.py +158 -0
  129. dstack/_internal/core/backends/nebius/__init__.py +0 -0
  130. dstack/_internal/core/backends/nebius/backend.py +16 -0
  131. dstack/_internal/core/backends/nebius/compute.py +401 -0
  132. dstack/_internal/core/backends/nebius/configurator.py +98 -0
  133. dstack/_internal/core/backends/nebius/models.py +185 -0
  134. dstack/_internal/core/backends/nebius/resources.py +433 -0
  135. dstack/_internal/core/backends/oci/__init__.py +0 -0
  136. dstack/_internal/core/backends/oci/auth.py +21 -0
  137. dstack/_internal/core/backends/oci/backend.py +16 -0
  138. dstack/_internal/core/backends/oci/compute.py +209 -0
  139. dstack/_internal/core/backends/oci/configurator.py +156 -0
  140. dstack/_internal/core/backends/oci/exceptions.py +15 -0
  141. dstack/_internal/core/backends/oci/models.py +87 -0
  142. dstack/_internal/core/backends/oci/region.py +86 -0
  143. dstack/_internal/core/backends/oci/resources.py +836 -0
  144. dstack/_internal/core/backends/runpod/__init__.py +0 -0
  145. dstack/_internal/core/backends/runpod/api_client.py +627 -0
  146. dstack/_internal/core/backends/runpod/backend.py +16 -0
  147. dstack/_internal/core/backends/runpod/compute.py +444 -0
  148. dstack/_internal/core/backends/runpod/configurator.py +63 -0
  149. dstack/_internal/core/backends/runpod/models.py +54 -0
  150. dstack/_internal/core/backends/template/__init__.py +0 -0
  151. dstack/_internal/core/backends/template/backend.py.jinja +16 -0
  152. dstack/_internal/core/backends/template/compute.py.jinja +95 -0
  153. dstack/_internal/core/backends/template/configurator.py.jinja +69 -0
  154. dstack/_internal/core/backends/template/models.py.jinja +62 -0
  155. dstack/_internal/core/backends/tensordock/models.py +40 -0
  156. dstack/_internal/core/backends/vastai/__init__.py +0 -0
  157. dstack/_internal/core/backends/vastai/api_client.py +143 -0
  158. dstack/_internal/core/backends/vastai/backend.py +16 -0
  159. dstack/_internal/core/backends/vastai/compute.py +141 -0
  160. dstack/_internal/core/backends/vastai/configurator.py +69 -0
  161. dstack/_internal/core/backends/vastai/models.py +37 -0
  162. dstack/_internal/core/backends/verda/__init__.py +0 -0
  163. dstack/_internal/core/backends/verda/backend.py +16 -0
  164. dstack/_internal/core/backends/verda/compute.py +266 -0
  165. dstack/_internal/core/backends/verda/configurator.py +73 -0
  166. dstack/_internal/core/backends/verda/models.py +38 -0
  167. dstack/_internal/core/backends/vultr/__init__.py +0 -0
  168. dstack/_internal/core/backends/vultr/api_client.py +116 -0
  169. dstack/_internal/core/backends/vultr/backend.py +16 -0
  170. dstack/_internal/core/backends/vultr/compute.py +167 -0
  171. dstack/_internal/core/backends/vultr/configurator.py +71 -0
  172. dstack/_internal/core/backends/vultr/models.py +34 -0
  173. dstack/_internal/core/compatibility/__init__.py +0 -0
  174. dstack/_internal/core/compatibility/events.py +13 -0
  175. dstack/_internal/core/compatibility/fleets.py +58 -0
  176. dstack/_internal/core/compatibility/gateways.py +39 -0
  177. dstack/_internal/core/compatibility/gpus.py +13 -0
  178. dstack/_internal/core/compatibility/logs.py +14 -0
  179. dstack/_internal/core/compatibility/runs.py +86 -0
  180. dstack/_internal/core/compatibility/volumes.py +37 -0
  181. dstack/_internal/core/consts.py +8 -0
  182. dstack/_internal/core/errors.py +160 -0
  183. dstack/_internal/core/models/__init__.py +0 -0
  184. dstack/_internal/core/models/auth.py +28 -0
  185. dstack/_internal/core/models/backends/__init__.py +0 -0
  186. dstack/_internal/core/models/backends/base.py +48 -0
  187. dstack/_internal/core/models/common.py +143 -0
  188. dstack/_internal/core/models/compute_groups.py +39 -0
  189. dstack/_internal/core/models/config.py +28 -0
  190. dstack/_internal/core/models/configurations.py +1123 -0
  191. dstack/_internal/core/models/envs.py +149 -0
  192. dstack/_internal/core/models/events.py +98 -0
  193. dstack/_internal/core/models/files.py +67 -0
  194. dstack/_internal/core/models/fleets.py +437 -0
  195. dstack/_internal/core/models/gateways.py +146 -0
  196. dstack/_internal/core/models/gpus.py +45 -0
  197. dstack/_internal/core/models/health.py +28 -0
  198. dstack/_internal/core/models/instances.py +346 -0
  199. dstack/_internal/core/models/logs.py +27 -0
  200. dstack/_internal/core/models/metrics.py +14 -0
  201. dstack/_internal/core/models/placement.py +27 -0
  202. dstack/_internal/core/models/profiles.py +431 -0
  203. dstack/_internal/core/models/projects.py +46 -0
  204. dstack/_internal/core/models/repos/__init__.py +34 -0
  205. dstack/_internal/core/models/repos/base.py +36 -0
  206. dstack/_internal/core/models/repos/local.py +96 -0
  207. dstack/_internal/core/models/repos/remote.py +341 -0
  208. dstack/_internal/core/models/repos/virtual.py +85 -0
  209. dstack/_internal/core/models/resources.py +424 -0
  210. dstack/_internal/core/models/routers.py +24 -0
  211. dstack/_internal/core/models/runs.py +618 -0
  212. dstack/_internal/core/models/secrets.py +16 -0
  213. dstack/_internal/core/models/server.py +7 -0
  214. dstack/_internal/core/models/services.py +76 -0
  215. dstack/_internal/core/models/unix.py +53 -0
  216. dstack/_internal/core/models/users.py +60 -0
  217. dstack/_internal/core/models/volumes.py +221 -0
  218. dstack/_internal/core/services/__init__.py +16 -0
  219. dstack/_internal/core/services/api_client.py +15 -0
  220. dstack/_internal/core/services/configs/__init__.py +116 -0
  221. dstack/_internal/core/services/diff.py +71 -0
  222. dstack/_internal/core/services/logs.py +58 -0
  223. dstack/_internal/core/services/profiles.py +46 -0
  224. dstack/_internal/core/services/repos.py +236 -0
  225. dstack/_internal/core/services/ssh/__init__.py +27 -0
  226. dstack/_internal/core/services/ssh/attach.py +241 -0
  227. dstack/_internal/core/services/ssh/client.py +113 -0
  228. dstack/_internal/core/services/ssh/key_manager.py +53 -0
  229. dstack/_internal/core/services/ssh/ports.py +89 -0
  230. dstack/_internal/core/services/ssh/tunnel.py +337 -0
  231. dstack/_internal/proxy/__init__.py +8 -0
  232. dstack/_internal/proxy/gateway/__init__.py +0 -0
  233. dstack/_internal/proxy/gateway/app.py +89 -0
  234. dstack/_internal/proxy/gateway/auth.py +26 -0
  235. dstack/_internal/proxy/gateway/const.py +7 -0
  236. dstack/_internal/proxy/gateway/deps.py +73 -0
  237. dstack/_internal/proxy/gateway/main.py +17 -0
  238. dstack/_internal/proxy/gateway/models.py +23 -0
  239. dstack/_internal/proxy/gateway/repo/__init__.py +0 -0
  240. dstack/_internal/proxy/gateway/repo/repo.py +121 -0
  241. dstack/_internal/proxy/gateway/repo/state_v1.py +164 -0
  242. dstack/_internal/proxy/gateway/resources/nginx/00-log-format.conf +11 -0
  243. dstack/_internal/proxy/gateway/resources/nginx/entrypoint.jinja2 +27 -0
  244. dstack/_internal/proxy/gateway/resources/nginx/router_workers.jinja2 +23 -0
  245. dstack/_internal/proxy/gateway/resources/nginx/service.jinja2 +105 -0
  246. dstack/_internal/proxy/gateway/routers/__init__.py +0 -0
  247. dstack/_internal/proxy/gateway/routers/auth.py +10 -0
  248. dstack/_internal/proxy/gateway/routers/config.py +28 -0
  249. dstack/_internal/proxy/gateway/routers/registry.py +124 -0
  250. dstack/_internal/proxy/gateway/routers/stats.py +18 -0
  251. dstack/_internal/proxy/gateway/schemas/__init__.py +0 -0
  252. dstack/_internal/proxy/gateway/schemas/common.py +5 -0
  253. dstack/_internal/proxy/gateway/schemas/config.py +9 -0
  254. dstack/_internal/proxy/gateway/schemas/registry.py +63 -0
  255. dstack/_internal/proxy/gateway/schemas/stats.py +15 -0
  256. dstack/_internal/proxy/gateway/services/__init__.py +0 -0
  257. dstack/_internal/proxy/gateway/services/model_routers/__init__.py +18 -0
  258. dstack/_internal/proxy/gateway/services/model_routers/base.py +91 -0
  259. dstack/_internal/proxy/gateway/services/model_routers/sglang.py +269 -0
  260. dstack/_internal/proxy/gateway/services/nginx.py +455 -0
  261. dstack/_internal/proxy/gateway/services/registry.py +426 -0
  262. dstack/_internal/proxy/gateway/services/server_client.py +95 -0
  263. dstack/_internal/proxy/gateway/services/stats.py +170 -0
  264. dstack/_internal/proxy/gateway/testing/__init__.py +0 -0
  265. dstack/_internal/proxy/gateway/testing/common.py +13 -0
  266. dstack/_internal/proxy/lib/__init__.py +0 -0
  267. dstack/_internal/proxy/lib/auth.py +7 -0
  268. dstack/_internal/proxy/lib/deps.py +106 -0
  269. dstack/_internal/proxy/lib/errors.py +14 -0
  270. dstack/_internal/proxy/lib/models.py +112 -0
  271. dstack/_internal/proxy/lib/repo.py +27 -0
  272. dstack/_internal/proxy/lib/routers/__init__.py +0 -0
  273. dstack/_internal/proxy/lib/routers/model_proxy.py +102 -0
  274. dstack/_internal/proxy/lib/schemas/__init__.py +0 -0
  275. dstack/_internal/proxy/lib/schemas/model_proxy.py +77 -0
  276. dstack/_internal/proxy/lib/services/__init__.py +0 -0
  277. dstack/_internal/proxy/lib/services/model_proxy/__init__.py +0 -0
  278. dstack/_internal/proxy/lib/services/model_proxy/clients/__init__.py +0 -0
  279. dstack/_internal/proxy/lib/services/model_proxy/clients/base.py +18 -0
  280. dstack/_internal/proxy/lib/services/model_proxy/clients/openai.py +67 -0
  281. dstack/_internal/proxy/lib/services/model_proxy/clients/tgi.py +208 -0
  282. dstack/_internal/proxy/lib/services/model_proxy/model_proxy.py +23 -0
  283. dstack/_internal/proxy/lib/services/service_connection.py +160 -0
  284. dstack/_internal/proxy/lib/testing/__init__.py +0 -0
  285. dstack/_internal/proxy/lib/testing/auth.py +11 -0
  286. dstack/_internal/proxy/lib/testing/common.py +51 -0
  287. dstack/_internal/server/__init__.py +0 -0
  288. dstack/_internal/server/alembic.ini +100 -0
  289. dstack/_internal/server/app.py +432 -0
  290. dstack/_internal/server/background/__init__.py +142 -0
  291. dstack/_internal/server/background/tasks/__init__.py +0 -0
  292. dstack/_internal/server/background/tasks/common.py +24 -0
  293. dstack/_internal/server/background/tasks/process_compute_groups.py +167 -0
  294. dstack/_internal/server/background/tasks/process_events.py +17 -0
  295. dstack/_internal/server/background/tasks/process_fleets.py +289 -0
  296. dstack/_internal/server/background/tasks/process_gateways.py +188 -0
  297. dstack/_internal/server/background/tasks/process_idle_volumes.py +145 -0
  298. dstack/_internal/server/background/tasks/process_instances.py +1186 -0
  299. dstack/_internal/server/background/tasks/process_metrics.py +172 -0
  300. dstack/_internal/server/background/tasks/process_placement_groups.py +104 -0
  301. dstack/_internal/server/background/tasks/process_probes.py +164 -0
  302. dstack/_internal/server/background/tasks/process_prometheus_metrics.py +150 -0
  303. dstack/_internal/server/background/tasks/process_running_jobs.py +1238 -0
  304. dstack/_internal/server/background/tasks/process_runs.py +842 -0
  305. dstack/_internal/server/background/tasks/process_submitted_jobs.py +1106 -0
  306. dstack/_internal/server/background/tasks/process_terminating_jobs.py +108 -0
  307. dstack/_internal/server/background/tasks/process_volumes.py +129 -0
  308. dstack/_internal/server/compatibility/__init__.py +0 -0
  309. dstack/_internal/server/compatibility/common.py +20 -0
  310. dstack/_internal/server/compatibility/gpus.py +22 -0
  311. dstack/_internal/server/db.py +127 -0
  312. dstack/_internal/server/deps.py +19 -0
  313. dstack/_internal/server/main.py +4 -0
  314. dstack/_internal/server/migrations/__init__.py +0 -0
  315. dstack/_internal/server/migrations/env.py +112 -0
  316. dstack/_internal/server/migrations/script.py.mako +28 -0
  317. dstack/_internal/server/migrations/versions/006512f572b4_add_projects_original_name.py +38 -0
  318. dstack/_internal/server/migrations/versions/065588ec72b8_add_vultr_to_backendtype_enum.py +81 -0
  319. dstack/_internal/server/migrations/versions/06e977bc61c7_add_usermodel_deleted_and_original_name.py +45 -0
  320. dstack/_internal/server/migrations/versions/0e33559e16ed_update_instancestatus.py +64 -0
  321. dstack/_internal/server/migrations/versions/112753bc17dd_remove_nullable_fields.py +50 -0
  322. dstack/_internal/server/migrations/versions/1338b788b612_reverse_job_instance_relationship.py +71 -0
  323. dstack/_internal/server/migrations/versions/14f2cb002fc2_add_jobmodel_removed_flag.py +44 -0
  324. dstack/_internal/server/migrations/versions/1a48dfe44a40_rework_termination_handling.py +42 -0
  325. dstack/_internal/server/migrations/versions/1aa9638ad963_added_email_index.py +31 -0
  326. dstack/_internal/server/migrations/versions/1e3fb39ef74b_add_remote_connection_details.py +26 -0
  327. dstack/_internal/server/migrations/versions/1e76fb0dde87_add_jobmodel_inactivity_secs.py +32 -0
  328. dstack/_internal/server/migrations/versions/20166748b60c_add_jobmodel_disconnected_at.py +100 -0
  329. dstack/_internal/server/migrations/versions/22d74df9897e_add_events_and_event_targets.py +99 -0
  330. dstack/_internal/server/migrations/versions/23e01c56279a_make_blob_nullable.py +32 -0
  331. dstack/_internal/server/migrations/versions/2498ab323443_add_fleetmodel_consolidation_attempt_.py +44 -0
  332. dstack/_internal/server/migrations/versions/252d3743b641_.py +40 -0
  333. dstack/_internal/server/migrations/versions/25479f540245_add_probes.py +43 -0
  334. dstack/_internal/server/migrations/versions/27d3e55759fa_add_pools.py +152 -0
  335. dstack/_internal/server/migrations/versions/29826f417010_remove_instancemodel_retry_policy.py +34 -0
  336. dstack/_internal/server/migrations/versions/29c08c6a8cb3_.py +36 -0
  337. dstack/_internal/server/migrations/versions/35e90e1b0d3e_add_rolling_deployment_fields.py +42 -0
  338. dstack/_internal/server/migrations/versions/35f732ee4cf5_add_projectmodel_is_public.py +39 -0
  339. dstack/_internal/server/migrations/versions/3cf77fb8bcf1_store_repo_clone_url.py +85 -0
  340. dstack/_internal/server/migrations/versions/3d7f6c2ec000_add_jobmodel_registered.py +28 -0
  341. dstack/_internal/server/migrations/versions/3dbdce90d0e0_fix_code_uq_constraint.py +33 -0
  342. dstack/_internal/server/migrations/versions/48ad3ecbaea2_do_not_delete_projects_and_runs.py +46 -0
  343. dstack/_internal/server/migrations/versions/4ae1a5b0e7f1_add_run_list_index.py +34 -0
  344. dstack/_internal/server/migrations/versions/4b4319398164_introduce_runs_processing.py +144 -0
  345. dstack/_internal/server/migrations/versions/50dd7ea98639_index_status_columns.py +55 -0
  346. dstack/_internal/server/migrations/versions/51d45659d574_add_instancemodel_blocks_fields.py +43 -0
  347. dstack/_internal/server/migrations/versions/54a77e19c64c_add_manager_project_role.py +67 -0
  348. dstack/_internal/server/migrations/versions/555138b1f77f_change_instancemodel_for_asynchronous_.py +61 -0
  349. dstack/_internal/server/migrations/versions/58aa5162dcc3_add_gatewaymodel_configuration.py +32 -0
  350. dstack/_internal/server/migrations/versions/5ad8debc8fe6_fixes_for_psql.py +329 -0
  351. dstack/_internal/server/migrations/versions/5ec538b70e71_replace_instansestatus.py +31 -0
  352. dstack/_internal/server/migrations/versions/5f1707c525d2_add_filearchivemodel.py +39 -0
  353. dstack/_internal/server/migrations/versions/5fd659afca82_add_ix_instances_fleet_id.py +31 -0
  354. dstack/_internal/server/migrations/versions/60e444118b6d_add_jobprometheusmetrics.py +40 -0
  355. dstack/_internal/server/migrations/versions/63c3f19cb184_add_jobterminationreason_inactivity_.py +83 -0
  356. dstack/_internal/server/migrations/versions/644b8a114187_add_secretmodel.py +49 -0
  357. dstack/_internal/server/migrations/versions/686fb8341ea5_add_user_emails.py +32 -0
  358. dstack/_internal/server/migrations/versions/6c1a9d6530ee_add_jobmodel_exit_status.py +26 -0
  359. dstack/_internal/server/migrations/versions/706e0acc3a7d_add_runmodel_desired_replica_counts.py +26 -0
  360. dstack/_internal/server/migrations/versions/710e5b3fac8f_add_encryption.py +54 -0
  361. dstack/_internal/server/migrations/versions/728b1488b1b4_add_instance_health.py +50 -0
  362. dstack/_internal/server/migrations/versions/74a1f55209bd_store_enums_as_strings.py +484 -0
  363. dstack/_internal/server/migrations/versions/7b24b1c8eba7_add_instancemodel_last_processed_at.py +68 -0
  364. dstack/_internal/server/migrations/versions/7ba3b59d7ca6_add_runmodel_resubmission_attempt.py +35 -0
  365. dstack/_internal/server/migrations/versions/7bc2586e8b9e_make_instancemodel_pool_id_optional.py +36 -0
  366. dstack/_internal/server/migrations/versions/7d1ec2b920ac_add_computegroupmodel.py +91 -0
  367. dstack/_internal/server/migrations/versions/803c7e9ed85d_add_jobmodel_job_runtime_data.py +32 -0
  368. dstack/_internal/server/migrations/versions/82b32a135ea2_.py +58 -0
  369. dstack/_internal/server/migrations/versions/866ec1d67184_replace_retrypolicy_limit_with_.py +93 -0
  370. dstack/_internal/server/migrations/versions/903c91e24634_add_instances_termination_reason_message.py +34 -0
  371. dstack/_internal/server/migrations/versions/91a12fff6c76_add_repocredsmodel.py +43 -0
  372. dstack/_internal/server/migrations/versions/91ac5e543037_extend_repos_creds_column.py +36 -0
  373. dstack/_internal/server/migrations/versions/98cd9c8b5927_add_volumemodel.py +73 -0
  374. dstack/_internal/server/migrations/versions/98d1b92988bc_add_jobterminationreason_terminated_due_.py +140 -0
  375. dstack/_internal/server/migrations/versions/99b4c8c954ea_add_termination_reason_message.py +71 -0
  376. dstack/_internal/server/migrations/versions/9eea6af28e10_added_fail_reason_for_instancemodel.py +36 -0
  377. dstack/_internal/server/migrations/versions/__init__.py +0 -0
  378. dstack/_internal/server/migrations/versions/a060e2440936_.py +206 -0
  379. dstack/_internal/server/migrations/versions/a751ef183f27_move_attachment_data_to_volumes_.py +34 -0
  380. dstack/_internal/server/migrations/versions/a7b46c073fa1_add_placementgroupmodel.py +58 -0
  381. dstack/_internal/server/migrations/versions/afbc600ff2b2_add_created_at_to_usermodel_and_.py +102 -0
  382. dstack/_internal/server/migrations/versions/b4d6ad60db08_add_instancemodel_unreachable.py +37 -0
  383. dstack/_internal/server/migrations/versions/b88d55c2a07d_replace_instancestatus_ready.py +21 -0
  384. dstack/_internal/server/migrations/versions/bc8ca4a505c6_store_backendtype_as_string.py +171 -0
  385. dstack/_internal/server/migrations/versions/bca2fdf130bf_add_runmodel_priority.py +34 -0
  386. dstack/_internal/server/migrations/versions/bfba43f6def2_.py +32 -0
  387. dstack/_internal/server/migrations/versions/c00090eaef21_support_fleets.py +108 -0
  388. dstack/_internal/server/migrations/versions/c154eece89da_add_fields_for_async_gateway_creation.py +74 -0
  389. dstack/_internal/server/migrations/versions/c20626d03cfb_add_jobmetricspoint.py +43 -0
  390. dstack/_internal/server/migrations/versions/c48df7985d57_add_instance_termination_retries.py +38 -0
  391. dstack/_internal/server/migrations/versions/c83d45f9a971_replace_string_with_text.py +150 -0
  392. dstack/_internal/server/migrations/versions/d0bb68e48b9f_add_project_owners_and_quotas.py +106 -0
  393. dstack/_internal/server/migrations/versions/d3e8af4786fa_gateway_compute_flag_deleted.py +34 -0
  394. dstack/_internal/server/migrations/versions/d4d9dc26cf58_add_ix_jobs_run_id.py +31 -0
  395. dstack/_internal/server/migrations/versions/d5863798bf41_add_volumemodel_last_job_processed_at.py +40 -0
  396. dstack/_internal/server/migrations/versions/d6b11105f659_add_usermodel_active.py +36 -0
  397. dstack/_internal/server/migrations/versions/da574e93fee0_add_jobmodel_volumes_detached_at.py +40 -0
  398. dstack/_internal/server/migrations/versions/dfffd6a1165c_add_fields_for_gateways_behind_alb.py +36 -0
  399. dstack/_internal/server/migrations/versions/e2d08cd1b8d9_add_jobmodel_fleet.py +41 -0
  400. dstack/_internal/server/migrations/versions/e3b7db07727f_add_gatewaycomputemodel_app_updated_at.py +61 -0
  401. dstack/_internal/server/migrations/versions/e6391ca6a264_separate_gateways_from_compute.py +72 -0
  402. dstack/_internal/server/migrations/versions/ea60480f82bb_add_membermodel_member_num.py +32 -0
  403. dstack/_internal/server/migrations/versions/ec02a26a256c_add_runmodel_next_triggered_at.py +38 -0
  404. dstack/_internal/server/migrations/versions/ed0ca30e13bb_migrate_instancestatus_provisioning.py +29 -0
  405. dstack/_internal/server/migrations/versions/fe72c4de8376_add_gateways.py +81 -0
  406. dstack/_internal/server/migrations/versions/ff1d94f65b08_user_ssh_key.py +34 -0
  407. dstack/_internal/server/migrations/versions/ffa99edd1988_add_jobterminationreason_max_duration_.py +81 -0
  408. dstack/_internal/server/models.py +930 -0
  409. dstack/_internal/server/routers/__init__.py +0 -0
  410. dstack/_internal/server/routers/auth.py +34 -0
  411. dstack/_internal/server/routers/backends.py +142 -0
  412. dstack/_internal/server/routers/events.py +60 -0
  413. dstack/_internal/server/routers/files.py +68 -0
  414. dstack/_internal/server/routers/fleets.py +202 -0
  415. dstack/_internal/server/routers/gateways.py +109 -0
  416. dstack/_internal/server/routers/gpus.py +32 -0
  417. dstack/_internal/server/routers/instances.py +77 -0
  418. dstack/_internal/server/routers/logs.py +34 -0
  419. dstack/_internal/server/routers/metrics.py +82 -0
  420. dstack/_internal/server/routers/projects.py +205 -0
  421. dstack/_internal/server/routers/prometheus.py +35 -0
  422. dstack/_internal/server/routers/repos.py +118 -0
  423. dstack/_internal/server/routers/runs.py +216 -0
  424. dstack/_internal/server/routers/secrets.py +86 -0
  425. dstack/_internal/server/routers/server.py +19 -0
  426. dstack/_internal/server/routers/users.py +158 -0
  427. dstack/_internal/server/routers/volumes.py +122 -0
  428. dstack/_internal/server/schemas/__init__.py +0 -0
  429. dstack/_internal/server/schemas/auth.py +83 -0
  430. dstack/_internal/server/schemas/backends.py +16 -0
  431. dstack/_internal/server/schemas/common.py +9 -0
  432. dstack/_internal/server/schemas/events.py +211 -0
  433. dstack/_internal/server/schemas/files.py +5 -0
  434. dstack/_internal/server/schemas/fleets.py +49 -0
  435. dstack/_internal/server/schemas/gateways.py +31 -0
  436. dstack/_internal/server/schemas/gpus.py +26 -0
  437. dstack/_internal/server/schemas/health/__init__.py +0 -0
  438. dstack/_internal/server/schemas/health/dcgm.py +56 -0
  439. dstack/_internal/server/schemas/instances.py +47 -0
  440. dstack/_internal/server/schemas/logs.py +17 -0
  441. dstack/_internal/server/schemas/projects.py +81 -0
  442. dstack/_internal/server/schemas/repos.py +24 -0
  443. dstack/_internal/server/schemas/runner.py +269 -0
  444. dstack/_internal/server/schemas/runs.py +66 -0
  445. dstack/_internal/server/schemas/secrets.py +16 -0
  446. dstack/_internal/server/schemas/users.py +72 -0
  447. dstack/_internal/server/schemas/volumes.py +29 -0
  448. dstack/_internal/server/security/__init__.py +0 -0
  449. dstack/_internal/server/security/permissions.py +251 -0
  450. dstack/_internal/server/services/__init__.py +0 -0
  451. dstack/_internal/server/services/auth.py +77 -0
  452. dstack/_internal/server/services/backends/__init__.py +404 -0
  453. dstack/_internal/server/services/backends/handlers.py +105 -0
  454. dstack/_internal/server/services/compute_groups.py +22 -0
  455. dstack/_internal/server/services/config.py +279 -0
  456. dstack/_internal/server/services/docker.py +162 -0
  457. dstack/_internal/server/services/encryption/__init__.py +102 -0
  458. dstack/_internal/server/services/encryption/keys/__init__.py +0 -0
  459. dstack/_internal/server/services/encryption/keys/aes.py +68 -0
  460. dstack/_internal/server/services/encryption/keys/base.py +19 -0
  461. dstack/_internal/server/services/encryption/keys/identity.py +28 -0
  462. dstack/_internal/server/services/events.py +477 -0
  463. dstack/_internal/server/services/files.py +91 -0
  464. dstack/_internal/server/services/fleets.py +1224 -0
  465. dstack/_internal/server/services/gateways/__init__.py +686 -0
  466. dstack/_internal/server/services/gateways/client.py +209 -0
  467. dstack/_internal/server/services/gateways/connection.py +139 -0
  468. dstack/_internal/server/services/gateways/pool.py +58 -0
  469. dstack/_internal/server/services/gpus.py +387 -0
  470. dstack/_internal/server/services/instances.py +731 -0
  471. dstack/_internal/server/services/jobs/__init__.py +840 -0
  472. dstack/_internal/server/services/jobs/configurators/__init__.py +0 -0
  473. dstack/_internal/server/services/jobs/configurators/base.py +469 -0
  474. dstack/_internal/server/services/jobs/configurators/dev.py +69 -0
  475. dstack/_internal/server/services/jobs/configurators/extensions/__init__.py +0 -0
  476. dstack/_internal/server/services/jobs/configurators/extensions/base.py +15 -0
  477. dstack/_internal/server/services/jobs/configurators/extensions/cursor.py +42 -0
  478. dstack/_internal/server/services/jobs/configurators/extensions/vscode.py +42 -0
  479. dstack/_internal/server/services/jobs/configurators/extensions/windsurf.py +43 -0
  480. dstack/_internal/server/services/jobs/configurators/service.py +28 -0
  481. dstack/_internal/server/services/jobs/configurators/task.py +39 -0
  482. dstack/_internal/server/services/locking.py +187 -0
  483. dstack/_internal/server/services/logging.py +29 -0
  484. dstack/_internal/server/services/logs/__init__.py +122 -0
  485. dstack/_internal/server/services/logs/aws.py +373 -0
  486. dstack/_internal/server/services/logs/base.py +47 -0
  487. dstack/_internal/server/services/logs/filelog.py +261 -0
  488. dstack/_internal/server/services/logs/fluentbit.py +329 -0
  489. dstack/_internal/server/services/logs/gcp.py +181 -0
  490. dstack/_internal/server/services/metrics.py +172 -0
  491. dstack/_internal/server/services/offers.py +249 -0
  492. dstack/_internal/server/services/permissions.py +37 -0
  493. dstack/_internal/server/services/placement.py +234 -0
  494. dstack/_internal/server/services/plugins.py +109 -0
  495. dstack/_internal/server/services/probes.py +10 -0
  496. dstack/_internal/server/services/projects.py +835 -0
  497. dstack/_internal/server/services/prometheus/__init__.py +0 -0
  498. dstack/_internal/server/services/prometheus/client_metrics.py +55 -0
  499. dstack/_internal/server/services/prometheus/custom_metrics.py +327 -0
  500. dstack/_internal/server/services/proxy/__init__.py +3 -0
  501. dstack/_internal/server/services/proxy/auth.py +12 -0
  502. dstack/_internal/server/services/proxy/deps.py +18 -0
  503. dstack/_internal/server/services/proxy/repo.py +189 -0
  504. dstack/_internal/server/services/proxy/routers/__init__.py +0 -0
  505. dstack/_internal/server/services/proxy/routers/service_proxy.py +49 -0
  506. dstack/_internal/server/services/proxy/services/__init__.py +0 -0
  507. dstack/_internal/server/services/proxy/services/service_proxy.py +135 -0
  508. dstack/_internal/server/services/repos.py +362 -0
  509. dstack/_internal/server/services/requirements/__init__.py +0 -0
  510. dstack/_internal/server/services/requirements/combine.py +260 -0
  511. dstack/_internal/server/services/resources.py +21 -0
  512. dstack/_internal/server/services/runner/__init__.py +0 -0
  513. dstack/_internal/server/services/runner/client.py +646 -0
  514. dstack/_internal/server/services/runner/ssh.py +128 -0
  515. dstack/_internal/server/services/runs/__init__.py +1026 -0
  516. dstack/_internal/server/services/runs/plan.py +703 -0
  517. dstack/_internal/server/services/runs/replicas.py +317 -0
  518. dstack/_internal/server/services/runs/spec.py +191 -0
  519. dstack/_internal/server/services/secrets.py +245 -0
  520. dstack/_internal/server/services/services/__init__.py +345 -0
  521. dstack/_internal/server/services/services/autoscalers.py +140 -0
  522. dstack/_internal/server/services/services/options.py +53 -0
  523. dstack/_internal/server/services/ssh.py +67 -0
  524. dstack/_internal/server/services/storage/__init__.py +37 -0
  525. dstack/_internal/server/services/storage/base.py +48 -0
  526. dstack/_internal/server/services/storage/gcs.py +66 -0
  527. dstack/_internal/server/services/storage/s3.py +69 -0
  528. dstack/_internal/server/services/users.py +461 -0
  529. dstack/_internal/server/services/volumes.py +496 -0
  530. dstack/_internal/server/settings.py +161 -0
  531. dstack/_internal/server/statics/00a6e1fb461ed2929fb9.png +0 -0
  532. dstack/_internal/server/statics/0cae4d9f0a36034984a7.png +0 -0
  533. dstack/_internal/server/statics/391de232cc0e30cae513.png +0 -0
  534. dstack/_internal/server/statics/4e0eead8c1a73689ef9d.svg +1 -0
  535. dstack/_internal/server/statics/544afa2f63428c2235b0.png +0 -0
  536. dstack/_internal/server/statics/54a4f50f74c6b9381530.svg +7 -0
  537. dstack/_internal/server/statics/68dd1360a7d2611e0132.svg +4 -0
  538. dstack/_internal/server/statics/69544b4c81973b54a66f.png +0 -0
  539. dstack/_internal/server/statics/77a8b02b17af19e39266.png +0 -0
  540. dstack/_internal/server/statics/83a93a8871c219104367.svg +9 -0
  541. dstack/_internal/server/statics/8f28bb8e9999e5e6a48b.svg +4 -0
  542. dstack/_internal/server/statics/9124086961ab8c366bc4.svg +9 -0
  543. dstack/_internal/server/statics/9a9ebaeb54b025dbac0a.svg +5 -0
  544. dstack/_internal/server/statics/a3428392dc534f3b15c4.svg +7 -0
  545. dstack/_internal/server/statics/ae22625574d69361f72c.png +0 -0
  546. dstack/_internal/server/statics/assets/android-chrome-144x144.png +0 -0
  547. dstack/_internal/server/statics/assets/android-chrome-192x192.png +0 -0
  548. dstack/_internal/server/statics/assets/android-chrome-256x256.png +0 -0
  549. dstack/_internal/server/statics/assets/android-chrome-36x36.png +0 -0
  550. dstack/_internal/server/statics/assets/android-chrome-384x384.png +0 -0
  551. dstack/_internal/server/statics/assets/android-chrome-48x48.png +0 -0
  552. dstack/_internal/server/statics/assets/android-chrome-512x512.png +0 -0
  553. dstack/_internal/server/statics/assets/android-chrome-72x72.png +0 -0
  554. dstack/_internal/server/statics/assets/android-chrome-96x96.png +0 -0
  555. dstack/_internal/server/statics/assets/apple-touch-icon-1024x1024.png +0 -0
  556. dstack/_internal/server/statics/assets/apple-touch-icon-114x114.png +0 -0
  557. dstack/_internal/server/statics/assets/apple-touch-icon-120x120.png +0 -0
  558. dstack/_internal/server/statics/assets/apple-touch-icon-144x144.png +0 -0
  559. dstack/_internal/server/statics/assets/apple-touch-icon-152x152.png +0 -0
  560. dstack/_internal/server/statics/assets/apple-touch-icon-167x167.png +0 -0
  561. dstack/_internal/server/statics/assets/apple-touch-icon-180x180.png +0 -0
  562. dstack/_internal/server/statics/assets/apple-touch-icon-57x57.png +0 -0
  563. dstack/_internal/server/statics/assets/apple-touch-icon-60x60.png +0 -0
  564. dstack/_internal/server/statics/assets/apple-touch-icon-72x72.png +0 -0
  565. dstack/_internal/server/statics/assets/apple-touch-icon-76x76.png +0 -0
  566. dstack/_internal/server/statics/assets/apple-touch-icon-precomposed.png +0 -0
  567. dstack/_internal/server/statics/assets/apple-touch-icon.png +0 -0
  568. dstack/_internal/server/statics/assets/apple-touch-startup-image-1125x2436.png +0 -0
  569. dstack/_internal/server/statics/assets/apple-touch-startup-image-1136x640.png +0 -0
  570. dstack/_internal/server/statics/assets/apple-touch-startup-image-1170x2532.png +0 -0
  571. dstack/_internal/server/statics/assets/apple-touch-startup-image-1179x2556.png +0 -0
  572. dstack/_internal/server/statics/assets/apple-touch-startup-image-1242x2208.png +0 -0
  573. dstack/_internal/server/statics/assets/apple-touch-startup-image-1242x2688.png +0 -0
  574. dstack/_internal/server/statics/assets/apple-touch-startup-image-1284x2778.png +0 -0
  575. dstack/_internal/server/statics/assets/apple-touch-startup-image-1290x2796.png +0 -0
  576. dstack/_internal/server/statics/assets/apple-touch-startup-image-1334x750.png +0 -0
  577. dstack/_internal/server/statics/assets/apple-touch-startup-image-1488x2266.png +0 -0
  578. dstack/_internal/server/statics/assets/apple-touch-startup-image-1536x2048.png +0 -0
  579. dstack/_internal/server/statics/assets/apple-touch-startup-image-1620x2160.png +0 -0
  580. dstack/_internal/server/statics/assets/apple-touch-startup-image-1640x2160.png +0 -0
  581. dstack/_internal/server/statics/assets/apple-touch-startup-image-1668x2224.png +0 -0
  582. dstack/_internal/server/statics/assets/apple-touch-startup-image-1668x2388.png +0 -0
  583. dstack/_internal/server/statics/assets/apple-touch-startup-image-1792x828.png +0 -0
  584. dstack/_internal/server/statics/assets/apple-touch-startup-image-2048x1536.png +0 -0
  585. dstack/_internal/server/statics/assets/apple-touch-startup-image-2048x2732.png +0 -0
  586. dstack/_internal/server/statics/assets/apple-touch-startup-image-2160x1620.png +0 -0
  587. dstack/_internal/server/statics/assets/apple-touch-startup-image-2160x1640.png +0 -0
  588. dstack/_internal/server/statics/assets/apple-touch-startup-image-2208x1242.png +0 -0
  589. dstack/_internal/server/statics/assets/apple-touch-startup-image-2224x1668.png +0 -0
  590. dstack/_internal/server/statics/assets/apple-touch-startup-image-2266x1488.png +0 -0
  591. dstack/_internal/server/statics/assets/apple-touch-startup-image-2388x1668.png +0 -0
  592. dstack/_internal/server/statics/assets/apple-touch-startup-image-2436x1125.png +0 -0
  593. dstack/_internal/server/statics/assets/apple-touch-startup-image-2532x1170.png +0 -0
  594. dstack/_internal/server/statics/assets/apple-touch-startup-image-2556x1179.png +0 -0
  595. dstack/_internal/server/statics/assets/apple-touch-startup-image-2688x1242.png +0 -0
  596. dstack/_internal/server/statics/assets/apple-touch-startup-image-2732x2048.png +0 -0
  597. dstack/_internal/server/statics/assets/apple-touch-startup-image-2778x1284.png +0 -0
  598. dstack/_internal/server/statics/assets/apple-touch-startup-image-2796x1290.png +0 -0
  599. dstack/_internal/server/statics/assets/apple-touch-startup-image-640x1136.png +0 -0
  600. dstack/_internal/server/statics/assets/apple-touch-startup-image-750x1334.png +0 -0
  601. dstack/_internal/server/statics/assets/apple-touch-startup-image-828x1792.png +0 -0
  602. dstack/_internal/server/statics/assets/browserconfig.xml +12 -0
  603. dstack/_internal/server/statics/assets/favicon-16x16.png +0 -0
  604. dstack/_internal/server/statics/assets/favicon-32x32.png +0 -0
  605. dstack/_internal/server/statics/assets/favicon-48x48.png +0 -0
  606. dstack/_internal/server/statics/assets/favicon.ico +0 -0
  607. dstack/{dashboard/statics/assets/manifest.json → _internal/server/statics/assets/manifest.webmanifest} +18 -9
  608. dstack/_internal/server/statics/assets/mstile-144x144.png +0 -0
  609. dstack/_internal/server/statics/assets/mstile-150x150.png +0 -0
  610. dstack/_internal/server/statics/assets/mstile-310x150.png +0 -0
  611. dstack/_internal/server/statics/assets/mstile-310x310.png +0 -0
  612. dstack/_internal/server/statics/assets/mstile-70x70.png +0 -0
  613. dstack/_internal/server/statics/assets/yandex-browser-50x50.png +0 -0
  614. dstack/_internal/server/statics/b7ae68f44193474fc578.png +0 -0
  615. dstack/_internal/server/statics/d2f008c75b2b5b191f3f.png +0 -0
  616. dstack/_internal/server/statics/d44c33e1b92e05c379fd.png +0 -0
  617. dstack/_internal/server/statics/dd43ff0552815179d7ab.png +0 -0
  618. dstack/_internal/server/statics/dd4e7166c0b9aac197d7.png +0 -0
  619. dstack/_internal/server/statics/e30b27916930d43d2271.png +0 -0
  620. dstack/_internal/server/statics/e467d7d60aae81ab198b.svg +6 -0
  621. dstack/_internal/server/statics/eb9b344b73818fe2b71a.png +0 -0
  622. dstack/_internal/server/statics/f517dd626eb964120de0.png +0 -0
  623. dstack/_internal/server/statics/f958aecddee5d8e3222c.png +0 -0
  624. dstack/_internal/server/statics/index.html +3 -0
  625. dstack/_internal/server/statics/logo-notext.svg +116 -0
  626. dstack/_internal/server/statics/main-2e6967bad9f29395eea6.css +3 -0
  627. dstack/_internal/server/statics/main-7dc0f6d20b8b41659acc.js +155547 -0
  628. dstack/_internal/server/statics/main-7dc0f6d20b8b41659acc.js.map +1 -0
  629. dstack/{dashboard → _internal/server}/statics/manifest.json +2 -2
  630. dstack/_internal/server/statics/static/media/entraID.d65d1f3e9486a8e56d24fc07b3230885.svg +9 -0
  631. dstack/_internal/server/statics/static/media/google.b194b06fafd0a52aeb566922160ea514.svg +1 -0
  632. dstack/{dashboard/statics/static/media/logo.f9d7170678f68f796e270698633770ec.svg → _internal/server/statics/static/media/logo.f602feeb138844eda97c8cb641461448.svg} +8 -6
  633. dstack/_internal/server/statics/static/media/okta.12f178e6873a1100965f2a4dbd18fcec.svg +2 -0
  634. dstack/_internal/server/statics/static/media/theme.3994c817bb7dda191c1c9640dee0bf42.svg +3 -0
  635. dstack/_internal/server/testing/__init__.py +0 -0
  636. dstack/_internal/server/testing/common.py +1220 -0
  637. dstack/_internal/server/testing/conf.py +53 -0
  638. dstack/_internal/server/testing/matchers.py +31 -0
  639. dstack/_internal/server/utils/__init__.py +0 -0
  640. dstack/_internal/server/utils/common.py +55 -0
  641. dstack/_internal/server/utils/logging.py +51 -0
  642. dstack/_internal/server/utils/provisioning.py +368 -0
  643. dstack/_internal/server/utils/routers.py +166 -0
  644. dstack/_internal/server/utils/sentry_utils.py +24 -0
  645. dstack/_internal/settings.py +49 -0
  646. dstack/_internal/utils/__init__.py +0 -0
  647. dstack/_internal/utils/common.py +318 -0
  648. dstack/_internal/utils/cron.py +5 -0
  649. dstack/_internal/utils/crypto.py +40 -0
  650. dstack/_internal/utils/env.py +88 -0
  651. dstack/_internal/utils/event_loop.py +30 -0
  652. dstack/_internal/utils/files.py +69 -0
  653. dstack/_internal/utils/gpu.py +59 -0
  654. dstack/_internal/utils/hash.py +31 -0
  655. dstack/_internal/utils/interpolator.py +91 -0
  656. dstack/_internal/utils/json_schema.py +11 -0
  657. dstack/_internal/utils/json_utils.py +54 -0
  658. dstack/_internal/utils/logging.py +5 -0
  659. dstack/_internal/utils/nested_list.py +47 -0
  660. dstack/_internal/utils/network.py +50 -0
  661. dstack/_internal/utils/path.py +57 -0
  662. dstack/_internal/utils/random_names.py +258 -0
  663. dstack/_internal/utils/ssh.py +346 -0
  664. dstack/_internal/utils/tags.py +42 -0
  665. dstack/_internal/utils/typing.py +14 -0
  666. dstack/_internal/utils/version.py +22 -0
  667. dstack/api/__init__.py +46 -0
  668. dstack/api/_public/__init__.py +96 -0
  669. dstack/api/_public/backends.py +42 -0
  670. dstack/api/_public/common.py +5 -0
  671. dstack/api/_public/repos.py +202 -0
  672. dstack/api/_public/runs.py +714 -0
  673. dstack/api/server/__init__.py +206 -0
  674. dstack/api/server/_auth.py +30 -0
  675. dstack/api/server/_backends.py +38 -0
  676. dstack/api/server/_events.py +64 -0
  677. dstack/api/server/_files.py +18 -0
  678. dstack/api/server/_fleets.py +82 -0
  679. dstack/api/server/_gateways.py +54 -0
  680. dstack/api/server/_gpus.py +27 -0
  681. dstack/api/server/_group.py +22 -0
  682. dstack/api/server/_logs.py +15 -0
  683. dstack/api/server/_metrics.py +23 -0
  684. dstack/api/server/_projects.py +124 -0
  685. dstack/api/server/_repos.py +64 -0
  686. dstack/api/server/_runs.py +102 -0
  687. dstack/api/server/_secrets.py +36 -0
  688. dstack/api/server/_users.py +82 -0
  689. dstack/api/server/_volumes.py +39 -0
  690. dstack/api/server/utils.py +34 -0
  691. dstack/api/utils.py +105 -0
  692. dstack/core/__init__.py +0 -0
  693. dstack/plugins/__init__.py +8 -0
  694. dstack/plugins/_base.py +72 -0
  695. dstack/plugins/_models.py +8 -0
  696. dstack/plugins/_utils.py +19 -0
  697. dstack/plugins/builtin/__init__.py +0 -0
  698. dstack/plugins/builtin/rest_plugin/__init__.py +18 -0
  699. dstack/plugins/builtin/rest_plugin/_models.py +48 -0
  700. dstack/plugins/builtin/rest_plugin/_plugin.py +147 -0
  701. dstack/version.py +3 -1
  702. dstack-0.20.7.dist-info/METADATA +519 -0
  703. dstack-0.20.7.dist-info/RECORD +720 -0
  704. {dstack-0.0.9.dist-info → dstack-0.20.7.dist-info}/WHEEL +1 -2
  705. dstack-0.20.7.dist-info/entry_points.txt +2 -0
  706. dstack-0.20.7.dist-info/licenses/LICENSE.md +353 -0
  707. dstack/aws/__init__.py +0 -180
  708. dstack/aws/artifacts.py +0 -111
  709. dstack/aws/config.py +0 -40
  710. dstack/aws/jobs.py +0 -245
  711. dstack/aws/logs.py +0 -186
  712. dstack/aws/repos.py +0 -137
  713. dstack/aws/run_names.py +0 -17
  714. dstack/aws/runners.py +0 -693
  715. dstack/aws/runs.py +0 -79
  716. dstack/aws/secrets.py +0 -99
  717. dstack/aws/tags.py +0 -138
  718. dstack/backend.py +0 -299
  719. dstack/cli/app.py +0 -41
  720. dstack/cli/artifacts.py +0 -87
  721. dstack/cli/common.py +0 -57
  722. dstack/cli/config.py +0 -194
  723. dstack/cli/dashboard.py +0 -26
  724. dstack/cli/delete.py +0 -49
  725. dstack/cli/init.py +0 -33
  726. dstack/cli/logs.py +0 -87
  727. dstack/cli/main.py +0 -81
  728. dstack/cli/restart.py +0 -43
  729. dstack/cli/run.py +0 -223
  730. dstack/cli/schema.py +0 -46
  731. dstack/cli/secrets.py +0 -97
  732. dstack/cli/status.py +0 -140
  733. dstack/cli/stop.py +0 -53
  734. dstack/cli/tags.py +0 -100
  735. dstack/config.py +0 -80
  736. dstack/dashboard/artifacts.py +0 -26
  737. dstack/dashboard/logs.py +0 -73
  738. dstack/dashboard/main.py +0 -45
  739. dstack/dashboard/repos.py +0 -41
  740. dstack/dashboard/runs.py +0 -140
  741. dstack/dashboard/secrets.py +0 -53
  742. dstack/dashboard/statics/4d6a4e032505c1efd23c.png +0 -0
  743. dstack/dashboard/statics/7e018c3e5566d7c349a8.png +0 -0
  744. dstack/dashboard/statics/assets/android-chrome-144x144.png +0 -0
  745. dstack/dashboard/statics/assets/android-chrome-192x192.png +0 -0
  746. dstack/dashboard/statics/assets/android-chrome-256x256.png +0 -0
  747. dstack/dashboard/statics/assets/android-chrome-36x36.png +0 -0
  748. dstack/dashboard/statics/assets/android-chrome-384x384.png +0 -0
  749. dstack/dashboard/statics/assets/android-chrome-48x48.png +0 -0
  750. dstack/dashboard/statics/assets/android-chrome-512x512.png +0 -0
  751. dstack/dashboard/statics/assets/android-chrome-72x72.png +0 -0
  752. dstack/dashboard/statics/assets/android-chrome-96x96.png +0 -0
  753. dstack/dashboard/statics/assets/apple-touch-icon-1024x1024.png +0 -0
  754. dstack/dashboard/statics/assets/apple-touch-icon-114x114.png +0 -0
  755. dstack/dashboard/statics/assets/apple-touch-icon-120x120.png +0 -0
  756. dstack/dashboard/statics/assets/apple-touch-icon-144x144.png +0 -0
  757. dstack/dashboard/statics/assets/apple-touch-icon-152x152.png +0 -0
  758. dstack/dashboard/statics/assets/apple-touch-icon-167x167.png +0 -0
  759. dstack/dashboard/statics/assets/apple-touch-icon-180x180.png +0 -0
  760. dstack/dashboard/statics/assets/apple-touch-icon-57x57.png +0 -0
  761. dstack/dashboard/statics/assets/apple-touch-icon-60x60.png +0 -0
  762. dstack/dashboard/statics/assets/apple-touch-icon-72x72.png +0 -0
  763. dstack/dashboard/statics/assets/apple-touch-icon-76x76.png +0 -0
  764. dstack/dashboard/statics/assets/apple-touch-icon-precomposed.png +0 -0
  765. dstack/dashboard/statics/assets/apple-touch-icon.png +0 -0
  766. dstack/dashboard/statics/assets/apple-touch-startup-image-1125x2436.png +0 -0
  767. dstack/dashboard/statics/assets/apple-touch-startup-image-1136x640.png +0 -0
  768. dstack/dashboard/statics/assets/apple-touch-startup-image-1242x2208.png +0 -0
  769. dstack/dashboard/statics/assets/apple-touch-startup-image-1242x2688.png +0 -0
  770. dstack/dashboard/statics/assets/apple-touch-startup-image-1334x750.png +0 -0
  771. dstack/dashboard/statics/assets/apple-touch-startup-image-1536x2048.png +0 -0
  772. dstack/dashboard/statics/assets/apple-touch-startup-image-1620x2160.png +0 -0
  773. dstack/dashboard/statics/assets/apple-touch-startup-image-1668x2224.png +0 -0
  774. dstack/dashboard/statics/assets/apple-touch-startup-image-1668x2388.png +0 -0
  775. dstack/dashboard/statics/assets/apple-touch-startup-image-1792x828.png +0 -0
  776. dstack/dashboard/statics/assets/apple-touch-startup-image-2048x1536.png +0 -0
  777. dstack/dashboard/statics/assets/apple-touch-startup-image-2048x2732.png +0 -0
  778. dstack/dashboard/statics/assets/apple-touch-startup-image-2160x1620.png +0 -0
  779. dstack/dashboard/statics/assets/apple-touch-startup-image-2208x1242.png +0 -0
  780. dstack/dashboard/statics/assets/apple-touch-startup-image-2224x1668.png +0 -0
  781. dstack/dashboard/statics/assets/apple-touch-startup-image-2388x1668.png +0 -0
  782. dstack/dashboard/statics/assets/apple-touch-startup-image-2436x1125.png +0 -0
  783. dstack/dashboard/statics/assets/apple-touch-startup-image-2688x1242.png +0 -0
  784. dstack/dashboard/statics/assets/apple-touch-startup-image-2732x2048.png +0 -0
  785. dstack/dashboard/statics/assets/apple-touch-startup-image-640x1136.png +0 -0
  786. dstack/dashboard/statics/assets/apple-touch-startup-image-750x1334.png +0 -0
  787. dstack/dashboard/statics/assets/apple-touch-startup-image-828x1792.png +0 -0
  788. dstack/dashboard/statics/assets/browserconfig.xml +0 -15
  789. dstack/dashboard/statics/assets/coast-228x228.png +0 -0
  790. dstack/dashboard/statics/assets/favicon-16x16.png +0 -0
  791. dstack/dashboard/statics/assets/favicon-32x32.png +0 -0
  792. dstack/dashboard/statics/assets/favicon-48x48.png +0 -0
  793. dstack/dashboard/statics/assets/favicon.ico +0 -0
  794. dstack/dashboard/statics/assets/firefox_app_128x128.png +0 -0
  795. dstack/dashboard/statics/assets/firefox_app_512x512.png +0 -0
  796. dstack/dashboard/statics/assets/firefox_app_60x60.png +0 -0
  797. dstack/dashboard/statics/assets/manifest.webapp +0 -14
  798. dstack/dashboard/statics/assets/mstile-144x144.png +0 -0
  799. dstack/dashboard/statics/assets/mstile-150x150.png +0 -0
  800. dstack/dashboard/statics/assets/mstile-310x150.png +0 -0
  801. dstack/dashboard/statics/assets/mstile-310x310.png +0 -0
  802. dstack/dashboard/statics/assets/mstile-70x70.png +0 -0
  803. dstack/dashboard/statics/assets/yandex-browser-50x50.png +0 -0
  804. dstack/dashboard/statics/d0f71e48806e25d72553.png +0 -0
  805. dstack/dashboard/statics/index.html +0 -7
  806. dstack/dashboard/statics/main-1d87e34eb0454da8ebb4.js +0 -3
  807. dstack/dashboard/statics/main-1d87e34eb0454da8ebb4.js.LICENSE.txt +0 -102
  808. dstack/dashboard/statics/main-1d87e34eb0454da8ebb4.js.map +0 -1
  809. dstack/dashboard/statics/main.css +0 -5058
  810. dstack/dashboard/statics/splash_thumbnail.png +0 -0
  811. dstack/dashboard/statics/static/media/check.3f68ffc787a15c0476793a6d18ecb71a.svg +0 -3
  812. dstack/dashboard/statics/static/media/chevron-down.bfd8f22c4a5db4d443e76bca3b02f334.svg +0 -3
  813. dstack/dashboard/statics/static/media/chevron-up.bade0c5d82d741cead615813264140c9.svg +0 -3
  814. dstack/dashboard/statics/static/media/clock.583b744f29b9d143718a55e7c35fe38e.svg +0 -3
  815. dstack/dashboard/statics/static/media/close.a8bb9e47361b03a3b5084dad676ba1da.svg +0 -3
  816. dstack/dashboard/statics/static/media/content-copy.73f5f2a175094757758e315243a4111e.svg +0 -3
  817. dstack/dashboard/statics/static/media/delete-outline.6a8abf4e4f9cb777781967efd56efe9b.svg +0 -3
  818. dstack/dashboard/statics/static/media/dots-vertical.82fc618192e0c7dc4d615ff93269246a.svg +0 -3
  819. dstack/dashboard/statics/static/media/earth.1ad57c7f59f4be5c8bb2fa00439c3149.svg +0 -3
  820. dstack/dashboard/statics/static/media/email.320bc3af24a5f1bb41ebd85f66a5dd70.svg +0 -3
  821. dstack/dashboard/statics/static/media/external-link.99b88e699c15afb820a1779d9a2261ed.svg +0 -3
  822. dstack/dashboard/statics/static/media/eye-off-outline.5b4afb7ad624a44dd307518ff93d1faa.svg +0 -3
  823. dstack/dashboard/statics/static/media/eye-outline.ca41708feaaed1edb15c5fff021fbafe.svg +0 -3
  824. dstack/dashboard/statics/static/media/file-download-outline.3634b41923ba79b297ff294ef898661c.svg +0 -3
  825. dstack/dashboard/statics/static/media/folder-outline.33378387af61821dd1207e4b2d061a07.svg +0 -3
  826. dstack/dashboard/statics/static/media/github-circle.1bb85d171c31a3c2eebad07319377171.svg +0 -3
  827. dstack/dashboard/statics/static/media/infinity.915f92939afc0a37f94adba211ceb172.svg +0 -3
  828. dstack/dashboard/statics/static/media/layers.b4b02cea267a617d7aa44c2719250c89.svg +0 -3
  829. dstack/dashboard/statics/static/media/linkedin.1c52fae553eee54397f0e63a79455a5e.svg +0 -3
  830. dstack/dashboard/statics/static/media/loading.e466be7b2c1f0ac9e7e51ca929d0e37d.svg +0 -3
  831. dstack/dashboard/statics/static/media/lock.4a4c7768d0fa60c716609ddc483470ef.svg +0 -3
  832. dstack/dashboard/statics/static/media/magnify.0c803314d039d21f3cb1504ccd1437a4.svg +0 -3
  833. dstack/dashboard/statics/static/media/mark.3f68ffc787a15c0476793a6d18ecb71a.svg +0 -3
  834. dstack/dashboard/statics/static/media/menu-close.3ee84714181017c6ff837830297c8437.svg +0 -3
  835. dstack/dashboard/statics/static/media/menu.922f81e0972fbcbb5adcd8def20c86a3.svg +0 -3
  836. dstack/dashboard/statics/static/media/pencil.f706a3b9dcbff4959a91bf72e1e6324f.svg +0 -3
  837. dstack/dashboard/statics/static/media/refresh.a80edb948e98b322cd73b67814a57a48.svg +0 -3
  838. dstack/dashboard/statics/static/media/shape-plus.63b093c7f4b44c3def774f30fcfbceca.svg +0 -3
  839. dstack/dashboard/statics/static/media/slack.ec2fca99c6b944950ac65404ddd26880.svg +0 -4
  840. dstack/dashboard/statics/static/media/small-logo.b9cc8d09f646a553e65fa336dafd8b10.svg +0 -116
  841. dstack/dashboard/statics/static/media/source-branch.b8d22cfc42a7bed81f0fc08130818e85.svg +0 -3
  842. dstack/dashboard/statics/static/media/source-commit.be2bb53c081b9b6836adffccc0b8d3e6.svg +0 -3
  843. dstack/dashboard/statics/static/media/stop.11488ff1437ad929476be8924a3b7075.svg +0 -3
  844. dstack/dashboard/statics/static/media/tag-minus.15680a815b0b8d027e973c84832c05e6.svg +0 -3
  845. dstack/dashboard/statics/static/media/tag-outline.19b0bf86a8afd7d6d9c716e9a91d94ca.svg +0 -3
  846. dstack/dashboard/statics/static/media/twitter.4af18861c84a2f3044c7546b55d5739c.svg +0 -3
  847. dstack/dashboard/tags.py +0 -119
  848. dstack/jobs.py +0 -255
  849. dstack/providers/__init__.py +0 -316
  850. dstack/providers/_python/main.py +0 -88
  851. dstack/providers/_tensorboard/main.py +0 -93
  852. dstack/providers/_torchrun/main.py +0 -121
  853. dstack/providers/bash/main.py +0 -90
  854. dstack/providers/code/main.py +0 -95
  855. dstack/providers/docker/main.py +0 -79
  856. dstack/providers/lab/main.py +0 -95
  857. dstack/providers/notebook/main.py +0 -90
  858. dstack/random_name.py +0 -29
  859. dstack/repo.py +0 -135
  860. dstack/runners.py +0 -35
  861. dstack/util.py +0 -15
  862. dstack-0.0.9.dist-info/METADATA +0 -176
  863. dstack-0.0.9.dist-info/RECORD +0 -179
  864. dstack-0.0.9.dist-info/entry_points.txt +0 -3
  865. dstack-0.0.9.dist-info/top_level.txt +0 -2
  866. tests/test_config.py +0 -70
  867. /dstack/{cli → _internal}/__init__.py +0 -0
  868. /dstack/{dashboard → _internal/cli}/__init__.py +0 -0
  869. /dstack/{providers/_python → _internal/cli/models}/__init__.py +0 -0
  870. /dstack/{providers/_tensorboard → _internal/cli/services}/__init__.py +0 -0
  871. /dstack/{providers/_torchrun → _internal/cli/utils}/__init__.py +0 -0
  872. /dstack/{providers/bash → _internal/core}/__init__.py +0 -0
  873. /dstack/{providers/code → _internal/core/backends}/__init__.py +0 -0
  874. /dstack/{providers/docker → _internal/core/backends/aws}/__init__.py +0 -0
  875. /dstack/{providers/lab → _internal/core/backends/azure}/__init__.py +0 -0
  876. /dstack/{providers/notebook → _internal/core/backends/base}/__init__.py +0 -0
  877. {tests → dstack/_internal/core/backends/cloudrift}/__init__.py +0 -0
  878. /dstack/{dashboard → _internal/server}/statics/assets/yandex-browser-manifest.json +0 -0
  879. /dstack/{dashboard → _internal/server}/statics/robots.txt +0 -0
@@ -0,0 +1,882 @@
1
+ import argparse
2
+ import json
3
+ import os
4
+ import shlex
5
+ import shutil
6
+ import subprocess
7
+ import sys
8
+ import time
9
+ from pathlib import Path
10
+ from typing import Dict, List, Optional, Set, TypeVar
11
+
12
+ import gpuhunt
13
+ from pydantic import parse_obj_as
14
+
15
+ from dstack._internal.cli.services.args import port_mapping
16
+ from dstack._internal.cli.services.configurators.base import (
17
+ ApplyEnvVarsConfiguratorMixin,
18
+ BaseApplyConfigurator,
19
+ )
20
+ from dstack._internal.cli.services.profile import apply_profile_args, register_profile_args
21
+ from dstack._internal.cli.services.repos import (
22
+ get_repo_from_dir,
23
+ get_repo_from_url,
24
+ init_default_virtual_repo,
25
+ is_git_repo_url,
26
+ register_init_repo_args,
27
+ )
28
+ from dstack._internal.cli.services.resources import apply_resources_args, register_resources_args
29
+ from dstack._internal.cli.utils.common import confirm_ask, console
30
+ from dstack._internal.cli.utils.rich import MultiItemStatus
31
+ from dstack._internal.cli.utils.run import get_runs_table, print_run_plan
32
+ from dstack._internal.core.errors import (
33
+ CLIError,
34
+ ConfigurationError,
35
+ ResourceNotExistsError,
36
+ ServerClientError,
37
+ )
38
+ from dstack._internal.core.models.common import ApplyAction, RegistryAuth
39
+ from dstack._internal.core.models.configurations import (
40
+ AnyRunConfiguration,
41
+ ApplyConfigurationType,
42
+ ConfigurationWithCommandsParams,
43
+ ConfigurationWithPortsParams,
44
+ DevEnvironmentConfiguration,
45
+ PortMapping,
46
+ RunConfigurationType,
47
+ ServiceConfiguration,
48
+ TaskConfiguration,
49
+ )
50
+ from dstack._internal.core.models.repos import RepoHeadWithCreds
51
+ from dstack._internal.core.models.repos.remote import RemoteRepo, RemoteRepoCreds
52
+ from dstack._internal.core.models.resources import CPUSpec
53
+ from dstack._internal.core.models.runs import JobStatus, JobSubmission, RunSpec, RunStatus
54
+ from dstack._internal.core.services.diff import diff_models
55
+ from dstack._internal.core.services.repos import (
56
+ InvalidRepoCredentialsError,
57
+ get_repo_creds_and_default_branch,
58
+ )
59
+ from dstack._internal.utils.common import local_time
60
+ from dstack._internal.utils.interpolator import InterpolatorError, VariablesInterpolator
61
+ from dstack._internal.utils.logging import get_logger
62
+ from dstack._internal.utils.nested_list import NestedList, NestedListItem
63
+ from dstack._internal.utils.path import is_absolute_posix_path
64
+ from dstack.api._public.runs import Run
65
+ from dstack.api.utils import load_profile
66
+
67
+ _KNOWN_AMD_GPUS = {gpu.name.lower() for gpu in gpuhunt.KNOWN_AMD_GPUS}
68
+ _KNOWN_NVIDIA_GPUS = {gpu.name.lower() for gpu in gpuhunt.KNOWN_NVIDIA_GPUS}
69
+ _KNOWN_TPU_VERSIONS = {gpu.name.lower() for gpu in gpuhunt.KNOWN_TPUS}
70
+ _KNOWN_TENSTORRENT_GPUS = {gpu.name.lower() for gpu in gpuhunt.KNOWN_TENSTORRENT_ACCELERATORS}
71
+ _BIND_ADDRESS_ARG = "bind_address"
72
+
73
+ logger = get_logger(__name__)
74
+
75
+ RunConfigurationT = TypeVar("RunConfigurationT", bound=AnyRunConfiguration)
76
+
77
+
78
+ class BaseRunConfigurator(
79
+ ApplyEnvVarsConfiguratorMixin,
80
+ BaseApplyConfigurator[RunConfigurationT],
81
+ ):
82
+ def apply_configuration(
83
+ self,
84
+ conf: RunConfigurationT,
85
+ configuration_path: str,
86
+ command_args: argparse.Namespace,
87
+ configurator_args: argparse.Namespace,
88
+ ):
89
+ if configurator_args.repo and configurator_args.no_repo:
90
+ raise CLIError("Either --repo or --no-repo can be specified")
91
+
92
+ self.apply_args(conf, configurator_args)
93
+ self.validate_gpu_vendor_and_image(conf)
94
+ self.validate_cpu_arch_and_image(conf)
95
+
96
+ if conf.working_dir is not None and not is_absolute_posix_path(conf.working_dir):
97
+ raise ConfigurationError("working_dir must be absolute")
98
+
99
+ repo = self.get_repo(conf, configuration_path, configurator_args)
100
+ if repo is None:
101
+ repo = init_default_virtual_repo(api=self.api)
102
+ profile = load_profile(Path.cwd(), configurator_args.profile)
103
+ with console.status("Getting apply plan..."):
104
+ run_plan = self.api.runs.get_run_plan(
105
+ configuration=conf,
106
+ repo=repo,
107
+ configuration_path=configuration_path,
108
+ profile=profile,
109
+ ssh_identity_file=configurator_args.ssh_identity_file,
110
+ )
111
+
112
+ no_fleets = False
113
+ if len(run_plan.job_plans[0].offers) == 0:
114
+ if len(self.api.client.fleets.list(self.api.project)) == 0:
115
+ no_fleets = True
116
+
117
+ print_run_plan(run_plan, max_offers=configurator_args.max_offers, no_fleets=no_fleets)
118
+
119
+ confirm_message = "Submit a new run?"
120
+ if conf.name:
121
+ confirm_message = f"Submit the run [code]{conf.name}[/]?"
122
+ stop_run_name = None
123
+ if run_plan.current_resource is not None:
124
+ diff = render_run_spec_diff(
125
+ run_plan.get_effective_run_spec(),
126
+ run_plan.current_resource.run_spec,
127
+ )
128
+ if run_plan.action == ApplyAction.UPDATE and diff is not None:
129
+ console.print(
130
+ f"Active run [code]{conf.name}[/] already exists."
131
+ f" Detected changes that [code]can[/] be updated in-place:\n{diff}"
132
+ )
133
+ confirm_message = "Update the run?"
134
+ elif run_plan.action == ApplyAction.UPDATE and diff is None:
135
+ stop_run_name = run_plan.current_resource.run_spec.run_name
136
+ console.print(
137
+ f"Active run [code]{conf.name}[/] already exists. Detected no changes."
138
+ )
139
+ if command_args.yes and not command_args.force:
140
+ console.print("Use --force to apply anyway.")
141
+ return
142
+ confirm_message = "Stop and override the run?"
143
+ elif not run_plan.current_resource.status.is_finished():
144
+ stop_run_name = run_plan.current_resource.run_spec.run_name
145
+ console.print(
146
+ f"Active run [code]{conf.name}[/] already exists."
147
+ f" Detected changes that [error]cannot[/] be updated in-place:\n{diff}"
148
+ )
149
+ confirm_message = "Stop and override the run?"
150
+
151
+ if not command_args.yes and not confirm_ask(confirm_message):
152
+ console.print("\nExiting...")
153
+ return
154
+
155
+ if stop_run_name is not None:
156
+ with console.status("Stopping run..."):
157
+ self.api.client.runs.stop(self.api.project, [stop_run_name], abort=False)
158
+ while True:
159
+ run = self.api.runs.get(stop_run_name)
160
+ if run is None or run.status.is_finished():
161
+ break
162
+ time.sleep(1)
163
+
164
+ try:
165
+ with console.status("Applying plan..."):
166
+ run = self.api.runs.apply_plan(
167
+ run_plan=run_plan, repo=repo, reserve_ports=not command_args.detach
168
+ )
169
+ except ServerClientError as e:
170
+ raise CLIError(e.msg)
171
+
172
+ if command_args.detach:
173
+ detach_message = f"Run [code]{run.name}[/] submitted, detaching..."
174
+ if run_plan.action == ApplyAction.UPDATE:
175
+ detach_message = f"Run [code]{run.name}[/] updated, detaching..."
176
+ console.print(detach_message)
177
+ return
178
+
179
+ abort_at_exit = False
180
+ try:
181
+ # We can attach to run multiple times if it goes from running to pending (retried).
182
+ while True:
183
+ with MultiItemStatus(f"Launching [code]{run.name}[/]...", console=console) as live:
184
+ while not _is_ready_to_attach(run):
185
+ table = get_runs_table([run])
186
+ live.update(table)
187
+ time.sleep(5)
188
+ run.refresh()
189
+
190
+ console.print(
191
+ get_runs_table(
192
+ [run],
193
+ verbose=run.status == RunStatus.FAILED,
194
+ format_date=local_time,
195
+ )
196
+ )
197
+
198
+ console.print(
199
+ f"\n[code]{run.name}[/] provisioning completed [secondary]({run.status.value})[/]"
200
+ )
201
+
202
+ current_job_submission = run._run.latest_job_submission
203
+ if run.status in (RunStatus.RUNNING, RunStatus.DONE):
204
+ _print_service_urls(run)
205
+ bind_address: Optional[str] = getattr(
206
+ configurator_args, _BIND_ADDRESS_ARG, None
207
+ )
208
+ try:
209
+ if run.attach(bind_address=bind_address):
210
+ for entry in run.logs():
211
+ sys.stdout.buffer.write(entry)
212
+ sys.stdout.buffer.flush()
213
+ else:
214
+ console.print("[error]Failed to attach, exiting...[/]")
215
+ exit(1)
216
+ finally:
217
+ run.detach()
218
+
219
+ # After reading the logs, the run may not be marked as finished immediately.
220
+ # Give the run some time to transition to a finished state before exiting.
221
+ reattach = False
222
+ for _ in range(30):
223
+ run.refresh()
224
+ if _run_resubmitted(run, current_job_submission):
225
+ # The run was resubmitted
226
+ reattach = True
227
+ break
228
+ if run.status.is_finished():
229
+ print_finished_message(run)
230
+ exit(get_run_exit_code(run))
231
+ time.sleep(1)
232
+ if not reattach:
233
+ console.print(
234
+ "[error]Lost run connection. Timed out waiting for run final status."
235
+ " Check `dstack ps` to see if it's done or failed."
236
+ )
237
+ exit(1)
238
+ except KeyboardInterrupt:
239
+ try:
240
+ if command_args.yes or not confirm_ask(
241
+ f"\nStop the run [code]{run.name}[/] before detaching?"
242
+ ):
243
+ console.print("Detached")
244
+ abort_at_exit = False
245
+ return
246
+ # Gently stop the run and wait for it to finish
247
+ with console.status("Stopping..."):
248
+ run.stop(abort=False)
249
+ while not run.status.is_finished():
250
+ time.sleep(2)
251
+ run.refresh()
252
+ console.print("Stopped")
253
+ except KeyboardInterrupt:
254
+ abort_at_exit = True
255
+ finally:
256
+ run.detach()
257
+ if abort_at_exit:
258
+ with console.status("Aborting..."):
259
+ run.stop(abort=True)
260
+ console.print("[error]Aborted[/]")
261
+
262
+ def delete_configuration(
263
+ self,
264
+ conf: RunConfigurationT,
265
+ configuration_path: str,
266
+ command_args: argparse.Namespace,
267
+ ):
268
+ if conf.name is None:
269
+ console.print("[error]Configuration specifies no run to delete[/]")
270
+ exit(1)
271
+ try:
272
+ self.api.client.runs.get(
273
+ project_name=self.api.project,
274
+ run_name=conf.name,
275
+ )
276
+ except ResourceNotExistsError:
277
+ console.print(f"Run [code]{conf.name}[/] does not exist")
278
+ exit(1)
279
+ if not command_args.yes and not confirm_ask(f"Delete the run [code]{conf.name}[/]?"):
280
+ console.print("\nExiting...")
281
+ return
282
+ with console.status("Deleting run..."):
283
+ self.api.client.runs.delete(
284
+ project_name=self.api.project,
285
+ runs_names=[conf.name],
286
+ )
287
+ console.print(f"Run [code]{conf.name}[/] deleted")
288
+
289
+ @classmethod
290
+ def register_args(cls, parser: argparse.ArgumentParser):
291
+ parser.add_argument(
292
+ "--ssh-identity",
293
+ metavar="SSH_PRIVATE_KEY",
294
+ help="The private SSH key path for SSH tunneling",
295
+ type=Path,
296
+ dest="ssh_identity_file",
297
+ )
298
+ configuration_group = parser.add_argument_group(f"{cls.TYPE.value} Options")
299
+ configuration_group.add_argument(
300
+ "-n",
301
+ "--name",
302
+ dest="run_name",
303
+ help="The name of the run. If not specified, a random name is assigned",
304
+ )
305
+ configuration_group.add_argument(
306
+ "--max-offers",
307
+ help="Number of offers to show in the run plan",
308
+ type=int,
309
+ default=3,
310
+ )
311
+ cls.register_env_args(configuration_group)
312
+ register_resources_args(configuration_group)
313
+ register_profile_args(parser)
314
+ repo_group = parser.add_argument_group("Repo Options")
315
+ repo_group.add_argument(
316
+ "-P",
317
+ "--repo",
318
+ help=("The repo to use for the run. Can be a local path or a Git repo URL."),
319
+ dest="repo",
320
+ )
321
+ repo_group.add_argument(
322
+ "--repo-branch",
323
+ help="The repo branch to use for the run",
324
+ dest="repo_branch",
325
+ )
326
+ repo_group.add_argument(
327
+ "--repo-hash",
328
+ help="The hash of the repo commit to use for the run",
329
+ dest="repo_hash",
330
+ )
331
+ repo_group.add_argument(
332
+ "--no-repo",
333
+ help="Do not use any repo for the run",
334
+ dest="no_repo",
335
+ action="store_true",
336
+ )
337
+ register_init_repo_args(repo_group)
338
+
339
+ def apply_args(self, conf: RunConfigurationT, args: argparse.Namespace):
340
+ apply_resources_args(args, conf)
341
+ apply_profile_args(args, conf)
342
+ if args.run_name:
343
+ conf.name = args.run_name
344
+ self.apply_env_vars(conf.env, args)
345
+ self.interpolate_env(conf)
346
+
347
+ def interpolate_env(self, conf: RunConfigurationT):
348
+ env_dict = conf.env.as_dict()
349
+ interpolator = VariablesInterpolator({"env": env_dict}, skip=["secrets"])
350
+ try:
351
+ if conf.registry_auth is not None:
352
+ conf.registry_auth = RegistryAuth(
353
+ username=interpolator.interpolate_or_error(conf.registry_auth.username),
354
+ password=interpolator.interpolate_or_error(conf.registry_auth.password),
355
+ )
356
+ if isinstance(conf, ServiceConfiguration):
357
+ for probe in conf.probes:
358
+ for header in probe.headers:
359
+ header.value = interpolator.interpolate_or_error(header.value)
360
+ if probe.url:
361
+ probe.url = interpolator.interpolate_or_error(probe.url)
362
+ if probe.body:
363
+ probe.body = interpolator.interpolate_or_error(probe.body)
364
+ except InterpolatorError as e:
365
+ raise ConfigurationError(e.args[0])
366
+
367
+ def validate_gpu_vendor_and_image(self, conf: RunConfigurationT) -> None:
368
+ """
369
+ Infers and sets `resources.gpu.vendor` if not set, requires `image` if the vendor is AMD.
370
+ """
371
+ gpu_spec = conf.resources.gpu
372
+ if gpu_spec is None:
373
+ return
374
+ if gpu_spec.count.max == 0:
375
+ return
376
+ has_amd_gpu: bool
377
+ has_tt_gpu: bool
378
+ vendor = gpu_spec.vendor
379
+ if vendor is None:
380
+ names = gpu_spec.name
381
+ if names:
382
+ # None is a placeholder for an unknown vendor.
383
+ vendors: Set[Optional[gpuhunt.AcceleratorVendor]] = set()
384
+ for name in names:
385
+ name = name.lower()
386
+ if name in _KNOWN_NVIDIA_GPUS:
387
+ vendors.add(gpuhunt.AcceleratorVendor.NVIDIA)
388
+ elif name in _KNOWN_AMD_GPUS:
389
+ vendors.add(gpuhunt.AcceleratorVendor.AMD)
390
+ elif name in _KNOWN_TENSTORRENT_GPUS:
391
+ vendors.add(gpuhunt.AcceleratorVendor.TENSTORRENT)
392
+ else:
393
+ maybe_tpu_version, _, maybe_tpu_cores = name.partition("-")
394
+ if maybe_tpu_version in _KNOWN_TPU_VERSIONS and maybe_tpu_cores.isdigit():
395
+ vendors.add(gpuhunt.AcceleratorVendor.GOOGLE)
396
+ else:
397
+ vendors.add(None)
398
+ if len(vendors) == 1:
399
+ # Only one vendor or all names are not known.
400
+ vendor = next(iter(vendors))
401
+ else:
402
+ # More than one vendor or some names are not known; in either case, we
403
+ # cannot set the vendor to a specific value, will use only names for matching.
404
+ vendor = None
405
+ # If some names are unknown, let's assume they are _not_ AMD products, otherwise
406
+ # ConfigurationError message may be confusing. In worst-case scenario we'll try
407
+ # to execute a run on an instance with an AMD accelerator with a default
408
+ # CUDA image, not a big deal.
409
+ has_amd_gpu = gpuhunt.AcceleratorVendor.AMD in vendors
410
+ has_tt_gpu = gpuhunt.AcceleratorVendor.TENSTORRENT in vendors
411
+ else:
412
+ # If neither gpu.vendor nor gpu.name is set, assume Nvidia.
413
+ vendor = gpuhunt.AcceleratorVendor.NVIDIA
414
+ has_amd_gpu = False
415
+ has_tt_gpu = False
416
+ gpu_spec.vendor = vendor
417
+ else:
418
+ has_amd_gpu = vendor == gpuhunt.AcceleratorVendor.AMD
419
+ has_tt_gpu = vendor == gpuhunt.AcceleratorVendor.TENSTORRENT
420
+ # When docker=True, the system uses Docker-in-Docker image, so no custom image is required
421
+ if has_amd_gpu and conf.image is None and conf.docker is not True:
422
+ raise ConfigurationError("`image` is required if `resources.gpu.vendor` is `amd`")
423
+ if has_tt_gpu and conf.image is None and conf.docker is not True:
424
+ raise ConfigurationError(
425
+ "`image` is required if `resources.gpu.vendor` is `tenstorrent`"
426
+ )
427
+
428
+ def validate_cpu_arch_and_image(self, conf: RunConfigurationT) -> None:
429
+ """
430
+ Infers `resources.cpu.arch` if not set, requires `image` if the architecture is ARM.
431
+ """
432
+ # TODO: Remove in 0.20. Use conf.resources.cpu directly
433
+ cpu_spec = parse_obj_as(CPUSpec, conf.resources.cpu)
434
+ arch = cpu_spec.arch
435
+ if arch is None:
436
+ gpu_spec = conf.resources.gpu
437
+ if (
438
+ gpu_spec is not None
439
+ and gpu_spec.vendor in [None, gpuhunt.AcceleratorVendor.NVIDIA]
440
+ and gpu_spec.name
441
+ and any(map(gpuhunt.is_nvidia_superchip, gpu_spec.name))
442
+ ):
443
+ arch = gpuhunt.CPUArchitecture.ARM
444
+ else:
445
+ arch = gpuhunt.CPUArchitecture.X86
446
+ # NOTE: We don't set the inferred resources.cpu.arch for compatibility with older servers.
447
+ # Servers with ARM support set the arch using the same logic.
448
+ if arch == gpuhunt.CPUArchitecture.ARM and conf.image is None:
449
+ raise ConfigurationError("`image` is required if `resources.cpu.arch` is `arm`")
450
+
451
+ def get_repo(
452
+ self,
453
+ conf: RunConfigurationT,
454
+ configuration_path: str,
455
+ configurator_args: argparse.Namespace,
456
+ ) -> Optional[RemoteRepo]:
457
+ if configurator_args.no_repo:
458
+ return None
459
+
460
+ repo: Optional[RemoteRepo] = None
461
+ repo_head: Optional[RepoHeadWithCreds] = None
462
+ repo_branch: Optional[str] = configurator_args.repo_branch
463
+ repo_hash: Optional[str] = configurator_args.repo_hash
464
+ repo_creds: Optional[RemoteRepoCreds] = None
465
+ git_identity_file: Optional[str] = configurator_args.git_identity_file
466
+ git_private_key: Optional[str] = None
467
+ oauth_token: Optional[str] = configurator_args.gh_token
468
+ # Should we (re)initialize the repo?
469
+ # If any Git credentials provided, we reinitialize the repo, as the user may have provided
470
+ # updated credentials.
471
+ init = git_identity_file is not None or oauth_token is not None
472
+
473
+ url: Optional[str] = None
474
+ local_path: Optional[Path] = None
475
+ # dummy value, safe to join with any path
476
+ root_dir = Path(".")
477
+ if repo_arg := configurator_args.repo:
478
+ if is_git_repo_url(repo_arg):
479
+ url = repo_arg
480
+ else:
481
+ local_path = Path(repo_arg)
482
+ # rel paths in `--repo` are resolved relative to the current working dir
483
+ root_dir = Path.cwd()
484
+ elif conf.repos:
485
+ repo_spec = conf.repos[0]
486
+ if repo_spec.url:
487
+ url = repo_spec.url
488
+ elif repo_spec.local_path:
489
+ local_path = Path(repo_spec.local_path)
490
+ # rel paths in the conf are resolved relative to the conf's parent dir
491
+ root_dir = Path(configuration_path).resolve().parent
492
+ else:
493
+ assert False, f"should not reach here: {repo_spec}"
494
+ if repo_branch is None:
495
+ repo_branch = repo_spec.branch
496
+ if repo_hash is None:
497
+ repo_hash = repo_spec.hash
498
+ else:
499
+ return None
500
+
501
+ if url:
502
+ repo = get_repo_from_url(url)
503
+ repo_head = self.api.repos.get(repo_id=repo.repo_id, with_creds=True)
504
+ elif local_path:
505
+ original_local_path = local_path
506
+ local_path = local_path.expanduser()
507
+ if not local_path.is_absolute():
508
+ local_path = (root_dir / local_path).resolve()
509
+ if not local_path.exists():
510
+ raise ConfigurationError(
511
+ f"Invalid repo path: {original_local_path} -> {local_path}"
512
+ )
513
+ repo = get_repo_from_dir(local_path)
514
+ repo_head = self.api.repos.get(repo_id=repo.repo_id, with_creds=True)
515
+ repo_branch = repo.run_repo_data.repo_branch
516
+ repo_hash = repo.run_repo_data.repo_hash
517
+ else:
518
+ assert False, "should not reach here"
519
+
520
+ assert repo.repo_url is not None
521
+
522
+ if repo_head is not None and repo_head.repo_creds is not None:
523
+ if git_identity_file is None and oauth_token is None:
524
+ git_private_key = repo_head.repo_creds.private_key
525
+ oauth_token = repo_head.repo_creds.oauth_token
526
+ else:
527
+ init = True
528
+
529
+ try:
530
+ repo_creds, _ = get_repo_creds_and_default_branch(
531
+ repo_url=repo.repo_url,
532
+ identity_file=git_identity_file,
533
+ private_key=git_private_key,
534
+ oauth_token=oauth_token,
535
+ )
536
+ except InvalidRepoCredentialsError as e:
537
+ raise CLIError(*e.args) from e
538
+
539
+ repo.run_repo_data.repo_branch = repo_branch
540
+ if repo_hash is not None:
541
+ repo.run_repo_data.repo_hash = repo_hash
542
+
543
+ if init:
544
+ self.api.repos.init(
545
+ repo=repo,
546
+ git_identity_file=git_identity_file,
547
+ oauth_token=oauth_token,
548
+ creds=repo_creds,
549
+ )
550
+
551
+ return repo
552
+
553
+
554
+ class RunWithPortsConfiguratorMixin:
555
+ @classmethod
556
+ def register_ports_args(cls, parser: argparse.ArgumentParser):
557
+ parser.add_argument(
558
+ "-p",
559
+ "--port",
560
+ type=port_mapping,
561
+ action="append",
562
+ help="Exposed port or mapping",
563
+ dest="ports",
564
+ metavar="MAPPING",
565
+ )
566
+ parser.add_argument(
567
+ "--host",
568
+ help="Local address to bind. Defaults to [code]localhost[/]",
569
+ dest=_BIND_ADDRESS_ARG,
570
+ metavar="HOST",
571
+ )
572
+
573
+ def apply_ports_args(
574
+ self,
575
+ conf: ConfigurationWithPortsParams,
576
+ args: argparse.Namespace,
577
+ ):
578
+ if args.ports:
579
+ conf.ports = list(_merge_ports(conf.ports, args.ports).values())
580
+
581
+
582
+ class RunWithCommandsConfiguratorMixin:
583
+ @classmethod
584
+ def register_commands_args(cls, parser: argparse.ArgumentParser):
585
+ parser.add_argument(
586
+ "run_args",
587
+ help=(
588
+ "Run arguments. Available in the configuration [code]commands[/code] as"
589
+ " [code]${{ run.args }}[/code]."
590
+ " Use [code]--[/code] to separate run options from [code]dstack[/code] options"
591
+ ),
592
+ nargs="*",
593
+ metavar="RUN_ARGS",
594
+ )
595
+
596
+ def apply_commands_args(
597
+ self,
598
+ conf: ConfigurationWithCommandsParams,
599
+ args: argparse.Namespace,
600
+ ):
601
+ commands = conf.commands
602
+ run_args = shlex.join(args.run_args)
603
+ interpolator = VariablesInterpolator({"run": {"args": run_args}}, skip=["secrets"])
604
+ try:
605
+ for i, command in enumerate(commands):
606
+ commands[i] = interpolator.interpolate_or_error(command)
607
+ except InterpolatorError as e:
608
+ raise ConfigurationError(e.args[0])
609
+
610
+
611
+ class TaskConfigurator(
612
+ RunWithPortsConfiguratorMixin, RunWithCommandsConfiguratorMixin, BaseRunConfigurator
613
+ ):
614
+ TYPE = ApplyConfigurationType.TASK
615
+
616
+ @classmethod
617
+ def register_args(cls, parser: argparse.ArgumentParser):
618
+ super().register_args(parser)
619
+ cls.register_ports_args(parser)
620
+ cls.register_commands_args(parser)
621
+
622
+ def apply_args(self, conf: TaskConfiguration, args: argparse.Namespace):
623
+ super().apply_args(conf, args)
624
+ self.apply_ports_args(conf, args)
625
+ self.apply_commands_args(conf, args)
626
+
627
+
628
+ class DevEnvironmentConfigurator(RunWithPortsConfiguratorMixin, BaseRunConfigurator):
629
+ TYPE = ApplyConfigurationType.DEV_ENVIRONMENT
630
+
631
+ @classmethod
632
+ def register_args(cls, parser: argparse.ArgumentParser):
633
+ super().register_args(parser)
634
+ cls.register_ports_args(parser)
635
+
636
+ def apply_args(self, conf: DevEnvironmentConfiguration, args: argparse.Namespace):
637
+ super().apply_args(conf, args)
638
+ self.apply_ports_args(conf, args)
639
+ if conf.ide == "vscode" and conf.version is None:
640
+ conf.version = _detect_vscode_version()
641
+ if conf.version is None:
642
+ console.print(
643
+ "[secondary]Unable to detect the VS Code version and pre-install extensions. "
644
+ "Fix by opening [code]Command Palette[/code], executing [code]Shell Command: "
645
+ "Install 'code' command in PATH[/code], and restarting terminal.[/]\n"
646
+ )
647
+ if conf.ide == "cursor" and conf.version is None:
648
+ conf.version = _detect_cursor_version()
649
+ if conf.version is None:
650
+ console.print(
651
+ "[secondary]Unable to detect the Cursor version and pre-install extensions. "
652
+ "Fix by opening [code]Command Palette[/code], executing [code]Shell Command: "
653
+ "Install 'cursor' command in PATH[/code], and restarting terminal.[/]\n"
654
+ )
655
+ if conf.ide == "windsurf" and conf.version is None:
656
+ conf.version = _detect_windsurf_version()
657
+ if conf.version is None:
658
+ console.print(
659
+ "[secondary]Unable to detect the Windsurf version and pre-install extensions. "
660
+ "Fix by opening [code]Command Palette[/code], executing [code]Shell Command: "
661
+ "Install 'surf' command in PATH[/code], and restarting terminal.[/]\n"
662
+ )
663
+
664
+
665
+ class ServiceConfigurator(RunWithCommandsConfiguratorMixin, BaseRunConfigurator):
666
+ TYPE = ApplyConfigurationType.SERVICE
667
+
668
+ @classmethod
669
+ def register_args(cls, parser: argparse.ArgumentParser):
670
+ super().register_args(parser)
671
+ cls.register_commands_args(parser)
672
+
673
+ def apply_args(self, conf: TaskConfiguration, args: argparse.Namespace):
674
+ super().apply_args(conf, args)
675
+ self.apply_commands_args(conf, args)
676
+
677
+
678
+ def _merge_ports(conf: List[PortMapping], args: List[PortMapping]) -> Dict[int, PortMapping]:
679
+ _unique_ports_constraint([pm.container_port for pm in conf])
680
+ _unique_ports_constraint([pm.container_port for pm in args])
681
+ ports = {pm.container_port: pm for pm in conf}
682
+ for pm in args: # override conf
683
+ ports[pm.container_port] = pm
684
+ _unique_ports_constraint([pm.local_port for pm in ports.values() if pm.local_port is not None])
685
+ return ports
686
+
687
+
688
+ def _unique_ports_constraint(ports: List[int]):
689
+ used_ports = set()
690
+ for i in ports:
691
+ if i in used_ports:
692
+ raise ConfigurationError(f"Port {i} is already in use")
693
+ used_ports.add(i)
694
+
695
+
696
+ def _detect_vscode_version(exe: str = "code") -> Optional[str]:
697
+ try:
698
+ run = subprocess.run([exe, "--version"], capture_output=True)
699
+ except FileNotFoundError:
700
+ return None
701
+ if run.returncode == 0:
702
+ return run.stdout.decode().split("\n")[1].strip()
703
+ return None
704
+
705
+
706
+ def _detect_cursor_version(exe: str = "cursor") -> Optional[str]:
707
+ try:
708
+ run = subprocess.run([exe, "--version"], capture_output=True)
709
+ except FileNotFoundError:
710
+ return None
711
+ if run.returncode == 0:
712
+ return run.stdout.decode().split("\n")[1].strip()
713
+ return None
714
+
715
+
716
+ def _detect_windsurf_version(exe: str = "windsurf") -> Optional[str]:
717
+ """
718
+ Detects the installed Windsurf product version and commit hash.
719
+ Returns string in format 'version@commit' (e.g., '1.13.5@97d7a...') or None.
720
+ """
721
+ # 1. Locate executable in PATH
722
+ cmd_path = shutil.which(exe)
723
+ if not cmd_path:
724
+ return None
725
+
726
+ try:
727
+ # 2. Resolve symlinks to find the actual installation directory
728
+ current_dir = os.path.dirname(os.path.realpath(cmd_path))
729
+
730
+ # 3. Walk up directory tree to find 'resources/app/product.json'
731
+ # Covers Linux (/opt/...), macOS (Contents/Resources/...), and Windows
732
+ for _ in range(6):
733
+ # Check standard lowercase and macOS TitleCase
734
+ for resource_folder in ["resources", "Resources"]:
735
+ json_path = os.path.join(current_dir, resource_folder, "app", "product.json")
736
+
737
+ if os.path.exists(json_path):
738
+ try:
739
+ with open(json_path, "r", encoding="utf-8") as f:
740
+ data = json.load(f)
741
+ # Key 'windsurfVersion' is the product version (1.13.5)
742
+ # Key 'version' is the base VS Code version (1.9x)
743
+ ver = data.get("windsurfVersion")
744
+ commit = data.get("commit")
745
+
746
+ if ver and commit:
747
+ return f"{ver}@{commit}"
748
+ except (OSError, json.JSONDecodeError):
749
+ continue
750
+
751
+ # Move up one directory level
752
+ parent = os.path.dirname(current_dir)
753
+ if parent == current_dir: # Reached filesystem root
754
+ break
755
+ current_dir = parent
756
+
757
+ except Exception:
758
+ return None
759
+
760
+ return None
761
+
762
+
763
+ def _print_service_urls(run: Run) -> None:
764
+ if run._run.run_spec.configuration.type != RunConfigurationType.SERVICE.value:
765
+ return
766
+ console.print(f"Service is published at:\n [link={run.service_url}]{run.service_url}[/]")
767
+ if model := run.service_model:
768
+ console.print(
769
+ f"Model [code]{model.name}[/] is published at:\n [link={model.url}]{model.url}[/]"
770
+ )
771
+ console.print()
772
+
773
+
774
+ def print_finished_message(run: Run):
775
+ status_message = (
776
+ run._run.latest_job_submission.status_message
777
+ if run._run.latest_job_submission
778
+ else run._run.status_message
779
+ )
780
+ error = (
781
+ run._run.latest_job_submission.error if run._run.latest_job_submission else run._run.error
782
+ )
783
+ termination_reason = (
784
+ run._run.latest_job_submission.termination_reason
785
+ if run._run.latest_job_submission
786
+ else None
787
+ )
788
+ termination_reason_message = (
789
+ run._run.latest_job_submission.termination_reason_message
790
+ if run._run.latest_job_submission
791
+ else None
792
+ )
793
+ if run.status == RunStatus.DONE:
794
+ console.print(f"[code]{status_message.capitalize()}[/code]")
795
+ return
796
+ else:
797
+ str = f"[error]{status_message.capitalize()}[/error]"
798
+ if error:
799
+ str += f" ([error]{error.capitalize()}[/error])"
800
+ console.print(str)
801
+
802
+ if termination_reason_message:
803
+ console.print(f"[error]{termination_reason_message}[/error]")
804
+
805
+ if termination_reason:
806
+ console.print(f"Check [code]dstack logs -d {run.name}[/code] for more details.")
807
+
808
+
809
+ def get_run_exit_code(run: Run) -> int:
810
+ if run.status == RunStatus.DONE:
811
+ return 0
812
+ return 1
813
+
814
+
815
+ def _is_ready_to_attach(run: Run) -> bool:
816
+ return not (
817
+ run.status
818
+ in [
819
+ RunStatus.SUBMITTED,
820
+ RunStatus.PENDING,
821
+ RunStatus.PROVISIONING,
822
+ RunStatus.TERMINATING,
823
+ ]
824
+ or run._run.jobs[0].job_submissions[-1].status
825
+ in [JobStatus.SUBMITTED, JobStatus.PROVISIONING, JobStatus.PULLING]
826
+ or run._run.is_deployment_in_progress()
827
+ )
828
+
829
+
830
+ def _run_resubmitted(run: Run, current_job_submission: Optional[JobSubmission]) -> bool:
831
+ if current_job_submission is None or run._run.latest_job_submission is None:
832
+ return False
833
+ return run.status == RunStatus.PENDING or (
834
+ not run.status.is_finished()
835
+ and run._run.latest_job_submission.submitted_at > current_job_submission.submitted_at
836
+ )
837
+
838
+
839
+ def render_run_spec_diff(old_spec: RunSpec, new_spec: RunSpec) -> Optional[str]:
840
+ changed_spec_fields = list(diff_models(old_spec, new_spec))
841
+ if not changed_spec_fields:
842
+ return None
843
+ friendly_spec_field_names = {
844
+ "repo_id": "Repo ID",
845
+ "repo_code_hash": "Repo files",
846
+ "repo_data": "Repo state (branch, commit, or other)",
847
+ "ssh_key_pub": "Public SSH key",
848
+ }
849
+ nested_list = NestedList()
850
+ for spec_field in changed_spec_fields:
851
+ if spec_field == "merged_profile":
852
+ continue
853
+ elif spec_field == "configuration":
854
+ if type(old_spec.configuration) is not type(new_spec.configuration):
855
+ item = NestedListItem("Configuration type")
856
+ else:
857
+ item = NestedListItem(
858
+ "Configuration properties:",
859
+ children=[
860
+ NestedListItem(field)
861
+ for field in diff_models(old_spec.configuration, new_spec.configuration)
862
+ ],
863
+ )
864
+ elif spec_field == "profile":
865
+ if type(old_spec.profile) is not type(new_spec.profile):
866
+ item = NestedListItem("Profile")
867
+ else:
868
+ assert old_spec.profile is not None
869
+ assert new_spec.profile is not None
870
+ item = NestedListItem(
871
+ "Profile properties:",
872
+ children=[
873
+ NestedListItem(field)
874
+ for field in diff_models(old_spec.profile, new_spec.profile)
875
+ ],
876
+ )
877
+ elif spec_field in friendly_spec_field_names:
878
+ item = NestedListItem(friendly_spec_field_names[spec_field])
879
+ else:
880
+ item = NestedListItem(spec_field.replace("_", " ").capitalize())
881
+ nested_list.children.append(item)
882
+ return nested_list.render()