skypilot-nightly 1.0.0.dev20250526__py3-none-any.whl → 1.0.0.dev20250527__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 (73) hide show
  1. sky/__init__.py +2 -2
  2. sky/check.py +28 -5
  3. sky/cli.py +5 -22
  4. sky/client/cli.py +5 -22
  5. sky/client/sdk.py +5 -2
  6. sky/clouds/cloud.py +2 -2
  7. sky/clouds/kubernetes.py +10 -5
  8. sky/clouds/service_catalog/kubernetes_catalog.py +4 -0
  9. sky/clouds/ssh.py +24 -8
  10. sky/core.py +20 -2
  11. sky/dashboard/out/404.html +1 -1
  12. sky/dashboard/out/_next/static/D5bjIfl4Ob3SV3LJz3CO0/_buildManifest.js +1 -0
  13. sky/dashboard/out/_next/static/chunks/236-e220ba0c35bf089e.js +6 -0
  14. sky/dashboard/out/_next/static/chunks/{498-d7722313e5e5b4e6.js → 320-afea3ddcc5bd1c6c.js} +1 -16
  15. sky/dashboard/out/_next/static/chunks/{480-5a0de8b6570ea105.js → 470-1d784f5c8750744a.js} +1 -1
  16. sky/dashboard/out/_next/static/chunks/627-31b701e69f52db0c.js +1 -0
  17. sky/dashboard/out/_next/static/chunks/843-e35d71cf1c7f706e.js +11 -0
  18. sky/dashboard/out/_next/static/chunks/990-f85643b521f7ca65.js +1 -0
  19. sky/dashboard/out/_next/static/chunks/pages/clusters/[cluster]/[job]-339b59921ccfe266.js +1 -0
  20. sky/dashboard/out/_next/static/chunks/pages/clusters/[cluster]-e23fcddf60578a0d.js +1 -0
  21. sky/dashboard/out/_next/static/chunks/pages/{clusters-9e6d1ec6e1ac5b29.js → clusters-8afda8efa5b74997.js} +1 -1
  22. sky/dashboard/out/_next/static/chunks/pages/config-72b8c6c2edfd0e39.js +6 -0
  23. sky/dashboard/out/_next/static/chunks/pages/infra-1521baab6992916b.js +1 -0
  24. sky/dashboard/out/_next/static/chunks/pages/jobs/[job]-4d913940b4fa6f5a.js +1 -0
  25. sky/dashboard/out/_next/static/chunks/pages/jobs-ff7e8e377d02b651.js +1 -0
  26. sky/dashboard/out/_next/static/chunks/pages/users-9900af52acf8648d.js +1 -0
  27. sky/dashboard/out/_next/static/chunks/pages/workspace/{new-bbf436f41381e169.js → new-63763ffa3edb4508.js} +1 -1
  28. sky/dashboard/out/_next/static/chunks/pages/workspaces/{[name]-7733c960685b4385.js → [name]-3ede7a13caf23375.js} +1 -1
  29. sky/dashboard/out/_next/static/chunks/pages/workspaces-72330c4d0fc9a4a2.js +1 -0
  30. sky/dashboard/out/_next/static/css/6a1c0d711a4bdaf1.css +3 -0
  31. sky/dashboard/out/clusters/[cluster]/[job].html +1 -1
  32. sky/dashboard/out/clusters/[cluster].html +1 -1
  33. sky/dashboard/out/clusters.html +1 -1
  34. sky/dashboard/out/config.html +1 -0
  35. sky/dashboard/out/index.html +1 -1
  36. sky/dashboard/out/infra.html +1 -1
  37. sky/dashboard/out/jobs/[job].html +1 -1
  38. sky/dashboard/out/jobs.html +1 -1
  39. sky/dashboard/out/users.html +1 -1
  40. sky/dashboard/out/workspace/new.html +1 -1
  41. sky/dashboard/out/workspaces/[name].html +1 -1
  42. sky/dashboard/out/workspaces.html +1 -1
  43. sky/server/constants.py +1 -1
  44. sky/server/requests/payloads.py +18 -5
  45. sky/server/requests/serializers/decoders.py +0 -11
  46. sky/server/server.py +19 -9
  47. sky/skypilot_config.py +4 -0
  48. sky/utils/kubernetes/exec_kubeconfig_converter.py +19 -0
  49. sky/utils/schemas.py +57 -5
  50. sky/workspaces/core.py +186 -50
  51. sky/workspaces/server.py +25 -0
  52. {skypilot_nightly-1.0.0.dev20250526.dist-info → skypilot_nightly-1.0.0.dev20250527.dist-info}/METADATA +1 -1
  53. {skypilot_nightly-1.0.0.dev20250526.dist-info → skypilot_nightly-1.0.0.dev20250527.dist-info}/RECORD +61 -58
  54. {skypilot_nightly-1.0.0.dev20250526.dist-info → skypilot_nightly-1.0.0.dev20250527.dist-info}/WHEEL +1 -1
  55. sky/dashboard/out/_next/static/7GEgRyZKRaSnYZCV1Jwol/_buildManifest.js +0 -1
  56. sky/dashboard/out/_next/static/chunks/25-062253ea41fb8eec.js +0 -6
  57. sky/dashboard/out/_next/static/chunks/734-a6e01d7f98904741.js +0 -1
  58. sky/dashboard/out/_next/static/chunks/938-59956af3950b02ed.js +0 -1
  59. sky/dashboard/out/_next/static/chunks/pages/clusters/[cluster]/[job]-3b5aad09a25f64b7.js +0 -1
  60. sky/dashboard/out/_next/static/chunks/pages/clusters/[cluster]-9529d9e882a0e75c.js +0 -16
  61. sky/dashboard/out/_next/static/chunks/pages/infra-abb7d744ecf15109.js +0 -1
  62. sky/dashboard/out/_next/static/chunks/pages/jobs/[job]-48dc8d67d4b60be1.js +0 -1
  63. sky/dashboard/out/_next/static/chunks/pages/jobs-73d5e0c369d00346.js +0 -16
  64. sky/dashboard/out/_next/static/chunks/pages/users-b8acf6e6735323a2.js +0 -1
  65. sky/dashboard/out/_next/static/chunks/pages/workspaces-5ed48b3201b998c8.js +0 -1
  66. sky/dashboard/out/_next/static/css/28558d57108b05ae.css +0 -3
  67. /sky/dashboard/out/_next/static/{7GEgRyZKRaSnYZCV1Jwol → D5bjIfl4Ob3SV3LJz3CO0}/_ssgManifest.js +0 -0
  68. /sky/dashboard/out/_next/static/chunks/{573-f17bd89d9f9118b3.js → 573-82bd40a37af834f1.js} +0 -0
  69. /sky/dashboard/out/_next/static/chunks/{578-d351125af46c293f.js → 578-24f35aa98d38d638.js} +0 -0
  70. /sky/dashboard/out/_next/static/chunks/pages/{_app-96a715a6fb01e228.js → _app-3985f074c163a856.js} +0 -0
  71. {skypilot_nightly-1.0.0.dev20250526.dist-info → skypilot_nightly-1.0.0.dev20250527.dist-info}/entry_points.txt +0 -0
  72. {skypilot_nightly-1.0.0.dev20250526.dist-info → skypilot_nightly-1.0.0.dev20250527.dist-info}/licenses/LICENSE +0 -0
  73. {skypilot_nightly-1.0.0.dev20250526.dist-info → skypilot_nightly-1.0.0.dev20250527.dist-info}/top_level.txt +0 -0
sky/__init__.py CHANGED
@@ -5,7 +5,7 @@ from typing import Optional
5
5
  import urllib.request
6
6
 
7
7
  # Replaced with the current commit when building the wheels.
8
- _SKYPILOT_COMMIT_SHA = 'bbcd11ba81d6312ae5e292e891da531024c321aa'
8
+ _SKYPILOT_COMMIT_SHA = '269ed1497f78a46da5dd05a528000da55eacf551'
9
9
 
10
10
 
11
11
  def _get_git_commit():
@@ -35,7 +35,7 @@ def _get_git_commit():
35
35
 
36
36
 
37
37
  __commit__ = _get_git_commit()
38
- __version__ = '1.0.0.dev20250526'
38
+ __version__ = '1.0.0.dev20250527'
39
39
  __root_dir__ = os.path.dirname(os.path.abspath(__file__))
40
40
 
41
41
 
sky/check.py CHANGED
@@ -504,6 +504,18 @@ def _format_context_details(cloud: Union[str, sky_clouds.Cloud],
504
504
  f'{str_to_format}'
505
505
  f'{colorama.Style.RESET_ALL}')
506
506
 
507
+ # For SSH, determine which contexts are disabled due to allowed_node_pools
508
+ disabled_due_to_allowed_node_pools = set()
509
+ if isinstance(cloud_type, sky_clouds.SSH):
510
+ # Get all node pool contexts from file
511
+ all_node_pool_contexts = sky_clouds.SSH.get_ssh_node_pool_contexts()
512
+ # Get allowed contexts (after filtering)
513
+ allowed_contexts = sky_clouds.SSH.existing_allowed_contexts()
514
+ # Contexts that exist in file but not in allowed list are disabled
515
+ # due to allowed_node_pools configuration
516
+ disabled_due_to_allowed_node_pools = (set(all_node_pool_contexts) -
517
+ set(allowed_contexts))
518
+
507
519
  # Format the context info with consistent styling
508
520
  contexts_formatted = []
509
521
  for i, context in enumerate(filtered_contexts):
@@ -519,11 +531,22 @@ def _format_context_details(cloud: Union[str, sky_clouds.Cloud],
519
531
  text_suffix = ''
520
532
  if show_details:
521
533
  if ctx2text is not None:
522
- text_suffix = (
523
- f': {ctx2text[context]}' if context in ctx2text else
524
- (': ' + _red_color('disabled. ') +
525
- _dim_color('Reason: Not set up. Use `sky ssh up --infra '
526
- f'{context.lstrip("ssh-")}` to set up.')))
534
+ if context in ctx2text:
535
+ text_suffix = f': {ctx2text[context]}'
536
+ elif (isinstance(cloud_type, sky_clouds.SSH) and
537
+ context in disabled_due_to_allowed_node_pools):
538
+ # Context is disabled due to allowed_node_pools config
539
+ text_suffix = (': ' + _red_color('disabled. ') +
540
+ _dim_color('Reason: Not included in '
541
+ 'allowed_node_pools '
542
+ 'configuration.'))
543
+ else:
544
+ # Default case - not set up
545
+ text_suffix = (': ' + _red_color('disabled. ') +
546
+ _dim_color('Reason: Not set up. Use '
547
+ '`sky ssh up --infra '
548
+ f'{context.lstrip("ssh-")}` '
549
+ 'to set up.'))
527
550
  contexts_formatted.append(
528
551
  f'\n {symbol}{cleaned_context}{text_suffix}')
529
552
  identity_str = ('SSH Node Pools' if isinstance(cloud_type, sky_clouds.SSH)
sky/cli.py CHANGED
@@ -1789,20 +1789,8 @@ def _show_enabled_infra(active_workspace: str, show_workspace: bool):
1789
1789
  title = (f'{colorama.Fore.CYAN}{colorama.Style.BRIGHT}Enabled Infra'
1790
1790
  f'{workspace_str}:'
1791
1791
  f'{colorama.Style.RESET_ALL} ')
1792
- enabled_clouds = sdk.get(sdk.enabled_clouds())
1793
- enabled_ssh_infras = []
1794
- enabled_k8s_infras = []
1795
- enabled_cloud_infras = []
1796
- for cloud in enabled_clouds:
1797
- cloud_infra = cloud.get_infras()
1798
- if isinstance(cloud, clouds.SSH):
1799
- enabled_ssh_infras.extend(cloud_infra)
1800
- elif isinstance(cloud, clouds.Kubernetes):
1801
- enabled_k8s_infras.extend(cloud_infra)
1802
- else:
1803
- enabled_cloud_infras.extend(cloud_infra)
1804
- all_infras = sorted(enabled_ssh_infras) + sorted(
1805
- enabled_k8s_infras) + sorted(enabled_cloud_infras)
1792
+ all_infras = sdk.get(
1793
+ sdk.enabled_clouds(workspace=active_workspace, expand=True))
1806
1794
  click.echo(f'{title}{", ".join(all_infras)}\n')
1807
1795
 
1808
1796
 
@@ -1997,7 +1985,7 @@ def status(verbose: bool, refresh: bool, ip: bool, endpoints: bool,
1997
1985
  workspace_request_id = sdk.workspaces()
1998
1986
  except RuntimeError:
1999
1987
  # Backward compatibility for API server before #5660.
2000
- # TODO(zhwu): remove this after 0.12.0.
1988
+ # TODO(zhwu): remove this after 0.10.0.
2001
1989
  logger.warning(f'{colorama.Style.DIM}SkyPilot API server is '
2002
1990
  'in an old version, and may miss feature: '
2003
1991
  'workspaces. Update with: sky api stop; '
@@ -3524,13 +3512,8 @@ def show_gpus(
3524
3512
  cloud_is_ssh = isinstance(cloud_obj, clouds.SSH)
3525
3513
  # TODO(romilb): We should move this to the backend.
3526
3514
  kubernetes_autoscaling = kubernetes_utils.get_autoscaler_type() is not None
3527
- kubernetes_is_enabled = False
3528
- ssh_is_enabled = False
3529
- for cloud in enabled_clouds:
3530
- if isinstance(cloud, clouds.SSH):
3531
- ssh_is_enabled = True
3532
- elif isinstance(cloud, clouds.Kubernetes):
3533
- kubernetes_is_enabled = True
3515
+ kubernetes_is_enabled = clouds.Kubernetes.canonical_name() in enabled_clouds
3516
+ ssh_is_enabled = clouds.SSH.canonical_name() in enabled_clouds
3534
3517
  query_k8s_realtime_gpu = (kubernetes_is_enabled and
3535
3518
  (cloud_name is None or cloud_is_kubernetes))
3536
3519
  query_ssh_realtime_gpu = (ssh_is_enabled and
sky/client/cli.py CHANGED
@@ -1789,20 +1789,8 @@ def _show_enabled_infra(active_workspace: str, show_workspace: bool):
1789
1789
  title = (f'{colorama.Fore.CYAN}{colorama.Style.BRIGHT}Enabled Infra'
1790
1790
  f'{workspace_str}:'
1791
1791
  f'{colorama.Style.RESET_ALL} ')
1792
- enabled_clouds = sdk.get(sdk.enabled_clouds())
1793
- enabled_ssh_infras = []
1794
- enabled_k8s_infras = []
1795
- enabled_cloud_infras = []
1796
- for cloud in enabled_clouds:
1797
- cloud_infra = cloud.get_infras()
1798
- if isinstance(cloud, clouds.SSH):
1799
- enabled_ssh_infras.extend(cloud_infra)
1800
- elif isinstance(cloud, clouds.Kubernetes):
1801
- enabled_k8s_infras.extend(cloud_infra)
1802
- else:
1803
- enabled_cloud_infras.extend(cloud_infra)
1804
- all_infras = sorted(enabled_ssh_infras) + sorted(
1805
- enabled_k8s_infras) + sorted(enabled_cloud_infras)
1792
+ all_infras = sdk.get(
1793
+ sdk.enabled_clouds(workspace=active_workspace, expand=True))
1806
1794
  click.echo(f'{title}{", ".join(all_infras)}\n')
1807
1795
 
1808
1796
 
@@ -1997,7 +1985,7 @@ def status(verbose: bool, refresh: bool, ip: bool, endpoints: bool,
1997
1985
  workspace_request_id = sdk.workspaces()
1998
1986
  except RuntimeError:
1999
1987
  # Backward compatibility for API server before #5660.
2000
- # TODO(zhwu): remove this after 0.12.0.
1988
+ # TODO(zhwu): remove this after 0.10.0.
2001
1989
  logger.warning(f'{colorama.Style.DIM}SkyPilot API server is '
2002
1990
  'in an old version, and may miss feature: '
2003
1991
  'workspaces. Update with: sky api stop; '
@@ -3524,13 +3512,8 @@ def show_gpus(
3524
3512
  cloud_is_ssh = isinstance(cloud_obj, clouds.SSH)
3525
3513
  # TODO(romilb): We should move this to the backend.
3526
3514
  kubernetes_autoscaling = kubernetes_utils.get_autoscaler_type() is not None
3527
- kubernetes_is_enabled = False
3528
- ssh_is_enabled = False
3529
- for cloud in enabled_clouds:
3530
- if isinstance(cloud, clouds.SSH):
3531
- ssh_is_enabled = True
3532
- elif isinstance(cloud, clouds.Kubernetes):
3533
- kubernetes_is_enabled = True
3515
+ kubernetes_is_enabled = clouds.Kubernetes.canonical_name() in enabled_clouds
3516
+ ssh_is_enabled = clouds.SSH.canonical_name() in enabled_clouds
3534
3517
  query_k8s_realtime_gpu = (kubernetes_is_enabled and
3535
3518
  (cloud_name is None or cloud_is_kubernetes))
3536
3519
  query_ssh_realtime_gpu = (ssh_is_enabled and
sky/client/sdk.py CHANGED
@@ -138,12 +138,14 @@ def check(infra_list: Optional[Tuple[str, ...]],
138
138
  @usage_lib.entrypoint
139
139
  @server_common.check_server_healthy_or_start
140
140
  @annotations.client_api
141
- def enabled_clouds(workspace: Optional[str] = None) -> server_common.RequestId:
141
+ def enabled_clouds(workspace: Optional[str] = None,
142
+ expand: bool = False) -> server_common.RequestId:
142
143
  """Gets the enabled clouds.
143
144
 
144
145
  Args:
145
146
  workspace: The workspace to get the enabled clouds for. If None, the
146
147
  active workspace will be used.
148
+ expand: Whether to expand Kubernetes and SSH to list of resource pools.
147
149
 
148
150
  Returns:
149
151
  The request ID of the enabled clouds request.
@@ -154,7 +156,7 @@ def enabled_clouds(workspace: Optional[str] = None) -> server_common.RequestId:
154
156
  if workspace is None:
155
157
  workspace = skypilot_config.get_active_workspace()
156
158
  response = requests.get((f'{server_common.get_server_url()}/enabled_clouds?'
157
- f'workspace={workspace}'),
159
+ f'workspace={workspace}&expand={expand}'),
158
160
  cookies=server_common.get_api_cookie_jar())
159
161
  return server_common.get_request_id(response)
160
162
 
@@ -1919,6 +1921,7 @@ def api_login(endpoint: Optional[str] = None, get_token: bool = False) -> None:
1919
1921
  if (endpoint is not None and not endpoint.startswith('http://') and
1920
1922
  not endpoint.startswith('https://')):
1921
1923
  raise click.BadParameter('Endpoint must be a valid URL.')
1924
+ endpoint = endpoint.rstrip('/')
1922
1925
 
1923
1926
  server_status = server_common.check_server_healthy(endpoint)
1924
1927
  if server_status == server_common.ApiServerStatus.NEEDS_AUTH or get_token:
sky/clouds/cloud.py CHANGED
@@ -492,13 +492,13 @@ class Cloud:
492
492
  f'{cls._REPR} does not support {CloudCapability.STORAGE.value}.')
493
493
 
494
494
  @classmethod
495
- def get_infras(cls) -> List[str]:
495
+ def expand_infras(cls) -> List[str]:
496
496
  """Returns a list of enabled infrastructures for this cloud.
497
497
 
498
498
  For Kubernetes and SSH, return a list of resource pools.
499
499
  For all other clouds, return self.
500
500
  """
501
- return [cls._REPR.lower()]
501
+ return [cls.canonical_name()]
502
502
 
503
503
  # TODO(zhwu): Make the return type immutable.
504
504
  @classmethod
sky/clouds/kubernetes.py CHANGED
@@ -164,15 +164,20 @@ class Kubernetes(clouds.Cloud):
164
164
 
165
165
  all_contexts = set(all_contexts)
166
166
 
167
+ # Allowed_contexts specified for workspace should take precedence over
168
+ # the global allowed_contexts.
169
+ allowed_contexts = skypilot_config.get_workspace_cloud(
170
+ 'kubernetes').get('allowed_contexts', None)
171
+ if allowed_contexts is None:
172
+ allowed_contexts = skypilot_config.get_nested(
173
+ ('kubernetes', 'allowed_contexts'), None)
174
+
167
175
  # Exclude contexts starting with `ssh-`
168
176
  # TODO(romilb): Remove when SSH Node Pools use a separate kubeconfig.
169
177
  all_contexts = [
170
178
  ctx for ctx in all_contexts if not ctx.startswith('ssh-')
171
179
  ]
172
180
 
173
- allowed_contexts = skypilot_config.get_nested(
174
- ('kubernetes', 'allowed_contexts'), None)
175
-
176
181
  if allowed_contexts is None:
177
182
  # Try kubeconfig if present
178
183
  current_context = (
@@ -894,8 +899,8 @@ class Kubernetes(clouds.Cloud):
894
899
  return True, None
895
900
 
896
901
  @classmethod
897
- def get_infras(cls) -> List[str]:
902
+ def expand_infras(cls) -> List[str]:
898
903
  return [
899
- f'{cls._REPR.lower()}/{c}'
904
+ f'{cls.canonical_name()}/{c}'
900
905
  for c in cls.existing_allowed_contexts(silent=True)
901
906
  ]
@@ -260,6 +260,10 @@ def _list_accelerators(
260
260
  container.resources.requests))
261
261
 
262
262
  accelerators_available = accelerator_count - allocated_qty
263
+ # Initialize the total_accelerators_available to make sure the
264
+ # key exists in the dictionary.
265
+ total_accelerators_available[accelerator_name] = (
266
+ total_accelerators_available.get(accelerator_name, 0))
263
267
 
264
268
  if accelerators_available >= min_quantity_filter:
265
269
  quantized_availability = min_quantity_filter * (
sky/clouds/ssh.py CHANGED
@@ -7,6 +7,7 @@ from typing import Dict, List, Optional, Set, Tuple, Union
7
7
  import yaml
8
8
 
9
9
  from sky import sky_logging
10
+ from sky import skypilot_config
10
11
  from sky.adaptors import kubernetes as kubernetes_adaptor
11
12
  from sky.clouds import kubernetes
12
13
  from sky.provision.kubernetes import utils as kubernetes_utils
@@ -134,29 +135,44 @@ class SSH(kubernetes.Kubernetes):
134
135
 
135
136
  all_contexts = set(all_contexts)
136
137
 
138
+ # Workspace-level allowed_node_pools should take precedence over
139
+ # the global allowed_node_pools.
140
+ allowed_node_pools = skypilot_config.get_workspace_cloud('ssh').get(
141
+ 'allowed_node_pools', None)
142
+ if allowed_node_pools is None:
143
+ allowed_node_pools = skypilot_config.get_nested(
144
+ ('ssh', 'allowed_node_pools'), None)
145
+
137
146
  # Filter for SSH contexts (those starting with 'ssh-')
138
147
  ssh_contexts = [
139
148
  context for context in all_contexts if context.startswith('ssh-')
140
149
  ]
141
150
 
142
151
  # Get contexts from SSH node pools file
143
- allowed_contexts = cls.get_ssh_node_pool_contexts()
152
+ all_node_pool_contexts = cls.get_ssh_node_pool_contexts()
153
+
154
+ def filter_by_allowed_node_pools(ctxs):
155
+ if allowed_node_pools is None:
156
+ return ctxs
157
+ return [
158
+ ctx for ctx in ctxs if ctx.lstrip('ssh-') in allowed_node_pools
159
+ ]
144
160
 
145
- if allowed_contexts:
161
+ if all_node_pool_contexts:
146
162
  # Only include allowed contexts that exist
147
163
  existing_contexts = []
148
164
  skipped_contexts = []
149
- for context in allowed_contexts:
165
+ for context in all_node_pool_contexts:
150
166
  if context in ssh_contexts:
151
167
  existing_contexts.append(context)
152
168
  else:
153
169
  skipped_contexts.append(context)
154
170
  if not silent:
155
171
  cls._ssh_log_skipped_contexts_once(tuple(skipped_contexts))
156
- return existing_contexts
172
+ return filter_by_allowed_node_pools(existing_contexts)
157
173
 
158
- # If no allowed_contexts found, return all SSH contexts
159
- return ssh_contexts
174
+ # If no all_node_pool_contexts found, return all SSH contexts
175
+ return filter_by_allowed_node_pools(ssh_contexts)
160
176
 
161
177
  @classmethod
162
178
  def _check_compute_credentials(
@@ -192,9 +208,9 @@ class SSH(kubernetes.Kubernetes):
192
208
  return success, ctx2text
193
209
 
194
210
  @classmethod
195
- def get_infras(cls) -> List[str]:
211
+ def expand_infras(cls) -> List[str]:
196
212
  return [
197
- f'{cls._REPR.lower()}/{c.lstrip("ssh-")}'
213
+ f'{cls.canonical_name()}/{c.lstrip("ssh-")}'
198
214
  for c in cls.existing_allowed_contexts(silent=True)
199
215
  ]
200
216
 
sky/core.py CHANGED
@@ -1010,11 +1010,29 @@ def storage_delete(name: str) -> None:
1010
1010
  # = Catalog Observe =
1011
1011
  # ===================
1012
1012
  @usage_lib.entrypoint
1013
- def enabled_clouds(workspace: Optional[str] = None) -> List[clouds.Cloud]:
1013
+ def enabled_clouds(workspace: Optional[str] = None,
1014
+ expand: bool = False) -> List[str]:
1014
1015
  if workspace is None:
1015
1016
  workspace = skypilot_config.get_active_workspace()
1016
- return global_user_state.get_cached_enabled_clouds(
1017
+ cached_clouds = global_user_state.get_cached_enabled_clouds(
1017
1018
  sky_cloud.CloudCapability.COMPUTE, workspace=workspace)
1019
+ with skypilot_config.local_active_workspace_ctx(workspace):
1020
+ if not expand:
1021
+ return [cloud.canonical_name() for cloud in cached_clouds]
1022
+ enabled_ssh_infras = []
1023
+ enabled_k8s_infras = []
1024
+ enabled_cloud_infras = []
1025
+ for cloud in cached_clouds:
1026
+ cloud_infra = cloud.expand_infras()
1027
+ if isinstance(cloud, clouds.SSH):
1028
+ enabled_ssh_infras.extend(cloud_infra)
1029
+ elif isinstance(cloud, clouds.Kubernetes):
1030
+ enabled_k8s_infras.extend(cloud_infra)
1031
+ else:
1032
+ enabled_cloud_infras.extend(cloud_infra)
1033
+ all_infras = sorted(enabled_ssh_infras) + sorted(
1034
+ enabled_k8s_infras) + sorted(enabled_cloud_infras)
1035
+ return all_infras
1018
1036
 
1019
1037
 
1020
1038
  @usage_lib.entrypoint
@@ -1 +1 @@
1
- <!DOCTYPE html><html><head><meta charSet="utf-8"/><meta name="viewport" content="width=device-width"/><title>404: This page could not be found</title><meta name="next-head-count" content="3"/><link rel="preload" href="/dashboard/_next/static/css/28558d57108b05ae.css" as="style"/><link rel="stylesheet" href="/dashboard/_next/static/css/28558d57108b05ae.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-deda68c926e8d0bc.js" defer=""></script><script src="/dashboard/_next/static/chunks/framework-87d061ee6ed71b28.js" defer=""></script><script src="/dashboard/_next/static/chunks/main-e0e2335212e72357.js" defer=""></script><script src="/dashboard/_next/static/chunks/pages/_app-96a715a6fb01e228.js" defer=""></script><script src="/dashboard/_next/static/chunks/pages/_error-1be831200e60c5c0.js" defer=""></script><script src="/dashboard/_next/static/7GEgRyZKRaSnYZCV1Jwol/_buildManifest.js" defer=""></script><script src="/dashboard/_next/static/7GEgRyZKRaSnYZCV1Jwol/_ssgManifest.js" defer=""></script></head><body><div id="__next"><div style="font-family:system-ui,&quot;Segoe UI&quot;,Roboto,Helvetica,Arial,sans-serif,&quot;Apple Color Emoji&quot;,&quot;Segoe UI Emoji&quot;;height:100vh;text-align:center;display:flex;flex-direction:column;align-items:center;justify-content:center"><div style="line-height:48px"><style>body{color:#000;background:#fff;margin:0}.next-error-h1{border-right:1px solid rgba(0,0,0,.3)}@media (prefers-color-scheme:dark){body{color:#fff;background:#000}.next-error-h1{border-right:1px solid rgba(255,255,255,.3)}}</style><h1 class="next-error-h1" style="display:inline-block;margin:0 20px 0 0;padding-right:23px;font-size:24px;font-weight:500;vertical-align:top">404</h1><div style="display:inline-block"><h2 style="font-size:14px;font-weight:400;line-height:28px">This page could not be found<!-- -->.</h2></div></div></div></div><script id="__NEXT_DATA__" type="application/json">{"props":{"pageProps":{"statusCode":404}},"page":"/_error","query":{},"buildId":"7GEgRyZKRaSnYZCV1Jwol","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"/><title>404: This page could not be found</title><meta name="next-head-count" content="3"/><link rel="preload" href="/dashboard/_next/static/css/6a1c0d711a4bdaf1.css" as="style"/><link rel="stylesheet" href="/dashboard/_next/static/css/6a1c0d711a4bdaf1.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-deda68c926e8d0bc.js" defer=""></script><script src="/dashboard/_next/static/chunks/framework-87d061ee6ed71b28.js" defer=""></script><script src="/dashboard/_next/static/chunks/main-e0e2335212e72357.js" defer=""></script><script src="/dashboard/_next/static/chunks/pages/_app-3985f074c163a856.js" defer=""></script><script src="/dashboard/_next/static/chunks/pages/_error-1be831200e60c5c0.js" defer=""></script><script src="/dashboard/_next/static/D5bjIfl4Ob3SV3LJz3CO0/_buildManifest.js" defer=""></script><script src="/dashboard/_next/static/D5bjIfl4Ob3SV3LJz3CO0/_ssgManifest.js" defer=""></script></head><body><div id="__next"><div style="font-family:system-ui,&quot;Segoe UI&quot;,Roboto,Helvetica,Arial,sans-serif,&quot;Apple Color Emoji&quot;,&quot;Segoe UI Emoji&quot;;height:100vh;text-align:center;display:flex;flex-direction:column;align-items:center;justify-content:center"><div style="line-height:48px"><style>body{color:#000;background:#fff;margin:0}.next-error-h1{border-right:1px solid rgba(0,0,0,.3)}@media (prefers-color-scheme:dark){body{color:#fff;background:#000}.next-error-h1{border-right:1px solid rgba(255,255,255,.3)}}</style><h1 class="next-error-h1" style="display:inline-block;margin:0 20px 0 0;padding-right:23px;font-size:24px;font-weight:500;vertical-align:top">404</h1><div style="display:inline-block"><h2 style="font-size:14px;font-weight:400;line-height:28px">This page could not be found<!-- -->.</h2></div></div></div></div><script id="__NEXT_DATA__" type="application/json">{"props":{"pageProps":{"statusCode":404}},"page":"/_error","query":{},"buildId":"D5bjIfl4Ob3SV3LJz3CO0","assetPrefix":"/dashboard","nextExport":true,"isFallback":false,"gip":true,"scriptLoader":[]}</script></body></html>
@@ -0,0 +1 @@
1
+ self.__BUILD_MANIFEST=function(s,c,e,a,t,r,f,u,n,j){return{__rewrites:{afterFiles:[],beforeFiles:[],fallback:[]},"/":["static/chunks/pages/index-6b0d9e5031b70c58.js"],"/_error":["static/chunks/pages/_error-1be831200e60c5c0.js"],"/clusters":[s,a,c,e,t,n,"static/chunks/pages/clusters-8afda8efa5b74997.js"],"/clusters/[cluster]":[s,a,c,e,t,r,n,"static/chunks/pages/clusters/[cluster]-e23fcddf60578a0d.js"],"/clusters/[cluster]/[job]":[s,c,"static/chunks/pages/clusters/[cluster]/[job]-339b59921ccfe266.js"],"/config":[f,s,u,c,e,"static/chunks/pages/config-72b8c6c2edfd0e39.js"],"/infra":[s,c,"static/chunks/pages/infra-1521baab6992916b.js"],"/jobs":[s,a,c,e,t,r,"static/chunks/pages/jobs-ff7e8e377d02b651.js"],"/jobs/[job]":[s,c,"static/chunks/pages/jobs/[job]-4d913940b4fa6f5a.js"],"/users":[s,c,"static/chunks/pages/users-9900af52acf8648d.js"],"/workspace/new":[f,s,a,u,c,e,t,r,j,"static/chunks/pages/workspace/new-63763ffa3edb4508.js"],"/workspaces":[f,s,a,u,c,e,t,r,"static/chunks/pages/workspaces-72330c4d0fc9a4a2.js"],"/workspaces/[name]":[f,s,a,u,c,e,t,r,j,"static/chunks/pages/workspaces/[name]-3ede7a13caf23375.js"],sortedPages:["/","/_app","/_error","/clusters","/clusters/[cluster]","/clusters/[cluster]/[job]","/config","/infra","/jobs","/jobs/[job]","/users","/workspace/new","/workspaces","/workspaces/[name]"]}}("static/chunks/573-82bd40a37af834f1.js","static/chunks/470-1d784f5c8750744a.js","static/chunks/990-f85643b521f7ca65.js","static/chunks/488-50d843fdb5396d32.js","static/chunks/627-31b701e69f52db0c.js","static/chunks/236-e220ba0c35bf089e.js","static/chunks/9f96d65d-5a3e4af68c26849e.js","static/chunks/320-afea3ddcc5bd1c6c.js","static/chunks/578-24f35aa98d38d638.js","static/chunks/843-e35d71cf1c7f706e.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([[236],{8236:function(e,s,t){t.d(s,{L4:function(){return D},Nk:function(){return R},x2:function(){return S}});var r=t(5893),n=t(7294),a=t(1163),l=t(1664),c=t.n(l),i=t(8799),o=t(803),d=t(7673),h=t(8764),u=t(6989),x=t(8969),m=t(3266),p=t(7324),j=t(9470),f=t(3626),g=t(998);/**
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 w=(0,g.Z)("RefreshCcw",[["path",{d:"M21 12a9 9 0 0 0-9-9 9.75 9.75 0 0 0-6.74 2.74L3 8",key:"14sxne"}],["path",{d:"M3 3v5h5",key:"1xhq8a"}],["path",{d:"M3 12a9 9 0 0 0 9 9 9.75 9.75 0 0 0 6.74-2.74L21 16",key:"1hlbsb"}],["path",{d:"M16 16h5v5",key:"ccwih5"}]]),b=(0,g.Z)("FileSearch",[["path",{d:"M14 2v4a2 2 0 0 0 2 2h4",key:"tnqrlb"}],["path",{d:"M4.268 21a2 2 0 0 0 1.727 1H18a2 2 0 0 0 2-2V7l-5-5H6a2 2 0 0 0-2 2v3",key:"ms7g94"}],["path",{d:"m9 18-1.5-1.5",key:"1j6qii"}],["circle",{cx:"5",cy:"14",r:"3",key:"ufru5t"}]]),y=(0,g.Z)("MonitorPlay",[["path",{d:"M10 7.75a.75.75 0 0 1 1.142-.638l3.664 2.249a.75.75 0 0 1 0 1.278l-3.664 2.25a.75.75 0 0 1-1.142-.64z",key:"1pctta"}],["path",{d:"M12 17v4",key:"1riwvh"}],["path",{d:"M8 21h8",key:"1ev6f3"}],["rect",{x:"2",y:"3",width:"20",height:"14",rx:"2",key:"x3v2xh"}]]);var N=t(9284),v=t(4545),k=t(9307),C=t(3001),_=t(8950);let S={active:["PENDING","RUNNING","RECOVERING","SUBMITTED","STARTING","CANCELLING"],finished:["SUCCEEDED","FAILED","CANCELLED","FAILED_SETUP","FAILED_PRECHECKS","FAILED_NO_RESOURCE","FAILED_CONTROLLER"]},E="__ALL_WORKSPACES__",L=e=>{if(!e)return"-";let s=(0,u.GV)(e);if(n.isValidElement(s)&&s.props&&s.props.children&&(s=n.isValidElement(s.props.children)&&s.props.children.props&&s.props.children.props.children?s.props.children.props.children:s.props.children),"string"!=typeof s)return s;if("just now"===s)return"now";let t=s.match(/^About\s+(\d+)\s+(\w+)\s+ago$/);if(t){let e=t[1],s=t[2],r={second:"sec",seconds:"secs",minute:"min",minutes:"mins",hour:"hr",hours:"hrs",day:"d",days:"d",month:"mo",months:"mos",year:"yr",years:"yrs"};if(r[s])return"~ ".concat(e," ").concat(r[s]," ago")}let r=s.match(/^a[n]?\s+(\w+)\s+ago$/);if(r){let e=r[1],s={second:"sec",minute:"min",hour:"hr",day:"d",month:"mo",year:"yr"};if(s[e])return"1 ".concat(s[e]," ago")}let a=s.match(/^(\d+)\s+(\w+)\s+ago$/);if(a){let e=a[1],s=a[2],t={seconds:"secs",minutes:"mins",hours:"hrs",days:"d",months:"mo",years:"yr"};if(t[s])return"".concat(e," ").concat(t[s]," ago")}return s};function R(){let e=(0,a.useRouter)(),[s,t]=(0,n.useState)(!1),l=n.useRef(null),[d,h]=(0,n.useState)({isOpen:!1,title:"",message:"",onConfirm:null}),m=(0,C.X)(),[g,w]=(0,n.useState)(E),[b,y]=(0,n.useState)([]);return(0,n.useEffect)(()=>{e.isReady&&e.query.workspace&&w(Array.isArray(e.query.workspace)?e.query.workspace[0]:e.query.workspace)},[e.isReady,e.query.workspace]),(0,n.useEffect)(()=>{(async()=>{try{let e=await (0,p.fX)(),s=Object.keys(e),t=(await (0,x.Vp)()).jobs||[],r=[...new Set(t.map(e=>e.workspace||"default").filter(e=>e))],n=new Set(s);r.forEach(e=>n.add(e)),y(Array.from(n).sort())}catch(e){console.error("Error fetching data for workspace filter:",e),y(["default"])}})()},[]),(0,r.jsxs)(j.A,{highlighted:"jobs",children:[(0,r.jsxs)("div",{className:"flex items-center justify-between mb-4 h-5",children:[(0,r.jsxs)("div",{className:"text-base flex items-center",children:[(0,r.jsx)(c(),{href:"/jobs",className:"text-sky-blue hover:underline leading-none",children:"Managed Jobs"}),(0,r.jsxs)(_.Ph,{value:g,onValueChange:w,children:[(0,r.jsx)(_.i4,{className:"h-8 w-48 ml-4 mr-2 text-sm border-none focus:ring-0 focus:outline-none",children:(0,r.jsx)(_.ki,{placeholder:"Filter by workspace...",children:g===E?"All Workspaces":g})}),(0,r.jsxs)(_.Bw,{children:[(0,r.jsx)(_.Ql,{value:E,children:"All Workspaces"}),b.map(e=>(0,r.jsx)(_.Ql,{value:e,children:e},e))]})]})]}),(0,r.jsxs)("div",{className:"flex items-center space-x-2",children:[s&&(0,r.jsxs)("div",{className:"flex items-center mr-2",children:[(0,r.jsx)(i.Z,{size:15,className:"mt-0"}),(0,r.jsx)("span",{className:"ml-2 text-gray-500 text-sm",children:"Loading..."})]}),(0,r.jsxs)(o.z,{variant:"ghost",size:"sm",onClick:()=>{l.current&&l.current()},disabled:s,className:"text-sky-blue hover:text-sky-blue-bright",title:"Refresh",children:[(0,r.jsx)(f.Z,{className:"h-4 w-4 mr-1.5"}),!m&&(0,r.jsx)("span",{children:"Refresh"})]})]})]}),(0,r.jsx)(O,{refreshInterval:u.yc,setLoading:t,refreshDataRef:l,workspaceFilter:g}),(0,r.jsx)(N.cV,{isOpen:d.isOpen,onClose:()=>h({...d,isOpen:!1}),onConfirm:d.onConfirm,title:d.title,message:d.message})]})}function O(e){let{refreshInterval:s,setLoading:t,refreshDataRef:a,workspaceFilter:l}=e,[p,j]=(0,n.useState)([]),[f,g]=(0,n.useState)({key:null,direction:"ascending"}),[b,y]=(0,n.useState)(!1),[C,_]=(0,n.useState)(!0),[R,O]=(0,n.useState)(1),[D,z]=(0,n.useState)(10),[P,J]=(0,n.useState)(null),q=(0,n.useRef)(null),[T,F]=(0,n.useState)([]),[W,U]=(0,n.useState)({}),[V,Z]=(0,n.useState)(!1),[B,G]=(0,n.useState)(!1),[H,X]=(0,n.useState)(!1),[$,K]=(0,n.useState)("active"),[Q,Y]=(0,n.useState)(!0),[ee,es]=(0,n.useState)({isOpen:!1,title:"",message:"",onConfirm:null}),et=async()=>{es({isOpen:!0,title:"Restart Controller",message:"Are you sure you want to restart the controller? This will temporarily interrupt job management.",onConfirm:async()=>{try{X(!0),y(!0),await (0,x.Ce)("restartcontroller"),await er()}catch(e){console.error("Error restarting controller:",e)}finally{X(!1),y(!1)}}})},er=n.useCallback(async()=>{y(!0),t(!0);try{let[e,s]=await Promise.all([(0,x.Vp)(),(0,m.zd)()]),{jobs:t,controllerStopped:r}=e,n=s.find(e=>(0,v.Ym)(e.cluster)),a=n?n.status:"NOT_FOUND",l=!1;"STOPPED"==a&&r&&(l=!0),"LAUNCHING"==a?G(!0):G(!1),j(t),Z(l)}catch(e){console.error("Error fetching data:",e),j([])}finally{y(!1),t(!1),_(!1)}},[t]);n.useEffect(()=>{a&&(a.current=er)},[a,er]),(0,n.useEffect)(()=>{j([]);let e=!0;er();let t=setInterval(()=>{e&&er()},s);return()=>{e=!1,clearInterval(t)}},[s,er]),(0,n.useEffect)(()=>{O(1)},[$,p.length]),(0,n.useEffect)(()=>{F([]),Y(!0)},[$]);let en=e=>{let s="ascending";f.key===e&&"ascending"===f.direction&&(s="descending"),g({key:e,direction:s})},ea=e=>f.key===e?"ascending"===f.direction?" ↑":" ↓":"";n.useMemo(()=>({active:p.filter(e=>S.active.includes(e.status)).length,finished:p.filter(e=>S.finished.includes(e.status)).length}),[p]);let el=e=>T.length>0?T.includes(e):S[$].includes(e),ec=n.useMemo(()=>{let e=l&&l!==E?p.filter(e=>(e.workspace||"default").toLowerCase()===l.toLowerCase()):p;return T.length>0?e.filter(e=>T.includes(e.status)):Q?e.filter(e=>S[$].includes(e.status)):[]},[p,$,T,Q,S,l]),ei=n.useMemo(()=>f.key?[...ec].sort((e,s)=>e[f.key]<s[f.key]?"ascending"===f.direction?-1:1:e[f.key]>s[f.key]?"ascending"===f.direction?1:-1:0):ec,[ec,f]),eo=Math.ceil(ei.length/D),ed=(R-1)*D,eh=ed+D,eu=ei.slice(ed,eh),ex=e=>{if(T.includes(e)){let s=T.filter(s=>s!==e);0===s.length?(Y(!0),F([])):(F(s),Y(!1))}else F([...T,e]),Y(!1)};return(0,n.useEffect)(()=>{U(p.reduce((e,s)=>(e[s.status]=(e[s.status]||0)+1,e),{}))},[p]),(0,r.jsxs)("div",{className:"relative",children:[(0,r.jsx)("div",{className:"flex flex-col space-y-1 mb-1",children:(0,r.jsxs)("div",{className:"flex flex-wrap items-center text-sm mb-1",children:[(0,r.jsx)("span",{className:"mr-2 text-sm font-medium",children:"Statuses:"}),(0,r.jsxs)("div",{className:"flex flex-wrap gap-2 items-center",children:[!b&&0===p.length&&!C&&(0,r.jsx)("span",{className:"text-gray-500 mr-2",children:"No jobs found"}),Object.entries(W).map(e=>{let[s,t]=e;return(0,r.jsxs)("button",{onClick:()=>ex(s),className:"px-3 py-1 rounded-full flex items-center space-x-2 ".concat(el(s)||T.includes(s)?(0,k.Cl)(s):"bg-gray-50 text-gray-600 hover:bg-gray-100"),children:[(0,r.jsx)("span",{children:s}),(0,r.jsx)("span",{className:"text-xs ".concat(el(s)||T.includes(s)?"bg-white/50":"bg-gray-200"," px-1.5 py-0.5 rounded"),children:t})]},s)}),p.length>0&&(0,r.jsxs)("div",{className:"flex items-center ml-2 gap-2",children:[(0,r.jsx)("span",{className:"text-gray-500",children:"("}),(0,r.jsx)("button",{onClick:()=>{K("active"),F([]),Y(!0)},className:"text-sm font-medium ".concat("active"===$&&Q?"text-green-700 underline":"text-gray-600 hover:text-green-700 hover:underline"),children:"show all active jobs"}),(0,r.jsx)("span",{className:"text-gray-500 mx-1",children:"|"}),(0,r.jsx)("button",{onClick:()=>{K("finished"),F([]),Y(!0)},className:"text-sm font-medium ".concat("finished"===$&&Q?"text-blue-700 underline":"text-gray-600 hover:text-blue-700 hover:underline"),children:"show all finished jobs"}),(0,r.jsx)("span",{className:"text-gray-500",children:")"})]})]})]})}),(0,r.jsx)(d.Zb,{children:(0,r.jsxs)(h.iA,{children:[(0,r.jsx)(h.xD,{children:(0,r.jsxs)(h.SC,{children:[(0,r.jsxs)(h.ss,{className:"sortable whitespace-nowrap",onClick:()=>en("id"),children:["ID",ea("id")]}),(0,r.jsxs)(h.ss,{className:"sortable whitespace-nowrap",onClick:()=>en("name"),children:["Name",ea("name")]}),(0,r.jsxs)(h.ss,{className:"sortable whitespace-nowrap",onClick:()=>en("user"),children:["User",ea("user")]}),(0,r.jsxs)(h.ss,{className:"sortable whitespace-nowrap",onClick:()=>en("workspace"),children:["Workspace",ea("workspace")]}),(0,r.jsxs)(h.ss,{className:"sortable whitespace-nowrap",onClick:()=>en("submitted_at"),children:["Submitted",ea("submitted_at")]}),(0,r.jsxs)(h.ss,{className:"sortable whitespace-nowrap",onClick:()=>en("job_duration"),children:["Duration",ea("job_duration")]}),(0,r.jsxs)(h.ss,{className:"sortable whitespace-nowrap",onClick:()=>en("status"),children:["Status",ea("status")]}),(0,r.jsxs)(h.ss,{className:"sortable whitespace-nowrap",onClick:()=>en("resources_str"),children:["Requested",ea("resources_str")]}),(0,r.jsxs)(h.ss,{className:"sortable whitespace-nowrap",onClick:()=>en("infra"),children:["Infra",ea("infra")]}),(0,r.jsxs)(h.ss,{className:"sortable whitespace-nowrap",onClick:()=>en("cluster"),children:["Resources",ea("cluster")]}),(0,r.jsxs)(h.ss,{className:"sortable whitespace-nowrap",onClick:()=>en("recoveries"),children:["Recoveries",ea("recoveries")]}),(0,r.jsx)(h.ss,{children:"Details"}),(0,r.jsx)(h.ss,{children:"Logs"})]})}),(0,r.jsx)(h.RM,{children:b&&C?(0,r.jsx)(h.SC,{children:(0,r.jsx)(h.pj,{colSpan:12,className:"text-center py-6 text-gray-500",children:(0,r.jsxs)("div",{className:"flex justify-center items-center",children:[(0,r.jsx)(i.Z,{size:20,className:"mr-2"}),(0,r.jsx)("span",{children:"Loading..."})]})})}):eu.length>0?(0,r.jsx)(r.Fragment,{children:eu.map(e=>(0,r.jsxs)(n.Fragment,{children:[(0,r.jsxs)(h.SC,{children:[(0,r.jsx)(h.pj,{children:(0,r.jsx)(c(),{href:"/jobs/".concat(e.id),className:"text-blue-600",children:e.id})}),(0,r.jsx)(h.pj,{children:(0,r.jsx)(c(),{href:"/jobs/".concat(e.id),className:"text-blue-600",children:e.name})}),(0,r.jsx)(h.pj,{children:e.user}),(0,r.jsx)(h.pj,{children:(0,r.jsx)(c(),{href:"/workspaces",className:"text-blue-600 hover:underline",children:e.workspace||"default"})}),(0,r.jsx)(h.pj,{children:L(e.submitted_at)}),(0,r.jsx)(h.pj,{children:(0,u.LU)(e.job_duration)}),(0,r.jsx)(h.pj,{children:(0,r.jsx)(k.OE,{status:e.status})}),(0,r.jsx)(h.pj,{children:e.requested_resources}),(0,r.jsx)(h.pj,{children:e.infra&&"-"!==e.infra?(0,r.jsx)(u.Md,{content:e.full_infra||e.infra,className:"text-sm text-muted-foreground",children:(0,r.jsxs)("span",{children:[(0,r.jsx)(c(),{href:"/infra",className:"text-blue-600 hover:underline",children:e.cloud||e.infra.split("(")[0].trim()}),e.infra.includes("(")&&(0,r.jsx)("span",{children:" "+e.infra.substring(e.infra.indexOf("("))})]})}):(0,r.jsx)("span",{children:e.infra||"-"})}),(0,r.jsx)(h.pj,{children:(0,r.jsx)(u.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:e.recoveries}),(0,r.jsx)(h.pj,{children:e.details?(0,r.jsx)(A,{text:e.details,rowId:e.id,expandedRowId:P,setExpandedRowId:J}):"-"}),(0,r.jsx)(h.pj,{children:(0,r.jsx)(M,{jobParent:"/jobs",jobId:e.id,managed:!0})})]}),P===e.id&&(0,r.jsx)(I,{text:e.details,colSpan:12,innerRef:q})]},e.id))}):(0,r.jsx)(h.SC,{children:(0,r.jsx)(h.pj,{colSpan:12,className:"text-center py-6",children:(0,r.jsxs)("div",{className:"flex flex-col items-center space-y-4",children:[B&&(0,r.jsxs)("div",{className:"flex flex-col items-center space-y-2",children:[(0,r.jsx)("p",{className:"text-gray-700",children:"The managed job controller is launching. Please wait for it to be ready."}),(0,r.jsxs)("div",{className:"flex items-center",children:[(0,r.jsx)(i.Z,{size:12,className:"mr-2"}),(0,r.jsx)("span",{className:"text-gray-500",children:"Launching..."})]})]}),!V&&!B&&(0,r.jsx)("p",{className:"text-gray-500",children:"No active jobs"}),V&&(0,r.jsxs)("div",{className:"flex flex-col items-center space-y-2",children:[(0,r.jsx)("p",{className:"text-gray-700",children:"The managed job controller has been stopped. Please restart it to check the latest job status."}),(0,r.jsx)(o.z,{variant:"outline",size:"sm",onClick:et,className:"text-sky-blue hover:text-sky-blue-bright",disabled:b||H,children:H?(0,r.jsxs)(r.Fragment,{children:[(0,r.jsx)(i.Z,{size:12,className:"mr-2"}),"Restarting..."]}):(0,r.jsxs)(r.Fragment,{children:[(0,r.jsx)(w,{className:"h-4 w-4 mr-2"}),"Restart Controller"]})})]})]})})})})]})}),ei.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:D,onChange:e=>{z(parseInt(e.target.value,10)),O(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.jsxs)("div",{children:[ed+1," – ",Math.min(eh,ei.length)," of"," ",ei.length]}),(0,r.jsxs)("div",{className:"flex items-center space-x-2",children:[(0,r.jsx)(o.z,{variant:"ghost",size:"icon",onClick:()=>{O(e=>Math.max(e-1,1))},disabled:1===R,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)(o.z,{variant:"ghost",size:"icon",onClick:()=>{O(e=>Math.min(e+1,eo))},disabled:R===eo||0===eo,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"})})})]})]})}),(0,r.jsx)(N.cV,{isOpen:ee.isOpen,onClose:()=>es({...ee,isOpen:!1}),onConfirm:ee.onConfirm,title:ee.title,message:ee.message})]})}function M(e){let{withLabel:s=!1,jobParent:t,jobId:n,managed:l}=e,c=(0,a.useRouter)(),i=(e,s)=>{e.preventDefault(),e.stopPropagation(),c.push({pathname:"".concat(t,"/").concat(n),query:{tab:s}})};return(0,r.jsxs)("div",{className:"flex items-center space-x-4",children:[(0,r.jsx)(u.WH,{content:"View Job Logs",className:"capitalize text-sm text-muted-foreground",children:(0,r.jsxs)("button",{onClick:e=>i(e,"logs"),className:"text-sky-blue hover:text-sky-blue-bright font-medium inline-flex items-center h-8",children:[(0,r.jsx)(b,{className:"w-4 h-4"}),s&&(0,r.jsx)("span",{className:"ml-1.5",children:"Logs"})]})},"logs"),l&&(0,r.jsx)(u.WH,{content:"View Controller Logs",className:"capitalize text-sm text-muted-foreground",children:(0,r.jsxs)("button",{onClick:e=>i(e,"controllerlogs"),className:"text-sky-blue hover:text-sky-blue-bright font-medium inline-flex items-center h-8",children:[(0,r.jsx)(y,{className:"w-4 h-4"}),s&&(0,r.jsx)("span",{className:"ml-2",children:"Controller Logs"})]})},"controllerlogs")]})}function D(e){let{clusterName:s,clusterJobData:t,loading:a}=e,[l,x]=(0,n.useState)(null),[m,p]=(0,n.useState)({key:null,direction:"ascending"}),[j,f]=(0,n.useState)(1),[g,w]=(0,n.useState)(10),b=(0,n.useRef)(null),[y,N]=(0,n.useState)(null);(0,n.useEffect)(()=>{let e=e=>{l&&b.current&&!b.current.contains(e.target)&&x(null)};return document.addEventListener("mousedown",e),()=>{document.removeEventListener("mousedown",e)}},[l]);let v=n.useMemo(()=>t||[],[t]);(0,n.useEffect)(()=>{JSON.stringify(t)!==JSON.stringify(y)&&N(t)},[t,y]);let C=n.useMemo(()=>m.key?[...v].sort((e,s)=>e[m.key]<s[m.key]?"ascending"===m.direction?-1:1:e[m.key]>s[m.key]?"ascending"===m.direction?1:-1:0):v,[v,m]),_=e=>{let s="ascending";m.key===e&&"ascending"===m.direction&&(s="descending"),p({key:e,direction:s})},S=e=>m.key===e?"ascending"===m.direction?" ↑":" ↓":"",E=Math.ceil(C.length/g),R=(j-1)*g,O=R+g,D=C.slice(R,O);return(0,r.jsxs)("div",{className:"relative",children:[(0,r.jsxs)(d.Zb,{children:[(0,r.jsxs)("div",{className:"flex items-center justify-between p-4",children:[(0,r.jsx)("h3",{className:"text-lg font-semibold",children:"Cluster Jobs"}),a&&(0,r.jsxs)("div",{className:"flex items-center mr-2",children:[(0,r.jsx)(i.Z,{size:15,className:"mt-0"}),(0,r.jsx)("span",{className:"ml-2 text-gray-500 text-sm",children:"Loading..."})]})]}),(0,r.jsxs)(h.iA,{children:[(0,r.jsx)(h.xD,{children:(0,r.jsxs)(h.SC,{children:[(0,r.jsxs)(h.ss,{className:"sortable whitespace-nowrap",onClick:()=>_("id"),children:["ID",S("id")]}),(0,r.jsxs)(h.ss,{className:"sortable whitespace-nowrap",onClick:()=>_("job"),children:["Name",S("job")]}),(0,r.jsxs)(h.ss,{className:"sortable whitespace-nowrap",onClick:()=>_("user"),children:["User",S("user")]}),(0,r.jsxs)(h.ss,{className:"sortable whitespace-nowrap",onClick:()=>_("workspace"),children:["Workspace",S("workspace")]}),(0,r.jsxs)(h.ss,{className:"sortable whitespace-nowrap",onClick:()=>_("submitted_at"),children:["Submitted",S("submitted_at")]}),(0,r.jsxs)(h.ss,{className:"sortable whitespace-nowrap",onClick:()=>_("job_duration"),children:["Duration",S("job_duration")]}),(0,r.jsxs)(h.ss,{className:"sortable whitespace-nowrap",onClick:()=>_("status"),children:["Status",S("status")]}),(0,r.jsxs)(h.ss,{className:"sortable whitespace-nowrap",onClick:()=>_("resources"),children:["Resources",S("resources")]}),(0,r.jsx)(h.ss,{className:"whitespace-nowrap",children:"Logs"})]})}),(0,r.jsx)(h.RM,{children:D.length>0?D.map(e=>(0,r.jsxs)(n.Fragment,{children:[(0,r.jsxs)(h.SC,{className:l===e.id?"selected-row":"",children:[(0,r.jsx)(h.pj,{children:(0,r.jsx)(c(),{href:"/clusters/".concat(s,"/").concat(e.id),className:"text-blue-600",children:e.id})}),(0,r.jsx)(h.pj,{children:(0,r.jsx)(c(),{href:"/clusters/".concat(s,"/").concat(e.id),className:"text-blue-600",children:(0,r.jsx)(A,{text:e.job||"Unnamed job",rowId:e.id,expandedRowId:l,setExpandedRowId:x})})}),(0,r.jsx)(h.pj,{children:e.user}),(0,r.jsx)(h.pj,{children:(0,r.jsx)(c(),{href:"/workspaces",className:"text-blue-600 hover:underline",children:e.workspace||"default"})}),(0,r.jsx)(h.pj,{children:L(e.submitted_at)}),(0,r.jsx)(h.pj,{children:(0,u.LU)(e.job_duration)}),(0,r.jsx)(h.pj,{children:(0,r.jsx)(k.OE,{status:e.status})}),(0,r.jsx)(h.pj,{children:e.resources}),(0,r.jsx)(h.pj,{className:"flex content-center items-center",children:(0,r.jsx)(M,{jobParent:"/clusters/".concat(s),jobId:e.id,managed:!1})})]}),l===e.id&&(0,r.jsx)(I,{text:e.job||"Unnamed job",colSpan:9,innerRef:b})]},e.id)):(0,r.jsx)(h.SC,{children:(0,r.jsx)(h.pj,{colSpan:9,className:"text-center py-6 text-gray-500",children:"No jobs found"})})})]})]}),C.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:g,onChange:e=>{w(parseInt(e.target.value,10)),f(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:5,children:"5"}),(0,r.jsx)("option",{value:10,children:"10"}),(0,r.jsx)("option",{value:20,children:"20"}),(0,r.jsx)("option",{value:50,children:"50"})]}),(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.jsxs)("div",{children:[R+1," – ",Math.min(O,C.length)," of"," ",C.length]}),(0,r.jsxs)("div",{className:"flex items-center space-x-2",children:[(0,r.jsx)(o.z,{variant:"ghost",size:"icon",onClick:()=>{f(e=>Math.max(e-1,1))},disabled:1===j,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)(o.z,{variant:"ghost",size:"icon",onClick:()=>{f(e=>Math.min(e+1,E))},disabled:j===E||0===E,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"})})})]})]})})]})}function I(e){let{text:s,colSpan:t,innerRef:n}=e;return(0,r.jsx)(h.SC,{className:"expanded-details",children:(0,r.jsx)(h.pj,{colSpan:t,children:(0,r.jsx)("div",{className:"p-4 bg-gray-50 rounded-md border border-gray-200",ref:n,children:(0,r.jsx)("div",{className:"flex justify-between items-start",children:(0,r.jsxs)("div",{className:"flex-1",children:[(0,r.jsx)("p",{className:"text-sm font-medium text-gray-900",children:"Full Details"}),(0,r.jsx)("p",{className:"mt-1 text-sm text-gray-700",style:{whiteSpace:"pre-wrap"},children:s})]})})})})})}function A(e){let{text:s,rowId:t,expandedRowId:a,setExpandedRowId:l}=e,c=s.length>50,i=a===t,o=c?"".concat(s.substring(0,50)):s,d=(0,n.useRef)(null);return(0,r.jsxs)("div",{className:"truncated-details relative max-w-full flex items-center",children:[(0,r.jsx)("span",{className:"truncate",children:o}),c&&(0,r.jsx)("button",{ref:d,type:"button",onClick:e=>{e.preventDefault(),e.stopPropagation(),l(i?null:t)},className:"text-blue-600 hover:text-blue-800 font-medium ml-1 flex-shrink-0","data-button-type":"show-more-less",children:i?"... show less":"... show more"})]})}},8969:function(e,s,t){t.d(s,{Ce:function(){return o},NJ:function(){return i},Pr:function(){return c},Vp:function(){return l}});var r=t(7294),n=t(5821),a=t(3225);async function l(){let{allUsers:e=!0}=arguments.length>0&&void 0!==arguments[0]?arguments[0]:{};try{let s=(await fetch("".concat(a.f4,"/jobs/queue"),{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({all_users:e})})).headers.get("X-Skypilot-Request-ID"),t=await fetch("".concat(a.f4,"/api/get?request_id=").concat(s));if(500===t.status){try{let e=await t.json();if(e.detail&&e.detail.error)try{let s=JSON.parse(e.detail.error);if(s.type&&s.type===a.iW)return{jobs:[],controllerStopped:!0}}catch(e){console.error("Error parsing JSON:",e)}}catch(e){console.error("Error parsing JSON:",e)}return{jobs:[],controllerStopped:!1}}let r=await t.json();return{jobs:(r.return_value?JSON.parse(r.return_value):[]).map(e=>{let s=[];e.submitted_at&&s.push({time:new Date(1e3*e.submitted_at),event:"Job submitted."}),e.start_at&&s.push({time:new Date(1e3*e.start_at),event:"Job started."}),e.end_at&&("CANCELLING"==e.status||"CANCELLED"==e.status?s.push({time:new Date(1e3*e.end_at),event:"Job cancelled."}):s.push({time:new Date(1e3*e.end_at),event:"Job completed."})),e.last_recovered_at&&e.last_recovered_at!=e.start_at&&s.push({time:new Date(1e3*e.last_recovered_at),event:"Job recovered."});let t=(e.end_at?e.end_at:Date.now()/1e3)-e.submitted_at,r=e.cloud,n=e.cluster_resources;if(!r){if(e.cluster_resources&&"-"!==e.cluster_resources)try{r=e.cluster_resources.split("(")[0].split("x").pop().trim(),n=e.cluster_resources.replace("".concat(r,"("),"(").replace("x ","x")}catch(e){r="Unknown"}else r="Unknown"}let a="",l=a=e.zone?e.zone:e.region;a&&a.length>15&&(a=a.substring(0,15)+"...");let c=r+" ("+a+")";"-"===a&&(c=r);let i=r+" ("+l+")";return"-"===l&&(i=r),{id:e.job_id,task:e.task_name,name:e.job_name,job_duration:e.job_duration,total_duration:t,workspace:e.workspace,status:e.status,requested_resources:e.resources,resources_str:n,resources_str_full:e.cluster_resources_full||n,cloud:r,infra:c,full_infra:i,recoveries:e.recovery_count,details:e.failure_reason,user:e.user_name,user_hash:e.user_hash,submitted_at:e.submitted_at?new Date(1e3*e.submitted_at):null,events:s}}),controllerStopped:!1}}catch(e){return console.error("Error fetching managed job data:",e),{jobs:[],controllerStopped:!1}}}function c(){let e=arguments.length>0&&void 0!==arguments[0]?arguments[0]:0,[s,t]=(0,r.useState)(null),[n,a]=(0,r.useState)(!0);return(0,r.useEffect)(()=>{(async function(){try{a(!0);let e=await l({allUsers:!0});t(e)}catch(e){console.error("Error fetching managed job data:",e)}finally{a(!1)}})()},[e]),{jobData:s,loading:n}}async function i(e){let{jobId:s,controller:t=!1,signal:r,onNewLog:l}=e,c=new Promise(e=>{setTimeout(()=>{e({timeout:!0})},1e4)}),i=(async()=>{try{let e=(await fetch("".concat(a.f4,"/jobs/logs"),{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({controller:t,follow:!1,job_id:s}),...r?{signal:r}:{}})).body.getReader();try{for(;;){let{done:s,value:t}=await e.read();if(s)break;let r=new TextDecoder().decode(t);l(r)}}finally{e.cancel()}return{timeout:!1}}catch(e){if("AbortError"===e.name)return{timeout:!1};throw e}})();if((await Promise.race([i,c])).timeout){(0,n.C)("Log request for job ".concat(s," timed out after ").concat(1e4,"ms"),"error");return}}async function o(e,s,t){let r="",l="",c="",i={};if("restartcontroller"===e)r="Restarting",l="restarted",c="jobs/queue",i={all_users:!0,refresh:!0},s="controller";else throw Error("Invalid action: ".concat(e));(0,n.C)("".concat(r," job ").concat(s,"..."),"info");try{try{let e=(await fetch("".concat(a.f4,"/").concat(c),{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify(i)})).headers.get("X-Skypilot-Request-ID"),o=await fetch("".concat(a.f4,"/api/get?request_id=").concat(e));if(200===o.status)(0,n.C)("Job ".concat(s," ").concat(l," successfully."),"success");else if(500===o.status)try{let e=await o.json();if(e.detail&&e.detail.error)try{let l=JSON.parse(e.detail.error);l.type&&l.type===a.Bo?(0,n.C)("".concat(r," job ").concat(s," is not supported!"),"error",1e4):l.type&&l.type===a.mF?(0,n.C)("Cluster ".concat(t," does not exist."),"error"):l.type&&l.type===a.iW?(0,n.C)("Cluster ".concat(t," is not up."),"error"):(0,n.C)("".concat(r," job ").concat(s," failed: ").concat(l.type),"error")}catch(t){(0,n.C)("".concat(r," job ").concat(s," failed: ").concat(e.detail.error),"error")}else(0,n.C)("".concat(r," job ").concat(s," failed with no details."),"error")}catch(e){(0,n.C)("".concat(r," job ").concat(s," failed with parse error."),"error")}else(0,n.C)("".concat(r," job ").concat(s," failed with status ").concat(o.status,"."),"error")}catch(e){console.error("Fetch error:",e),(0,n.C)("Network error ".concat(r," job ").concat(s,": ").concat(e.message),"error")}}catch(e){console.error("Error in handleStop:",e),(0,n.C)("Critical error ".concat(r," job ").concat(s,": ").concat(e.message),"error")}}}}]);