skypilot-nightly 1.0.0.dev20251210__py3-none-any.whl → 1.0.0.dev20260112__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 (207) hide show
  1. sky/__init__.py +4 -2
  2. sky/adaptors/slurm.py +159 -72
  3. sky/backends/backend_utils.py +52 -10
  4. sky/backends/cloud_vm_ray_backend.py +192 -32
  5. sky/backends/task_codegen.py +40 -2
  6. sky/catalog/data_fetchers/fetch_gcp.py +9 -1
  7. sky/catalog/data_fetchers/fetch_nebius.py +1 -1
  8. sky/catalog/data_fetchers/fetch_vast.py +4 -2
  9. sky/catalog/seeweb_catalog.py +30 -15
  10. sky/catalog/shadeform_catalog.py +5 -2
  11. sky/catalog/slurm_catalog.py +0 -7
  12. sky/catalog/vast_catalog.py +30 -6
  13. sky/check.py +11 -8
  14. sky/client/cli/command.py +106 -54
  15. sky/client/interactive_utils.py +190 -0
  16. sky/client/sdk.py +8 -0
  17. sky/client/sdk_async.py +9 -0
  18. sky/clouds/aws.py +60 -2
  19. sky/clouds/azure.py +2 -0
  20. sky/clouds/kubernetes.py +2 -0
  21. sky/clouds/runpod.py +38 -7
  22. sky/clouds/slurm.py +44 -12
  23. sky/clouds/ssh.py +1 -1
  24. sky/clouds/vast.py +30 -17
  25. sky/core.py +69 -1
  26. sky/dashboard/out/404.html +1 -1
  27. sky/dashboard/out/_next/static/3nu-b8raeKRNABZ2d4GAG/_buildManifest.js +1 -0
  28. sky/dashboard/out/_next/static/chunks/1871-0565f8975a7dcd10.js +6 -0
  29. sky/dashboard/out/_next/static/chunks/2109-55a1546d793574a7.js +11 -0
  30. sky/dashboard/out/_next/static/chunks/2521-099b07cd9e4745bf.js +26 -0
  31. sky/dashboard/out/_next/static/chunks/2755.a636e04a928a700e.js +31 -0
  32. sky/dashboard/out/_next/static/chunks/3495.05eab4862217c1a5.js +6 -0
  33. sky/dashboard/out/_next/static/chunks/3785.cfc5dcc9434fd98c.js +1 -0
  34. sky/dashboard/out/_next/static/chunks/3981.645d01bf9c8cad0c.js +21 -0
  35. sky/dashboard/out/_next/static/chunks/4083-0115d67c1fb57d6c.js +21 -0
  36. sky/dashboard/out/_next/static/chunks/{8640.5b9475a2d18c5416.js → 429.a58e9ba9742309ed.js} +2 -2
  37. sky/dashboard/out/_next/static/chunks/4555.8e221537181b5dc1.js +6 -0
  38. sky/dashboard/out/_next/static/chunks/4725.937865b81fdaaebb.js +6 -0
  39. sky/dashboard/out/_next/static/chunks/6082-edabd8f6092300ce.js +25 -0
  40. sky/dashboard/out/_next/static/chunks/6989-49cb7dca83a7a62d.js +1 -0
  41. sky/dashboard/out/_next/static/chunks/6990-630bd2a2257275f8.js +1 -0
  42. sky/dashboard/out/_next/static/chunks/7248-a99800d4db8edabd.js +1 -0
  43. sky/dashboard/out/_next/static/chunks/754-cfc5d4ad1b843d29.js +18 -0
  44. sky/dashboard/out/_next/static/chunks/8050-dd8aa107b17dce00.js +16 -0
  45. sky/dashboard/out/_next/static/chunks/8056-d4ae1e0cb81e7368.js +1 -0
  46. sky/dashboard/out/_next/static/chunks/8555.011023e296c127b3.js +6 -0
  47. sky/dashboard/out/_next/static/chunks/8821-93c25df904a8362b.js +1 -0
  48. sky/dashboard/out/_next/static/chunks/8969-0662594b69432ade.js +1 -0
  49. sky/dashboard/out/_next/static/chunks/9025.f15c91c97d124a5f.js +6 -0
  50. sky/dashboard/out/_next/static/chunks/{9353-8369df1cf105221c.js → 9353-7ad6bd01858556f1.js} +1 -1
  51. sky/dashboard/out/_next/static/chunks/pages/_app-5a86569acad99764.js +34 -0
  52. sky/dashboard/out/_next/static/chunks/pages/clusters/[cluster]/[job]-8297476714acb4ac.js +6 -0
  53. sky/dashboard/out/_next/static/chunks/pages/clusters/[cluster]-337c3ba1085f1210.js +1 -0
  54. sky/dashboard/out/_next/static/chunks/pages/{clusters-9e5d47818b9bdadd.js → clusters-57632ff3684a8b5c.js} +1 -1
  55. sky/dashboard/out/_next/static/chunks/pages/infra/[context]-5fd3a453c079c2ea.js +1 -0
  56. sky/dashboard/out/_next/static/chunks/pages/infra-9f85c02c9c6cae9e.js +1 -0
  57. sky/dashboard/out/_next/static/chunks/pages/jobs/[job]-90f16972cbecf354.js +1 -0
  58. sky/dashboard/out/_next/static/chunks/pages/jobs/pools/[pool]-2dd42fc37aad427a.js +16 -0
  59. sky/dashboard/out/_next/static/chunks/pages/jobs-ed806aeace26b972.js +1 -0
  60. sky/dashboard/out/_next/static/chunks/pages/users-bec34706b36f3524.js +1 -0
  61. sky/dashboard/out/_next/static/chunks/pages/{volumes-ef19d49c6d0e8500.js → volumes-a83ba9b38dff7ea9.js} +1 -1
  62. sky/dashboard/out/_next/static/chunks/pages/workspaces/{[name]-96e0f298308da7e2.js → [name]-c781e9c3e52ef9fc.js} +1 -1
  63. sky/dashboard/out/_next/static/chunks/pages/workspaces-91e0942f47310aae.js +1 -0
  64. sky/dashboard/out/_next/static/chunks/webpack-cfe59cf684ee13b9.js +1 -0
  65. sky/dashboard/out/_next/static/css/b0dbca28f027cc19.css +3 -0
  66. sky/dashboard/out/clusters/[cluster]/[job].html +1 -1
  67. sky/dashboard/out/clusters/[cluster].html +1 -1
  68. sky/dashboard/out/clusters.html +1 -1
  69. sky/dashboard/out/config.html +1 -1
  70. sky/dashboard/out/index.html +1 -1
  71. sky/dashboard/out/infra/[context].html +1 -1
  72. sky/dashboard/out/infra.html +1 -1
  73. sky/dashboard/out/jobs/[job].html +1 -1
  74. sky/dashboard/out/jobs/pools/[pool].html +1 -1
  75. sky/dashboard/out/jobs.html +1 -1
  76. sky/dashboard/out/plugins/[...slug].html +1 -1
  77. sky/dashboard/out/users.html +1 -1
  78. sky/dashboard/out/volumes.html +1 -1
  79. sky/dashboard/out/workspace/new.html +1 -1
  80. sky/dashboard/out/workspaces/[name].html +1 -1
  81. sky/dashboard/out/workspaces.html +1 -1
  82. sky/data/data_utils.py +26 -12
  83. sky/data/mounting_utils.py +29 -4
  84. sky/global_user_state.py +108 -16
  85. sky/jobs/client/sdk.py +8 -3
  86. sky/jobs/controller.py +191 -31
  87. sky/jobs/recovery_strategy.py +109 -11
  88. sky/jobs/server/core.py +81 -4
  89. sky/jobs/server/server.py +14 -0
  90. sky/jobs/state.py +417 -19
  91. sky/jobs/utils.py +73 -80
  92. sky/models.py +9 -0
  93. sky/optimizer.py +2 -1
  94. sky/provision/__init__.py +11 -9
  95. sky/provision/kubernetes/utils.py +122 -15
  96. sky/provision/kubernetes/volume.py +52 -17
  97. sky/provision/provisioner.py +2 -1
  98. sky/provision/runpod/instance.py +3 -1
  99. sky/provision/runpod/utils.py +13 -1
  100. sky/provision/runpod/volume.py +25 -9
  101. sky/provision/slurm/instance.py +75 -29
  102. sky/provision/slurm/utils.py +213 -107
  103. sky/provision/vast/utils.py +1 -0
  104. sky/resources.py +135 -13
  105. sky/schemas/api/responses.py +4 -0
  106. sky/schemas/db/global_user_state/010_save_ssh_key.py +1 -1
  107. sky/schemas/db/spot_jobs/008_add_full_resources.py +34 -0
  108. sky/schemas/db/spot_jobs/009_job_events.py +32 -0
  109. sky/schemas/db/spot_jobs/010_job_events_timestamp_with_timezone.py +43 -0
  110. sky/schemas/db/spot_jobs/011_add_links.py +34 -0
  111. sky/schemas/generated/jobsv1_pb2.py +9 -5
  112. sky/schemas/generated/jobsv1_pb2.pyi +12 -0
  113. sky/schemas/generated/jobsv1_pb2_grpc.py +44 -0
  114. sky/schemas/generated/managed_jobsv1_pb2.py +32 -28
  115. sky/schemas/generated/managed_jobsv1_pb2.pyi +11 -2
  116. sky/serve/serve_utils.py +232 -40
  117. sky/server/common.py +17 -0
  118. sky/server/constants.py +1 -1
  119. sky/server/metrics.py +6 -3
  120. sky/server/plugins.py +16 -0
  121. sky/server/requests/payloads.py +18 -0
  122. sky/server/requests/request_names.py +2 -0
  123. sky/server/requests/requests.py +28 -10
  124. sky/server/requests/serializers/encoders.py +5 -0
  125. sky/server/requests/serializers/return_value_serializers.py +14 -4
  126. sky/server/server.py +434 -107
  127. sky/server/uvicorn.py +5 -0
  128. sky/setup_files/MANIFEST.in +1 -0
  129. sky/setup_files/dependencies.py +21 -10
  130. sky/sky_logging.py +2 -1
  131. sky/skylet/constants.py +22 -5
  132. sky/skylet/executor/slurm.py +4 -6
  133. sky/skylet/job_lib.py +89 -4
  134. sky/skylet/services.py +18 -3
  135. sky/ssh_node_pools/deploy/tunnel/cleanup-tunnel.sh +62 -0
  136. sky/ssh_node_pools/deploy/tunnel/ssh-tunnel.sh +379 -0
  137. sky/templates/kubernetes-ray.yml.j2 +4 -6
  138. sky/templates/slurm-ray.yml.j2 +32 -2
  139. sky/templates/websocket_proxy.py +18 -41
  140. sky/users/permission.py +61 -51
  141. sky/utils/auth_utils.py +42 -0
  142. sky/utils/cli_utils/status_utils.py +19 -5
  143. sky/utils/cluster_utils.py +10 -3
  144. sky/utils/command_runner.py +256 -94
  145. sky/utils/command_runner.pyi +16 -0
  146. sky/utils/common_utils.py +30 -29
  147. sky/utils/context.py +32 -0
  148. sky/utils/db/db_utils.py +36 -6
  149. sky/utils/db/migration_utils.py +41 -21
  150. sky/utils/infra_utils.py +5 -1
  151. sky/utils/instance_links.py +139 -0
  152. sky/utils/interactive_utils.py +49 -0
  153. sky/utils/kubernetes/generate_kubeconfig.sh +42 -33
  154. sky/utils/kubernetes/rsync_helper.sh +5 -1
  155. sky/utils/plugin_extensions/__init__.py +14 -0
  156. sky/utils/plugin_extensions/external_failure_source.py +176 -0
  157. sky/utils/resources_utils.py +10 -8
  158. sky/utils/rich_utils.py +9 -11
  159. sky/utils/schemas.py +63 -20
  160. sky/utils/status_lib.py +7 -0
  161. sky/utils/subprocess_utils.py +17 -0
  162. sky/volumes/client/sdk.py +6 -3
  163. sky/volumes/server/core.py +65 -27
  164. sky_templates/ray/start_cluster +8 -4
  165. {skypilot_nightly-1.0.0.dev20251210.dist-info → skypilot_nightly-1.0.0.dev20260112.dist-info}/METADATA +53 -57
  166. {skypilot_nightly-1.0.0.dev20251210.dist-info → skypilot_nightly-1.0.0.dev20260112.dist-info}/RECORD +172 -162
  167. sky/dashboard/out/_next/static/KYAhEFa3FTfq4JyKVgo-s/_buildManifest.js +0 -1
  168. sky/dashboard/out/_next/static/chunks/1141-9c810f01ff4f398a.js +0 -11
  169. sky/dashboard/out/_next/static/chunks/1871-7e202677c42f43fe.js +0 -6
  170. sky/dashboard/out/_next/static/chunks/2260-7703229c33c5ebd5.js +0 -1
  171. sky/dashboard/out/_next/static/chunks/2350.fab69e61bac57b23.js +0 -1
  172. sky/dashboard/out/_next/static/chunks/2369.fc20f0c2c8ed9fe7.js +0 -15
  173. sky/dashboard/out/_next/static/chunks/2755.edd818326d489a1d.js +0 -26
  174. sky/dashboard/out/_next/static/chunks/3294.ddda8c6c6f9f24dc.js +0 -1
  175. sky/dashboard/out/_next/static/chunks/3785.7e245f318f9d1121.js +0 -1
  176. sky/dashboard/out/_next/static/chunks/3800-b589397dc09c5b4e.js +0 -1
  177. sky/dashboard/out/_next/static/chunks/4725.172ede95d1b21022.js +0 -1
  178. sky/dashboard/out/_next/static/chunks/4937.a2baa2df5572a276.js +0 -15
  179. sky/dashboard/out/_next/static/chunks/6212-7bd06f60ba693125.js +0 -13
  180. sky/dashboard/out/_next/static/chunks/6856-da20c5fd999f319c.js +0 -1
  181. sky/dashboard/out/_next/static/chunks/6989-01359c57e018caa4.js +0 -1
  182. sky/dashboard/out/_next/static/chunks/6990-09cbf02d3cd518c3.js +0 -1
  183. sky/dashboard/out/_next/static/chunks/7359-c8d04e06886000b3.js +0 -30
  184. sky/dashboard/out/_next/static/chunks/7411-b15471acd2cba716.js +0 -41
  185. sky/dashboard/out/_next/static/chunks/7615-019513abc55b3b47.js +0 -1
  186. sky/dashboard/out/_next/static/chunks/8969-452f9d5cbdd2dc73.js +0 -1
  187. sky/dashboard/out/_next/static/chunks/9025.fa408f3242e9028d.js +0 -6
  188. sky/dashboard/out/_next/static/chunks/9360.a536cf6b1fa42355.js +0 -31
  189. sky/dashboard/out/_next/static/chunks/9847.3aaca6bb33455140.js +0 -30
  190. sky/dashboard/out/_next/static/chunks/pages/_app-68b647e26f9d2793.js +0 -34
  191. sky/dashboard/out/_next/static/chunks/pages/clusters/[cluster]/[job]-33f525539665fdfd.js +0 -16
  192. sky/dashboard/out/_next/static/chunks/pages/clusters/[cluster]-a7565f586ef86467.js +0 -1
  193. sky/dashboard/out/_next/static/chunks/pages/infra/[context]-12c559ec4d81fdbd.js +0 -1
  194. sky/dashboard/out/_next/static/chunks/pages/infra-d187cd0413d72475.js +0 -1
  195. sky/dashboard/out/_next/static/chunks/pages/jobs/[job]-895847b6cf200b04.js +0 -16
  196. sky/dashboard/out/_next/static/chunks/pages/jobs/pools/[pool]-8d0f4655400b4eb9.js +0 -21
  197. sky/dashboard/out/_next/static/chunks/pages/jobs-e5a98f17f8513a96.js +0 -1
  198. sky/dashboard/out/_next/static/chunks/pages/users-2f7646eb77785a2c.js +0 -1
  199. sky/dashboard/out/_next/static/chunks/pages/workspaces-cb4da3abe08ebf19.js +0 -1
  200. sky/dashboard/out/_next/static/chunks/webpack-fba3de387ff6bb08.js +0 -1
  201. sky/dashboard/out/_next/static/css/c5a4cfd2600fc715.css +0 -3
  202. /sky/dashboard/out/_next/static/{KYAhEFa3FTfq4JyKVgo-s → 3nu-b8raeKRNABZ2d4GAG}/_ssgManifest.js +0 -0
  203. /sky/dashboard/out/_next/static/chunks/pages/plugins/{[...slug]-4f46050ca065d8f8.js → [...slug]-449a9f5a3bb20fb3.js} +0 -0
  204. {skypilot_nightly-1.0.0.dev20251210.dist-info → skypilot_nightly-1.0.0.dev20260112.dist-info}/WHEEL +0 -0
  205. {skypilot_nightly-1.0.0.dev20251210.dist-info → skypilot_nightly-1.0.0.dev20260112.dist-info}/entry_points.txt +0 -0
  206. {skypilot_nightly-1.0.0.dev20251210.dist-info → skypilot_nightly-1.0.0.dev20260112.dist-info}/licenses/LICENSE +0 -0
  207. {skypilot_nightly-1.0.0.dev20251210.dist-info → skypilot_nightly-1.0.0.dev20260112.dist-info}/top_level.txt +0 -0
sky/clouds/aws.py CHANGED
@@ -12,6 +12,7 @@ import typing
12
12
  from typing import (Any, Callable, Dict, Iterator, List, Literal, Optional, Set,
13
13
  Tuple, TypeVar, Union)
14
14
 
15
+ import colorama
15
16
  from typing_extensions import ParamSpec
16
17
 
17
18
  from sky import catalog
@@ -758,6 +759,36 @@ class AWS(clouds.Cloud):
758
759
  max_efa_interfaces = 0
759
760
  enable_efa = False
760
761
 
762
+ use_internal_ips = skypilot_config.get_effective_region_config(
763
+ cloud='aws',
764
+ region=region_name,
765
+ keys=('use_internal_ips',),
766
+ default_value=False)
767
+ if max_efa_interfaces > 1 and not use_internal_ips:
768
+ logger.warning(
769
+ f'{colorama.Fore.YELLOW}'
770
+ f'Instance type {resources.instance_type} supports up to '
771
+ f'{max_efa_interfaces} EFA interfaces, but '
772
+ '`use_internal_ips` is not enabled.\nLaunching with the '
773
+ 'current configuration will use only 1 EFA interface.\n'
774
+ f'To use all {max_efa_interfaces} EFA interfaces, enable '
775
+ 'internal IPs by adding one of the following '
776
+ 'configurations to SkyPilot config:\n'
777
+ 'Option 1 (with SSM):\n'
778
+ ' aws:\n'
779
+ ' use_internal_ips: true\n'
780
+ ' use_ssm: true\n'
781
+ 'Option 2 (with SSH proxy):\n'
782
+ ' aws:\n'
783
+ ' use_internal_ips: true\n'
784
+ ' ssh_proxy_command: ssh -W %h:%p -i <ssh key path> '
785
+ '-o StrictHostKeyChecking=no <user>@<jump server public'
786
+ ' ip>\n'
787
+ 'Refer to '
788
+ 'https://docs.skypilot.co/en/latest/reference/config.html'
789
+ '#aws-use-internal-ips for more details.'
790
+ f'{colorama.Style.RESET_ALL}')
791
+
761
792
  docker_run_options = []
762
793
  if resources.extract_docker_image() is not None:
763
794
  image_id_to_use = None
@@ -1005,8 +1036,10 @@ class AWS(clouds.Cloud):
1005
1036
  hints = 'AWS SSO is set.'
1006
1037
  if static_credential_exists:
1007
1038
  hints += (
1008
- ' To ensure multiple clouds work correctly, please use SkyPilot '
1009
- 'with static credentials (e.g., ~/.aws/credentials) by unsetting '
1039
+ ' To ensure S3 mounting and other features work correctly '
1040
+ 'on Kubernetes and other clouds, '
1041
+ 'please use SkyPilot with static AWS credentials '
1042
+ '(e.g., ~/.aws/credentials) by unsetting '
1010
1043
  'the AWS_PROFILE environment variable.')
1011
1044
  else:
1012
1045
  hints += single_cloud_hint
@@ -1081,6 +1114,31 @@ class AWS(clouds.Cloud):
1081
1114
  return identity_type
1082
1115
  return AWSIdentityType.SHARED_CREDENTIALS_FILE
1083
1116
 
1117
+ @classmethod
1118
+ def should_use_env_auth_for_s3(cls) -> bool:
1119
+ """Returns True if S3 should use environment-based auth.
1120
+
1121
+ When using non-static AWS credentials (SSO, IAM role, container role),
1122
+ we should not embed credentials into rclone config. Instead, we should
1123
+ use env_auth=true so that rclone uses the AWS SDK credential chain,
1124
+ which properly handles temporary credentials and IAM roles.
1125
+
1126
+ Returns:
1127
+ True if environment-based auth should be used, False for static
1128
+ credentials that can be embedded.
1129
+ """
1130
+ identity_type = cls._current_identity_type()
1131
+ if identity_type is None:
1132
+ return False
1133
+ # These credential types use temporary credentials that should not be
1134
+ # embedded in config files. They rely on the AWS SDK credential chain.
1135
+ non_static_types = {
1136
+ AWSIdentityType.SSO,
1137
+ AWSIdentityType.IAM_ROLE,
1138
+ AWSIdentityType.CONTAINER_ROLE,
1139
+ }
1140
+ return identity_type in non_static_types
1141
+
1084
1142
  @classmethod
1085
1143
  @aws_profile_aware_lru_cache(scope='request',
1086
1144
  maxsize=_AWS_PROFILE_SCOPED_FUNC_CACHE_SIZE)
sky/clouds/azure.py CHANGED
@@ -97,6 +97,8 @@ class Azure(clouds.Cloud):
97
97
  clouds.CloudImplementationFeatures.HIGH_AVAILABILITY_CONTROLLERS: (
98
98
  f'High availability controllers are not supported on {cls._REPR}.'
99
99
  ),
100
+ clouds.CloudImplementationFeatures.CUSTOM_NETWORK_TIER:
101
+ (f'Custom network tier is not supported on {cls._REPR}.'),
100
102
  clouds.CloudImplementationFeatures.CUSTOM_MULTI_NETWORK: (
101
103
  f'Customized multiple network interfaces are not supported on {cls._REPR}.'
102
104
  ),
sky/clouds/kubernetes.py CHANGED
@@ -766,6 +766,8 @@ class Kubernetes(clouds.Cloud):
766
766
  'ha_recovery_log_path':
767
767
  constants.HA_PERSISTENT_RECOVERY_LOG_PATH.format(''),
768
768
  'sky_python_cmd': constants.SKY_PYTHON_CMD,
769
+ 'sky_unset_pythonpath_and_set_cwd':
770
+ constants.SKY_UNSET_PYTHONPATH_AND_SET_CWD,
769
771
  'k8s_high_availability_storage_class_name':
770
772
  (k8s_ha_storage_class_name),
771
773
  'avoid_label_keys': avoid_label_keys,
sky/clouds/runpod.py CHANGED
@@ -7,6 +7,7 @@ from typing import Dict, Iterator, List, Optional, Tuple, Union
7
7
 
8
8
  from sky import catalog
9
9
  from sky import clouds
10
+ from sky.utils import common_utils
10
11
  from sky.utils import registry
11
12
  from sky.utils import resources_utils
12
13
 
@@ -312,18 +313,48 @@ class RunPod(clouds.Cloud):
312
313
  # If that happens to be set to None, then ValueError is raised.
313
314
  return False, dependency_error_msg
314
315
 
316
+ hint_msg = (
317
+ 'Credentials can be set up by running: \n'
318
+ ' $ pip install runpod \n'
319
+ ' $ runpod config\n'
320
+ ' For more information, see https://docs.skypilot.co/en/latest/getting-started/installation.html#runpod' # pylint: disable=line-too-long
321
+ )
322
+
315
323
  valid, error = cls._check_runpod_credentials()
316
324
  if not valid:
317
- return False, (
318
- f'{error} \n' # First line is indented by 4 spaces
319
- ' Credentials can be set up by running: \n'
320
- f' $ pip install runpod \n'
321
- f' $ runpod config\n'
322
- ' For more information, see https://docs.skypilot.co/en/latest/getting-started/installation.html#runpod' # pylint: disable=line-too-long
323
- )
325
+ return False, (f'{error} \n {hint_msg}')
326
+
327
+ # Validate credentials by making an actual API call
328
+ valid, error = cls._validate_api_key()
329
+ if not valid:
330
+ return False, (f'{error} \n {hint_msg}')
324
331
 
325
332
  return True, None
326
333
 
334
+ @classmethod
335
+ def _validate_api_key(cls) -> Tuple[bool, Optional[str]]:
336
+ """Validate RunPod API key by making an actual API call."""
337
+ # Import here to avoid circular imports and ensure runpod is configured
338
+ # pylint: disable=import-outside-toplevel
339
+ from sky.provision.runpod import utils as runpod_utils
340
+ try:
341
+ # Try to list instances to validate the API key works
342
+ runpod_utils.list_instances()
343
+ return True, None
344
+ except Exception as e: # pylint: disable=broad-except
345
+ from sky.adaptors import runpod
346
+ error_msg = common_utils.format_exception(e, use_bracket=True)
347
+ if isinstance(e, runpod.runpod.error.QueryError):
348
+ error_msg_lower = str(e).lower()
349
+ auth_keywords = ['unauthorized', 'forbidden', '401', '403']
350
+ if any(keyword in error_msg_lower for keyword in auth_keywords):
351
+ return False, (
352
+ 'RunPod API key is invalid or lacks required '
353
+ f'permissions. {error_msg}')
354
+ return False, (f'Failed to verify RunPod API key. {error_msg}')
355
+ return False, ('An unexpected error occurred during RunPod API '
356
+ f'key validation. {error_msg}')
357
+
327
358
  @classmethod
328
359
  def _check_runpod_credentials(cls, profile: str = 'default'):
329
360
  """Checks if the credentials file exists and is valid."""
sky/clouds/slurm.py CHANGED
@@ -9,6 +9,7 @@ from sky import sky_logging
9
9
  from sky import skypilot_config
10
10
  from sky.adaptors import slurm
11
11
  from sky.provision.slurm import utils as slurm_utils
12
+ from sky.skylet import constants
12
13
  from sky.utils import annotations
13
14
  from sky.utils import common_utils
14
15
  from sky.utils import registry
@@ -55,10 +56,20 @@ class Slurm(clouds.Cloud):
55
56
  _regions: List[clouds.Region] = []
56
57
  _INDENT_PREFIX = ' '
57
58
 
59
+ # Same as Kubernetes.
60
+ _DEFAULT_NUM_VCPUS_WITH_GPU = 4
61
+ _DEFAULT_MEMORY_CPU_RATIO_WITH_GPU = 4
62
+
58
63
  # Using the latest SkyPilot provisioner API to provision and check status.
59
64
  PROVISIONER_VERSION = clouds.ProvisionerVersion.SKYPILOT
60
65
  STATUS_VERSION = clouds.StatusVersion.SKYPILOT
61
66
 
67
+ _SSH_CONFIG_KEY_MAPPING = {
68
+ 'identityfile': 'IdentityFile',
69
+ 'user': 'User',
70
+ 'hostname': 'HostName',
71
+ }
72
+
62
73
  @classmethod
63
74
  def _unsupported_features_for_resources(
64
75
  cls,
@@ -323,7 +334,13 @@ class Slurm(clouds.Cloud):
323
334
  if zones and len(zones) > 0:
324
335
  partition = zones[0].name
325
336
  else:
326
- partition = slurm_utils.get_cluster_default_partition(cluster)
337
+ partitions = slurm_utils.get_partitions(cluster)
338
+ if not partitions:
339
+ raise ValueError(f'No partitions found for cluster {cluster}.')
340
+ # get_partitions returns the default partition first, then sorted
341
+ # alphabetically, so this also handles the case where the cluster
342
+ # does not have a default partition.
343
+ partition = partitions[0]
327
344
 
328
345
  # cluster is our target slurmctld host.
329
346
  ssh_config = slurm_utils.get_slurm_ssh_config()
@@ -344,6 +361,10 @@ class Slurm(clouds.Cloud):
344
361
  # Optionally populate accelerator information.
345
362
  acc_count = s.accelerator_count if s.accelerator_count else 0
346
363
  acc_type = s.accelerator_type if s.accelerator_type else None
364
+ # Resolve the actual GPU type as it appears in the cluster's GRES.
365
+ # Slurm GRES types are case-sensitive.
366
+ if acc_type:
367
+ acc_type = slurm_utils.get_gres_gpu_type(cluster, acc_type)
347
368
 
348
369
  deploy_vars = {
349
370
  'instance_type': resources.instance_type,
@@ -359,9 +380,14 @@ class Slurm(clouds.Cloud):
359
380
  'ssh_port': str(ssh_config_dict.get('port', 22)),
360
381
  'ssh_user': ssh_config_dict['user'],
361
382
  'slurm_proxy_command': ssh_config_dict.get('proxycommand', None),
383
+ 'slurm_proxy_jump': ssh_config_dict.get('proxyjump', None),
362
384
  # TODO(jwj): Solve naming collision with 'ssh_private_key'.
363
385
  # Please refer to slurm-ray.yml.j2 'ssh' and 'auth' sections.
364
386
  'slurm_private_key': ssh_config_dict['identityfile'][0],
387
+ 'slurm_sshd_host_key_filename':
388
+ (slurm_utils.SLURM_SSHD_HOST_KEY_FILENAME),
389
+ 'slurm_cluster_name_env_var':
390
+ (constants.SKY_CLUSTER_NAME_ENV_VAR_KEY),
365
391
  }
366
392
 
367
393
  return deploy_vars
@@ -423,13 +449,12 @@ class Slurm(clouds.Cloud):
423
449
  from_instance_type(default_instance_type))
424
450
 
425
451
  gpu_task_cpus = slurm_instance_type.cpus
426
- gpu_task_memory = slurm_instance_type.memory
427
- # if resources.cpus is None:
428
- # gpu_task_cpus = self._DEFAULT_NUM_VCPUS_WITH_GPU * acc_count
429
- # gpu_task_memory = (float(resources.memory.strip('+')) if
430
- # resources.memory is not None else
431
- # gpu_task_cpus *
432
- # self._DEFAULT_MEMORY_CPU_RATIO_WITH_GPU)
452
+ if resources.cpus is None:
453
+ gpu_task_cpus = self._DEFAULT_NUM_VCPUS_WITH_GPU * acc_count
454
+ # Special handling to bump up memory multiplier for GPU instances
455
+ gpu_task_memory = (float(resources.memory.strip('+')) if
456
+ resources.memory is not None else gpu_task_cpus *
457
+ self._DEFAULT_MEMORY_CPU_RATIO_WITH_GPU)
433
458
 
434
459
  chosen_instance_type = (
435
460
  slurm_utils.SlurmInstanceType.from_resources(
@@ -470,8 +495,8 @@ class Slurm(clouds.Cloud):
470
495
  existing_allowed_clusters = cls.existing_allowed_clusters()
471
496
 
472
497
  if not existing_allowed_clusters:
473
- return (False, 'No SLURM clusters found in ~/.slurm/config. '
474
- 'Please configure at least one SLURM cluster.')
498
+ return (False, 'No Slurm clusters found in ~/.slurm/config. '
499
+ 'Please configure at least one Slurm cluster.')
475
500
 
476
501
  # Check credentials for each cluster and return ctx2text mapping
477
502
  ctx2text = {}
@@ -479,18 +504,25 @@ class Slurm(clouds.Cloud):
479
504
  for cluster in existing_allowed_clusters:
480
505
  # Retrieve the config options for a given SlurmctldHost name alias.
481
506
  ssh_config_dict = ssh_config.lookup(cluster)
482
-
483
507
  try:
484
508
  client = slurm.SlurmClient(
485
509
  ssh_config_dict['hostname'],
486
510
  int(ssh_config_dict.get('port', 22)),
487
511
  ssh_config_dict['user'],
488
512
  ssh_config_dict['identityfile'][0],
489
- ssh_proxy_command=ssh_config_dict.get('proxycommand', None))
513
+ ssh_proxy_command=ssh_config_dict.get('proxycommand', None),
514
+ ssh_proxy_jump=ssh_config_dict.get('proxyjump', None))
490
515
  info = client.info()
491
516
  logger.debug(f'Slurm cluster {cluster} sinfo: {info}')
492
517
  ctx2text[cluster] = 'enabled'
493
518
  success = True
519
+ except KeyError as e:
520
+ key = e.args[0]
521
+ ctx2text[cluster] = (
522
+ f'disabled. '
523
+ f'{cls._SSH_CONFIG_KEY_MAPPING.get(key, key.capitalize())} '
524
+ 'is missing, please check your ~/.slurm/config '
525
+ 'and try again.')
494
526
  except Exception as e: # pylint: disable=broad-except
495
527
  error_msg = (f'Credential check failed: '
496
528
  f'{common_utils.format_exception(e)}')
sky/clouds/ssh.py CHANGED
@@ -255,7 +255,7 @@ class SSH(kubernetes.Kubernetes):
255
255
  @classmethod
256
256
  def expand_infras(cls) -> List[str]:
257
257
  return [
258
- f'{cls.canonical_name()}/{c.lstrip("ssh-")}'
258
+ f'{cls.canonical_name()}/{common_utils.removeprefix(c, "ssh-")}'
259
259
  for c in cls.existing_allowed_contexts(silent=True)
260
260
  ]
261
261
 
sky/clouds/vast.py CHANGED
@@ -147,20 +147,24 @@ class Vast(clouds.Cloud):
147
147
  return 0.0
148
148
 
149
149
  @classmethod
150
- def get_default_instance_type(cls,
151
- cpus: Optional[str] = None,
152
- memory: Optional[str] = None,
153
- disk_tier: Optional[
154
- resources_utils.DiskTier] = None,
155
- region: Optional[str] = None,
156
- zone: Optional[str] = None) -> Optional[str]:
150
+ def get_default_instance_type(
151
+ cls,
152
+ cpus: Optional[str] = None,
153
+ memory: Optional[str] = None,
154
+ disk_tier: Optional[resources_utils.DiskTier] = None,
155
+ region: Optional[str] = None,
156
+ zone: Optional[str] = None,
157
+ datacenter_only: bool = False) -> Optional[str]:
157
158
  """Returns the default instance type for Vast."""
158
- return catalog.get_default_instance_type(cpus=cpus,
159
- memory=memory,
160
- disk_tier=disk_tier,
161
- region=region,
162
- zone=zone,
163
- clouds='vast')
159
+ # pylint: disable=import-outside-toplevel
160
+ from sky.catalog import vast_catalog
161
+ return vast_catalog.get_default_instance_type(
162
+ cpus=cpus,
163
+ memory=memory,
164
+ disk_tier=disk_tier,
165
+ region=region,
166
+ zone=zone,
167
+ datacenter_only=datacenter_only)
164
168
 
165
169
  @classmethod
166
170
  def get_accelerators_from_instance_type(
@@ -200,7 +204,7 @@ class Vast(clouds.Cloud):
200
204
  secure_only = skypilot_config.get_effective_region_config(
201
205
  cloud='vast',
202
206
  region=region.name,
203
- keys=('secure_only',),
207
+ keys=('datacenter_only',),
204
208
  default_value=False,
205
209
  override_configs=resources.cluster_config_overrides,
206
210
  )
@@ -217,6 +221,8 @@ class Vast(clouds.Cloud):
217
221
  self, resources: 'resources_lib.Resources'
218
222
  ) -> 'resources_utils.FeasibleResources':
219
223
  """Returns a list of feasible resources for the given resources."""
224
+ # pylint: disable=import-outside-toplevel
225
+ from sky.catalog import vast_catalog
220
226
  if resources.instance_type is not None:
221
227
  assert resources.is_launchable(), resources
222
228
  resources = resources.copy(accelerators=None)
@@ -234,6 +240,12 @@ class Vast(clouds.Cloud):
234
240
  resource_list.append(r)
235
241
  return resource_list
236
242
 
243
+ # Resolve datacenter_only config first (used for all instance filtering)
244
+ datacenter_only = skypilot_config.get_nested(
245
+ ('vast', 'datacenter_only'),
246
+ False,
247
+ override_configs=resources.cluster_config_overrides)
248
+
237
249
  # Currently, handle a filter on accelerators only.
238
250
  accelerators = resources.accelerators
239
251
  if accelerators is None:
@@ -243,7 +255,8 @@ class Vast(clouds.Cloud):
243
255
  memory=resources.memory,
244
256
  disk_tier=resources.disk_tier,
245
257
  region=resources.region,
246
- zone=resources.zone)
258
+ zone=resources.zone,
259
+ datacenter_only=datacenter_only)
247
260
  if default_instance_type is None:
248
261
  # TODO: Add hints to all return values in this method to help
249
262
  # users understand why the resources are not launchable.
@@ -255,7 +268,7 @@ class Vast(clouds.Cloud):
255
268
  assert len(accelerators) == 1, resources
256
269
  acc, acc_count = list(accelerators.items())[0]
257
270
  (instance_list,
258
- fuzzy_candidate_list) = catalog.get_instance_type_for_accelerator(
271
+ fuzzy_candidate_list) = vast_catalog.get_instance_type_for_accelerator(
259
272
  acc,
260
273
  acc_count,
261
274
  use_spot=resources.use_spot,
@@ -263,7 +276,7 @@ class Vast(clouds.Cloud):
263
276
  region=resources.region,
264
277
  zone=resources.zone,
265
278
  memory=resources.memory,
266
- clouds='vast')
279
+ datacenter_only=datacenter_only)
267
280
  if instance_list is None:
268
281
  return resources_utils.FeasibleResources([], fuzzy_candidate_list,
269
282
  None)
sky/core.py CHANGED
@@ -1,6 +1,6 @@
1
1
  """SDK functions for cluster/job management."""
2
2
  import typing
3
- from typing import Any, Dict, List, Optional, Tuple, Union
3
+ from typing import Any, Dict, List, Literal, Optional, Tuple, Union
4
4
 
5
5
  import colorama
6
6
 
@@ -279,6 +279,74 @@ all_clusters, unmanaged_clusters, all_jobs, context
279
279
  return all_clusters, unmanaged_clusters, all_jobs, context
280
280
 
281
281
 
282
+ @typing.overload
283
+ def get_cluster_events(
284
+ cluster_name: Optional[str] = ...,
285
+ cluster_hash: Optional[str] = ...,
286
+ event_type: str = ...,
287
+ include_timestamps: Literal[False] = ...,
288
+ limit: Optional[int] = ...,
289
+ ) -> List[str]:
290
+ ...
291
+
292
+
293
+ @typing.overload
294
+ def get_cluster_events(
295
+ cluster_name: Optional[str] = ...,
296
+ cluster_hash: Optional[str] = ...,
297
+ event_type: str = ...,
298
+ include_timestamps: Literal[True] = ...,
299
+ limit: Optional[int] = ...,
300
+ ) -> List[Dict[str, Union[str, int]]]:
301
+ ...
302
+
303
+
304
+ @typing.overload
305
+ def get_cluster_events(
306
+ cluster_name: Optional[str] = ...,
307
+ cluster_hash: Optional[str] = ...,
308
+ event_type: str = ...,
309
+ include_timestamps: bool = ...,
310
+ limit: Optional[int] = ...,
311
+ ) -> Union[List[str], List[Dict[str, Union[str, int]]]]:
312
+ ...
313
+
314
+
315
+ def get_cluster_events(
316
+ cluster_name: Optional[str] = None,
317
+ cluster_hash: Optional[str] = None,
318
+ event_type: str = 'STATUS_CHANGE',
319
+ include_timestamps: bool = False,
320
+ limit: Optional[int] = None
321
+ ) -> Union[List[str], List[Dict[str, Union[str, int]]]]:
322
+ """Get events for a cluster.
323
+
324
+ Args:
325
+ cluster_name: Name of the cluster. Cannot be specified if cluster_hash
326
+ is specified.
327
+ cluster_hash: Hash of the cluster. Cannot be specified if cluster_name
328
+ is specified.
329
+ event_type: Type of events to retrieve ('STATUS_CHANGE' or 'DEBUG').
330
+ include_timestamps: If True, returns list of dicts with 'reason' and
331
+ 'transitioned_at' fields. If False, returns list of reason strings.
332
+ limit: If specified, returns at most this many events (most recent).
333
+ If None, returns all events.
334
+
335
+ Returns:
336
+ If include_timestamps is False: List of event reason strings.
337
+ If include_timestamps is True: List of dicts with 'reason' and
338
+ 'transitioned_at' (unix timestamp) fields.
339
+ Events are ordered from oldest to newest.
340
+ """
341
+ event_type_enum = global_user_state.ClusterEventType(event_type)
342
+ return global_user_state.get_cluster_events(
343
+ cluster_name=cluster_name,
344
+ cluster_hash=cluster_hash,
345
+ event_type=event_type_enum,
346
+ include_timestamps=include_timestamps,
347
+ limit=limit)
348
+
349
+
282
350
  def endpoints(cluster: str,
283
351
  port: Optional[Union[int, str]] = None) -> Dict[int, str]:
284
352
  """Gets the endpoint for a given cluster and port number (endpoint).
@@ -1 +1 @@
1
- <!DOCTYPE html><html><head><meta charSet="utf-8"/><meta name="viewport" content="width=device-width"/><meta name="next-head-count" content="2"/><link rel="preload" href="/dashboard/_next/static/css/c5a4cfd2600fc715.css" as="style"/><link rel="stylesheet" href="/dashboard/_next/static/css/c5a4cfd2600fc715.css" data-n-g=""/><noscript data-n-css=""></noscript><script defer="" nomodule="" src="/dashboard/_next/static/chunks/polyfills-78c92fac7aa8fdd8.js"></script><script src="/dashboard/_next/static/chunks/webpack-fba3de387ff6bb08.js" defer=""></script><script src="/dashboard/_next/static/chunks/framework-cf60a09ccd051a10.js" defer=""></script><script src="/dashboard/_next/static/chunks/main-f15ccb73239a3bf1.js" defer=""></script><script src="/dashboard/_next/static/chunks/pages/_app-68b647e26f9d2793.js" defer=""></script><script src="/dashboard/_next/static/chunks/pages/_error-c66a4e8afc46f17b.js" defer=""></script><script src="/dashboard/_next/static/KYAhEFa3FTfq4JyKVgo-s/_buildManifest.js" defer=""></script><script src="/dashboard/_next/static/KYAhEFa3FTfq4JyKVgo-s/_ssgManifest.js" defer=""></script></head><body><div id="__next"></div><script id="__NEXT_DATA__" type="application/json">{"props":{"pageProps":{"statusCode":404}},"page":"/_error","query":{},"buildId":"KYAhEFa3FTfq4JyKVgo-s","assetPrefix":"/dashboard","nextExport":true,"isFallback":false,"gip":true,"scriptLoader":[]}</script></body></html>
1
+ <!DOCTYPE html><html><head><meta charSet="utf-8"/><meta name="viewport" content="width=device-width"/><meta name="next-head-count" content="2"/><link rel="preload" href="/dashboard/_next/static/css/b0dbca28f027cc19.css" as="style"/><link rel="stylesheet" href="/dashboard/_next/static/css/b0dbca28f027cc19.css" data-n-g=""/><noscript data-n-css=""></noscript><script defer="" nomodule="" src="/dashboard/_next/static/chunks/polyfills-78c92fac7aa8fdd8.js"></script><script src="/dashboard/_next/static/chunks/webpack-cfe59cf684ee13b9.js" defer=""></script><script src="/dashboard/_next/static/chunks/framework-cf60a09ccd051a10.js" defer=""></script><script src="/dashboard/_next/static/chunks/main-f15ccb73239a3bf1.js" defer=""></script><script src="/dashboard/_next/static/chunks/pages/_app-5a86569acad99764.js" defer=""></script><script src="/dashboard/_next/static/chunks/pages/_error-c66a4e8afc46f17b.js" defer=""></script><script src="/dashboard/_next/static/3nu-b8raeKRNABZ2d4GAG/_buildManifest.js" defer=""></script><script src="/dashboard/_next/static/3nu-b8raeKRNABZ2d4GAG/_ssgManifest.js" defer=""></script></head><body><div id="__next"></div><script id="__NEXT_DATA__" type="application/json">{"props":{"pageProps":{"statusCode":404}},"page":"/_error","query":{},"buildId":"3nu-b8raeKRNABZ2d4GAG","assetPrefix":"/dashboard","nextExport":true,"isFallback":false,"gip":true,"scriptLoader":[]}</script></body></html>
@@ -0,0 +1 @@
1
+ self.__BUILD_MANIFEST=function(s,c,a,e,t,u,n,f,i,b,j,o,r,k,d,l){return{__rewrites:{afterFiles:[],beforeFiles:[],fallback:[]},"/":["static/chunks/pages/index-444f1804401f04ea.js"],"/_error":["static/chunks/pages/_error-c66a4e8afc46f17b.js"],"/clusters":["static/chunks/pages/clusters-57632ff3684a8b5c.js"],"/clusters/[cluster]":[c,s,a,b,u,j,f,e,t,n,o,r,i,k,d,"static/chunks/1871-0565f8975a7dcd10.js","static/chunks/pages/clusters/[cluster]-337c3ba1085f1210.js"],"/clusters/[cluster]/[job]":[c,s,a,e,t,l,"static/chunks/pages/clusters/[cluster]/[job]-8297476714acb4ac.js"],"/config":["static/chunks/pages/config-718cdc365de82689.js"],"/infra":["static/chunks/pages/infra-9f85c02c9c6cae9e.js"],"/infra/[context]":["static/chunks/pages/infra/[context]-5fd3a453c079c2ea.js"],"/jobs":["static/chunks/pages/jobs-ed806aeace26b972.js"],"/jobs/pools/[pool]":[c,s,a,u,f,"static/chunks/8821-93c25df904a8362b.js",e,t,n,i,"static/chunks/pages/jobs/pools/[pool]-2dd42fc37aad427a.js"],"/jobs/[job]":[c,s,a,u,f,e,t,n,l,"static/chunks/pages/jobs/[job]-90f16972cbecf354.js"],"/plugins/[...slug]":[s,"static/chunks/pages/plugins/[...slug]-449a9f5a3bb20fb3.js"],"/users":["static/chunks/pages/users-bec34706b36f3524.js"],"/volumes":["static/chunks/pages/volumes-a83ba9b38dff7ea9.js"],"/workspace/new":["static/chunks/pages/workspace/new-3f88a1c7e86a3f86.js"],"/workspaces":["static/chunks/pages/workspaces-91e0942f47310aae.js"],"/workspaces/[name]":[c,s,a,b,u,j,e,t,n,o,r,i,k,d,"static/chunks/8050-dd8aa107b17dce00.js","static/chunks/pages/workspaces/[name]-c781e9c3e52ef9fc.js"],sortedPages:["/","/_app","/_error","/clusters","/clusters/[cluster]","/clusters/[cluster]/[job]","/config","/infra","/infra/[context]","/jobs","/jobs/pools/[pool]","/jobs/[job]","/plugins/[...slug]","/users","/volumes","/workspace/new","/workspaces","/workspaces/[name]"]}}("static/chunks/5739-d67458fcb1386c92.js","static/chunks/616-3d59f75e2ccf9321.js","static/chunks/6130-2be46d70a38f1e82.js","static/chunks/6989-49cb7dca83a7a62d.js","static/chunks/3850-fd5696f3bbbaddae.js","static/chunks/1272-1ef0bf0237faccdb.js","static/chunks/8969-0662594b69432ade.js","static/chunks/754-cfc5d4ad1b843d29.js","static/chunks/7248-a99800d4db8edabd.js","static/chunks/6082-edabd8f6092300ce.js","static/chunks/2521-099b07cd9e4745bf.js","static/chunks/6990-630bd2a2257275f8.js","static/chunks/8056-d4ae1e0cb81e7368.js","static/chunks/9353-7ad6bd01858556f1.js","static/chunks/2109-55a1546d793574a7.js","static/chunks/4083-0115d67c1fb57d6c.js"),self.__BUILD_MANIFEST_CB&&self.__BUILD_MANIFEST_CB();
@@ -0,0 +1,6 @@
1
+ "use strict";(self.webpackChunk_N_E=self.webpackChunk_N_E||[]).push([[1871],{39037:function(e,s,t){t.r(s),t.d(s,{ClusterTable:function(){return P},Clusters:function(){return O},Status2Actions:function(){return F},enabledActions:function(){return H},handleVSCodeConnection:function(){return Z}});var r=t(85893),l=t(67294),a=t(11163),n=t(55739),i=t(36989),c=t(41664),o=t.n(c),u=t(30803),d=t(37673),h=t(68764),x=t(23266),p=t(17324),m=t(94545),f=t(13626),j=t(60998);/**
2
+ * @license lucide-react v0.407.0 - ISC
3
+ *
4
+ * This source code is licensed under the ISC license.
5
+ * See the LICENSE file in the root directory of this source tree.
6
+ */let v=(0,j.Z)("Terminal",[["polyline",{points:"4 17 10 11 4 5",key:"akl6gq"}],["line",{x1:"12",x2:"20",y1:"19",y2:"19",key:"q2wloq"}]]),g=(0,j.Z)("SquareCode",[["path",{d:"M10 9.5 8 12l2 2.5",key:"3mjy60"}],["path",{d:"m14 9.5 2 2.5-2 2.5",key:"1bir2l"}],["rect",{width:"18",height:"18",x:"3",y:"3",rx:"2",key:"afitv7"}]]);t(9353);var y=t(92128),b=t(99307),w=t(23001),k=t(55988),N=t(88950),C=t(6378),S=t(36856);t(1272);var L=t(20546),M=t(10546);let E=[{label:"Status",value:"status"},{label:"Cluster",value:"cluster"},{label:"User",value:"user"},{label:"Workspace",value:"workspace"},{label:"Infra",value:"infra"},{label:"Labels",value:"labels"}],R=(e,s)=>{let t="",r="";return e>=0&&(t=e+"m",r=" "),s&&(t+="".concat(r,"(down)")),""===t&&(t="-"),t},I=(e,s)=>{if(e&&e.includes("@")){let t=e.split("@")[0];return s&&s!==t?"".concat(t," (").concat(s,")"):t}let t=e||s||"N/A";return s&&s!==t?"".concat(t," (").concat(s,")"):t},_=e=>{if(!e||0===e)return"-";let s=e=Math.floor(e),t="",r=0;for(let e of[{value:31536e3,label:"y"},{value:2592e3,label:"mo"},{value:86400,label:"d"},{value:3600,label:"h"},{value:60,label:"m"},{value:1,label:"s"}])if(s>=e.value&&r<2){let l=Math.floor(s/e.value);t+="".concat(l).concat(e.label," "),s%=e.value,r++}return t.trim()||"0s"};function O(){let e=(0,a.useRouter)(),[s,t]=(0,l.useState)(!1),c=l.useRef(null),[u,d]=(0,l.useState)(!1),[h,m]=(0,l.useState)(!1),[j,v]=(0,l.useState)(null),[g,b]=(0,l.useState)(()=>!!e.isReady&&"true"===e.query.history),[k,L]=(0,l.useState)(!0),[M,R]=(0,l.useState)(()=>{if(e.isReady){let s=e.query.historyDays;if(s&&"string"==typeof s&&["1","5","10","30"].includes(s))return parseInt(s)}return 1}),_=(0,w.X)(),[O,Z]=(0,l.useState)([]),[q,H]=(0,l.useState)({status:[],cluster:[],user:[],workspace:[],infra:[],labels:[]}),[W,F]=(0,l.useState)(!1),[z,A]=(0,l.useState)(null);(0,l.useEffect)(()=>{if(e.isReady){T();let s="true"===e.query.history;g!==s&&(L(!1),b(s),setTimeout(()=>L(!0),50));let t=e.query.historyDays;if(t&&"string"==typeof t&&["1","5","10","30"].includes(t)){let e=parseInt(t);M!==e&&R(e)}}},[e.isReady,e.query.history,e.query.historyDays]),(0,l.useEffect)(()=>{(async()=>{try{await S.ZP.preloadForPage("clusters");let e=await C.ZP.get(p.getWorkspaces),s=Object.keys(e),t=await C.ZP.get(x.getClusters),r=[...new Set(t.map(e=>e.workspace||"default").filter(e=>e))],l=new Set(s);r.includes("default")&&l.has("default"),r.forEach(e=>l.add(e));let a=[...new Set(t.map(e=>({userId:e.user_hash||e.user,username:e.user})).filter(e=>e.userId)).values()],n=new Map;a.forEach(e=>{n.set(e.userId,{userId:e.userId,username:e.username,display:I(e.username,e.userId)})}),F(!0),A(new Date)}catch(e){console.error("Error fetching data for filters:",e),F(!0),A(new Date)}})()},[]);let B=s=>{let t={...e.query},r=[],l=[],a=[];s.map((e,s)=>{var t;r.push(null!==(t=e.property.toLowerCase())&&void 0!==t?t:""),l.push(e.operator),a.push(e.value)}),t.property=r,t.operator=l,t.value=a,e.replace({pathname:e.pathname,query:t},void 0,{shallow:!0})},U=s=>{let t={...e.query};t.history=s.toString(),e.replace({pathname:e.pathname,query:t},void 0,{shallow:!0})},Q=s=>{let t={...e.query};t.historyDays=s.toString(),e.replace({pathname:e.pathname,query:t},void 0,{shallow:!0})},T=()=>{let s={...e.query},t=s.property,r=s.operator,l=s.value;if(void 0===t)return;let a=[],n=Array.isArray(t)?t.length:1,i=new Map;if(i.set("",""),i.set("status","Status"),i.set("cluster","Cluster"),i.set("user","User"),i.set("workspace","Workspace"),i.set("infra","Infra"),1===n)a.push({property:i.get(t),operator:r,value:l});else for(let e=0;e<n;e++)a.push({property:i.get(t[e]),operator:r[e],value:l[e]});Z(a)};return(0,r.jsxs)(r.Fragment,{children:[(0,r.jsxs)("div",{className:"flex flex-wrap items-center gap-2 mb-1 min-h-[20px]",children:[(0,r.jsx)("div",{className:"flex items-center gap-2",children:(0,r.jsx)(o(),{href:"/clusters",className:"text-sky-blue hover:underline leading-none text-base",children:"Sky Clusters"})}),(0,r.jsx)("div",{className:"w-full sm:w-auto",children:(0,r.jsx)(V,{propertyList:E,valueList:q,setFilters:Z,updateURLParams:B,placeholder:"Filter clusters",filters:O})}),(0,r.jsxs)("div",{className:"flex items-center gap-2 ml-auto",children:[(0,r.jsxs)("div",{className:"flex items-center gap-2",children:[(0,r.jsxs)("label",{className:"flex items-center cursor-pointer",children:[(0,r.jsx)("input",{type:"checkbox",checked:g,onChange:e=>{let s=e.target.checked;b(s),U(s)},className:"sr-only"}),(0,r.jsx)("div",{className:"relative inline-flex h-5 w-9 items-center rounded-full ".concat(k?"transition-colors":""," ").concat(g?"bg-sky-600":"bg-gray-300"),children:(0,r.jsx)("span",{className:"inline-block h-3 w-3 transform rounded-full bg-white ".concat(k?"transition-transform":""," ").concat(g?"translate-x-5":"translate-x-1")})}),(0,r.jsx)("span",{className:"ml-2 text-sm text-gray-700",children:"Show history"})]}),g&&(0,r.jsxs)(N.Ph,{value:M.toString(),onValueChange:e=>{let s=parseInt(e);R(s),Q(s)},children:[(0,r.jsx)(N.i4,{className:"w-24 h-8 text-xs",children:(0,r.jsx)(N.ki,{})}),(0,r.jsxs)(N.Bw,{children:[(0,r.jsx)(N.Ql,{value:"1",children:"1 day"}),(0,r.jsx)(N.Ql,{value:"5",children:"5 days"}),(0,r.jsx)(N.Ql,{value:"10",children:"10 days"}),(0,r.jsx)(N.Ql,{value:"30",children:"30 days"})]})]})]}),s&&(0,r.jsxs)("div",{className:"flex items-center",children:[(0,r.jsx)(n.Z,{size:15,className:"mt-0"}),(0,r.jsx)("span",{className:"ml-2 text-gray-500 text-sm",children:"Loading..."})]}),!s&&z&&(0,r.jsx)(i.$3,{timestamp:z}),(0,r.jsxs)("button",{onClick:()=>{C.ZP.invalidate(x.getClusters),C.ZP.invalidate(p.getWorkspaces),g&&C.ZP.invalidate(x.uR),F(!1),S.ZP.preloadForPage("clusters",{force:!0}).then(()=>{F(!0),A(new Date),c.current&&c.current()})},disabled:s,className:"text-sky-blue hover:text-sky-blue-bright flex items-center",children:[(0,r.jsx)(f.Z,{className:"h-4 w-4 mr-1.5"}),!_&&(0,r.jsx)("span",{children:"Refresh"})]})]})]}),(0,r.jsx)(D,{filters:O,setFilters:Z,updateURLParams:B}),(0,r.jsx)(P,{refreshInterval:i.yc,setLoading:t,refreshDataRef:c,filters:O,showHistory:g,historyDays:M,onOpenSSHModal:e=>{v(e),d(!0)},onOpenVSCodeModal:e=>{v(e),m(!0)},setOptionValues:H,preloadingComplete:W}),(0,r.jsx)(y.Oh,{isOpen:u,onClose:()=>d(!1),cluster:j}),(0,r.jsx)(y._R,{isOpen:h,onClose:()=>m(!1),cluster:j})]})}function P(e){let{refreshInterval:s,setLoading:t,refreshDataRef:a,filters:c,showHistory:p,historyDays:f,onOpenSSHModal:j,onOpenVSCodeModal:v,setOptionValues:g,preloadingComplete:y}=e,[w,N]=(0,l.useState)([]),[S,E]=(0,l.useState)({key:null,direction:"ascending"}),[I,O]=(0,l.useState)(!1),[P,Z]=(0,l.useState)(!0),[q,H]=(0,l.useState)(1),[W,V]=(0,l.useState)(10),D=e=>{let s={status:[],cluster:[],user:[],workspace:[],infra:[],labels:[]},t=(e,s)=>{e.includes(s)||e.push(s)};return e.map(e=>{t(s.status,e.status),t(s.cluster,e.cluster),t(s.user,e.user),t(s.workspace,e.workspace),t(s.infra,e.infra);let r=e.labels||{};r&&"object"==typeof r&&Object.entries(r).forEach(e=>{let[r,l]=e;r&&l&&t(s.labels,"".concat(r,":").concat(l))})}),s},z=l.useCallback(async()=>{t(!0),O(!0);try{let e=await C.ZP.get(x.getClusters);if(p){let s=[];try{s=await C.ZP.get(x.uR,[null,f])}catch(e){console.error("Error fetching cluster history:",e)}let t=e.map(e=>({...e,isHistorical:!1})),r=s.map(e=>({...e,isHistorical:!0})),l=[...t];r.forEach(s=>{e.some(e=>e.cluster_hash===s.cluster_hash)||l.push(s)}),g(D(l)),N(l)}else{let s=e.map(e=>({...e,isHistorical:!1}));g(D(s)),N(s)}}catch(e){console.error("Error fetching cluster data:",e),g(D([])),N([])}t(!1),O(!1),Z(!1)},[t,p,f,g]),A=l.useMemo(()=>{let e=0===c.length?w:w.filter(e=>{let s=null;for(let t=0;t<c.length;t++){let r=c[t],l=(0,M.mu)(e,r);s=null===s?l:s&&l}return s});return(0,m.R0)(e,S.key,S.direction)},[w,S,c]);l.useEffect(()=>{a&&(a.current=z)},[a,z]),(0,l.useEffect)(()=>{N([]);let e=!0;if(y){z();let t=setInterval(()=>{e&&"visible"===window.document.visibilityState&&z()},s);return()=>{e=!1,clearInterval(t)}}return()=>{e=!1}},[s,z,y]),(0,l.useEffect)(()=>{H(1)},[w.length]);let B=e=>{let s="ascending";S.key===e&&"ascending"===S.direction&&(s="descending"),E({key:e,direction:s})},U=e=>S.key===e?"ascending"===S.direction?" ↑":" ↓":"",Q=Math.ceil(A.length/W),T=(q-1)*W,X=T+W,$=A.slice(T,X);return(0,r.jsxs)("div",{children:[(0,r.jsx)(d.Zb,{children:(0,r.jsx)("div",{className:"overflow-x-auto rounded-lg",children:(0,r.jsxs)(h.iA,{className:"min-w-full",children:[(0,r.jsx)(h.xD,{children:(0,r.jsxs)(h.SC,{children:[(0,r.jsxs)(h.ss,{className:"sortable whitespace-nowrap",onClick:()=>B("status"),children:["Status",U("status")]}),(0,r.jsxs)(h.ss,{className:"sortable whitespace-nowrap",onClick:()=>B("cluster"),children:["Cluster",U("cluster")]}),(0,r.jsxs)(h.ss,{className:"sortable whitespace-nowrap",onClick:()=>B("user"),children:["User",U("user")]}),(0,r.jsxs)(h.ss,{className:"sortable whitespace-nowrap",onClick:()=>B("workspace"),children:["Workspace",U("workspace")]}),(0,r.jsxs)(h.ss,{className:"sortable whitespace-nowrap",onClick:()=>B("infra"),children:["Infra",U("infra")]}),(0,r.jsxs)(h.ss,{className:"sortable whitespace-nowrap",onClick:()=>B("resources_str"),children:["Resources",U("resources_str")]}),(0,r.jsxs)(h.ss,{className:"sortable whitespace-nowrap",onClick:()=>B("time"),children:["Started",U("time")]}),p&&(0,r.jsxs)(h.ss,{className:"sortable whitespace-nowrap",onClick:()=>B("duration"),children:["Duration",U("duration")]}),(0,r.jsxs)(h.ss,{className:"sortable whitespace-nowrap",onClick:()=>B("autostop"),children:["Autostop",U("autostop")]}),(0,r.jsx)(h.ss,{className:"md:sticky md:right-0 md:bg-white",children:"Actions"})]})}),(0,r.jsx)(h.RM,{children:I||!y?(0,r.jsx)(h.SC,{children:(0,r.jsx)(h.pj,{colSpan:9,className:"text-center py-6 text-gray-500",children:(0,r.jsxs)("div",{className:"flex justify-center items-center",children:[(0,r.jsx)(n.Z,{size:20,className:"mr-2"}),(0,r.jsx)("span",{children:"Loading..."})]})})}):$.length>0?$.map((e,s)=>(0,r.jsxs)(h.SC,{children:[(0,r.jsx)(h.pj,{children:(0,r.jsx)(k.j,{name:"clusters.table.status.badge",context:e,fallback:(0,r.jsx)(b.OE,{status:e.status,statusTooltip:e.statusTooltip})})}),(0,r.jsx)(h.pj,{children:(0,r.jsx)(o(),{href:"/clusters/".concat(e.isHistorical?e.cluster_hash:e.cluster||e.name),className:"text-blue-600",children:e.cluster||e.name})}),(0,r.jsx)(h.pj,{children:(0,r.jsx)(L.H,{username:e.user,userHash:e.user_hash})}),(0,r.jsx)(h.pj,{children:(0,r.jsx)(o(),{href:"/workspaces",className:"text-gray-700 hover:text-blue-600 hover:underline",children:e.workspace||"default"})}),(0,r.jsx)(h.pj,{children:(0,r.jsx)(i.Md,{content:e.full_infra||e.infra,className:"text-sm text-muted-foreground",children:(0,r.jsxs)("span",{children:[(0,r.jsx)(o(),{href:"/infra",className:"text-blue-600 hover:underline",children:e.cloud}),e.infra.includes("(")&&(0,r.jsx)("span",{children:" "+e.infra.substring(e.infra.indexOf("("))})]})})}),(0,r.jsx)(h.pj,{children:(0,r.jsx)(i.Md,{content:e.resources_str_full||e.resources_str,className:"text-sm text-muted-foreground",children:(0,r.jsx)("span",{children:e.resources_str})})}),(0,r.jsx)(h.pj,{children:(0,r.jsx)(i.Zg,{date:e.time})}),p&&(0,r.jsx)(h.pj,{children:_(e.duration)}),(0,r.jsx)(h.pj,{children:e.isHistorical?"-":R(e.autostop,e.to_down)}),(0,r.jsx)(h.pj,{className:"text-left md:sticky md:right-0 md:bg-white",children:!e.isHistorical&&(0,r.jsx)(F,{cluster:e.cluster,status:e.status,onOpenSSHModal:j,onOpenVSCodeModal:v})})]},s)):(0,r.jsx)(h.SC,{children:(0,r.jsx)(h.pj,{colSpan:9,className:"text-center py-6 text-gray-500",children:p?"No clusters found":"No active clusters"})})})]})})}),w.length>0&&(0,r.jsx)("div",{className:"flex justify-end items-center py-2 px-4 text-sm text-gray-700",children:(0,r.jsxs)("div",{className:"flex items-center space-x-4",children:[(0,r.jsxs)("div",{className:"flex items-center",children:[(0,r.jsx)("span",{className:"mr-2",children:"Rows per page:"}),(0,r.jsxs)("div",{className:"relative inline-block",children:[(0,r.jsxs)("select",{value:W,onChange:e=>{V(parseInt(e.target.value,10)),H(1)},className:"py-1 pl-2 pr-6 appearance-none outline-none cursor-pointer border-none bg-transparent",style:{minWidth:"40px"},children:[(0,r.jsx)("option",{value:10,children:"10"}),(0,r.jsx)("option",{value:30,children:"30"}),(0,r.jsx)("option",{value:50,children:"50"}),(0,r.jsx)("option",{value:100,children:"100"}),(0,r.jsx)("option",{value:200,children:"200"})]}),(0,r.jsx)("svg",{xmlns:"http://www.w3.org/2000/svg",className:"h-4 w-4 text-gray-500 absolute right-0 top-1/2 transform -translate-y-1/2 pointer-events-none",fill:"none",viewBox:"0 0 24 24",stroke:"currentColor",children:(0,r.jsx)("path",{strokeLinecap:"round",strokeLinejoin:"round",strokeWidth:2,d:"M19 9l-7 7-7-7"})})]})]}),(0,r.jsx)("div",{children:"".concat(T+1," - ").concat(Math.min(X,A.length)," of ").concat(A.length)}),(0,r.jsxs)("div",{className:"flex items-center space-x-2",children:[(0,r.jsx)(u.z,{variant:"ghost",size:"icon",onClick:()=>{H(e=>Math.max(e-1,1))},disabled:1===q,className:"text-gray-500 h-8 w-8 p-0",children:(0,r.jsx)("svg",{xmlns:"http://www.w3.org/2000/svg",width:"16",height:"16",viewBox:"0 0 24 24",fill:"none",stroke:"currentColor",strokeWidth:"2",strokeLinecap:"round",strokeLinejoin:"round",className:"chevron-left",children:(0,r.jsx)("path",{d:"M15 18l-6-6 6-6"})})}),(0,r.jsx)(u.z,{variant:"ghost",size:"icon",onClick:()=>{H(e=>Math.min(e+1,Q))},disabled:q===Q||0===Q,className:"text-gray-500 h-8 w-8 p-0",children:(0,r.jsx)("svg",{xmlns:"http://www.w3.org/2000/svg",width:"16",height:"16",viewBox:"0 0 24 24",fill:"none",stroke:"currentColor",strokeWidth:"2",strokeLinecap:"round",strokeLinejoin:"round",className:"chevron-right",children:(0,r.jsx)("path",{d:"M9 18l6-6-6-6"})})})]})]})})]})}let Z=(e,s)=>{s&&s(e)},q=(e,s)=>{s?s(e):window.open("ssh://".concat(e))},H=e=>"RUNNING"===e?["connect","VSCode"]:[],W={connect:(0,r.jsx)(v,{className:"w-4 h-4 text-gray-500 inline-block"}),VSCode:(0,r.jsx)(g,{className:"w-4 h-4 text-gray-500 inline-block"})};function F(e){let{withLabel:s=!1,cluster:t,status:l,onOpenSSHModal:a,onOpenVSCodeModal:n}=e,c=H(l),o=(0,w.X)(),u=e=>{switch(e){case"connect":q(t,a);break;case"VSCode":Z(t,n);break;default:return}};return(0,r.jsx)(r.Fragment,{children:(0,r.jsx)("div",{className:"flex items-center space-x-4",children:Object.entries(W).map(e=>{let t,l,[a,n]=e;switch(a){case"connect":t="Connect",l="Connect with SSH";break;case"VSCode":t="VSCode",l="Open in VS Code"}return(s||(t=""),c.includes(a))?(0,r.jsx)(i.WH,{content:l,className:"capitalize text-sm text-muted-foreground",children:(0,r.jsxs)("button",{onClick:()=>u(a),className:"text-sky-blue hover:text-sky-blue-bright font-medium inline-flex items-center",children:[n,!o&&(0,r.jsx)("span",{className:"ml-1.5",children:t})]})},a):(0,r.jsx)(i.WH,{content:l,className:"capitalize text-sm text-muted-foreground",children:(0,r.jsxs)("span",{className:"opacity-30 flex items-center cursor-not-allowed text-sm",title:a,children:[n,!o&&(0,r.jsx)("span",{className:"ml-1.5",children:t})]})},a)})})})}let V=e=>{let{propertyList:s=[],valueList:t,setFilters:a,updateURLParams:n,placeholder:i="Filter clusters",filters:c=[]}=e,o=(0,l.useRef)(null),u=(0,l.useRef)(null),[d,h]=(0,l.useState)(!1),[x,p]=(0,l.useState)(""),[m,f]=(0,l.useState)("status"),[j,v]=(0,l.useState)([]);(0,l.useEffect)(()=>{let e=e=>{u.current&&!u.current.contains(e.target)&&o.current&&!o.current.contains(e.target)&&h(!1)};return document.addEventListener("mousedown",e),()=>{document.removeEventListener("mousedown",e)}},[]),(0,l.useEffect)(()=>{let e=[],s=m||"";if(s.length>1){s=m[0].toUpperCase();for(let e=1;e<m.length;e++)s+=m[e]}let r=c.filter(e=>e.property===s).map(e=>e.value);if(t&&"object"==typeof t)switch(m){case"status":e=t.status.filter(e=>!r.find(s=>s===e))||[];break;case"user":e=t.user.filter(e=>!r.find(s=>s===e))||[];break;case"cluster":e=t.cluster.filter(e=>!r.find(s=>s===e))||[];break;case"workspace":e=t.workspace.filter(e=>!r.find(s=>s===e))||[];break;case"infra":e=t.infra.filter(e=>!r.find(s=>s===e))||[];break;case"labels":e=t.labels.filter(e=>!r.find(s=>s===e))||[]}""!==x.trim()&&(e=e.filter(e=>e&&e.toString().toLowerCase().includes(x.toLowerCase()))),v(e)},[m,t,x,c]);let g=e=>{let t=s.find(s=>s.value===e);return t?t.label:e},y=e=>{a(s=>{let t=[...s,{property:g(m),operator:":",value:e}];return n(t),t}),h(!1),p(""),o.current.focus()};return(0,r.jsxs)("div",{className:"flex flex-row border border-gray-300 rounded-md overflow-visible",children:[(0,r.jsx)("div",{className:"border-r border-gray-300 flex-shrink-0",children:(0,r.jsxs)(N.Ph,{onValueChange:f,value:m,children:[(0,r.jsx)(N.i4,{"aria-label":"Filter Property",className:"focus:ring-0 focus:ring-offset-0 border-none rounded-l-md rounded-r-none w-20 sm:w-24 md:w-32 h-8 text-xs sm:text-sm",children:(0,r.jsx)(N.ki,{placeholder:"Status"})}),(0,r.jsx)(N.Bw,{children:s.map((e,s)=>(0,r.jsx)(N.Ql,{value:e.value,children:e.label},"property-item-".concat(s)))})]})}),(0,r.jsxs)("div",{className:"relative flex-1",children:[(0,r.jsx)("input",{type:"text",ref:o,placeholder:i,value:x,onChange:e=>{p(e.target.value),d||h(!0)},onFocus:()=>{h(!0)},onKeyDown:e=>{"Enter"===e.key&&""!==x.trim()?(a(e=>{let s=[...e,{property:g(m),operator:":",value:x}];return n(s),s}),p(""),h(!1)):"Escape"===e.key&&(h(!1),o.current.blur())},className:"h-8 w-full sm:w-96 px-3 pr-8 text-sm border-none rounded-l-none rounded-r-md focus:ring-0 focus:outline-none",autoComplete:"off"}),x&&(0,r.jsx)("button",{onClick:()=>{p(""),h(!1)},className:"absolute right-2 top-1/2 transform -translate-y-1/2 text-gray-400 hover:text-gray-600",title:"Clear filter",tabIndex:-1,children:(0,r.jsx)("svg",{className:"h-4 w-4",fill:"none",stroke:"currentColor",viewBox:"0 0 24 24",children:(0,r.jsx)("path",{strokeLinecap:"round",strokeLinejoin:"round",strokeWidth:2,d:"M6 18L18 6M6 6l12 12"})})}),d&&j.length>0&&(0,r.jsx)("div",{ref:u,className:"absolute z-50 mt-1 w-full bg-white border border-gray-200 rounded-md shadow-lg max-h-60 overflow-y-auto",style:{zIndex:9999},children:j.map((e,s)=>(0,r.jsx)("div",{className:"px-3 py-2 cursor-pointer hover:bg-gray-50 text-sm ".concat(s!==j.length-1?"border-b border-gray-100":""),onClick:()=>y(e),children:(0,r.jsx)("span",{className:"text-sm text-gray-700",children:e})},"".concat(e,"-").concat(s)))})]})]})},D=e=>{let{filters:s=[],setFilters:t,updateURLParams:l}=e,a=e=>{t(s=>{let t=s.filter((s,t)=>t!==e);return l(t),t})};return(0,r.jsx)(r.Fragment,{children:(0,r.jsx)("div",{className:"flex items-center gap-4 py-2 px-2",children:(0,r.jsxs)("div",{className:"flex flex-wrap items-content gap-2",children:[s.map((e,s)=>(0,r.jsx)(z,{filter:e,onRemove:()=>a(s)},"filteritem-".concat(s))),s.length>0&&(0,r.jsx)(r.Fragment,{children:(0,r.jsx)("button",{onClick:()=>{l([]),t([])},className:"rounded-full px-4 py-1 text-sm text-gray-700 bg-gray-200 hover:bg-gray-300",children:"Clear filters"})})]})})})},z=e=>{let{filter:s,onRemove:t}=e;return(0,r.jsx)(r.Fragment,{children:(0,r.jsxs)("div",{className:"flex items-center text-blue-600 bg-blue-100 px-1 py-1 rounded-full text-sm",children:[(0,r.jsxs)("div",{className:"flex items-center gap-1 px-2",children:[(0,r.jsx)("span",{children:"".concat(s.property," ")}),(0,r.jsx)("span",{children:"".concat(s.operator," ")}),(0,r.jsx)("span",{children:" ".concat(s.value)})]}),(0,r.jsx)("button",{onClick:()=>t(),className:"p-0.5 ml-1 transform text-gray-400 hover:text-gray-600 bg-blue-500 hover:bg-blue-600 rounded-full flex flex-col items-center",title:"Clear filter",children:(0,r.jsx)("svg",{className:"h-3 w-3",fill:"none",stroke:"white",viewBox:"0 0 24 24",children:(0,r.jsx)("path",{strokeLinecap:"round",strokeLinejoin:"round",strokeWidth:5,d:"M6 18L18 6M6 6l12 12"})})})]})})}},12003:function(e,s,t){t.d(s,{j:function(){return n}});var r=t(90512);let l=e=>"boolean"==typeof e?`${e}`:0===e?"0":e,a=r.W,n=(e,s)=>t=>{var r;if((null==s?void 0:s.variants)==null)return a(e,null==t?void 0:t.class,null==t?void 0:t.className);let{variants:n,defaultVariants:i}=s,c=Object.keys(n).map(e=>{let s=null==t?void 0:t[e],r=null==i?void 0:i[e];if(null===s)return null;let a=l(s)||l(r);return n[e][a]}),o=t&&Object.entries(t).reduce((e,s)=>{let[t,r]=s;return void 0===r||(e[t]=r),e},{});return a(e,c,null==s?void 0:null===(r=s.compoundVariants)||void 0===r?void 0:r.reduce((e,s)=>{let{class:t,className:r,...l}=s;return Object.entries(l).every(e=>{let[s,t]=e;return Array.isArray(t)?t.includes({...i,...o}[s]):({...i,...o})[s]===t})?[...e,t,r]:e},[]),null==t?void 0:t.class,null==t?void 0:t.className)}}}]);