skypilot-nightly 1.0.0.dev20250905__py3-none-any.whl → 1.0.0.dev20251210__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 (429) hide show
  1. sky/__init__.py +12 -2
  2. sky/adaptors/aws.py +27 -22
  3. sky/adaptors/common.py +25 -2
  4. sky/adaptors/coreweave.py +278 -0
  5. sky/adaptors/do.py +8 -2
  6. sky/adaptors/gcp.py +11 -0
  7. sky/adaptors/ibm.py +5 -2
  8. sky/adaptors/kubernetes.py +64 -0
  9. sky/adaptors/nebius.py +3 -1
  10. sky/adaptors/primeintellect.py +1 -0
  11. sky/adaptors/seeweb.py +183 -0
  12. sky/adaptors/shadeform.py +89 -0
  13. sky/adaptors/slurm.py +478 -0
  14. sky/admin_policy.py +20 -0
  15. sky/authentication.py +157 -263
  16. sky/backends/__init__.py +3 -2
  17. sky/backends/backend.py +11 -3
  18. sky/backends/backend_utils.py +630 -185
  19. sky/backends/cloud_vm_ray_backend.py +1111 -928
  20. sky/backends/local_docker_backend.py +9 -5
  21. sky/backends/task_codegen.py +971 -0
  22. sky/backends/wheel_utils.py +18 -0
  23. sky/catalog/__init__.py +8 -3
  24. sky/catalog/aws_catalog.py +4 -0
  25. sky/catalog/common.py +19 -1
  26. sky/catalog/data_fetchers/fetch_aws.py +102 -80
  27. sky/catalog/data_fetchers/fetch_gcp.py +30 -3
  28. sky/catalog/data_fetchers/fetch_nebius.py +9 -6
  29. sky/catalog/data_fetchers/fetch_runpod.py +698 -0
  30. sky/catalog/data_fetchers/fetch_seeweb.py +329 -0
  31. sky/catalog/data_fetchers/fetch_shadeform.py +142 -0
  32. sky/catalog/kubernetes_catalog.py +36 -32
  33. sky/catalog/primeintellect_catalog.py +95 -0
  34. sky/catalog/runpod_catalog.py +5 -1
  35. sky/catalog/seeweb_catalog.py +184 -0
  36. sky/catalog/shadeform_catalog.py +165 -0
  37. sky/catalog/slurm_catalog.py +243 -0
  38. sky/check.py +87 -46
  39. sky/client/cli/command.py +1004 -434
  40. sky/client/cli/flags.py +4 -2
  41. sky/{volumes/utils.py → client/cli/table_utils.py} +111 -13
  42. sky/client/cli/utils.py +79 -0
  43. sky/client/common.py +12 -2
  44. sky/client/sdk.py +188 -65
  45. sky/client/sdk_async.py +34 -33
  46. sky/cloud_stores.py +82 -3
  47. sky/clouds/__init__.py +8 -0
  48. sky/clouds/aws.py +337 -129
  49. sky/clouds/azure.py +24 -18
  50. sky/clouds/cloud.py +47 -13
  51. sky/clouds/cudo.py +16 -13
  52. sky/clouds/do.py +9 -7
  53. sky/clouds/fluidstack.py +12 -5
  54. sky/clouds/gcp.py +14 -7
  55. sky/clouds/hyperbolic.py +12 -5
  56. sky/clouds/ibm.py +12 -5
  57. sky/clouds/kubernetes.py +80 -45
  58. sky/clouds/lambda_cloud.py +12 -5
  59. sky/clouds/nebius.py +23 -9
  60. sky/clouds/oci.py +19 -12
  61. sky/clouds/paperspace.py +4 -1
  62. sky/clouds/primeintellect.py +317 -0
  63. sky/clouds/runpod.py +85 -24
  64. sky/clouds/scp.py +12 -8
  65. sky/clouds/seeweb.py +477 -0
  66. sky/clouds/shadeform.py +400 -0
  67. sky/clouds/slurm.py +578 -0
  68. sky/clouds/ssh.py +6 -3
  69. sky/clouds/utils/scp_utils.py +61 -50
  70. sky/clouds/vast.py +43 -27
  71. sky/clouds/vsphere.py +14 -16
  72. sky/core.py +296 -195
  73. sky/dashboard/out/404.html +1 -1
  74. sky/dashboard/out/_next/static/KYAhEFa3FTfq4JyKVgo-s/_buildManifest.js +1 -0
  75. sky/dashboard/out/_next/static/chunks/1141-9c810f01ff4f398a.js +11 -0
  76. sky/dashboard/out/_next/static/chunks/1871-7e202677c42f43fe.js +6 -0
  77. sky/dashboard/out/_next/static/chunks/2260-7703229c33c5ebd5.js +1 -0
  78. sky/dashboard/out/_next/static/chunks/2369.fc20f0c2c8ed9fe7.js +15 -0
  79. sky/dashboard/out/_next/static/chunks/2755.edd818326d489a1d.js +26 -0
  80. sky/dashboard/out/_next/static/chunks/3294.ddda8c6c6f9f24dc.js +1 -0
  81. sky/dashboard/out/_next/static/chunks/3785.7e245f318f9d1121.js +1 -0
  82. sky/dashboard/out/_next/static/chunks/{6601-06114c982db410b6.js → 3800-b589397dc09c5b4e.js} +1 -1
  83. sky/dashboard/out/_next/static/chunks/3850-fd5696f3bbbaddae.js +1 -0
  84. sky/dashboard/out/_next/static/chunks/4725.172ede95d1b21022.js +1 -0
  85. sky/dashboard/out/_next/static/chunks/4937.a2baa2df5572a276.js +15 -0
  86. sky/dashboard/out/_next/static/chunks/6212-7bd06f60ba693125.js +13 -0
  87. sky/dashboard/out/_next/static/chunks/6856-da20c5fd999f319c.js +1 -0
  88. sky/dashboard/out/_next/static/chunks/6990-09cbf02d3cd518c3.js +1 -0
  89. sky/dashboard/out/_next/static/chunks/7359-c8d04e06886000b3.js +30 -0
  90. sky/dashboard/out/_next/static/chunks/7615-019513abc55b3b47.js +1 -0
  91. sky/dashboard/out/_next/static/chunks/8640.5b9475a2d18c5416.js +16 -0
  92. sky/dashboard/out/_next/static/chunks/8969-452f9d5cbdd2dc73.js +1 -0
  93. sky/dashboard/out/_next/static/chunks/9025.fa408f3242e9028d.js +6 -0
  94. sky/dashboard/out/_next/static/chunks/9353-8369df1cf105221c.js +1 -0
  95. sky/dashboard/out/_next/static/chunks/9360.a536cf6b1fa42355.js +31 -0
  96. sky/dashboard/out/_next/static/chunks/9847.3aaca6bb33455140.js +30 -0
  97. sky/dashboard/out/_next/static/chunks/pages/_app-68b647e26f9d2793.js +34 -0
  98. sky/dashboard/out/_next/static/chunks/pages/clusters/[cluster]/[job]-33f525539665fdfd.js +16 -0
  99. sky/dashboard/out/_next/static/chunks/pages/clusters/[cluster]-a7565f586ef86467.js +1 -0
  100. sky/dashboard/out/_next/static/chunks/pages/clusters-9e5d47818b9bdadd.js +1 -0
  101. sky/dashboard/out/_next/static/chunks/pages/{config-dfb9bf07b13045f4.js → config-718cdc365de82689.js} +1 -1
  102. sky/dashboard/out/_next/static/chunks/pages/infra/{[context]-6563820e094f68ca.js → [context]-12c559ec4d81fdbd.js} +1 -1
  103. sky/dashboard/out/_next/static/chunks/pages/{infra-aabba60d57826e0f.js → infra-d187cd0413d72475.js} +1 -1
  104. sky/dashboard/out/_next/static/chunks/pages/jobs/[job]-895847b6cf200b04.js +16 -0
  105. sky/dashboard/out/_next/static/chunks/pages/jobs/pools/[pool]-8d0f4655400b4eb9.js +21 -0
  106. sky/dashboard/out/_next/static/chunks/pages/jobs-e5a98f17f8513a96.js +1 -0
  107. sky/dashboard/out/_next/static/chunks/pages/plugins/[...slug]-4f46050ca065d8f8.js +1 -0
  108. sky/dashboard/out/_next/static/chunks/pages/users-2f7646eb77785a2c.js +1 -0
  109. sky/dashboard/out/_next/static/chunks/pages/volumes-ef19d49c6d0e8500.js +1 -0
  110. sky/dashboard/out/_next/static/chunks/pages/workspaces/{[name]-af76bb06dbb3954f.js → [name]-96e0f298308da7e2.js} +1 -1
  111. sky/dashboard/out/_next/static/chunks/pages/{workspaces-7598c33a746cdc91.js → workspaces-cb4da3abe08ebf19.js} +1 -1
  112. sky/dashboard/out/_next/static/chunks/webpack-fba3de387ff6bb08.js +1 -0
  113. sky/dashboard/out/_next/static/css/c5a4cfd2600fc715.css +3 -0
  114. sky/dashboard/out/clusters/[cluster]/[job].html +1 -1
  115. sky/dashboard/out/clusters/[cluster].html +1 -1
  116. sky/dashboard/out/clusters.html +1 -1
  117. sky/dashboard/out/config.html +1 -1
  118. sky/dashboard/out/index.html +1 -1
  119. sky/dashboard/out/infra/[context].html +1 -1
  120. sky/dashboard/out/infra.html +1 -1
  121. sky/dashboard/out/jobs/[job].html +1 -1
  122. sky/dashboard/out/jobs/pools/[pool].html +1 -1
  123. sky/dashboard/out/jobs.html +1 -1
  124. sky/dashboard/out/plugins/[...slug].html +1 -0
  125. sky/dashboard/out/users.html +1 -1
  126. sky/dashboard/out/volumes.html +1 -1
  127. sky/dashboard/out/workspace/new.html +1 -1
  128. sky/dashboard/out/workspaces/[name].html +1 -1
  129. sky/dashboard/out/workspaces.html +1 -1
  130. sky/data/data_utils.py +92 -1
  131. sky/data/mounting_utils.py +177 -30
  132. sky/data/storage.py +200 -19
  133. sky/data/storage_utils.py +10 -45
  134. sky/exceptions.py +18 -7
  135. sky/execution.py +74 -31
  136. sky/global_user_state.py +605 -191
  137. sky/jobs/__init__.py +2 -0
  138. sky/jobs/client/sdk.py +101 -4
  139. sky/jobs/client/sdk_async.py +31 -5
  140. sky/jobs/constants.py +15 -8
  141. sky/jobs/controller.py +726 -284
  142. sky/jobs/file_content_utils.py +128 -0
  143. sky/jobs/log_gc.py +193 -0
  144. sky/jobs/recovery_strategy.py +250 -100
  145. sky/jobs/scheduler.py +271 -173
  146. sky/jobs/server/core.py +367 -114
  147. sky/jobs/server/server.py +81 -35
  148. sky/jobs/server/utils.py +89 -35
  149. sky/jobs/state.py +1498 -620
  150. sky/jobs/utils.py +771 -306
  151. sky/logs/agent.py +40 -5
  152. sky/logs/aws.py +9 -19
  153. sky/metrics/utils.py +282 -39
  154. sky/models.py +2 -0
  155. sky/optimizer.py +7 -6
  156. sky/provision/__init__.py +38 -1
  157. sky/provision/aws/config.py +34 -13
  158. sky/provision/aws/instance.py +5 -2
  159. sky/provision/azure/instance.py +5 -3
  160. sky/provision/common.py +22 -0
  161. sky/provision/cudo/instance.py +4 -3
  162. sky/provision/do/instance.py +4 -3
  163. sky/provision/docker_utils.py +112 -28
  164. sky/provision/fluidstack/instance.py +6 -5
  165. sky/provision/gcp/config.py +6 -1
  166. sky/provision/gcp/instance.py +4 -2
  167. sky/provision/hyperbolic/instance.py +4 -2
  168. sky/provision/instance_setup.py +66 -20
  169. sky/provision/kubernetes/__init__.py +2 -0
  170. sky/provision/kubernetes/config.py +7 -44
  171. sky/provision/kubernetes/constants.py +0 -1
  172. sky/provision/kubernetes/instance.py +609 -213
  173. sky/provision/kubernetes/manifests/fusermount-server-daemonset.yaml +1 -2
  174. sky/provision/kubernetes/network.py +12 -8
  175. sky/provision/kubernetes/network_utils.py +8 -25
  176. sky/provision/kubernetes/utils.py +422 -422
  177. sky/provision/kubernetes/volume.py +150 -18
  178. sky/provision/lambda_cloud/instance.py +16 -13
  179. sky/provision/nebius/instance.py +6 -2
  180. sky/provision/nebius/utils.py +103 -86
  181. sky/provision/oci/instance.py +4 -2
  182. sky/provision/paperspace/instance.py +4 -3
  183. sky/provision/primeintellect/__init__.py +10 -0
  184. sky/provision/primeintellect/config.py +11 -0
  185. sky/provision/primeintellect/instance.py +454 -0
  186. sky/provision/primeintellect/utils.py +398 -0
  187. sky/provision/provisioner.py +45 -15
  188. sky/provision/runpod/__init__.py +2 -0
  189. sky/provision/runpod/instance.py +4 -3
  190. sky/provision/runpod/volume.py +69 -13
  191. sky/provision/scp/instance.py +307 -130
  192. sky/provision/seeweb/__init__.py +11 -0
  193. sky/provision/seeweb/config.py +13 -0
  194. sky/provision/seeweb/instance.py +812 -0
  195. sky/provision/shadeform/__init__.py +11 -0
  196. sky/provision/shadeform/config.py +12 -0
  197. sky/provision/shadeform/instance.py +351 -0
  198. sky/provision/shadeform/shadeform_utils.py +83 -0
  199. sky/provision/slurm/__init__.py +12 -0
  200. sky/provision/slurm/config.py +13 -0
  201. sky/provision/slurm/instance.py +572 -0
  202. sky/provision/slurm/utils.py +583 -0
  203. sky/provision/vast/instance.py +9 -4
  204. sky/provision/vast/utils.py +10 -6
  205. sky/provision/volume.py +164 -0
  206. sky/provision/vsphere/common/ssl_helper.py +1 -1
  207. sky/provision/vsphere/common/vapiconnect.py +2 -1
  208. sky/provision/vsphere/common/vim_utils.py +3 -2
  209. sky/provision/vsphere/instance.py +8 -6
  210. sky/provision/vsphere/vsphere_utils.py +8 -1
  211. sky/resources.py +11 -3
  212. sky/schemas/api/responses.py +107 -6
  213. sky/schemas/db/global_user_state/008_skylet_ssh_tunnel_metadata.py +34 -0
  214. sky/schemas/db/global_user_state/009_last_activity_and_launched_at.py +89 -0
  215. sky/schemas/db/global_user_state/010_save_ssh_key.py +66 -0
  216. sky/schemas/db/global_user_state/011_is_ephemeral.py +34 -0
  217. sky/schemas/db/kv_cache/001_initial_schema.py +29 -0
  218. sky/schemas/db/serve_state/002_yaml_content.py +34 -0
  219. sky/schemas/db/skypilot_config/001_initial_schema.py +30 -0
  220. sky/schemas/db/spot_jobs/002_cluster_pool.py +3 -3
  221. sky/schemas/db/spot_jobs/004_job_file_contents.py +42 -0
  222. sky/schemas/db/spot_jobs/005_logs_gc.py +38 -0
  223. sky/schemas/db/spot_jobs/006_controller_pid_started_at.py +34 -0
  224. sky/schemas/db/spot_jobs/007_config_file_content.py +34 -0
  225. sky/schemas/generated/jobsv1_pb2.py +86 -0
  226. sky/schemas/generated/jobsv1_pb2.pyi +254 -0
  227. sky/schemas/generated/jobsv1_pb2_grpc.py +542 -0
  228. sky/schemas/generated/managed_jobsv1_pb2.py +76 -0
  229. sky/schemas/generated/managed_jobsv1_pb2.pyi +278 -0
  230. sky/schemas/generated/managed_jobsv1_pb2_grpc.py +278 -0
  231. sky/schemas/generated/servev1_pb2.py +58 -0
  232. sky/schemas/generated/servev1_pb2.pyi +115 -0
  233. sky/schemas/generated/servev1_pb2_grpc.py +322 -0
  234. sky/serve/autoscalers.py +2 -0
  235. sky/serve/client/impl.py +55 -21
  236. sky/serve/constants.py +4 -3
  237. sky/serve/controller.py +17 -11
  238. sky/serve/load_balancing_policies.py +1 -1
  239. sky/serve/replica_managers.py +219 -142
  240. sky/serve/serve_rpc_utils.py +179 -0
  241. sky/serve/serve_state.py +63 -54
  242. sky/serve/serve_utils.py +145 -109
  243. sky/serve/server/core.py +46 -25
  244. sky/serve/server/impl.py +311 -162
  245. sky/serve/server/server.py +21 -19
  246. sky/serve/service.py +84 -68
  247. sky/serve/service_spec.py +45 -7
  248. sky/server/auth/loopback.py +38 -0
  249. sky/server/auth/oauth2_proxy.py +12 -7
  250. sky/server/common.py +47 -24
  251. sky/server/config.py +62 -28
  252. sky/server/constants.py +9 -1
  253. sky/server/daemons.py +109 -38
  254. sky/server/metrics.py +76 -96
  255. sky/server/middleware_utils.py +166 -0
  256. sky/server/plugins.py +222 -0
  257. sky/server/requests/executor.py +384 -145
  258. sky/server/requests/payloads.py +83 -19
  259. sky/server/requests/preconditions.py +15 -13
  260. sky/server/requests/request_names.py +123 -0
  261. sky/server/requests/requests.py +511 -157
  262. sky/server/requests/serializers/decoders.py +48 -17
  263. sky/server/requests/serializers/encoders.py +102 -20
  264. sky/server/requests/serializers/return_value_serializers.py +60 -0
  265. sky/server/requests/threads.py +117 -0
  266. sky/server/rest.py +116 -24
  267. sky/server/server.py +497 -179
  268. sky/server/server_utils.py +30 -0
  269. sky/server/stream_utils.py +219 -45
  270. sky/server/uvicorn.py +30 -19
  271. sky/setup_files/MANIFEST.in +6 -1
  272. sky/setup_files/alembic.ini +8 -0
  273. sky/setup_files/dependencies.py +64 -19
  274. sky/setup_files/setup.py +44 -44
  275. sky/sky_logging.py +13 -5
  276. sky/skylet/attempt_skylet.py +116 -24
  277. sky/skylet/configs.py +3 -1
  278. sky/skylet/constants.py +139 -29
  279. sky/skylet/events.py +74 -14
  280. sky/skylet/executor/__init__.py +1 -0
  281. sky/skylet/executor/slurm.py +189 -0
  282. sky/skylet/job_lib.py +143 -105
  283. sky/skylet/log_lib.py +252 -8
  284. sky/skylet/log_lib.pyi +47 -7
  285. sky/skylet/providers/ibm/node_provider.py +12 -8
  286. sky/skylet/providers/ibm/vpc_provider.py +13 -12
  287. sky/skylet/runtime_utils.py +21 -0
  288. sky/skylet/services.py +524 -0
  289. sky/skylet/skylet.py +27 -2
  290. sky/skylet/subprocess_daemon.py +104 -28
  291. sky/skypilot_config.py +99 -79
  292. sky/ssh_node_pools/constants.py +12 -0
  293. sky/ssh_node_pools/core.py +40 -3
  294. sky/ssh_node_pools/deploy/__init__.py +4 -0
  295. sky/ssh_node_pools/deploy/deploy.py +952 -0
  296. sky/ssh_node_pools/deploy/tunnel_utils.py +199 -0
  297. sky/ssh_node_pools/deploy/utils.py +173 -0
  298. sky/ssh_node_pools/server.py +20 -21
  299. sky/{utils/kubernetes/ssh_utils.py → ssh_node_pools/utils.py} +9 -6
  300. sky/task.py +221 -104
  301. sky/templates/aws-ray.yml.j2 +1 -0
  302. sky/templates/azure-ray.yml.j2 +1 -0
  303. sky/templates/cudo-ray.yml.j2 +1 -0
  304. sky/templates/do-ray.yml.j2 +1 -0
  305. sky/templates/fluidstack-ray.yml.j2 +1 -0
  306. sky/templates/gcp-ray.yml.j2 +1 -0
  307. sky/templates/hyperbolic-ray.yml.j2 +1 -0
  308. sky/templates/ibm-ray.yml.j2 +2 -1
  309. sky/templates/jobs-controller.yaml.j2 +3 -0
  310. sky/templates/kubernetes-ray.yml.j2 +204 -55
  311. sky/templates/lambda-ray.yml.j2 +1 -0
  312. sky/templates/nebius-ray.yml.j2 +3 -0
  313. sky/templates/oci-ray.yml.j2 +1 -0
  314. sky/templates/paperspace-ray.yml.j2 +1 -0
  315. sky/templates/primeintellect-ray.yml.j2 +72 -0
  316. sky/templates/runpod-ray.yml.j2 +1 -0
  317. sky/templates/scp-ray.yml.j2 +1 -0
  318. sky/templates/seeweb-ray.yml.j2 +171 -0
  319. sky/templates/shadeform-ray.yml.j2 +73 -0
  320. sky/templates/slurm-ray.yml.j2 +85 -0
  321. sky/templates/vast-ray.yml.j2 +2 -0
  322. sky/templates/vsphere-ray.yml.j2 +1 -0
  323. sky/templates/websocket_proxy.py +188 -43
  324. sky/usage/usage_lib.py +16 -4
  325. sky/users/model.conf +1 -1
  326. sky/users/permission.py +84 -44
  327. sky/users/rbac.py +31 -3
  328. sky/utils/accelerator_registry.py +6 -3
  329. sky/utils/admin_policy_utils.py +18 -5
  330. sky/utils/annotations.py +128 -6
  331. sky/utils/asyncio_utils.py +78 -0
  332. sky/utils/atomic.py +1 -1
  333. sky/utils/auth_utils.py +153 -0
  334. sky/utils/cli_utils/status_utils.py +12 -7
  335. sky/utils/cluster_utils.py +28 -6
  336. sky/utils/command_runner.py +283 -30
  337. sky/utils/command_runner.pyi +63 -7
  338. sky/utils/common.py +3 -1
  339. sky/utils/common_utils.py +55 -7
  340. sky/utils/config_utils.py +1 -14
  341. sky/utils/context.py +127 -40
  342. sky/utils/context_utils.py +73 -18
  343. sky/utils/controller_utils.py +229 -70
  344. sky/utils/db/db_utils.py +95 -18
  345. sky/utils/db/kv_cache.py +149 -0
  346. sky/utils/db/migration_utils.py +24 -7
  347. sky/utils/env_options.py +4 -0
  348. sky/utils/git.py +559 -1
  349. sky/utils/kubernetes/create_cluster.sh +15 -30
  350. sky/utils/kubernetes/delete_cluster.sh +10 -7
  351. sky/utils/kubernetes/generate_kind_config.py +6 -66
  352. sky/utils/kubernetes/gpu_labeler.py +13 -3
  353. sky/utils/kubernetes/k8s_gpu_labeler_job.yaml +2 -1
  354. sky/utils/kubernetes/k8s_gpu_labeler_setup.yaml +16 -16
  355. sky/utils/kubernetes/kubernetes_deploy_utils.py +187 -260
  356. sky/utils/kubernetes/rsync_helper.sh +11 -3
  357. sky/utils/kubernetes/ssh-tunnel.sh +7 -376
  358. sky/utils/kubernetes_enums.py +7 -15
  359. sky/utils/lock_events.py +4 -4
  360. sky/utils/locks.py +128 -31
  361. sky/utils/log_utils.py +0 -319
  362. sky/utils/resource_checker.py +13 -10
  363. sky/utils/resources_utils.py +53 -29
  364. sky/utils/rich_utils.py +8 -4
  365. sky/utils/schemas.py +138 -52
  366. sky/utils/subprocess_utils.py +17 -4
  367. sky/utils/thread_utils.py +91 -0
  368. sky/utils/timeline.py +2 -1
  369. sky/utils/ux_utils.py +35 -1
  370. sky/utils/volume.py +88 -4
  371. sky/utils/yaml_utils.py +9 -0
  372. sky/volumes/client/sdk.py +48 -10
  373. sky/volumes/server/core.py +59 -22
  374. sky/volumes/server/server.py +46 -17
  375. sky/volumes/volume.py +54 -42
  376. sky/workspaces/core.py +57 -21
  377. sky/workspaces/server.py +13 -12
  378. sky_templates/README.md +3 -0
  379. sky_templates/__init__.py +3 -0
  380. sky_templates/ray/__init__.py +0 -0
  381. sky_templates/ray/start_cluster +183 -0
  382. sky_templates/ray/stop_cluster +75 -0
  383. {skypilot_nightly-1.0.0.dev20250905.dist-info → skypilot_nightly-1.0.0.dev20251210.dist-info}/METADATA +343 -65
  384. skypilot_nightly-1.0.0.dev20251210.dist-info/RECORD +629 -0
  385. skypilot_nightly-1.0.0.dev20251210.dist-info/top_level.txt +2 -0
  386. sky/client/cli/git.py +0 -549
  387. sky/dashboard/out/_next/static/chunks/1121-408ed10b2f9fce17.js +0 -1
  388. sky/dashboard/out/_next/static/chunks/1141-943efc7aff0f0c06.js +0 -1
  389. sky/dashboard/out/_next/static/chunks/1836-37fede578e2da5f8.js +0 -40
  390. sky/dashboard/out/_next/static/chunks/3015-86cabed5d4669ad0.js +0 -1
  391. sky/dashboard/out/_next/static/chunks/3294.c80326aec9bfed40.js +0 -6
  392. sky/dashboard/out/_next/static/chunks/3785.4872a2f3aa489880.js +0 -1
  393. sky/dashboard/out/_next/static/chunks/3850-ff4a9a69d978632b.js +0 -1
  394. sky/dashboard/out/_next/static/chunks/4045.b30465273dc5e468.js +0 -21
  395. sky/dashboard/out/_next/static/chunks/4676-9da7fdbde90b5549.js +0 -10
  396. sky/dashboard/out/_next/static/chunks/4725.10f7a9a5d3ea8208.js +0 -1
  397. sky/dashboard/out/_next/static/chunks/5339.3fda4a4010ff4e06.js +0 -51
  398. sky/dashboard/out/_next/static/chunks/6135-4b4d5e824b7f9d3c.js +0 -1
  399. sky/dashboard/out/_next/static/chunks/649.b9d7f7d10c1b8c53.js +0 -45
  400. sky/dashboard/out/_next/static/chunks/6856-dca7962af4814e1b.js +0 -1
  401. sky/dashboard/out/_next/static/chunks/6990-08b2a1cae076a943.js +0 -1
  402. sky/dashboard/out/_next/static/chunks/7325.b4bc99ce0892dcd5.js +0 -6
  403. sky/dashboard/out/_next/static/chunks/754-d0da8ab45f9509e9.js +0 -18
  404. sky/dashboard/out/_next/static/chunks/7669.1f5d9a402bf5cc42.js +0 -36
  405. sky/dashboard/out/_next/static/chunks/8969-0be3036bf86f8256.js +0 -1
  406. sky/dashboard/out/_next/static/chunks/9025.c12318fb6a1a9093.js +0 -6
  407. sky/dashboard/out/_next/static/chunks/9037-fa1737818d0a0969.js +0 -6
  408. sky/dashboard/out/_next/static/chunks/pages/_app-ce361c6959bc2001.js +0 -34
  409. sky/dashboard/out/_next/static/chunks/pages/clusters/[cluster]/[job]-1cbba24bd1bd35f8.js +0 -16
  410. sky/dashboard/out/_next/static/chunks/pages/clusters/[cluster]-0b4b35dc1dfe046c.js +0 -16
  411. sky/dashboard/out/_next/static/chunks/pages/clusters-469814d711d63b1b.js +0 -1
  412. sky/dashboard/out/_next/static/chunks/pages/jobs/[job]-dd64309c3fe67ed2.js +0 -11
  413. sky/dashboard/out/_next/static/chunks/pages/jobs/pools/[pool]-07349868f7905d37.js +0 -16
  414. sky/dashboard/out/_next/static/chunks/pages/jobs-1f70d9faa564804f.js +0 -1
  415. sky/dashboard/out/_next/static/chunks/pages/users-018bf31cda52e11b.js +0 -1
  416. sky/dashboard/out/_next/static/chunks/pages/volumes-739726d6b823f532.js +0 -1
  417. sky/dashboard/out/_next/static/chunks/webpack-4fe903277b57b523.js +0 -1
  418. sky/dashboard/out/_next/static/css/4614e06482d7309e.css +0 -3
  419. sky/dashboard/out/_next/static/mS-4qZPSkRuA1u-g2wQhg/_buildManifest.js +0 -1
  420. sky/templates/kubernetes-ssh-jump.yml.j2 +0 -94
  421. sky/utils/kubernetes/cleanup-tunnel.sh +0 -62
  422. sky/utils/kubernetes/deploy_remote_cluster.py +0 -1299
  423. sky/utils/kubernetes/ssh_jump_lifecycle_manager.py +0 -191
  424. skypilot_nightly-1.0.0.dev20250905.dist-info/RECORD +0 -547
  425. skypilot_nightly-1.0.0.dev20250905.dist-info/top_level.txt +0 -1
  426. /sky/dashboard/out/_next/static/{mS-4qZPSkRuA1u-g2wQhg → KYAhEFa3FTfq4JyKVgo-s}/_ssgManifest.js +0 -0
  427. {skypilot_nightly-1.0.0.dev20250905.dist-info → skypilot_nightly-1.0.0.dev20251210.dist-info}/WHEEL +0 -0
  428. {skypilot_nightly-1.0.0.dev20250905.dist-info → skypilot_nightly-1.0.0.dev20251210.dist-info}/entry_points.txt +0 -0
  429. {skypilot_nightly-1.0.0.dev20250905.dist-info → skypilot_nightly-1.0.0.dev20251210.dist-info}/licenses/LICENSE +0 -0
@@ -0,0 +1,2 @@
1
+ sky
2
+ sky_templates
sky/client/cli/git.py DELETED
@@ -1,549 +0,0 @@
1
- """Git utilities for SkyPilot."""
2
- import enum
3
- import os
4
- import re
5
- from typing import List, Optional, Union
6
-
7
- import git
8
- import requests
9
-
10
- from sky import exceptions
11
- from sky import sky_logging
12
-
13
- logger = sky_logging.init_logger(__name__)
14
-
15
-
16
- class GitRefType(enum.Enum):
17
- """Type of git reference."""
18
-
19
- BRANCH = 'branch'
20
- TAG = 'tag'
21
- COMMIT = 'commit'
22
-
23
-
24
- class GitUrlInfo:
25
- """Information extracted from a git URL."""
26
-
27
- def __init__(self,
28
- host: str,
29
- path: str,
30
- protocol: str,
31
- user: Optional[str] = None,
32
- port: Optional[int] = None):
33
- self.host = host
34
- # Repository path (e.g., 'user/repo' or 'org/subgroup/repo').
35
- # The path is the part after the host.
36
- self.path = path
37
- # 'https', 'ssh'
38
- self.protocol = protocol
39
- # SSH username
40
- self.user = user
41
- self.port = port
42
-
43
-
44
- class GitCloneInfo:
45
- """Information about a git clone."""
46
-
47
- def __init__(self,
48
- url: str,
49
- envs: Optional[dict] = None,
50
- token: Optional[str] = None,
51
- ssh_key: Optional[str] = None):
52
- self.url = url
53
- self.envs = envs
54
- self.token = token
55
- self.ssh_key = ssh_key
56
-
57
-
58
- class GitRepo:
59
- """Git utilities for SkyPilot."""
60
-
61
- def __init__(self,
62
- repo_url: str,
63
- ref: str = 'main',
64
- git_token: Optional[str] = None,
65
- git_ssh_key_path: Optional[str] = None):
66
- """Initialize Git utility.
67
-
68
- Args:
69
- repo_url: Git repository URL.
70
- ref: Git reference (branch, tag, or commit hash).
71
- git_token: GitHub token for private repositories.
72
- git_ssh_key_path: Path to SSH private key for authentication.
73
- """
74
- self.repo_url = repo_url
75
- self.ref = ref
76
- self.git_token = git_token
77
- self.git_ssh_key_path = git_ssh_key_path
78
-
79
- # Parse URL during initialization to catch format errors early
80
- self._parsed_url = self._parse_git_url(self.repo_url)
81
-
82
- def _parse_git_url(self, url: str) -> GitUrlInfo:
83
- """Parse git URL into components.
84
-
85
- Supports various git URL formats:
86
- - HTTPS: https://github.com/user/repo.git
87
- - SSH: git@github.com:user/repo.git (SCP-like)
88
- - SSH full: ssh://git@github.com/user/repo.git
89
- - SSH with port: ssh://git@github.com:2222/user/repo.git
90
-
91
- Args:
92
- url: Git repository URL in any supported format.
93
-
94
- Returns:
95
- GitUrlInfo with parsed components.
96
-
97
- Raises:
98
- exceptions.GitError: If URL format is not supported.
99
- """
100
- # Remove trailing .git if present
101
- clean_url = url.rstrip('/')
102
- if clean_url.endswith('.git'):
103
- clean_url = clean_url[:-4]
104
-
105
- # Pattern for HTTPS/HTTP URLs
106
- https_pattern = r'^(https?)://(?:([^@]+)@)?([^:/]+)(?::(\d+))?/(.+)$'
107
- https_match = re.match(https_pattern, clean_url)
108
-
109
- if https_match:
110
- protocol, user, host, port_str, path = https_match.groups()
111
- port = int(port_str) if port_str else None
112
-
113
- # Validate that path is not empty
114
- if not path or path == '/':
115
- raise exceptions.GitError(
116
- f'Invalid repository path in URL: {url}')
117
-
118
- return GitUrlInfo(host=host,
119
- path=path,
120
- protocol=protocol,
121
- user=user,
122
- port=port)
123
-
124
- # Pattern for SSH URLs (full format)
125
- ssh_full_pattern = r'^ssh://(?:([^@]+)@)?([^:/]+)(?::(\d+))?/(.+)$'
126
- ssh_full_match = re.match(ssh_full_pattern, clean_url)
127
-
128
- if ssh_full_match:
129
- user, host, port_str, path = ssh_full_match.groups()
130
- port = int(port_str) if port_str else None
131
-
132
- # Validate that path is not empty
133
- if not path or path == '/':
134
- raise exceptions.GitError(
135
- f'Invalid repository path in SSH URL: {url}')
136
-
137
- return GitUrlInfo(host=host,
138
- path=path,
139
- protocol='ssh',
140
- user=user,
141
- port=port)
142
-
143
- # Pattern for SSH SCP-like format (exclude URLs with ://)
144
- scp_pattern = r'^(?:([^@]+)@)?([^:/]+):(.+)$'
145
- scp_match = re.match(scp_pattern, clean_url)
146
-
147
- # Make sure it's not a URL with protocol (should not contain ://)
148
- if scp_match and '://' not in clean_url:
149
- user, host, path = scp_match.groups()
150
-
151
- # Validate that path is not empty
152
- if not path:
153
- raise exceptions.GitError(
154
- f'Invalid repository path in SSH URL: {url}')
155
-
156
- return GitUrlInfo(host=host,
157
- path=path,
158
- protocol='ssh',
159
- user=user,
160
- port=None)
161
-
162
- raise exceptions.GitError(
163
- f'Unsupported git URL format: {url}. '
164
- 'Supported formats: https://host/owner/repo, '
165
- 'ssh://user@host/owner/repo, user@host:owner/repo')
166
-
167
- def get_https_url(self, with_token: bool = False) -> str:
168
- """Get HTTPS URL for the repository.
169
-
170
- Args:
171
- with_token: If True, includes token in URL for authentication
172
-
173
- Returns:
174
- HTTPS URL string.
175
- """
176
- port_str = f':{self._parsed_url.port}' if self._parsed_url.port else ''
177
- path = self._parsed_url.path
178
- # Remove .git suffix if present (but not individual characters)
179
- if path.endswith('.git'):
180
- path = path[:-4]
181
-
182
- if with_token and self.git_token:
183
- return f'https://{self.git_token}@{self._parsed_url.host}' \
184
- f'{port_str}/{path}.git'
185
- return f'https://{self._parsed_url.host}{port_str}/{path}.git'
186
-
187
- def get_ssh_url(self) -> str:
188
- """Get SSH URL for the repository in full format.
189
-
190
- Returns:
191
- SSH URL string in full format.
192
- """
193
- # Use original user from URL, or default to 'git'
194
- ssh_user = self._parsed_url.user or 'git'
195
- port_str = f':{self._parsed_url.port}' if self._parsed_url.port else ''
196
- path = self._parsed_url.path
197
- # Remove .git suffix if present (but not individual characters)
198
- if path.endswith('.git'):
199
- path = path[:-4]
200
- return f'ssh://{ssh_user}@{self._parsed_url.host}{port_str}/{path}.git'
201
-
202
- def get_repo_clone_info(self) -> GitCloneInfo:
203
- """Validate the repository access with comprehensive authentication
204
- and return the appropriate clone info.
205
-
206
- This method implements a sequential validation approach:
207
- 1. Try public access (no authentication)
208
- 2. If has token and URL is https, try token access
209
- 3. If URL is ssh, try ssh access with user provided ssh key or
210
- default ssh credential
211
-
212
- Returns:
213
- GitCloneInfo instance with successful access method.
214
-
215
- Raises:
216
- exceptions.GitError: If the git URL format is invalid or
217
- the repository cannot be accessed.
218
- """
219
- logger.debug(f'Validating access to {self._parsed_url.host}'
220
- f'/{self._parsed_url.path}')
221
-
222
- # Step 1: Try public access first (most common case)
223
- try:
224
- https_url = self.get_https_url()
225
- logger.debug(f'Trying public HTTPS access to {https_url}')
226
-
227
- # Use /info/refs endpoint to check public access.
228
- # This is more reliable than git ls-remote as it doesn't
229
- # use local git config.
230
- stripped_url = https_url.rstrip('/')
231
- info_refs_url = f'{stripped_url}/info/refs?service=git-upload-pack'
232
-
233
- # Make a simple HTTP request without any authentication
234
- response = requests.get(
235
- info_refs_url,
236
- timeout=10,
237
- allow_redirects=True,
238
- # Ensure no local credentials are used
239
- auth=None)
240
-
241
- if response.status_code == 200:
242
- logger.info(
243
- f'Successfully validated repository {https_url} access '
244
- 'using public access')
245
- return GitCloneInfo(url=https_url)
246
- except Exception as e: # pylint: disable=broad-except
247
- logger.debug(f'Public access failed: {str(e)}')
248
-
249
- # Step 2: Try with token if provided
250
- if self.git_token and self._parsed_url.protocol == 'https':
251
- try:
252
- https_url = self.get_https_url()
253
- auth_url = self.get_https_url(with_token=True)
254
- logger.debug(f'Trying token authentication to {https_url}')
255
- git_cmd = git.cmd.Git()
256
- git_cmd.ls_remote(auth_url)
257
- logger.info(
258
- f'Successfully validated repository {https_url} access '
259
- 'using token authentication')
260
- return GitCloneInfo(url=https_url, token=self.git_token)
261
- except Exception as e:
262
- logger.info(f'Token access failed: {str(e)}')
263
- raise exceptions.GitError(
264
- f'Failed to access repository {self.repo_url} using token '
265
- 'authentication. Please verify your token and repository '
266
- f'access permissions. Original error: {str(e)}') from e
267
-
268
- # Step 3: Try SSH access with available keys
269
- if self._parsed_url.protocol == 'ssh':
270
- try:
271
- ssh_url = self.get_ssh_url()
272
-
273
- # Get SSH key info using the combined method
274
- ssh_key_info = self._get_ssh_key_info()
275
-
276
- if ssh_key_info:
277
- key_path, key_content = ssh_key_info
278
- git_ssh_command = f'ssh -F none -i {key_path} ' \
279
- '-o StrictHostKeyChecking=no ' \
280
- '-o UserKnownHostsFile=/dev/null ' \
281
- '-o IdentitiesOnly=yes'
282
- ssh_env = {'GIT_SSH_COMMAND': git_ssh_command}
283
-
284
- logger.debug(f'Trying SSH authentication to {ssh_url} '
285
- f'with {key_path}')
286
- git_cmd = git.cmd.Git()
287
- git_cmd.update_environment(**ssh_env)
288
- git_cmd.ls_remote(ssh_url)
289
- logger.info(
290
- f'Successfully validated repository {ssh_url} access '
291
- f'using SSH key: {key_path}')
292
- return GitCloneInfo(url=ssh_url,
293
- ssh_key=key_content,
294
- envs=ssh_env)
295
- else:
296
- raise exceptions.GitError(
297
- f'No SSH keys found for {self.repo_url}.')
298
- except Exception as e: # pylint: disable=broad-except
299
- raise exceptions.GitError(
300
- f'Failed to access repository {self.repo_url} using '
301
- 'SSH key authentication. Please verify your SSH key and '
302
- 'repository access permissions. '
303
- f'Original error: {str(e)}') from e
304
-
305
- # If we get here, no authentication methods are available
306
- raise exceptions.GitError(
307
- f'Failed to access repository {self.repo_url}. '
308
- 'If this is a private repository, please provide authentication'
309
- f' using either: GIT_TOKEN for token-based access, or'
310
- f' GIT_SSH_KEY_PATH for SSH access.')
311
-
312
- def _parse_ssh_config(self) -> Optional[str]:
313
- """Parse SSH config file to find IdentityFile for the target host.
314
-
315
- Returns:
316
- Path to SSH private key specified in config, or None if not found.
317
- """
318
- ssh_config_path = os.path.expanduser('~/.ssh/config')
319
- if not os.path.exists(ssh_config_path):
320
- logger.debug('SSH config file ~/.ssh/config does not exist')
321
- return None
322
-
323
- try:
324
- # Try to use paramiko's SSH config parser if available
325
- try:
326
- import paramiko # pylint: disable=import-outside-toplevel
327
- ssh_config = paramiko.SSHConfig()
328
- with open(ssh_config_path, 'r', encoding='utf-8') as f:
329
- ssh_config.parse(f)
330
- # Get config for the target host
331
- host_config = ssh_config.lookup(self._parsed_url.host)
332
-
333
- # Look for identity files in the config
334
- identity_files: Union[str, List[str]] = host_config.get(
335
- 'identityfile', [])
336
- if not isinstance(identity_files, list):
337
- identity_files = [identity_files]
338
-
339
- # Find the first existing identity file
340
- for identity_file in identity_files:
341
- key_path = os.path.expanduser(identity_file)
342
- if os.path.exists(key_path):
343
- logger.debug(f'Found SSH key in config for '
344
- f'{self._parsed_url.host}: {key_path}')
345
- return key_path
346
-
347
- logger.debug(f'No valid SSH keys found in config for host: '
348
- f'{self._parsed_url.host}')
349
- return None
350
-
351
- except ImportError:
352
- logger.debug('paramiko not available')
353
- return None
354
-
355
- except Exception as e: # pylint: disable=broad-except
356
- logger.debug(f'Error parsing SSH config: {str(e)}')
357
- return None
358
-
359
- def _get_ssh_key_info(self) -> Optional[tuple]:
360
- """Get SSH key path and content using comprehensive strategy.
361
-
362
- Strategy:
363
- 1. Check provided git_ssh_key_path if given
364
- 2. Check SSH config for host-specific IdentityFile
365
- 3. Search for common SSH key types in ~/.ssh/ directory
366
-
367
- Returns:
368
- Tuple of (key_path, key_content) if found, None otherwise.
369
- """
370
- # Step 1: Check provided SSH key path first
371
- if self.git_ssh_key_path:
372
- try:
373
- key_path = os.path.expanduser(self.git_ssh_key_path)
374
-
375
- # Validate SSH key before using it
376
- if not os.path.exists(key_path):
377
- raise exceptions.GitError(
378
- f'SSH key not found at path: {self.git_ssh_key_path}')
379
-
380
- # Check key permissions
381
- key_stat = os.stat(key_path)
382
- if key_stat.st_mode & 0o077:
383
- logger.warning(
384
- f'SSH key {key_path} has too open permissions. '
385
- f'Recommended: chmod 600 {key_path}')
386
-
387
- # Check if it's a valid private key and read content
388
- with open(key_path, 'r', encoding='utf-8') as f:
389
- key_content = f.read()
390
- if not (key_content.startswith('-----BEGIN') and
391
- 'PRIVATE KEY' in key_content):
392
- raise exceptions.GitError(
393
- f'SSH key {key_path} is invalid.')
394
-
395
- logger.debug(f'Using provided SSH key: {key_path}')
396
- return (key_path, key_content)
397
- except Exception as e: # pylint: disable=broad-except
398
- raise exceptions.GitError(
399
- f'Validate provided SSH key error: {str(e)}') from e
400
-
401
- # Step 2: Check SSH config for host-specific configuration
402
- config_key_path = self._parse_ssh_config()
403
- if config_key_path:
404
- try:
405
- with open(config_key_path, 'r', encoding='utf-8') as f:
406
- key_content = f.read()
407
- logger.debug(f'Using SSH key from config: {config_key_path}')
408
- return (config_key_path, key_content)
409
- except Exception as e: # pylint: disable=broad-except
410
- logger.debug(f'Could not read SSH key: {str(e)}')
411
-
412
- # Step 3: Search for default SSH keys
413
- ssh_dir = os.path.expanduser('~/.ssh')
414
- if not os.path.exists(ssh_dir):
415
- logger.debug('SSH directory ~/.ssh does not exist')
416
- return None
417
-
418
- # Common SSH key file names in order of preference
419
- key_candidates = [
420
- 'id_rsa', # Most common
421
- 'id_ed25519', # Modern, recommended
422
- ]
423
-
424
- for key_name in key_candidates:
425
- private_key_path = os.path.join(ssh_dir, key_name)
426
-
427
- # Check if both private and public keys exist
428
- if not os.path.exists(private_key_path):
429
- continue
430
-
431
- # Check private key permissions
432
- try:
433
- key_stat = os.stat(private_key_path)
434
- if key_stat.st_mode & 0o077:
435
- logger.warning(
436
- f'SSH key {private_key_path} has too open permissions. '
437
- f'Consider: chmod 600 {private_key_path}')
438
-
439
- # Validate private key format and read content
440
- with open(private_key_path, 'r', encoding='utf-8') as f:
441
- key_content = f.read()
442
- if not (key_content.startswith('-----BEGIN') and
443
- 'PRIVATE KEY' in key_content):
444
- logger.debug(f'SSH key {private_key_path} is invalid.')
445
- continue
446
-
447
- logger.debug(f'Discovered default SSH key: {private_key_path}')
448
- return (private_key_path, key_content)
449
-
450
- except Exception as e: # pylint: disable=broad-except
451
- logger.debug(
452
- f'Error checking SSH key {private_key_path}: {str(e)}')
453
- continue
454
-
455
- logger.debug('No suitable SSH keys found')
456
- return None
457
-
458
- def get_ref_type(self) -> GitRefType:
459
- """Get the type of the reference.
460
-
461
- Returns:
462
- GitRefType.COMMIT if it's a commit hash,
463
- GitRefType.BRANCH if it's a branch,
464
- GitRefType.TAG if it's a tag.
465
-
466
- Raises:
467
- exceptions.GitError: If the reference is invalid.
468
- """
469
- clone_info = self.get_repo_clone_info()
470
- git_cmd = git.cmd.Git()
471
- if clone_info.envs:
472
- git_cmd.update_environment(**clone_info.envs)
473
-
474
- try:
475
- # Get all remote refs
476
- refs = git_cmd.ls_remote(clone_info.url).split('\n')
477
-
478
- # Collect all commit hashes from refs
479
- all_commit_hashes = set()
480
-
481
- # Check if it's a branch or tag name
482
- for ref in refs:
483
- if not ref:
484
- continue
485
- hash_val, ref_name = ref.split('\t')
486
-
487
- # Store the commit hash for later validation
488
- all_commit_hashes.add(hash_val)
489
-
490
- # Check if it's a branch
491
- if ref_name.startswith(
492
- 'refs/heads/') and ref_name[11:] == self.ref:
493
- return GitRefType.BRANCH
494
-
495
- # Check if it's a tag
496
- if ref_name.startswith(
497
- 'refs/tags/') and ref_name[10:] == self.ref:
498
- return GitRefType.TAG
499
-
500
- # If we get here, it's not a branch or tag name
501
- # Check if it looks like a commit hash (hex string)
502
- if len(self.ref) >= 4 and all(
503
- c in '0123456789abcdef' for c in self.ref.lower()):
504
- # First check if it's a complete match with any known commit
505
- if self.ref in all_commit_hashes:
506
- logger.debug(f'Found exact commit hash match: {self.ref}')
507
- return GitRefType.COMMIT
508
-
509
- # Check if it's a prefix match with any known commit
510
- matching_commits = [
511
- h for h in all_commit_hashes if h.startswith(self.ref)
512
- ]
513
- if len(matching_commits) == 1:
514
- logger.debug(
515
- f'Found commit hash prefix match: {self.ref} -> '
516
- f'{matching_commits[0]}')
517
- return GitRefType.COMMIT
518
- elif len(matching_commits) > 1:
519
- # Multiple matches - ambiguous
520
- raise exceptions.GitError(
521
- f'Ambiguous commit hash {self.ref!r}. '
522
- f'Multiple commits match: '
523
- f'{", ".join(matching_commits[:5])}...')
524
-
525
- # If no match found in ls-remote output, we can't verify
526
- # the commit exists. This could be a valid commit that's
527
- # not at the tip of any branch/tag. We'll assume it's valid
528
- # if it looks like a commit hash and let git handle validation
529
- # during clone.
530
- logger.debug(f'Commit hash not found in ls-remote output, '
531
- f'assuming valid: {self.ref}')
532
- logger.warning(
533
- f'Cannot verify commit {self.ref} exists - it may be a '
534
- 'commit in history not at any branch/tag tip')
535
- return GitRefType.COMMIT
536
-
537
- # If it's not a branch, tag, or hex string, it's invalid
538
- raise exceptions.GitError(
539
- f'Git reference {self.ref!r} not found. '
540
- 'Please provide a valid branch, tag, or commit hash.')
541
-
542
- except git.exc.GitCommandError as e:
543
- if not (self.git_token or self.git_ssh_key_path):
544
- raise exceptions.GitError(
545
- 'Failed to check repository. If this is a private '
546
- 'repository, please provide authentication using either '
547
- 'GIT_TOKEN or GIT_SSH_KEY_PATH.') from e
548
- raise exceptions.GitError(
549
- f'Failed to check git reference: {str(e)}') from e
@@ -1 +0,0 @@
1
- "use strict";(self.webpackChunk_N_E=self.webpackChunk_N_E||[]).push([[1121],{50326:function(e,t,a){a.d(t,{$N:function(){return _},Be:function(){return g},Vq:function(){return c},cN:function(){return m},cZ:function(){return d},fK:function(){return f}});var r=a(85893),s=a(67294),o=a(6327),n=a(32350),l=a(43767);let c=o.fC;o.xz;let u=o.h_;o.x8;let i=s.forwardRef((e,t)=>{let{className:a,...s}=e;return(0,r.jsx)(o.aV,{ref:t,className:(0,n.cn)("fixed inset-0 z-50 bg-black/50 backdrop-blur-sm data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0",a),...s})});i.displayName=o.aV.displayName;let d=s.forwardRef((e,t)=>{let{className:a,children:s,...c}=e;return(0,r.jsxs)(u,{children:[(0,r.jsx)(i,{}),(0,r.jsxs)(o.VY,{ref:t,className:(0,n.cn)("fixed left-[50%] top-[50%] z-50 grid w-full max-w-lg translate-x-[-50%] translate-y-[-50%] gap-4 border border-gray-200 bg-white p-6 shadow-lg duration-200 data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[state=closed]:slide-out-to-left-1/2 data-[state=closed]:slide-out-to-top-[48%] data-[state=open]:slide-in-from-left-1/2 data-[state=open]:slide-in-from-top-[48%] sm:rounded-lg",a),...c,children:[s,(0,r.jsxs)(o.x8,{className:"absolute right-4 top-4 rounded-sm opacity-70 ring-offset-white transition-opacity hover:opacity-100 focus:outline-none focus:ring-2 focus:ring-gray-400 focus:ring-offset-2 disabled:pointer-events-none data-[state=open]:bg-gray-100 data-[state=open]:text-gray-500",children:[(0,r.jsx)(l.Z,{className:"h-4 w-4"}),(0,r.jsx)("span",{className:"sr-only",children:"Close"})]})]})]})});d.displayName=o.VY.displayName;let f=e=>{let{className:t,...a}=e;return(0,r.jsx)("div",{className:(0,n.cn)("flex flex-col space-y-1.5 text-center sm:text-left",t),...a})};f.displayName="DialogHeader";let m=e=>{let{className:t,...a}=e;return(0,r.jsx)("div",{className:(0,n.cn)("flex flex-col-reverse sm:flex-row sm:justify-end sm:space-x-2",t),...a})};m.displayName="DialogFooter";let _=s.forwardRef((e,t)=>{let{className:a,...s}=e;return(0,r.jsx)(o.Dx,{ref:t,className:(0,n.cn)("text-lg font-semibold leading-none tracking-tight",a),...s})});_.displayName=o.Dx.displayName;let g=s.forwardRef((e,t)=>{let{className:a,...s}=e;return(0,r.jsx)(o.dk,{ref:t,className:(0,n.cn)("text-sm text-gray-500",a),...s})});g.displayName=o.dk.displayName},23266:function(e,t,a){a.d(t,{GH:function(){return f},QL:function(){return _},Sl:function(){return d},getClusters:function(){return u},uR:function(){return i}});var r=a(67294),s=a(15821),o=a(47145),n=a(93225),l=a(6378);let c={UP:"RUNNING",STOPPED:"STOPPED",INIT:"LAUNCHING",null:"TERMINATED"};async function u(){let{clusterNames:e=null}=arguments.length>0&&void 0!==arguments[0]?arguments[0]:{};try{return(await o.x.fetch("/status",{cluster_names:e,all_users:!0,include_credentials:!1})).map(e=>{let t="",a=t=e.zone?e.zone:e.region;return t&&t.length>25&&(t=function(e){let t=arguments.length>1&&void 0!==arguments[1]?arguments[1]:15;if(!e||e.length<=t)return e;if(t<=3)return"...";let a=Math.floor((t-3)/2),r=a+(t-3)%2;return 0===a?e.substring(0,r)+"...":e.substring(0,r)+"..."+e.substring(e.length-a)}(t,25)),{status:c[e.status],cluster:e.name,user:e.user_name,user_hash:e.user_hash,cluster_hash:e.cluster_hash,cloud:e.cloud,region:e.region,infra:t?e.cloud+" ("+t+")":e.cloud,full_infra:a?"".concat(e.cloud," (").concat(a,")"):e.cloud,cpus:e.cpus,mem:e.memory,gpus:e.accelerators,resources_str:e.resources_str,resources_str_full:e.resources_str_full,time:new Date(1e3*e.launched_at),num_nodes:e.nodes,workspace:e.workspace,autostop:e.autostop,last_event:e.last_event,to_down:e.to_down,jobs:[],command:e.last_creation_command||e.last_use,task_yaml:e.last_creation_yaml||"{}",events:[{time:new Date(1e3*e.launched_at),event:"Cluster created."}]}})}catch(e){return console.error("Error fetching clusters:",e),[]}}async function i(){try{let e=await o.x.fetch("/cost_report",{days:30});console.log("Raw cluster history data:",e);let t=e.map(e=>{let t="Unknown";e.cloud?t=e.cloud:e.resources&&e.resources.cloud&&(t=e.resources.cloud);let a=e.user_name||"-";return{status:e.status?c[e.status]:"TERMINATED",cluster:e.name,user:a,user_hash:e.user_hash,cluster_hash:e.cluster_hash,cloud:t,region:"",infra:t,full_infra:t,resources_str:e.resources_str,resources_str_full:e.resources_str_full,time:e.launched_at?new Date(1e3*e.launched_at):null,num_nodes:e.num_nodes||1,duration:e.duration,total_cost:e.total_cost,workspace:e.workspace||"default",autostop:-1,last_event:e.last_event,to_down:!1,usage_intervals:e.usage_intervals,command:e.last_creation_command||"",task_yaml:e.last_creation_yaml||"{}",events:[{time:e.launched_at?new Date(1e3*e.launched_at):new Date,event:"Cluster created."}]}});return console.log("Processed cluster history data:",t),t}catch(e){return console.error("Error fetching cluster history:",e),[]}}async function d(e){let{clusterName:t,jobId:a,onNewLog:r,workspace:n}=e;try{await o.x.stream("/logs",{follow:!1,cluster_name:t,job_id:a,tail:1e4,override_skypilot_config:{active_workspace:n||"default"}},r)}catch(e){console.error("Error in streamClusterJobLogs:",e),(0,s.C)("Error in streamClusterJobLogs: ".concat(e.message),"error")}}async function f(e){let{clusterName:t,jobIds:a=null,workspace:r}=e;try{let e=await o.x.fetch("/download_logs",{cluster_name:t,job_ids:a?a.map(String):null,override_skypilot_config:{active_workspace:r||"default"}}),l=Object.values(e||{});if(!l.length){(0,s.C)("No logs found to download.","warning");return}let c=window.location.origin,u="".concat(c).concat(n.f4,"/download"),i=await fetch("".concat(u,"?relative=items"),{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({folder_paths:l})});if(!i.ok){let e=await i.text();throw Error("Download failed: ".concat(i.status," ").concat(e))}let d=await i.blob(),f=window.URL.createObjectURL(d),m=document.createElement("a"),_=new Date().toISOString().replace(/[:.]/g,"-"),g=a&&1===a.length?"job-".concat(a[0]):"jobs";m.href=f,m.download="".concat(t,"-").concat(g,"-logs-").concat(_,".zip"),document.body.appendChild(m),m.click(),m.remove(),window.URL.revokeObjectURL(f)}catch(e){console.error("Error downloading logs:",e),(0,s.C)("Error downloading logs: ".concat(e.message),"error")}}async function m(e){let{clusterName:t,workspace:a}=e;try{return(await o.x.fetch("/queue",{cluster_name:t,all_users:!0,override_skypilot_config:{active_workspace:a}})).map(e=>{var r;let s=e.end_at?e.end_at:Date.now()/1e3,o=0,n=0;return e.submitted_at&&(o=s-e.submitted_at),e.start_at&&(n=s-e.start_at),{id:e.job_id,status:e.status,job:e.job_name,user:e.username,user_hash:e.user_hash,gpus:e.accelerators||{},submitted_at:e.submitted_at?new Date(1e3*e.submitted_at):null,resources:e.resources,cluster:t,total_duration:o,job_duration:n,infra:"",logs:"",workspace:a||"default",git_commit:(null===(r=e.metadata)||void 0===r?void 0:r.git_commit)||"-"}})}catch(e){return console.error("Error fetching cluster jobs:",e),[]}}function _(e){let{cluster:t,job:a=null}=e,[s,o]=(0,r.useState)(null),[n,c]=(0,r.useState)(null),[i,d]=(0,r.useState)(!0),[f,_]=(0,r.useState)(!0),g=(0,r.useCallback)(async()=>{if(t)try{d(!0);let e=await l.default.get(u,[{clusterNames:[t]}]);return o(e[0]),e[0]}catch(e){console.error("Error fetching cluster data:",e)}finally{d(!1)}return null},[t]),h=(0,r.useCallback)(async e=>{if(t)try{_(!0);let a=await l.default.get(m,[{clusterName:t,workspace:e||"default"}]);c(a)}catch(e){console.error("Error fetching cluster job data:",e)}finally{_(!1)}},[t]),p=(0,r.useCallback)(async()=>{l.default.invalidate(u,[{clusterNames:[t]}]);let e=await g();e&&(l.default.invalidate(m,[{clusterName:t,workspace:e.workspace||"default"}]),await h(e.workspace))},[g,h,t]),w=(0,r.useCallback)(async()=>{s&&(l.default.invalidate(m,[{clusterName:t,workspace:s.workspace||"default"}]),await h(s.workspace))},[h,s,t]);return(0,r.useEffect)(()=>{(async()=>{let e=await g();e&&h(e.workspace)})()},[t,a,g,h]),{clusterData:s,clusterJobData:n,loading:i,clusterDetailsLoading:i,clusterJobsLoading:f,refreshData:p,refreshClusterJobsOnly:w}}},53081:function(e,t,a){a.d(t,{R:function(){return s}}),a(23266),a(68969);var r=a(47145);async function s(){try{let e=await r.x.get("/users");if(!e.ok)throw Error("HTTP error! status: ".concat(e.status));return(await e.json()).map(e=>({userId:e.id,username:e.name,role:e.role,created_at:e.created_at}))||[]}catch(e){return console.error("Failed to fetch users:",e),[]}}}}]);
@@ -1 +0,0 @@
1
- "use strict";(self.webpackChunk_N_E=self.webpackChunk_N_E||[]).push([[1141],{1812:function(e,s,r){r.d(s,{X:function(){return n}});var l=r(85893),t=r(67294);let a=e=>{if(!(null==e?void 0:e.message))return"An unexpected error occurred.";let s=e.message;return s.includes("failed:")&&(s=s.split("failed:")[1].trim()),s},n=e=>{let{error:s,title:r="Error",onDismiss:n}=e,[c,i]=(0,t.useState)(!1);if((0,t.useEffect)(()=>{s&&i(!1)},[s]),!s||c)return null;let o="string"==typeof s?s:a(s);return(0,l.jsx)("div",{className:"bg-red-50 border border-red-200 rounded-md p-3 mb-4",children:(0,l.jsxs)("div",{className:"flex items-center justify-between",children:[(0,l.jsxs)("div",{className:"flex",children:[(0,l.jsx)("div",{className:"flex-shrink-0",children:(0,l.jsx)("svg",{className:"h-5 w-5 text-red-400",viewBox:"0 0 20 20",fill:"currentColor",children:(0,l.jsx)("path",{fillRule:"evenodd",d:"M10 18a8 8 0 100-16 8 8 0 000 16zM8.707 7.293a1 1 0 00-1.414 1.414L8.586 10l-1.293 1.293a1 1 0 101.414 1.414L10 11.414l1.293 1.293a1 1 0 001.414-1.414L11.414 10l1.293-1.293a1 1 0 00-1.414-1.414L10 8.586 8.707 7.293z",clipRule:"evenodd"})})}),(0,l.jsx)("div",{className:"ml-3",children:(0,l.jsxs)("div",{className:"text-sm text-red-800",children:[(0,l.jsxs)("strong",{children:[r,":"]})," ",o]})})]}),(0,l.jsx)("button",{onClick:()=>{i(!0),n&&n()},className:"flex-shrink-0 ml-4 text-red-400 hover:text-red-600 focus:outline-none focus:ring-2 focus:ring-red-500 focus:ring-offset-2 focus:ring-offset-red-50 rounded","aria-label":"Dismiss error",children:(0,l.jsx)("svg",{className:"h-4 w-4",viewBox:"0 0 20 20",fill:"currentColor",children:(0,l.jsx)("path",{fillRule:"evenodd",d:"M4.293 4.293a1 1 0 011.414 0L10 8.586l4.293-4.293a1 1 0 111.414 1.414L11.414 10l4.293 4.293a1 1 0 01-1.414 1.414L10 11.414l-4.293 4.293a1 1 0 01-1.414-1.414L8.586 10 4.293 5.707a1 1 0 010-1.414z",clipRule:"evenodd"})})})]})})}},69123:function(e,s,r){r.d(s,{g:function(){return n}});var l=r(85893),t=r(67294),a=r(32350);let n=t.forwardRef((e,s)=>{let{className:r,...t}=e;return(0,l.jsx)("textarea",{className:(0,a.cn)("flex min-h-[80px] w-full rounded-md border border-input bg-background px-3 py-2 text-sm ring-offset-background placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50",r),ref:s,...t})});n.displayName="Textarea"},11141:function(e,s,r){r.r(s),r.d(s,{WorkspaceEditor:function(){return _}});var l=r(85893),t=r(67294),a=r(11163),n=r(17324),c=r(23266),i=r(68969);r(6135);var o=r(41664),d=r.n(o),u=r(9008),x=r.n(u),m=r(37673),h=r(30803),f=r(69123),g=r(55739),p=r(70282),b=r(6021),j=r(13626),y=r(98418),N=r(99333),w=r(50326);let v=e=>{let{className:s="",variant:r="default",children:t,...a}=e;return(0,l.jsx)("div",{role:"alert",className:"".concat("relative w-full rounded-lg border p-4 flex items-start space-x-2"," ").concat({default:"bg-blue-50 border-blue-200 text-blue-800",destructive:"bg-red-50 border-red-200 text-red-800"}[r]," ").concat(s),...a,children:t})},k=e=>{let{className:s="",children:r,...t}=e;return(0,l.jsx)("div",{className:"text-sm leading-relaxed ".concat(s),...t,children:r})};var C=r(53850),L=r(1812),E=r(23015),S=r(1272),A=r(93225),W=r(53081);let D=e=>{let{message:s}=e;return s?(0,l.jsxs)(v,{className:"border-green-200 bg-green-50",children:[(0,l.jsx)(p.Z,{className:"h-4 w-4 text-green-600"}),(0,l.jsx)(k,{className:"text-green-800",children:s})]}):null},P=e=>{let{workspaceName:s,config:r,enabledClouds:t=[]}=e;if(!r)return null;let a="default"===s,n=0===Object.keys(r).length;if(a&&n)return(0,l.jsx)("div",{className:"text-sm text-gray-500 mb-3 italic p-3 bg-sky-50 rounded border border-sky-200",children:"Workspace 'default' can use all accessible infrastructure."});let c=[],i=[],o=[],d=new Set(t.map(e=>e.toLowerCase()));Object.entries(r).forEach(e=>{let[s,r]=e;if("private"===s||"allowed_users"===s)return;let t=A.Z2[s.toLowerCase()]||s.toUpperCase(),a=null==t?void 0:t.toLowerCase(),n=d.has(a)||Array.from(d).some(e=>e.startsWith(a+"/")),u=()=>"kubernetes"===s.toLowerCase()?Array.from(d).filter(e=>e.startsWith(a+"/")).map(e=>e.split("/")[1]):[];if((null==r?void 0:r.disabled)===!0)i.push(t);else if(r&&Object.keys(r).length>0){let e="";if("gcp"===s.toLowerCase()&&r.project_id)e=" (Project ID: ".concat(r.project_id,")");else if("aws"===s.toLowerCase()&&r.region)e=" (Region: ".concat(r.region,")");else if("kubernetes"===s.toLowerCase()){let s=u();s.length>0&&(e=" (Contexts: ".concat(s.join(", "),")"))}n?c.push((0,l.jsxs)("span",{className:"block",children:[t,e," is enabled."]},"".concat(s,"-enabled"))):o.push((0,l.jsxs)("span",{className:"block text-amber-700",children:[t,e," is configured but not currently available."]},"".concat(s,"-configured-not-enabled")))}else if(n){let e="";if("kubernetes"===s.toLowerCase()){let s=u();s.length>0&&(e=" (Contexts: ".concat(s.join(", "),")"))}c.push((0,l.jsxs)("span",{className:"block",children:[t,e," is enabled (using default settings)."]},"".concat(s,"-default-enabled")))}else o.push((0,l.jsxs)("span",{className:"block text-amber-700",children:[t," is configured but not currently available."]},"".concat(s,"-default-not-enabled")))});let u=[];if(i.length>0){let e=i.join(" and ");u.push((0,l.jsxs)("span",{className:"block",children:[e," ",1===i.length?"is":"are"," explicitly disabled."]},"disabled-clouds"))}return(u.push(...c),u.push(...o),u.length>0)?(0,l.jsx)("div",{className:"text-sm text-gray-700 mb-3 p-3 bg-sky-50 rounded border border-sky-200",children:u}):!a&&n?(0,l.jsx)("div",{className:"text-sm text-gray-500 mb-3 italic p-3 bg-sky-50 rounded border border-sky-200",children:"This workspace has no specific cloud resource configurations and can use all accessible infrastructure."}):null},R=e=>{let{isPrivate:s}=e;return s?(0,l.jsx)("span",{className:"inline-flex items-center px-2 py-1 rounded-full text-xs font-medium bg-gray-100 text-gray-700 border border-gray-300",children:"Private"}):(0,l.jsx)("span",{className:"inline-flex items-center px-2 py-1 rounded-full text-xs font-medium bg-green-100 text-green-700 border border-green-300",children:"Public"})},Z=e=>{let{workspaceConfig:s,allUsers:r}=e;if(!s.private)return null;let t=s.allowed_users||[],a=(r||[]).filter(e=>"admin"===e.role).map(e=>e.username),n=[...new Set([...t,...a])];return 0===n.length?(0,l.jsxs)("div",{className:"mt-4",children:[(0,l.jsx)("h4",{className:"mb-2 text-xs text-gray-500 tracking-wider",children:"Allowed Users (0)"}),(0,l.jsx)("div",{className:"text-amber-600 text-xs italic p-2 bg-amber-50 rounded border border-amber-200",children:"No users configured (workspace may be inaccessible)"})]}):(0,l.jsxs)("div",{className:"mt-4",children:[(0,l.jsxs)("h4",{className:"mb-2 text-xs text-gray-500 tracking-wider",children:["Allowed Users (",n.length,")"]}),(0,l.jsx)("div",{className:"space-y-1 max-h-48 overflow-y-auto border border-gray-200 rounded",children:n.map(e=>{let s=a.includes(e);return(0,l.jsxs)("div",{className:"flex items-center justify-between text-xs p-2 bg-gray-50 hover:bg-gray-100 border-b border-gray-100 last:border-b-0",children:[(0,l.jsx)("span",{className:"font-medium text-gray-700",children:e}),s?(0,l.jsxs)("span",{className:"inline-flex items-center text-blue-600",children:[(0,l.jsx)(C.r7,{className:"w-3 h-3 mr-1"}),"Admin"]}):(0,l.jsxs)("span",{className:"inline-flex items-center text-gray-600",children:[(0,l.jsx)(b.Z,{className:"w-3 h-3 mr-1"}),"User"]})]},e)})})]})};function _(e){let{workspaceName:s,isNewWorkspace:r=!1}=e,o=(0,a.useRouter)(),[u,p]=(0,t.useState)({}),[b,v]=(0,t.useState)({}),[k,A]=(0,t.useState)(""),[_,z]=(0,t.useState)(!0),[M,O]=(0,t.useState)(!1),[U,Y]=(0,t.useState)(!1),[J,T]=(0,t.useState)(null),[I,F]=(0,t.useState)(null),[X,B]=(0,t.useState)(null),[G,H]=(0,t.useState)([]),[V,q]=(0,t.useState)({showDialog:!1,deleting:!1,error:null}),[K,Q]=(0,t.useState)({totalClusterCount:0,runningClusterCount:0,managedJobsCount:0,clouds:[]}),[$,ee]=(0,t.useState)(!1),es=(0,t.useCallback)(async()=>{z(!0),T(null);try{let e;let[r,l]=await Promise.all([(0,n.fX)(),(0,W.R)()]),t=r[s]||{};p(t),v(t),H(l||[]),e=0===Object.keys(t).length?"".concat(s,":\n # Empty workspace configuration - uses all accessible infrastructure\n"):S.ZP.dump({[s]:t},{indent:2,lineWidth:-1,noRefs:!0,skipInvalid:!0,flowLevel:-1}),A(e)}catch(e){console.error("Error fetching workspace config:",e),T(e)}finally{z(!1)}},[s]),er=(0,t.useCallback)(async()=>{if(!r){ee(!0);try{let[e,r,l]=await Promise.all([(0,c.getClusters)(),(0,i.getManagedJobs)({allUsers:!0}),(0,n.yz)(s,!0)]),t=e.filter(e=>(e.workspace||"default")===s),a=t.filter(e=>"RUNNING"===e.status||"LAUNCHING"===e.status),o={};e.forEach(e=>{o[e.cluster]=e.workspace||"default"});let d=r.jobs||[],u=new Set(E.statusGroups.active),x=0;d.forEach(e=>{let r=e.cluster_name||e.resources&&e.resources.cluster_name;r&&o[r]===s&&u.has(e.status)&&x++}),Q({totalClusterCount:t.length,runningClusterCount:a.length,managedJobsCount:x,clouds:Array.isArray(l)?l:[]})}catch(e){console.error("Failed to fetch workspace stats:",e)}finally{ee(!1)}}},[s,r]);(0,t.useEffect)(()=>{r?(z(!1),A("".concat(s,":\n # New workspace configuration\n # Leave empty to use all accessible infrastructure\n"))):(es(),er())},[s,r,es,er]),(0,t.useEffect)(()=>{Y(JSON.stringify(u)!==JSON.stringify(b))},[u,b]);let el=e=>{A(e),B(null);try{let r=S.ZP.load(e)||{},l=Object.keys(r);if(0===l.length)p({});else if(1===l.length){let e=l[0];if(e!==s){B('Workspace name cannot be changed. Expected "'.concat(s,'" but found "').concat(e,'".'));return}let t=r[s]||{};p(t)}else B("Configuration must contain only one workspace. Found: ".concat(l.join(", ")))}catch(e){B("Invalid YAML: ".concat(e.message))}},et=async()=>{O(!0),T(null),F(null);try{if(X)throw Error("Please fix YAML errors before saving");let e=S.ZP.load(k)||{},l=Object.keys(e);if(l.length>0&&l[0]!==s)throw Error('Workspace name cannot be changed. Expected "'.concat(s,'".'));r?(await (0,n.MB)(s,u),F("Workspace created successfully!"),setTimeout(()=>{o.push("/workspaces/".concat(s))},1500)):(await (0,n.eA)(s,u),F("Workspace updated successfully!"),v(u),er())}catch(e){console.error("Error saving workspace:",e),T(e)}finally{O(!1)}},ea=async()=>{q(e=>({...e,deleting:!0,error:null}));try{await (0,n.zl)(s),F("Workspace deleted successfully!"),setTimeout(()=>{o.push("/workspaces")},1500)}catch(e){console.error("Error deleting workspace:",e),q(s=>({...s,deleting:!1,error:e}))}},en=()=>{q({showDialog:!1,deleting:!1,error:null})},ec=async()=>{await Promise.all([es(),er()])};if(!o.isReady)return(0,l.jsx)("div",{children:"Loading..."});let ei=r?"Create New Workspace | SkyPilot Dashboard":"Workspace: ".concat(s," | SkyPilot Dashboard");return(0,l.jsxs)(l.Fragment,{children:[(0,l.jsx)(x(),{children:(0,l.jsx)("title",{children:ei})}),(0,l.jsxs)(l.Fragment,{children:[(0,l.jsxs)("div",{className:"flex items-center justify-between mb-4 h-5",children:[(0,l.jsxs)("div",{className:"text-base flex items-center",children:[(0,l.jsx)(d(),{href:"/workspaces",className:"text-sky-blue hover:underline",children:"Workspaces"}),(0,l.jsx)("span",{className:"mx-2 text-gray-500",children:"›"}),(0,l.jsx)(d(),{href:r?"/workspace/new":"/workspaces/".concat(s),className:"text-sky-blue hover:underline",children:r?"New Workspace":s}),U&&(0,l.jsx)("span",{className:"ml-3 px-2 py-1 bg-yellow-100 text-yellow-800 text-xs rounded",children:"Unsaved changes"})]}),(0,l.jsxs)("div",{className:"text-sm flex items-center",children:[(_||M||$)&&(0,l.jsxs)("div",{className:"flex items-center mr-4",children:[(0,l.jsx)(g.Z,{size:15,className:"mt-0"}),(0,l.jsx)("span",{className:"ml-2 text-gray-500",children:M?"Saving...":"Loading..."})]}),(0,l.jsxs)("div",{className:"flex items-center space-x-4",children:[!r&&(0,l.jsxs)("button",{onClick:ec,disabled:_||M||$,className:"text-sky-blue hover:text-sky-blue-bright font-medium inline-flex items-center",children:[(0,l.jsx)(j.Z,{className:"w-4 h-4 mr-1.5"}),"Refresh"]}),!r&&"default"!==s&&(0,l.jsxs)("button",{onClick:()=>q({...V,showDialog:!0}),disabled:V.deleting||M,className:"text-red-600 hover:text-red-700 font-medium inline-flex items-center",children:[(0,l.jsx)(y.Z,{className:"w-4 h-4 mr-1.5"}),"Delete"]})]})]})]}),_?(0,l.jsxs)("div",{className:"flex justify-center items-center py-12",children:[(0,l.jsx)(g.Z,{size:24,className:"mr-2"}),(0,l.jsx)("span",{className:"text-gray-500",children:"Loading workspace configuration..."})]}):(0,l.jsxs)("div",{className:"space-y-6",children:[(0,l.jsx)(L.X,{error:J,title:"Error",onDismiss:()=>T(null)}),(0,l.jsx)(D,{message:I}),(0,l.jsxs)("div",{className:"grid grid-cols-1 lg:grid-cols-3 gap-6",children:[!r&&(0,l.jsx)("div",{className:"lg:col-span-1",children:(0,l.jsxs)(m.Zb,{className:"h-full",children:[(0,l.jsx)(m.Ol,{children:(0,l.jsx)(m.ll,{className:"text-base font-normal",children:(0,l.jsxs)("div",{className:"flex items-center justify-between",children:[(0,l.jsxs)("div",{children:[(0,l.jsx)("span",{className:"font-semibold",children:"Workspace:"})," ",s]}),(0,l.jsx)(R,{isPrivate:!0===b.private})]})})}),(0,l.jsxs)(m.aY,{className:"text-sm pb-2 flex-1",children:[(0,l.jsxs)("div",{className:"py-2 flex items-center justify-between",children:[(0,l.jsxs)("div",{className:"flex items-center text-gray-600",children:[(0,l.jsx)(C.QT,{className:"w-4 h-4 mr-2 text-gray-500"}),(0,l.jsx)("span",{children:"Clusters (Running / Total)"})]}),(0,l.jsx)("span",{className:"font-normal text-gray-800",children:$?"...":"".concat(K.runningClusterCount," / ").concat(K.totalClusterCount)})]}),(0,l.jsxs)("div",{className:"py-2 flex items-center justify-between border-t border-gray-100",children:[(0,l.jsxs)("div",{className:"flex items-center text-gray-600",children:[(0,l.jsx)(C.Vp,{className:"w-4 h-4 mr-2 text-gray-500"}),(0,l.jsx)("span",{children:"Managed Jobs"})]}),(0,l.jsx)("span",{className:"font-normal text-gray-800",children:$?"...":K.managedJobsCount})]})]}),(0,l.jsxs)("div",{className:"px-6 pb-6 text-sm pt-3",children:[(0,l.jsx)("h4",{className:"mb-2 text-xs text-gray-500 tracking-wider",children:"Enabled Infra"}),(0,l.jsx)("div",{className:"flex flex-wrap gap-x-4 gap-y-1",children:$?(0,l.jsx)("span",{className:"text-gray-500",children:"Loading..."}):K.clouds.length>0?K.clouds.map(e=>(0,l.jsxs)("div",{className:"flex items-center text-gray-700",children:[(0,l.jsx)(C.Ye,{className:"w-3.5 h-3.5 mr-1.5 text-green-500"}),(0,l.jsx)("span",{children:e})]},e)):(0,l.jsx)("span",{className:"text-gray-500 italic",children:"No enabled infrastructure"})}),(0,l.jsx)("div",{className:"mt-4",children:(0,l.jsx)(P,{workspaceName:s,config:b,enabledClouds:K.clouds})}),(0,l.jsx)(Z,{workspaceConfig:b,allUsers:G})]})]})}),(0,l.jsx)("div",{className:r?"lg:col-span-3":"lg:col-span-2",children:(0,l.jsxs)(m.Zb,{className:"h-full flex flex-col",children:[(0,l.jsx)(m.Ol,{children:(0,l.jsx)(m.ll,{className:"text-base font-normal",children:r?"New Workspace YAML":"Edit Workspace YAML"})}),(0,l.jsx)(m.aY,{className:"flex-1 flex flex-col",children:(0,l.jsxs)("div",{className:"space-y-4 flex-1 flex flex-col",children:[X&&(0,l.jsx)(L.X,{error:X,onDismiss:()=>B(null)}),(0,l.jsxs)("div",{className:"flex-1 flex flex-col",children:[(0,l.jsxs)("p",{className:"text-sm text-gray-600 mb-3",children:["Configure infra-specific settings for this workspace. Leave empty to use all accessible infrastructure. Refer to"," ",(0,l.jsx)("a",{href:"https://docs.skypilot.co/en/latest/admin/workspaces.html#configuration",target:"_blank",rel:"noopener noreferrer",className:"text-blue-600",children:"SkyPilot Docs"})," ","for more details."]}),(0,l.jsxs)("div",{className:"mb-4",children:[(0,l.jsx)("h4",{className:"text-sm font-medium text-gray-700 mb-2",children:"Example configuration:"}),(0,l.jsx)("div",{className:"p-3 bg-gray-50 border rounded-lg",children:(0,l.jsx)("pre",{className:"text-xs font-mono text-gray-600 whitespace-pre-wrap",children:"".concat(s||"my-workspace",":\n private: true\n allowed_users:\n - user1@mydomain.com\n - user2@mydomain.com\n gcp:\n project_id: xxx\n disabled: false\n kubernetes:\n allowed_contexts:\n - context-1")})})]}),(0,l.jsx)(f.g,{value:k,onChange:e=>el(e.target.value),className:"font-mono text-sm flex-1 resize-none",style:{minHeight:"350px"},spellCheck:!1,placeholder:"# Enter workspace configuration in YAML format"}),(0,l.jsx)("div",{className:"flex justify-end space-x-3 pt-3 border-gray-200",children:(0,l.jsxs)(h.z,{onClick:et,disabled:M||X||_,className:"inline-flex items-center bg-sky-600 hover:bg-sky-700 text-white",children:[(0,l.jsx)(N.Z,{className:"w-4 h-4 mr-1.5"}),M?"Applying...":"Apply"]})})]})]})})]})})]})]}),(0,l.jsx)(w.Vq,{open:V.showDialog,onOpenChange:en,children:(0,l.jsxs)(w.cZ,{className:"sm:max-w-md",children:[(0,l.jsxs)(w.fK,{className:"",children:[(0,l.jsx)(w.$N,{children:"Delete Workspace"}),(0,l.jsxs)(w.Be,{children:['Are you sure you want to delete workspace "',s,'"? This action cannot be undone.']})]}),V.error&&(0,l.jsx)(L.X,{error:V.error,title:"Deletion Failed",onDismiss:()=>q(e=>({...e,error:null}))}),(0,l.jsxs)(w.cN,{className:"",children:[(0,l.jsx)(h.z,{variant:"outline",onClick:en,disabled:V.deleting,children:"Cancel"}),(0,l.jsx)(h.z,{variant:"destructive",onClick:ea,disabled:V.deleting,children:V.deleting?"Deleting...":"Delete"})]})]})})]})]})}}}]);