skypilot-nightly 1.0.0.dev20250621__py3-none-any.whl → 1.0.0.dev20250624__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.
- sky/__init__.py +2 -2
- sky/admin_policy.py +16 -5
- sky/backends/__init__.py +2 -1
- sky/backends/backend_utils.py +12 -0
- sky/backends/cloud_vm_ray_backend.py +36 -13
- sky/client/cli/command.py +42 -21
- sky/client/sdk.py +12 -6
- sky/clouds/kubernetes.py +1 -0
- sky/core.py +88 -15
- sky/dashboard/out/404.html +1 -1
- sky/dashboard/out/_next/static/chunks/37-4650f214e2119168.js +6 -0
- sky/dashboard/out/_next/static/chunks/42.2273cc2415291ceb.js +6 -0
- sky/dashboard/out/_next/static/chunks/470-1494c899266cf5c9.js +1 -0
- sky/dashboard/out/_next/static/chunks/{513.211357a2914a34b2.js → 513.309df9e18a9ff005.js} +1 -1
- sky/dashboard/out/_next/static/chunks/856-bfddc18e16f3873c.js +1 -0
- sky/dashboard/out/_next/static/chunks/938-ce7991c156584b06.js +1 -0
- sky/dashboard/out/_next/static/chunks/969-d3a0b53f728d280a.js +1 -0
- sky/dashboard/out/_next/static/chunks/989-db34c16ad7ea6155.js +1 -0
- sky/dashboard/out/_next/static/chunks/pages/{_app-c416e87d5c2715cf.js → _app-ce31493da9747ef4.js} +1 -1
- sky/dashboard/out/_next/static/chunks/pages/clusters/[cluster]/[job]-4e065c812a52460b.js +6 -0
- sky/dashboard/out/_next/static/chunks/pages/clusters/[cluster]-520ec1ab65e2f2a4.js +6 -0
- sky/dashboard/out/_next/static/chunks/pages/clusters-7e9736af1c6345a6.js +1 -0
- sky/dashboard/out/_next/static/chunks/pages/config-e4f473661889e7cd.js +1 -0
- sky/dashboard/out/_next/static/chunks/pages/infra/[context]-00fd23b9577492ca.js +1 -0
- sky/dashboard/out/_next/static/chunks/pages/infra-8a4bf7370d4d9bb7.js +1 -0
- sky/dashboard/out/_next/static/chunks/pages/jobs/{[job]-cf490d1fa38f3740.js → [job]-171c27f4ca94861c.js} +1 -1
- sky/dashboard/out/_next/static/chunks/pages/jobs-55e5bcb16d563231.js +1 -0
- sky/dashboard/out/_next/static/chunks/pages/users-c9f4d785cdaa52d8.js +1 -0
- sky/dashboard/out/_next/static/chunks/pages/workspaces/{[name]-c4ff1ec05e2f3daf.js → [name]-ecc5a7003776cfa7.js} +1 -1
- sky/dashboard/out/_next/static/chunks/pages/workspaces-f00cba35691483b1.js +1 -0
- sky/dashboard/out/_next/static/chunks/webpack-c85998e6a5722f21.js +1 -0
- sky/dashboard/out/_next/static/css/6ab927686b492a4a.css +3 -0
- sky/dashboard/out/_next/static/zsALxITkbP8J8NVwSDwMo/_buildManifest.js +1 -0
- sky/dashboard/out/clusters/[cluster]/[job].html +1 -1
- sky/dashboard/out/clusters/[cluster].html +1 -1
- sky/dashboard/out/clusters.html +1 -1
- sky/dashboard/out/config.html +1 -1
- sky/dashboard/out/index.html +1 -1
- sky/dashboard/out/infra/[context].html +1 -1
- sky/dashboard/out/infra.html +1 -1
- sky/dashboard/out/jobs/[job].html +1 -1
- sky/dashboard/out/jobs.html +1 -1
- sky/dashboard/out/users.html +1 -1
- sky/dashboard/out/workspace/new.html +1 -1
- sky/dashboard/out/workspaces/[name].html +1 -1
- sky/dashboard/out/workspaces.html +1 -1
- sky/exceptions.py +11 -0
- sky/global_user_state.py +134 -20
- sky/jobs/client/sdk.py +0 -1
- sky/jobs/controller.py +5 -1
- sky/jobs/scheduler.py +4 -3
- sky/jobs/server/core.py +117 -51
- sky/jobs/state.py +15 -0
- sky/jobs/utils.py +114 -8
- sky/resources.py +1 -1
- sky/server/requests/payloads.py +6 -3
- sky/server/requests/requests.py +24 -1
- sky/server/server.py +4 -3
- sky/skylet/constants.py +5 -11
- sky/task.py +1 -26
- sky/templates/jobs-controller.yaml.j2 +12 -1
- sky/templates/kubernetes-ray.yml.j2 +1 -1
- sky/utils/admin_policy_utils.py +5 -1
- sky/utils/cli_utils/status_utils.py +25 -17
- sky/utils/command_runner.py +118 -12
- sky/utils/command_runner.pyi +57 -0
- sky/utils/common_utils.py +9 -1
- sky/utils/controller_utils.py +1 -2
- sky/utils/schemas.py +34 -35
- {skypilot_nightly-1.0.0.dev20250621.dist-info → skypilot_nightly-1.0.0.dev20250624.dist-info}/METADATA +1 -1
- {skypilot_nightly-1.0.0.dev20250621.dist-info → skypilot_nightly-1.0.0.dev20250624.dist-info}/RECORD +78 -77
- sky/dashboard/out/_next/static/PZWXta2b3IpViuIKI97hg/_buildManifest.js +0 -1
- sky/dashboard/out/_next/static/chunks/37-3a4d77ad62932eaf.js +0 -6
- sky/dashboard/out/_next/static/chunks/42.d39e24467181b06b.js +0 -6
- sky/dashboard/out/_next/static/chunks/470-4d1a5dbe58a8a2b9.js +0 -1
- sky/dashboard/out/_next/static/chunks/856-c2c39c0912285e54.js +0 -1
- sky/dashboard/out/_next/static/chunks/938-1493ac755eadeb35.js +0 -1
- sky/dashboard/out/_next/static/chunks/969-20d54a9d998dc102.js +0 -1
- sky/dashboard/out/_next/static/chunks/pages/clusters/[cluster]/[job]-89216c616dbaa9c5.js +0 -6
- sky/dashboard/out/_next/static/chunks/pages/clusters/[cluster]-36bc0962129f72df.js +0 -6
- sky/dashboard/out/_next/static/chunks/pages/clusters-82a651dbad53ec6e.js +0 -1
- sky/dashboard/out/_next/static/chunks/pages/config-497a35a7ed49734a.js +0 -1
- sky/dashboard/out/_next/static/chunks/pages/infra/[context]-d2910be98e9227cb.js +0 -1
- sky/dashboard/out/_next/static/chunks/pages/infra-780860bcc1103945.js +0 -1
- sky/dashboard/out/_next/static/chunks/pages/jobs-336ab80e270ce2ce.js +0 -1
- sky/dashboard/out/_next/static/chunks/pages/users-928edf039219e47b.js +0 -1
- sky/dashboard/out/_next/static/chunks/pages/workspaces-82e6601baa5dd280.js +0 -1
- sky/dashboard/out/_next/static/chunks/webpack-0263b00d6a10e64a.js +0 -1
- sky/dashboard/out/_next/static/css/6c12ecc3bd2239b6.css +0 -3
- /sky/dashboard/out/_next/static/chunks/{843-b3040e493f6e7947.js → 843-bde186946d353355.js} +0 -0
- /sky/dashboard/out/_next/static/chunks/{973-db3c97c2bfbceb65.js → 973-56412c7976b4655b.js} +0 -0
- /sky/dashboard/out/_next/static/{PZWXta2b3IpViuIKI97hg → zsALxITkbP8J8NVwSDwMo}/_ssgManifest.js +0 -0
- {skypilot_nightly-1.0.0.dev20250621.dist-info → skypilot_nightly-1.0.0.dev20250624.dist-info}/WHEEL +0 -0
- {skypilot_nightly-1.0.0.dev20250621.dist-info → skypilot_nightly-1.0.0.dev20250624.dist-info}/entry_points.txt +0 -0
- {skypilot_nightly-1.0.0.dev20250621.dist-info → skypilot_nightly-1.0.0.dev20250624.dist-info}/licenses/LICENSE +0 -0
- {skypilot_nightly-1.0.0.dev20250621.dist-info → skypilot_nightly-1.0.0.dev20250624.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 = '
|
8
|
+
_SKYPILOT_COMMIT_SHA = '0b29731d6bfe91381de57805e1fc1dd723e4b3a0'
|
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.
|
38
|
+
__version__ = '1.0.0.dev20250624'
|
39
39
|
__root_dir__ = os.path.dirname(os.path.abspath(__file__))
|
40
40
|
|
41
41
|
|
sky/admin_policy.py
CHANGED
@@ -4,11 +4,13 @@ import dataclasses
|
|
4
4
|
import typing
|
5
5
|
from typing import Any, Dict, Optional
|
6
6
|
|
7
|
+
import colorama
|
7
8
|
import pydantic
|
8
9
|
|
9
10
|
import sky
|
10
11
|
from sky import exceptions
|
11
12
|
from sky.adaptors import common as adaptors_common
|
13
|
+
from sky.utils import common_utils
|
12
14
|
from sky.utils import config_utils
|
13
15
|
from sky.utils import ux_utils
|
14
16
|
|
@@ -218,18 +220,27 @@ class RestfulAdminPolicy(PolicyTemplate):
|
|
218
220
|
headers={'Content-Type': 'application/json'},
|
219
221
|
# TODO(aylei): make this configurable
|
220
222
|
timeout=30)
|
223
|
+
if response.status_code == 400:
|
224
|
+
raise exceptions.UserRequestRejectedByPolicy(
|
225
|
+
f'{colorama.Fore.RED}User request is rejected by admin '
|
226
|
+
f'policy {self.policy_url}{colorama.Fore.RESET}: '
|
227
|
+
f'{response.text}')
|
221
228
|
response.raise_for_status()
|
222
229
|
except requests.exceptions.RequestException as e:
|
223
230
|
with ux_utils.print_exception_no_traceback():
|
224
|
-
raise exceptions.
|
225
|
-
f'Failed to
|
226
|
-
f'{self.policy_url}: {e}') from
|
231
|
+
raise exceptions.RestfulPolicyError(
|
232
|
+
f'Failed to call admin policy URL '
|
233
|
+
f'{self.policy_url}: {e}') from None
|
227
234
|
|
228
235
|
try:
|
229
236
|
mutated_user_request = MutatedUserRequest.decode(response.json())
|
230
237
|
except Exception as e: # pylint: disable=broad-except
|
231
238
|
with ux_utils.print_exception_no_traceback():
|
232
|
-
raise exceptions.
|
239
|
+
raise exceptions.RestfulPolicyError(
|
233
240
|
f'Failed to decode response from admin policy URL '
|
234
|
-
f'{self.policy_url}: {e}'
|
241
|
+
f'{self.policy_url}: {common_utils.format_exception(e, use_bracket=True)}'
|
242
|
+
) from None
|
235
243
|
return mutated_user_request
|
244
|
+
|
245
|
+
def __repr__(self):
|
246
|
+
return f'RestfulAdminPolicy(policy_url={self.policy_url})'
|
sky/backends/__init__.py
CHANGED
@@ -3,11 +3,12 @@ from sky.backends.backend import Backend
|
|
3
3
|
from sky.backends.backend import ResourceHandle
|
4
4
|
from sky.backends.cloud_vm_ray_backend import CloudVmRayBackend
|
5
5
|
from sky.backends.cloud_vm_ray_backend import CloudVmRayResourceHandle
|
6
|
+
from sky.backends.cloud_vm_ray_backend import LocalResourcesHandle
|
6
7
|
from sky.backends.local_docker_backend import LocalDockerBackend
|
7
8
|
from sky.backends.local_docker_backend import LocalDockerResourceHandle
|
8
9
|
|
9
10
|
__all__ = [
|
10
11
|
'Backend', 'ResourceHandle', 'CloudVmRayBackend',
|
11
|
-
'CloudVmRayResourceHandle', 'LocalDockerBackend',
|
12
|
+
'CloudVmRayResourceHandle', 'LocalResourcesHandle', 'LocalDockerBackend',
|
12
13
|
'LocalDockerResourceHandle'
|
13
14
|
]
|
sky/backends/backend_utils.py
CHANGED
@@ -33,6 +33,7 @@ from sky import provision as provision_lib
|
|
33
33
|
from sky import sky_logging
|
34
34
|
from sky import skypilot_config
|
35
35
|
from sky.adaptors import common as adaptors_common
|
36
|
+
from sky.jobs import utils as managed_job_utils
|
36
37
|
from sky.provision import instance_setup
|
37
38
|
from sky.provision.kubernetes import utils as kubernetes_utils
|
38
39
|
from sky.skylet import constants
|
@@ -2454,6 +2455,17 @@ def is_controller_accessible(
|
|
2454
2455
|
exceptions.ClusterNotUpError: if the controller is not accessible, or
|
2455
2456
|
failed to be connected.
|
2456
2457
|
"""
|
2458
|
+
if (managed_job_utils.is_consolidation_mode() and
|
2459
|
+
controller == controller_utils.Controllers.JOBS_CONTROLLER):
|
2460
|
+
cn = 'local-controller-consolidation'
|
2461
|
+
return backends.LocalResourcesHandle(
|
2462
|
+
cluster_name=cn,
|
2463
|
+
cluster_name_on_cloud=cn,
|
2464
|
+
cluster_yaml=None,
|
2465
|
+
launched_nodes=1,
|
2466
|
+
launched_resources=sky.Resources(cloud=clouds.Cloud(),
|
2467
|
+
instance_type=cn),
|
2468
|
+
)
|
2457
2469
|
if non_existent_message is None:
|
2458
2470
|
non_existent_message = controller.value.default_hint_if_non_existent
|
2459
2471
|
cluster_name = controller.value.cluster_name
|
@@ -2696,6 +2696,21 @@ class CloudVmRayResourceHandle(backends.backend.ResourceHandle):
|
|
2696
2696
|
pass
|
2697
2697
|
|
2698
2698
|
|
2699
|
+
class LocalResourcesHandle(CloudVmRayResourceHandle):
|
2700
|
+
"""A handle for local resources."""
|
2701
|
+
|
2702
|
+
@context_utils.cancellation_guard
|
2703
|
+
@annotations.lru_cache(scope='global')
|
2704
|
+
@timeline.event
|
2705
|
+
def get_command_runners(self,
|
2706
|
+
force_cached: bool = False,
|
2707
|
+
avoid_ssh_control: bool = False
|
2708
|
+
) -> List[command_runner.CommandRunner]:
|
2709
|
+
"""Returns a list of local command runners."""
|
2710
|
+
del force_cached, avoid_ssh_control # Unused.
|
2711
|
+
return [command_runner.LocalProcessCommandRunner()]
|
2712
|
+
|
2713
|
+
|
2699
2714
|
@registry.BACKEND_REGISTRY.type_register(name='cloudvmray')
|
2700
2715
|
class CloudVmRayBackend(backends.Backend['CloudVmRayResourceHandle']):
|
2701
2716
|
"""Backend: runs on cloud virtual machines, managed by Ray.
|
@@ -4043,19 +4058,27 @@ class CloudVmRayBackend(backends.Backend['CloudVmRayResourceHandle']):
|
|
4043
4058
|
# list should aready be in descending order
|
4044
4059
|
job_id = job_ids[0]
|
4045
4060
|
|
4046
|
-
|
4047
|
-
|
4048
|
-
|
4049
|
-
|
4050
|
-
|
4051
|
-
|
4052
|
-
|
4053
|
-
|
4054
|
-
|
4055
|
-
|
4056
|
-
|
4057
|
-
|
4058
|
-
|
4061
|
+
if isinstance(handle, LocalResourcesHandle):
|
4062
|
+
# In consolidation mode, we don't submit a ray job, therefore no
|
4063
|
+
# run_timestamp is available. We use a dummy run_timestamp here.
|
4064
|
+
run_timestamps = {
|
4065
|
+
job_id: f'managed-jobs-consolidation-mode-{job_id}'
|
4066
|
+
}
|
4067
|
+
else:
|
4068
|
+
# get the run_timestamp
|
4069
|
+
# the function takes in [job_id]
|
4070
|
+
code = job_lib.JobLibCodeGen.get_log_dirs_for_jobs([str(job_id)])
|
4071
|
+
returncode, run_timestamps_payload, stderr = self.run_on_head(
|
4072
|
+
handle,
|
4073
|
+
code,
|
4074
|
+
stream_logs=False,
|
4075
|
+
require_outputs=True,
|
4076
|
+
separate_stderr=True)
|
4077
|
+
subprocess_utils.handle_returncode(returncode, code,
|
4078
|
+
'Failed to sync logs.', stderr)
|
4079
|
+
# returns with a dict of {job_id: run_timestamp}
|
4080
|
+
run_timestamps = message_utils.decode_payload(
|
4081
|
+
run_timestamps_payload)
|
4059
4082
|
if not run_timestamps:
|
4060
4083
|
logger.info(f'{colorama.Fore.YELLOW}'
|
4061
4084
|
'No matching log directories found'
|
sky/client/cli/command.py
CHANGED
@@ -632,7 +632,6 @@ def _make_task_or_dag_from_entrypoint_with_overrides(
|
|
632
632
|
field_to_ignore: Optional[List[str]] = None,
|
633
633
|
# job launch specific
|
634
634
|
job_recovery: Optional[str] = None,
|
635
|
-
priority: Optional[int] = None,
|
636
635
|
config_override: Optional[Dict[str, Any]] = None,
|
637
636
|
) -> Union[sky.Task, sky.Dag]:
|
638
637
|
"""Creates a task or a dag from an entrypoint with overrides.
|
@@ -714,9 +713,6 @@ def _make_task_or_dag_from_entrypoint_with_overrides(
|
|
714
713
|
task.num_nodes = num_nodes
|
715
714
|
if name is not None:
|
716
715
|
task.name = name
|
717
|
-
# job launch specific.
|
718
|
-
if priority is not None:
|
719
|
-
task.set_job_priority(priority)
|
720
716
|
return task
|
721
717
|
|
722
718
|
|
@@ -1243,7 +1239,7 @@ def _handle_jobs_queue_request(
|
|
1243
1239
|
try:
|
1244
1240
|
if not is_called_by_user:
|
1245
1241
|
usage_lib.messages.usage.set_internal()
|
1246
|
-
managed_jobs_ = sdk.
|
1242
|
+
managed_jobs_ = sdk.stream_and_get(request_id)
|
1247
1243
|
num_in_progress_jobs = len(set(job['job_id'] for job in managed_jobs_))
|
1248
1244
|
except exceptions.ClusterNotUpError as e:
|
1249
1245
|
controller_status = e.cluster_status
|
@@ -1800,8 +1796,13 @@ def status(verbose: bool, refresh: bool, ip: bool, endpoints: bool,
|
|
1800
1796
|
@cli.command()
|
1801
1797
|
@flags.config_option(expose_value=False)
|
1802
1798
|
@flags.all_option('Show all cluster information.')
|
1799
|
+
@click.option('--days',
|
1800
|
+
default=30,
|
1801
|
+
type=int,
|
1802
|
+
help='Show clusters from the last N days. Default is 30 days. '
|
1803
|
+
'If set to 0, show all clusters.')
|
1803
1804
|
@usage_lib.entrypoint
|
1804
|
-
def cost_report(all: bool): # pylint: disable=redefined-builtin
|
1805
|
+
def cost_report(all: bool, days: int): # pylint: disable=redefined-builtin
|
1805
1806
|
# NOTE(dev): Keep the docstring consistent between the Python API and CLI.
|
1806
1807
|
"""Show estimated costs for launched clusters.
|
1807
1808
|
|
@@ -1820,13 +1821,22 @@ def cost_report(all: bool): # pylint: disable=redefined-builtin
|
|
1820
1821
|
|
1821
1822
|
- Clusters that were terminated/stopped on the cloud console.
|
1822
1823
|
"""
|
1823
|
-
|
1824
|
+
days_to_query: Optional[int] = days
|
1825
|
+
if days == 0:
|
1826
|
+
days_to_query = None
|
1827
|
+
cluster_records = sdk.get(sdk.cost_report(days=days_to_query))
|
1824
1828
|
|
1825
1829
|
normal_cluster_records = []
|
1826
1830
|
controllers = dict()
|
1827
1831
|
for cluster_record in cluster_records:
|
1828
1832
|
cluster_name = cluster_record['name']
|
1829
|
-
|
1833
|
+
try:
|
1834
|
+
controller = controller_utils.Controllers.from_name(cluster_name)
|
1835
|
+
except AssertionError:
|
1836
|
+
# There could be some old controller clusters from previous
|
1837
|
+
# versions that we should not show in the cost report.
|
1838
|
+
logger.debug(f'Cluster {cluster_name} is not a controller cluster.')
|
1839
|
+
continue
|
1830
1840
|
if controller is not None:
|
1831
1841
|
controller_name = controller.value.name
|
1832
1842
|
# to display most recent entry for each controller cluster
|
@@ -1839,10 +1849,14 @@ def cost_report(all: bool): # pylint: disable=redefined-builtin
|
|
1839
1849
|
total_cost = status_utils.get_total_cost_of_displayed_records(
|
1840
1850
|
normal_cluster_records, all)
|
1841
1851
|
|
1842
|
-
status_utils.show_cost_report_table(normal_cluster_records,
|
1852
|
+
status_utils.show_cost_report_table(normal_cluster_records,
|
1853
|
+
all,
|
1854
|
+
days=days_to_query)
|
1843
1855
|
for controller_name, cluster_record in controllers.items():
|
1844
|
-
status_utils.show_cost_report_table(
|
1845
|
-
|
1856
|
+
status_utils.show_cost_report_table([cluster_record],
|
1857
|
+
all,
|
1858
|
+
controller_name=controller_name,
|
1859
|
+
days=days_to_query)
|
1846
1860
|
total_cost += cluster_record['total_cost']
|
1847
1861
|
|
1848
1862
|
click.echo(f'\n{colorama.Style.BRIGHT}'
|
@@ -2643,6 +2657,23 @@ def _hint_or_raise_for_down_jobs_controller(controller_name: str,
|
|
2643
2657
|
# the controller being STOPPED or being firstly launched, i.e.,
|
2644
2658
|
# there is no in-prgress managed jobs.
|
2645
2659
|
managed_jobs_ = []
|
2660
|
+
except exceptions.InconsistentConsolidationModeError:
|
2661
|
+
# If this error is raised, it means the user switched to the
|
2662
|
+
# consolidation mode but the previous controller cluster is still
|
2663
|
+
# running. We should allow the user to tear down the controller
|
2664
|
+
# cluster in this case.
|
2665
|
+
with skypilot_config.override_skypilot_config(
|
2666
|
+
{'jobs': {
|
2667
|
+
'controller': {
|
2668
|
+
'consolidation_mode': False
|
2669
|
+
}
|
2670
|
+
}}):
|
2671
|
+
# Check again with the consolidation mode disabled. This is to
|
2672
|
+
# make sure there is no in-progress managed jobs.
|
2673
|
+
request_id = managed_jobs.queue(refresh=False,
|
2674
|
+
skip_finished=True,
|
2675
|
+
all_users=True)
|
2676
|
+
managed_jobs_ = sdk.stream_and_get(request_id)
|
2646
2677
|
|
2647
2678
|
msg = (f'{colorama.Fore.YELLOW}WARNING: Tearing down the managed '
|
2648
2679
|
'jobs controller. Please be aware of the following:'
|
@@ -3762,14 +3793,6 @@ def jobs():
|
|
3762
3793
|
default=None,
|
3763
3794
|
type=str,
|
3764
3795
|
help='Recovery strategy to use for managed jobs.')
|
3765
|
-
@click.option('--priority',
|
3766
|
-
type=click.IntRange(constants.MIN_PRIORITY,
|
3767
|
-
constants.MAX_PRIORITY),
|
3768
|
-
default=None,
|
3769
|
-
show_default=True,
|
3770
|
-
help=f'Job priority from ({constants.MIN_PRIORITY} '
|
3771
|
-
f'to {constants.MAX_PRIORITY}). '
|
3772
|
-
f'Default: {constants.DEFAULT_PRIORITY}.')
|
3773
3796
|
@click.option(
|
3774
3797
|
'--detach-run',
|
3775
3798
|
'-d',
|
@@ -3804,7 +3827,6 @@ def jobs_launch(
|
|
3804
3827
|
disk_tier: Optional[str],
|
3805
3828
|
network_tier: Optional[str],
|
3806
3829
|
ports: Tuple[str],
|
3807
|
-
priority: Optional[int],
|
3808
3830
|
detach_run: bool,
|
3809
3831
|
yes: bool,
|
3810
3832
|
async_call: bool,
|
@@ -3853,7 +3875,6 @@ def jobs_launch(
|
|
3853
3875
|
network_tier=network_tier,
|
3854
3876
|
ports=ports,
|
3855
3877
|
job_recovery=job_recovery,
|
3856
|
-
priority=priority,
|
3857
3878
|
config_override=config_override,
|
3858
3879
|
)
|
3859
3880
|
|
sky/client/sdk.py
CHANGED
@@ -12,7 +12,6 @@ Usage example:
|
|
12
12
|
"""
|
13
13
|
import base64
|
14
14
|
import binascii
|
15
|
-
import getpass
|
16
15
|
from http import cookiejar
|
17
16
|
import json
|
18
17
|
import logging
|
@@ -551,7 +550,8 @@ def _launch(
|
|
551
550
|
clusters = get(request_id)
|
552
551
|
cluster_user_hash = common_utils.get_user_hash()
|
553
552
|
cluster_user_hash_str = ''
|
554
|
-
|
553
|
+
current_user = common_utils.get_current_user_name()
|
554
|
+
cluster_user_name = current_user
|
555
555
|
if not clusters:
|
556
556
|
# Show the optimize log before the prompt if the cluster does not
|
557
557
|
# exist.
|
@@ -563,7 +563,7 @@ def _launch(
|
|
563
563
|
cluster_status = cluster_record['status']
|
564
564
|
cluster_user_hash = cluster_record['user_hash']
|
565
565
|
cluster_user_name = cluster_record['user_name']
|
566
|
-
if cluster_user_name ==
|
566
|
+
if cluster_user_name == current_user:
|
567
567
|
# Only show the hash if the username is the same as the local
|
568
568
|
# username, to avoid confusion.
|
569
569
|
cluster_user_hash_str = f' (hash: {cluster_user_hash})'
|
@@ -1338,7 +1338,7 @@ def endpoints(
|
|
1338
1338
|
@usage_lib.entrypoint
|
1339
1339
|
@server_common.check_server_healthy_or_start
|
1340
1340
|
@annotations.client_api
|
1341
|
-
def cost_report() -> server_common.RequestId: # pylint: disable=redefined-builtin
|
1341
|
+
def cost_report(days: Optional[int] = None) -> server_common.RequestId: # pylint: disable=redefined-builtin
|
1342
1342
|
"""Gets all cluster cost reports, including those that have been downed.
|
1343
1343
|
|
1344
1344
|
The estimated cost column indicates price for the cluster based on the type
|
@@ -1348,6 +1348,10 @@ def cost_report() -> server_common.RequestId: # pylint: disable=redefined-built
|
|
1348
1348
|
cache of the cluster status, and may not be accurate for the cluster with
|
1349
1349
|
autostop/use_spot set or terminated/stopped on the cloud console.
|
1350
1350
|
|
1351
|
+
Args:
|
1352
|
+
days: The number of days to get the cost report for. If not provided,
|
1353
|
+
the default is 30 days.
|
1354
|
+
|
1351
1355
|
Returns:
|
1352
1356
|
The request ID of the cost report request.
|
1353
1357
|
|
@@ -1369,8 +1373,10 @@ def cost_report() -> server_common.RequestId: # pylint: disable=redefined-built
|
|
1369
1373
|
'total_cost': (float) cost given resources and usage intervals,
|
1370
1374
|
}
|
1371
1375
|
"""
|
1372
|
-
|
1373
|
-
|
1376
|
+
body = payloads.CostReportBody(days=days)
|
1377
|
+
response = requests.post(f'{server_common.get_server_url()}/cost_report',
|
1378
|
+
json=json.loads(body.model_dump_json()),
|
1379
|
+
cookies=server_common.get_api_cookie_jar())
|
1374
1380
|
return server_common.get_request_id(response)
|
1375
1381
|
|
1376
1382
|
|
sky/clouds/kubernetes.py
CHANGED
@@ -653,6 +653,7 @@ class Kubernetes(clouds.Cloud):
|
|
653
653
|
(constants.PERSISTENT_RUN_SCRIPT_DIR),
|
654
654
|
'k8s_high_availability_restarting_signal_file':
|
655
655
|
(constants.PERSISTENT_RUN_RESTARTING_SIGNAL_FILE),
|
656
|
+
'ha_recovery_log_path': constants.HA_PERSISTENT_RECOVERY_LOG_PATH,
|
656
657
|
'sky_python_cmd': constants.SKY_PYTHON_CMD,
|
657
658
|
'k8s_high_availability_storage_class_name':
|
658
659
|
(k8s_ha_storage_class_name),
|
sky/core.py
CHANGED
@@ -33,6 +33,7 @@ from sky.utils import admin_policy_utils
|
|
33
33
|
from sky.utils import common
|
34
34
|
from sky.utils import common_utils
|
35
35
|
from sky.utils import controller_utils
|
36
|
+
from sky.utils import resources_utils
|
36
37
|
from sky.utils import rich_utils
|
37
38
|
from sky.utils import status_lib
|
38
39
|
from sky.utils import subprocess_utils
|
@@ -265,7 +266,7 @@ def endpoints(cluster: str,
|
|
265
266
|
the dictionary will contain all ports:endpoints exposed on the cluster.
|
266
267
|
|
267
268
|
Raises:
|
268
|
-
|
269
|
+
ValueError: if the cluster is not UP or the endpoint is not exposed.
|
269
270
|
RuntimeError: if the cluster has no ports to be exposed or no endpoints
|
270
271
|
are exposed yet.
|
271
272
|
"""
|
@@ -276,7 +277,7 @@ def endpoints(cluster: str,
|
|
276
277
|
|
277
278
|
|
278
279
|
@usage_lib.entrypoint
|
279
|
-
def cost_report() -> List[Dict[str, Any]]:
|
280
|
+
def cost_report(days: Optional[int] = None) -> List[Dict[str, Any]]:
|
280
281
|
# NOTE(dev): Keep the docstring consistent between the Python API and CLI.
|
281
282
|
"""Get all cluster cost reports, including those that have been downed.
|
282
283
|
|
@@ -294,6 +295,13 @@ def cost_report() -> List[Dict[str, Any]]:
|
|
294
295
|
'cluster_hash': (str) unique hash identifying cluster,
|
295
296
|
'usage_intervals': (List[Tuple[int, int]]) cluster usage times,
|
296
297
|
'total_cost': (float) cost given resources and usage intervals,
|
298
|
+
'cloud': (str) cloud of the cluster,
|
299
|
+
'region': (str) region of the cluster,
|
300
|
+
'cpus': (str) number of vCPUs of the cluster,
|
301
|
+
'memory': (str) memory of the cluster,
|
302
|
+
'accelerators': (str) accelerators of the cluster,
|
303
|
+
'resources_str': (str) resources string of the cluster,
|
304
|
+
'resources_str_full': (str) full resources string of the cluster,
|
297
305
|
}
|
298
306
|
|
299
307
|
The estimated cost column indicates price for the cluster based on the type
|
@@ -303,27 +311,92 @@ def cost_report() -> List[Dict[str, Any]]:
|
|
303
311
|
cache of the cluster status, and may not be accurate for the cluster with
|
304
312
|
autostop/use_spot set or terminated/stopped on the cloud console.
|
305
313
|
|
314
|
+
Args:
|
315
|
+
days: Number of days to look back from now. Active clusters are always
|
316
|
+
included. Historical clusters are only included if they were last
|
317
|
+
used within the past 'days' days. Defaults to 30 days.
|
318
|
+
|
306
319
|
Returns:
|
307
320
|
A list of dicts, with each dict containing the cost information of a
|
308
321
|
cluster.
|
309
322
|
"""
|
310
|
-
|
323
|
+
if days is None:
|
324
|
+
days = constants.COST_REPORT_DEFAULT_DAYS
|
325
|
+
|
326
|
+
cluster_reports = global_user_state.get_clusters_from_history(days=days)
|
327
|
+
logger.debug(
|
328
|
+
f'{len(cluster_reports)} clusters found from history with {days} days.')
|
329
|
+
|
330
|
+
def _process_cluster_report(
|
331
|
+
cluster_report: Dict[str, Any]) -> Dict[str, Any]:
|
332
|
+
"""Process cluster report by calculating cost and adding fields."""
|
333
|
+
# Make a copy to avoid modifying the original
|
334
|
+
report = cluster_report.copy()
|
335
|
+
|
336
|
+
def get_total_cost(cluster_report: dict) -> float:
|
337
|
+
duration = cluster_report['duration']
|
338
|
+
launched_nodes = cluster_report['num_nodes']
|
339
|
+
launched_resources = cluster_report['resources']
|
340
|
+
|
341
|
+
cost = (launched_resources.get_cost(duration) * launched_nodes)
|
342
|
+
return cost
|
343
|
+
|
344
|
+
def _update_record_with_resources(record: Dict[str, Any]) -> None:
|
345
|
+
"""Add resource fields for dashboard compatibility."""
|
346
|
+
if record is None:
|
347
|
+
return
|
348
|
+
resources = record.get('resources')
|
349
|
+
if resources is None:
|
350
|
+
return
|
351
|
+
fields = ['cloud', 'region', 'cpus', 'memory', 'accelerators']
|
352
|
+
for field in fields:
|
353
|
+
try:
|
354
|
+
record[field] = str(getattr(resources, field))
|
355
|
+
except Exception as e: # pylint: disable=broad-except
|
356
|
+
# Ok to skip the fields as this is just for display
|
357
|
+
# purposes.
|
358
|
+
logger.debug(f'Failed to get resources.{field} for cluster '
|
359
|
+
f'{record["name"]}: {str(e)}')
|
360
|
+
record[field] = None
|
361
|
+
|
362
|
+
# Add resources_str and resources_str_full for dashboard
|
363
|
+
# compatibility
|
364
|
+
num_nodes = record.get('num_nodes', 1)
|
365
|
+
try:
|
366
|
+
resource_str_simple = resources_utils.format_resource(
|
367
|
+
resources, simplify=True)
|
368
|
+
resource_str_full = resources_utils.format_resource(
|
369
|
+
resources, simplify=False)
|
370
|
+
record['resources_str'] = f'{num_nodes}x{resource_str_simple}'
|
371
|
+
record[
|
372
|
+
'resources_str_full'] = f'{num_nodes}x{resource_str_full}'
|
373
|
+
except Exception as e: # pylint: disable=broad-except
|
374
|
+
logger.debug(f'Failed to get resources_str for cluster '
|
375
|
+
f'{record["name"]}: {str(e)}')
|
376
|
+
for field in fields:
|
377
|
+
record[field] = None
|
378
|
+
record['resources_str'] = '-'
|
379
|
+
record['resources_str_full'] = '-'
|
380
|
+
|
381
|
+
try:
|
382
|
+
report['total_cost'] = get_total_cost(report)
|
383
|
+
except Exception as e: # pylint: disable=broad-except
|
384
|
+
# Ok to skip the total cost as this is just for display purposes.
|
385
|
+
logger.warning(f'Failed to get total cost for cluster '
|
386
|
+
f'{report["name"]}: {str(e)}')
|
387
|
+
report['total_cost'] = 0.0
|
311
388
|
|
312
|
-
|
313
|
-
|
314
|
-
launched_nodes = cluster_report['num_nodes']
|
315
|
-
launched_resources = cluster_report['resources']
|
389
|
+
_update_record_with_resources(report)
|
390
|
+
return report
|
316
391
|
|
317
|
-
|
318
|
-
|
392
|
+
# Process clusters in parallel
|
393
|
+
if not cluster_reports:
|
394
|
+
return []
|
319
395
|
|
320
|
-
|
321
|
-
|
322
|
-
cluster_report['cloud'] = str(cluster_report['resources'].cloud)
|
323
|
-
cluster_report['accelerators'] = cluster_report[
|
324
|
-
'resources'].accelerators
|
396
|
+
processed_reports = subprocess_utils.run_in_parallel(
|
397
|
+
_process_cluster_report, cluster_reports)
|
325
398
|
|
326
|
-
return
|
399
|
+
return processed_reports
|
327
400
|
|
328
401
|
|
329
402
|
def _start(
|
sky/dashboard/out/404.html
CHANGED
@@ -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/
|
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/6ab927686b492a4a.css" as="style"/><link rel="stylesheet" href="/dashboard/_next/static/css/6ab927686b492a4a.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-c85998e6a5722f21.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-ce31493da9747ef4.js" defer=""></script><script src="/dashboard/_next/static/chunks/pages/_error-1be831200e60c5c0.js" defer=""></script><script src="/dashboard/_next/static/zsALxITkbP8J8NVwSDwMo/_buildManifest.js" defer=""></script><script src="/dashboard/_next/static/zsALxITkbP8J8NVwSDwMo/_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":"zsALxITkbP8J8NVwSDwMo","assetPrefix":"/dashboard","nextExport":true,"isFallback":false,"gip":true,"scriptLoader":[]}</script></body></html>
|
@@ -0,0 +1,6 @@
|
|
1
|
+
"use strict";(self.webpackChunk_N_E=self.webpackChunk_N_E||[]).push([[37,211],{9037:function(e,s,r){r.r(s),r.d(s,{ClusterTable:function(){return O},Clusters:function(){return L},Status2Actions:function(){return z},enabledActions:function(){return V},filterClustersByName:function(){return A},handleVSCodeConnection:function(){return E}});var t=r(5893),l=r(7294),a=r(1163),n=r(8799),i=r(6989),c=r(1664),o=r.n(c),d=r(803),u=r(7673),h=r(8764),m=r(3266),x=r(7324),p=r(3081),f=r(4545),j=r(3626),g=r(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 y=(0,g.Z)("Terminal",[["polyline",{points:"4 17 10 11 4 5",key:"akl6gq"}],["line",{x1:"12",x2:"20",y1:"19",y2:"19",key:"q2wloq"}]]),v=(0,g.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"}]]);r(9470);var w=r(9284),b=r(9307),N=r(3001),k=r(8950),C=r(6378),S=r(6856);r(1272);let _="__ALL_WORKSPACES__",R="__ALL_USERS__";function A(e,s){if(!s||""===s.trim())return e;let r=s.toLowerCase().trim();return e.filter(e=>(e.cluster||"").toLowerCase().includes(r))}let M=(e,s)=>{let r="",t="";return e>=0&&(r=e+"m",t=" "),s&&(r+="".concat(t,"(down)")),""===r&&(r="-"),r},q=(e,s)=>{if(e&&e.includes("@")){let r=e.split("@")[0];return s&&s!==r?"".concat(r," (").concat(s,")"):r}let r=e||s||"N/A";return s&&s!==r?"".concat(r," (").concat(s,")"):r},I=e=>{if(!e||0===e)return"-";let s=e=Math.floor(e),r="",t=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&&t<2){let l=Math.floor(s/e.value);r+="".concat(l).concat(e.label," "),s%=e.value,t++}return r.trim()||"0s"};function L(){var e;let s=(0,a.useRouter)(),[r,c]=(0,l.useState)(!1),d=l.useRef(null),[u,h]=(0,l.useState)(!1),[f,g]=(0,l.useState)(!1),[y,v]=(0,l.useState)(null),[b,A]=(0,l.useState)(_),[M,I]=(0,l.useState)(R),[L,E]=(0,l.useState)(""),[H,V]=(0,l.useState)([]),[W,z]=(0,l.useState)([]),[F,T]=(0,l.useState)(!1),Z=(0,N.X)();(0,l.useEffect)(()=>{s.isReady&&(s.query.workspace&&A(Array.isArray(s.query.workspace)?s.query.workspace[0]:s.query.workspace),s.query.user&&I(Array.isArray(s.query.user)?s.query.user[0]:s.query.user),s.query.name&&E(Array.isArray(s.query.name)?s.query.name[0]:s.query.name))},[s.isReady,s.query.workspace,s.query.user,s.query.name]);let B=(e,r,t)=>{let l={...s.query};e&&e!==_?l.workspace=e:delete l.workspace,r&&r!==R?l.user=r:delete l.user,t&&""!==t.trim()?l.name=t.trim():delete l.name,s.replace({pathname:s.pathname,query:l},void 0,{shallow:!0})},P=e=>{E(e),B(b,M,e)};return(0,l.useEffect)(()=>{(async()=>{try{await S.ZP.preloadForPage("clusters");let e=await C.default.get(x.fX),s=Object.keys(e),r=await C.default.get(m.getClusters),t=[...new Set(r.map(e=>e.workspace||"default").filter(e=>e))],l=new Set(s);t.includes("default")&&l.has("default"),t.forEach(e=>l.add(e)),V(Array.from(l).sort());let a=await C.default.get(p.R),n=[...new Set(r.map(e=>({userId:e.user_hash||e.user,username:e.user})).filter(e=>e.userId)).values()],i=new Map;a.forEach(e=>{i.set(e.userId,{userId:e.userId,username:e.username,display:q(e.username,e.userId)})}),n.forEach(e=>{i.has(e.userId)||i.set(e.userId,{userId:e.userId,username:e.username,display:q(e.username,e.userId)})}),z(Array.from(i.values()).sort((e,s)=>e.display.localeCompare(s.display)))}catch(e){console.error("Error fetching data for filters:",e),V(["default"]),z([])}})()},[]),(0,t.jsxs)(t.Fragment,{children:[(0,t.jsxs)("div",{className:"flex items-center justify-between mb-4 h-5",children:[(0,t.jsxs)("div",{className:"text-base flex items-center",children:[(0,t.jsx)(o(),{href:"/clusters",className:"text-sky-blue hover:underline leading-none",children:"Sky Clusters"}),(0,t.jsx)("div",{className:"flex items-center ml-6 space-x-3",children:(0,t.jsxs)("label",{className:"flex items-center cursor-pointer",children:[(0,t.jsx)("input",{type:"checkbox",checked:F,onChange:e=>T(e.target.checked),className:"sr-only"}),(0,t.jsx)("div",{className:"relative inline-flex h-5 w-9 items-center rounded-full transition-colors ".concat(F?"bg-sky-600":"bg-gray-300"),children:(0,t.jsx)("span",{className:"inline-block h-3 w-3 transform rounded-full bg-white transition-transform ".concat(F?"translate-x-5":"translate-x-1")})}),(0,t.jsx)("span",{className:"ml-2 text-sm text-gray-700",children:"Show History (last 30 days)"})]})}),(0,t.jsxs)("div",{className:"relative ml-4 mr-2",children:[(0,t.jsx)("input",{type:"text",placeholder:"Filter by cluster name",value:L,onChange:e=>P(e.target.value),className:"h-8 w-32 sm:w-48 px-3 pr-8 text-sm border border-gray-300 rounded-md focus:ring-1 focus:ring-sky-500 focus:border-sky-500 outline-none"}),L&&(0,t.jsx)("button",{onClick:()=>P(""),className:"absolute right-2 top-1/2 transform -translate-y-1/2 text-gray-400 hover:text-gray-600",title:"Clear filter",children:(0,t.jsx)("svg",{className:"h-4 w-4",fill:"none",stroke:"currentColor",viewBox:"0 0 24 24",children:(0,t.jsx)("path",{strokeLinecap:"round",strokeLinejoin:"round",strokeWidth:2,d:"M6 18L18 6M6 6l12 12"})})})]}),(0,t.jsxs)(k.Ph,{value:b,onValueChange:e=>{A(e),B(e,M,L)},children:[(0,t.jsx)(k.i4,{className:"h-8 w-48 ml-2 mr-2 text-sm border-none focus:ring-0 focus:outline-none",children:(0,t.jsx)(k.ki,{placeholder:"Filter by workspace...",children:b===_?"All Workspaces":b})}),(0,t.jsxs)(k.Bw,{children:[(0,t.jsx)(k.Ql,{value:_,children:"All Workspaces"}),H.map(e=>(0,t.jsx)(k.Ql,{value:e,children:e},e))]})]}),(0,t.jsxs)(k.Ph,{value:M,onValueChange:e=>{I(e),B(b,e,L)},children:[(0,t.jsx)(k.i4,{className:"h-8 w-48 ml-2 mr-2 text-sm border-none focus:ring-0 focus:outline-none",children:(0,t.jsx)(k.ki,{placeholder:"Filter by user...",children:M===R?"All Users":(null===(e=W.find(e=>e.userId===M))||void 0===e?void 0:e.display)||M})}),(0,t.jsxs)(k.Bw,{children:[(0,t.jsx)(k.Ql,{value:R,children:"All Users"}),W.map(e=>(0,t.jsx)(k.Ql,{value:e.userId,children:e.display},e.userId))]})]})]}),(0,t.jsxs)("div",{className:"flex items-center",children:[r&&(0,t.jsxs)("div",{className:"flex items-center mr-2",children:[(0,t.jsx)(n.Z,{size:15,className:"mt-0"}),(0,t.jsx)("span",{className:"ml-2 text-gray-500",children:"Loading..."})]}),(0,t.jsxs)("button",{onClick:()=>{C.default.invalidate(m.getClusters),C.default.invalidate(m.uR),C.default.invalidate(x.fX),C.default.invalidate(p.R),d.current&&d.current()},disabled:r,className:"text-sky-blue hover:text-sky-blue-bright flex items-center",children:[(0,t.jsx)(j.Z,{className:"h-4 w-4 mr-1.5"}),!Z&&(0,t.jsx)("span",{children:"Refresh"})]})]})]}),(0,t.jsx)(O,{refreshInterval:i.yc,setLoading:c,refreshDataRef:d,workspaceFilter:b,userFilter:M,nameFilter:L,showHistory:F,onOpenSSHModal:e=>{v(e),h(!0)},onOpenVSCodeModal:e=>{v(e),g(!0)}}),(0,t.jsx)(w.Oh,{isOpen:u,onClose:()=>h(!1),cluster:y}),(0,t.jsx)(w._R,{isOpen:f,onClose:()=>g(!1),cluster:y})]})}function O(e){let{refreshInterval:s,setLoading:r,refreshDataRef:a,workspaceFilter:c,userFilter:x,nameFilter:p,showHistory:j,onOpenSSHModal:g,onOpenVSCodeModal:y}=e,[v,w]=(0,l.useState)([]),[N,k]=(0,l.useState)({key:null,direction:"ascending"}),[S,q]=(0,l.useState)(!1),[L,O]=(0,l.useState)(!0),[E,H]=(0,l.useState)(1),[V,W]=(0,l.useState)(10),F=l.useCallback(async()=>{r(!0),q(!0);try{let e=await C.default.get(m.getClusters);if(j){let s=await C.default.get(m.uR),r=e.map(e=>({...e,isHistorical:!1})),t=s.map(e=>({...e,isHistorical:!0})),l=[...r];t.forEach(s=>{e.some(e=>(e.cluster||e.name)===(s.cluster||s.name))||l.push(s)}),w(l)}else{let s=e.map(e=>({...e,isHistorical:!1}));w(s)}}catch(e){console.error("Error fetching cluster data:",e),w([])}r(!1),q(!1),O(!1)},[r,j]),T=l.useMemo(()=>{let e=v;return c&&c!==_&&(e=e.filter(e=>(e.workspace||"default").toLowerCase()===c.toLowerCase())),x&&x!==R&&(e=e.filter(e=>(e.user_hash||e.user)===x)),p&&(e=A(e,p)),(0,f.R0)(e,N.key,N.direction)},[v,N,c,x,p]);l.useEffect(()=>{a&&(a.current=F)},[a,F]),(0,l.useEffect)(()=>{w([]);let e=!0;F();let r=setInterval(()=>{e&&F()},s);return()=>{e=!1,clearInterval(r)}},[s,F]),(0,l.useEffect)(()=>{H(1)},[v.length]);let Z=e=>{let s="ascending";N.key===e&&"ascending"===N.direction&&(s="descending"),k({key:e,direction:s})},B=e=>N.key===e?"ascending"===N.direction?" ↑":" ↓":"",P=Math.ceil(T.length/V),U=(E-1)*V,D=U+V,Q=T.slice(U,D);return(0,t.jsxs)("div",{children:[(0,t.jsx)(u.Zb,{children:(0,t.jsx)("div",{className:"overflow-x-auto",children:(0,t.jsxs)(h.iA,{className:"min-w-full",children:[(0,t.jsx)(h.xD,{children:(0,t.jsxs)(h.SC,{children:[(0,t.jsxs)(h.ss,{className:"sortable whitespace-nowrap",onClick:()=>Z("status"),children:["Status",B("status")]}),(0,t.jsxs)(h.ss,{className:"sortable whitespace-nowrap",onClick:()=>Z("cluster"),children:["Cluster",B("cluster")]}),(0,t.jsxs)(h.ss,{className:"sortable whitespace-nowrap hidden sm:table-cell",onClick:()=>Z("user"),children:["User",B("user")]}),(0,t.jsxs)(h.ss,{className:"sortable whitespace-nowrap hidden md:table-cell",onClick:()=>Z("workspace"),children:["Workspace",B("workspace")]}),(0,t.jsxs)(h.ss,{className:"sortable whitespace-nowrap hidden lg:table-cell",onClick:()=>Z("infra"),children:["Infra",B("infra")]}),(0,t.jsxs)(h.ss,{className:"sortable whitespace-nowrap hidden xl:table-cell",onClick:()=>Z("resources_str"),children:["Resources",B("resources_str")]}),(0,t.jsxs)(h.ss,{className:"sortable whitespace-nowrap",onClick:()=>Z("time"),children:["Started",B("time")]}),j&&(0,t.jsxs)(h.ss,{className:"sortable whitespace-nowrap hidden lg:table-cell",onClick:()=>Z("duration"),children:["Duration",B("duration")]}),(0,t.jsxs)(h.ss,{className:"sortable whitespace-nowrap hidden md:table-cell",onClick:()=>Z("autostop"),children:["Autostop",B("autostop")]}),(0,t.jsx)(h.ss,{className:"sticky right-0 bg-white",children:"Actions"})]})}),(0,t.jsx)(h.RM,{children:S&&L?(0,t.jsx)(h.SC,{children:(0,t.jsx)(h.pj,{colSpan:9,className:"text-center py-6 text-gray-500",children:(0,t.jsxs)("div",{className:"flex justify-center items-center",children:[(0,t.jsx)(n.Z,{size:20,className:"mr-2"}),(0,t.jsx)("span",{children:"Loading..."})]})})}):Q.length>0?Q.map((e,s)=>(0,t.jsxs)(h.SC,{children:[(0,t.jsx)(h.pj,{children:(0,t.jsx)(b.OE,{status:e.status})}),(0,t.jsx)(h.pj,{children:(0,t.jsx)(o(),{href:"/clusters/".concat(e.isHistorical?e.cluster_hash:e.cluster||e.name),className:"text-blue-600",children:e.cluster||e.name})}),(0,t.jsx)(h.pj,{className:"hidden sm:table-cell",children:e.user}),(0,t.jsx)(h.pj,{className:"hidden md:table-cell",children:(0,t.jsx)(o(),{href:"/workspaces",className:"text-blue-600 hover:underline",children:e.workspace||"default"})}),(0,t.jsx)(h.pj,{className:"hidden lg:table-cell",children:(0,t.jsx)(i.Md,{content:e.full_infra||e.infra,className:"text-sm text-muted-foreground",children:(0,t.jsxs)("span",{children:[(0,t.jsx)(o(),{href:"/infra",className:"text-blue-600 hover:underline",children:e.cloud}),e.infra.includes("(")&&(0,t.jsx)("span",{children:" "+e.infra.substring(e.infra.indexOf("("))})]})})}),(0,t.jsx)(h.pj,{className:"hidden xl:table-cell",children:(0,t.jsx)(i.Md,{content:e.resources_str_full||e.resources_str,className:"text-sm text-muted-foreground",children:(0,t.jsx)("span",{children:e.resources_str})})}),(0,t.jsx)(h.pj,{children:(0,t.jsx)(i.Zg,{date:e.time})}),j&&(0,t.jsx)(h.pj,{className:"hidden lg:table-cell",children:I(e.duration)}),(0,t.jsx)(h.pj,{className:"hidden md:table-cell",children:e.isHistorical?"-":M(e.autostop,e.to_down)}),(0,t.jsx)(h.pj,{className:"text-left sticky right-0 bg-white",children:!e.isHistorical&&(0,t.jsx)(z,{cluster:e.cluster,status:e.status,onOpenSSHModal:g,onOpenVSCodeModal:y})})]},s)):(0,t.jsx)(h.SC,{children:(0,t.jsx)(h.pj,{colSpan:9,className:"text-center py-6 text-gray-500",children:j?"No clusters found":"No active clusters"})})})]})})}),v.length>0&&(0,t.jsx)("div",{className:"flex justify-end items-center py-2 px-4 text-sm text-gray-700",children:(0,t.jsxs)("div",{className:"flex items-center space-x-4",children:[(0,t.jsxs)("div",{className:"flex items-center",children:[(0,t.jsx)("span",{className:"mr-2",children:"Rows per page:"}),(0,t.jsxs)("div",{className:"relative inline-block",children:[(0,t.jsxs)("select",{value:V,onChange:e=>{W(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,t.jsx)("option",{value:10,children:"10"}),(0,t.jsx)("option",{value:30,children:"30"}),(0,t.jsx)("option",{value:50,children:"50"}),(0,t.jsx)("option",{value:100,children:"100"}),(0,t.jsx)("option",{value:200,children:"200"})]}),(0,t.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,t.jsx)("path",{strokeLinecap:"round",strokeLinejoin:"round",strokeWidth:2,d:"M19 9l-7 7-7-7"})})]})]}),(0,t.jsxs)("div",{children:[U+1," – ",Math.min(D,v.length)," of"," ",v.length]}),(0,t.jsxs)("div",{className:"flex items-center space-x-2",children:[(0,t.jsx)(d.z,{variant:"ghost",size:"icon",onClick:()=>{H(e=>Math.max(e-1,1))},disabled:1===E,className:"text-gray-500 h-8 w-8 p-0",children:(0,t.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,t.jsx)("path",{d:"M15 18l-6-6 6-6"})})}),(0,t.jsx)(d.z,{variant:"ghost",size:"icon",onClick:()=>{H(e=>Math.min(e+1,P))},disabled:E===P||0===P,className:"text-gray-500 h-8 w-8 p-0",children:(0,t.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,t.jsx)("path",{d:"M9 18l6-6-6-6"})})})]})]})})]})}let E=(e,s)=>{s&&s(e)},H=(e,s)=>{s?s(e):window.open("ssh://".concat(e))},V=e=>"RUNNING"===e?["connect","VSCode"]:[],W={connect:(0,t.jsx)(y,{className:"w-4 h-4 text-gray-500 inline-block"}),VSCode:(0,t.jsx)(v,{className:"w-4 h-4 text-gray-500 inline-block"})};function z(e){let{withLabel:s=!1,cluster:r,status:l,onOpenSSHModal:a,onOpenVSCodeModal:n}=e,c=V(l),o=(0,N.X)(),d=e=>{switch(e){case"connect":H(r,a);break;case"VSCode":E(r,n);break;default:return}};return(0,t.jsx)(t.Fragment,{children:(0,t.jsx)("div",{className:"flex items-center space-x-4",children:Object.entries(W).map(e=>{let r,l,[a,n]=e;switch(a){case"connect":r="Connect",l="Connect with SSH";break;case"VSCode":r="VSCode",l="Open in VS Code"}return(s||(r=""),c.includes(a))?(0,t.jsx)(i.WH,{content:l,className:"capitalize text-sm text-muted-foreground",children:(0,t.jsxs)("button",{onClick:()=>d(a),className:"text-sky-blue hover:text-sky-blue-bright font-medium inline-flex items-center",children:[n,!o&&(0,t.jsx)("span",{className:"ml-1.5",children:r})]})},a):(0,t.jsx)(i.WH,{content:l,className:"capitalize text-sm text-muted-foreground",children:(0,t.jsxs)("span",{className:"opacity-30 flex items-center cursor-not-allowed text-sm",title:a,children:[n,!o&&(0,t.jsx)("span",{className:"ml-1.5",children:r})]})},a)})})})}},803:function(e,s,r){r.d(s,{z:function(){return o}});var t=r(5893),l=r(7294),a=r(8426),n=r(2003),i=r(2350);let c=(0,n.j)("inline-flex items-center justify-center whitespace-nowrap rounded-md text-sm font-medium ring-offset-background transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50",{variants:{variant:{default:"bg-primary text-primary-foreground hover:bg-primary/90",destructive:"bg-destructive text-destructive-foreground hover:bg-destructive/90",outline:"border border-input bg-background hover:bg-accent hover:text-accent-foreground",secondary:"bg-secondary text-secondary-foreground hover:bg-secondary/80",ghost:"hover:bg-accent hover:text-accent-foreground",link:"text-primary underline-offset-4 hover:underline"},size:{default:"h-10 px-4 py-2",sm:"h-9 rounded-md px-3",lg:"h-11 rounded-md px-8",icon:"h-10 w-10"}},defaultVariants:{variant:"default",size:"default"}}),o=l.forwardRef((e,s)=>{let{className:r,variant:l,size:n,asChild:o=!1,...d}=e,u=o?a.g7:"button";return(0,t.jsx)(u,{className:(0,i.cn)(c({variant:l,size:n,className:r})),ref:s,...d})});o.displayName="Button"},7673:function(e,s,r){r.d(s,{Ol:function(){return o},Zb:function(){return c},aY:function(){return h},eW:function(){return m},ll:function(){return d}});var t=r(5893),l=r(7294),a=r(5697),n=r.n(a),i=r(2350);let c=l.forwardRef((e,s)=>{let{className:r,children:l,...a}=e;return(0,t.jsx)("div",{ref:s,className:(0,i.cn)("rounded-lg border bg-card text-card-foreground shadow-sm",r),...a,children:l})});c.displayName="Card",c.propTypes={className:n().string,children:n().node};let o=l.forwardRef((e,s)=>{let{className:r,children:l,...a}=e;return(0,t.jsx)("div",{ref:s,className:(0,i.cn)("flex flex-col space-y-1.5 p-6",r),...a,children:l})});o.displayName="CardHeader",o.propTypes={className:n().string,children:n().node};let d=l.forwardRef((e,s)=>{let{className:r,children:l,...a}=e;return(0,t.jsx)("h3",{ref:s,className:(0,i.cn)("text-2xl font-semibold leading-none tracking-tight",r),...a,children:l})});d.displayName="CardTitle",d.propTypes={className:n().string,children:n().node};let u=l.forwardRef((e,s)=>{let{className:r,children:l,...a}=e;return(0,t.jsx)("p",{ref:s,className:(0,i.cn)("text-sm text-muted-foreground",r),...a,children:l})});u.displayName="CardDescription",u.propTypes={className:n().string,children:n().node};let h=l.forwardRef((e,s)=>{let{className:r,children:l,...a}=e;return(0,t.jsx)("div",{ref:s,className:(0,i.cn)("p-6 pt-0",r),...a,children:l})});h.displayName="CardContent",h.propTypes={className:n().string,children:n().node};let m=l.forwardRef((e,s)=>{let{className:r,children:l,...a}=e;return(0,t.jsx)("div",{ref:s,className:(0,i.cn)("flex items-center p-6 pt-0",r),...a,children:l})});m.displayName="CardFooter",m.propTypes={className:n().string,children:n().node}},2003:function(e,s,r){r.d(s,{j:function(){return n}});var t=r(512);let l=e=>"boolean"==typeof e?`${e}`:0===e?"0":e,a=t.W,n=(e,s)=>r=>{var t;if((null==s?void 0:s.variants)==null)return a(e,null==r?void 0:r.class,null==r?void 0:r.className);let{variants:n,defaultVariants:i}=s,c=Object.keys(n).map(e=>{let s=null==r?void 0:r[e],t=null==i?void 0:i[e];if(null===s)return null;let a=l(s)||l(t);return n[e][a]}),o=r&&Object.entries(r).reduce((e,s)=>{let[r,t]=s;return void 0===t||(e[r]=t),e},{});return a(e,c,null==s?void 0:null===(t=s.compoundVariants)||void 0===t?void 0:t.reduce((e,s)=>{let{class:r,className:t,...l}=s;return Object.entries(l).every(e=>{let[s,r]=e;return Array.isArray(r)?r.includes({...i,...o}[s]):({...i,...o})[s]===r})?[...e,r,t]:e},[]),null==r?void 0:r.class,null==r?void 0:r.className)}}}]);
|
@@ -0,0 +1,6 @@
|
|
1
|
+
"use strict";(self.webpackChunk_N_E=self.webpackChunk_N_E||[]).push([[42,211],{9333:function(e,r,n){n.d(r,{Z:function(){return t}});/**
|
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 t=(0,n(998).Z)("Save",[["path",{d:"M15.2 3a2 2 0 0 1 1.4.6l3.8 3.8a2 2 0 0 1 .6 1.4V19a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2z",key:"1c8476"}],["path",{d:"M17 21v-7a1 1 0 0 0-1-1H8a1 1 0 0 0-1 1v7",key:"1ydtos"}],["path",{d:"M7 3v4a1 1 0 0 0 1 1h7",key:"t51u73"}]])},4042:function(e,r,n){n.r(r),n.d(r,{Config:function(){return g}});var t=n(5893),s=n(7294),l=n(1163),a=n(803),i=n(7673),o=n(7324),c=n(1812),d=n(8799),u=n(9333),f=n(1272),m=n(6989),x=n(7145);function h(){let[e,r]=(0,s.useState)(null),[n,l]=(0,s.useState)(null),a=async()=>{let e=await x.x.get("/api/health"),n=await e.json();n.version&&r(n.version),n.commit&&l(n.commit)};return((0,s.useEffect)(()=>{a()},[]),e)?(0,t.jsx)(m.Md,{content:n?"Commit: ".concat(n):"Commit information not available",className:"text-sm text-muted-foreground",children:(0,t.jsxs)("div",{className:"text-sm text-gray-500 cursor-help border-b border-dotted border-gray-400 inline-block",children:["Version: ",e]})}):null}function g(){let e=(0,l.useRouter)(),[r,n]=(0,s.useState)(""),[m,g]=(0,s.useState)(!0),[p,v]=(0,s.useState)(!1),[b,j]=(0,s.useState)(null),[y,N]=(0,s.useState)(!1),w=(0,s.useRef)(null);(0,s.useEffect)(()=>{k()},[]),(0,s.useEffect)(()=>()=>{w.current&&clearTimeout(w.current)},[]);let k=async()=>{g(!0),j(null);try{let e=await (0,o.iE)();0===Object.keys(e).length?n(""):n(f.ZP.dump(e,{indent:2}))}catch(e){console.error("Error loading config:",e),j(e)}finally{g(!1)}},C=async()=>{v(!0),j(null),w.current&&(clearTimeout(w.current),w.current=null);try{let e=await x.x.get("/users/role");if(!e.ok){let r=await e.json();throw Error(r.detail||"Failed to get user role")}let n=await e.json(),t=n.role;if("admin"!=t){j(Error("".concat(n.name," is logged in as non-admin and cannot edit config"))),v(!1);return}let s=f.ZP.load(r);if(null==s&&(s={}),"object"!=typeof s||Array.isArray(s)){let e="Invalid config structure: Configuration must be a mapping (key-value pairs) in YAML format.";Array.isArray(s),e="Invalid config structure: Configuration must be a mapping (key-value pairs) in YAML format.",j(Error(e)),v(!1);return}await (0,o.rF)(s),N(!0),w.current=setTimeout(()=>{N(!1),w.current=null},5e3)}catch(e){console.error("Error saving config:",e),j(e)}finally{v(!1)}};return(0,t.jsxs)(t.Fragment,{children:[(0,t.jsxs)("div",{className:"flex items-center justify-between mb-4 h-5",children:[(0,t.jsx)("div",{className:"text-base flex items-center",children:(0,t.jsx)("span",{className:"text-sky-blue",children:"SkyPilot Configuration"})}),(0,t.jsxs)("div",{className:"flex items-center",children:[(0,t.jsx)("div",{className:"text-sm flex items-center",children:(m||p)&&(0,t.jsxs)("div",{className:"flex items-center mr-4",children:[(0,t.jsx)(d.Z,{size:15,className:"mt-0"}),(0,t.jsx)("span",{className:"ml-2 text-gray-500",children:p?"Applying...":"Loading..."})]})}),(0,t.jsx)(h,{})]})]}),(0,t.jsxs)(i.Zb,{className:"w-full",children:[(0,t.jsx)(i.Ol,{children:(0,t.jsx)(i.ll,{className:"text-base font-normal flex items-center justify-between",children:(0,t.jsx)("span",{children:"Edit SkyPilot API Server Configuration"})})}),(0,t.jsxs)(i.aY,{className:"space-y-4",children:[(0,t.jsxs)("p",{className:"text-sm text-gray-600 mb-3",children:["Refer to the"," ",(0,t.jsx)("a",{href:"https://docs.skypilot.co/en/latest/reference/config.html",target:"_blank",rel:"noopener noreferrer",className:"text-blue-600 hover:underline",children:"SkyPilot Docs"})," ","for details. The configuration should be in YAML format."]}),y&&(0,t.jsx)("div",{className:"bg-green-50 border border-green-200 rounded p-4 mb-6",children:(0,t.jsxs)("div",{className:"flex items-center justify-between",children:[(0,t.jsxs)("div",{className:"flex items-center",children:[(0,t.jsx)("div",{className:"flex-shrink-0",children:(0,t.jsx)("svg",{className:"h-5 w-5 text-green-400",viewBox:"0 0 20 20",fill:"currentColor",children:(0,t.jsx)("path",{fillRule:"evenodd",d:"M10 18a8 8 0 100-16 8 8 0 000 16zm3.707-9.293a1 1 0 00-1.414-1.414L9 10.586 7.707 9.293a1 1 0 00-1.414 1.414l2 2a1 1 0 001.414 0l4-4z",clipRule:"evenodd"})})}),(0,t.jsx)("div",{className:"ml-3",children:(0,t.jsx)("p",{className:"text-sm font-medium text-green-800",children:"Configuration saved successfully!"})})]}),(0,t.jsx)("div",{className:"ml-auto pl-3",children:(0,t.jsx)("div",{className:"-mx-1.5 -my-1.5",children:(0,t.jsxs)("button",{type:"button",onClick:()=>{N(!1),w.current&&(clearTimeout(w.current),w.current=null)},className:"inline-flex rounded-md bg-green-50 p-1.5 text-green-500 hover:bg-green-100 focus:outline-none focus:ring-2 focus:ring-green-600 focus:ring-offset-2 focus:ring-offset-green-50",children:[(0,t.jsx)("span",{className:"sr-only",children:"Dismiss"}),(0,t.jsx)("svg",{className:"h-5 w-5",viewBox:"0 0 20 20",fill:"currentColor",children:(0,t.jsx)("path",{fillRule:"evenodd",d:"M4.293 4.293a1 1 0 011.414 0L10 8.586l4.293-4.293a1 1 0 111.414 1.414L11.414 10l4.293 4.293a1 1 0 01-1.414 1.414L10 11.414l-4.293 4.293a1 1 0 01-1.414-1.414L8.586 10 4.293 5.707a1 1 0 010-1.414z",clipRule:"evenodd"})})]})})})]})}),b&&(0,t.jsx)("div",{className:"mb-6",children:(0,t.jsx)(c.X,{error:b,title:"Failed to apply new configuration",onDismiss:()=>j(null)})}),(0,t.jsx)("div",{className:"w-full",children:(0,t.jsx)("textarea",{value:r,onChange:e=>n(e.target.value),className:"w-full h-96 p-3 border border-gray-300 rounded font-mono text-sm resize-vertical focus:outline-none focus:ring-2 focus:ring-blue-500",placeholder:m?"Loading configuration...":"# Enter SkyPilot configuration in YAML format\n# Example:\n# kubernetes:\n# allowed_contexts: [default, my-context]",disabled:m||p})}),(0,t.jsxs)("div",{className:"flex justify-end space-x-3 pt-3",children:[(0,t.jsx)(a.z,{variant:"outline",onClick:()=>{e.push("/workspaces")},disabled:p,children:"Cancel"}),(0,t.jsx)(a.z,{onClick:C,disabled:m||p,className:"inline-flex items-center bg-blue-600 hover:bg-blue-700 text-white",children:p?(0,t.jsxs)(t.Fragment,{children:[(0,t.jsx)(d.Z,{size:16,className:"mr-2"}),"Applying..."]}):(0,t.jsxs)(t.Fragment,{children:[(0,t.jsx)(u.Z,{className:"w-4 h-4 mr-1.5"}),"Apply"]})})]})]})]})]})}},1812:function(e,r,n){n.d(r,{X:function(){return a}});var t=n(5893),s=n(7294);let l=e=>{if(!(null==e?void 0:e.message))return"An unexpected error occurred.";let r=e.message;return r.includes("failed:")&&(r=r.split("failed:")[1].trim()),r},a=e=>{let{error:r,title:n="Error",onDismiss:a}=e,[i,o]=(0,s.useState)(!1);if((0,s.useEffect)(()=>{r&&o(!1)},[r]),!r||i)return null;let c="string"==typeof r?r:l(r);return(0,t.jsx)("div",{className:"bg-red-50 border border-red-200 rounded-md p-3 mb-4",children:(0,t.jsxs)("div",{className:"flex items-center justify-between",children:[(0,t.jsxs)("div",{className:"flex",children:[(0,t.jsx)("div",{className:"flex-shrink-0",children:(0,t.jsx)("svg",{className:"h-5 w-5 text-red-400",viewBox:"0 0 20 20",fill:"currentColor",children:(0,t.jsx)("path",{fillRule:"evenodd",d:"M10 18a8 8 0 100-16 8 8 0 000 16zM8.707 7.293a1 1 0 00-1.414 1.414L8.586 10l-1.293 1.293a1 1 0 101.414 1.414L10 11.414l1.293 1.293a1 1 0 001.414-1.414L11.414 10l1.293-1.293a1 1 0 00-1.414-1.414L10 8.586 8.707 7.293z",clipRule:"evenodd"})})}),(0,t.jsx)("div",{className:"ml-3",children:(0,t.jsxs)("div",{className:"text-sm text-red-800",children:[(0,t.jsxs)("strong",{children:[n,":"]})," ",c]})})]}),(0,t.jsx)("button",{onClick:()=>{o(!0),a&&a()},className:"flex-shrink-0 ml-4 text-red-400 hover:text-red-600 focus:outline-none focus:ring-2 focus:ring-red-500 focus:ring-offset-2 focus:ring-offset-red-50 rounded","aria-label":"Dismiss error",children:(0,t.jsx)("svg",{className:"h-4 w-4",viewBox:"0 0 20 20",fill:"currentColor",children:(0,t.jsx)("path",{fillRule:"evenodd",d:"M4.293 4.293a1 1 0 011.414 0L10 8.586l4.293-4.293a1 1 0 111.414 1.414L11.414 10l4.293 4.293a1 1 0 01-1.414 1.414L10 11.414l-4.293 4.293a1 1 0 01-1.414-1.414L8.586 10 4.293 5.707a1 1 0 010-1.414z",clipRule:"evenodd"})})})]})})}},803:function(e,r,n){n.d(r,{z:function(){return c}});var t=n(5893),s=n(7294),l=n(8426),a=n(2003),i=n(2350);let o=(0,a.j)("inline-flex items-center justify-center whitespace-nowrap rounded-md text-sm font-medium ring-offset-background transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50",{variants:{variant:{default:"bg-primary text-primary-foreground hover:bg-primary/90",destructive:"bg-destructive text-destructive-foreground hover:bg-destructive/90",outline:"border border-input bg-background hover:bg-accent hover:text-accent-foreground",secondary:"bg-secondary text-secondary-foreground hover:bg-secondary/80",ghost:"hover:bg-accent hover:text-accent-foreground",link:"text-primary underline-offset-4 hover:underline"},size:{default:"h-10 px-4 py-2",sm:"h-9 rounded-md px-3",lg:"h-11 rounded-md px-8",icon:"h-10 w-10"}},defaultVariants:{variant:"default",size:"default"}}),c=s.forwardRef((e,r)=>{let{className:n,variant:s,size:a,asChild:c=!1,...d}=e,u=c?l.g7:"button";return(0,t.jsx)(u,{className:(0,i.cn)(o({variant:s,size:a,className:n})),ref:r,...d})});c.displayName="Button"},7673:function(e,r,n){n.d(r,{Ol:function(){return c},Zb:function(){return o},aY:function(){return f},eW:function(){return m},ll:function(){return d}});var t=n(5893),s=n(7294),l=n(5697),a=n.n(l),i=n(2350);let o=s.forwardRef((e,r)=>{let{className:n,children:s,...l}=e;return(0,t.jsx)("div",{ref:r,className:(0,i.cn)("rounded-lg border bg-card text-card-foreground shadow-sm",n),...l,children:s})});o.displayName="Card",o.propTypes={className:a().string,children:a().node};let c=s.forwardRef((e,r)=>{let{className:n,children:s,...l}=e;return(0,t.jsx)("div",{ref:r,className:(0,i.cn)("flex flex-col space-y-1.5 p-6",n),...l,children:s})});c.displayName="CardHeader",c.propTypes={className:a().string,children:a().node};let d=s.forwardRef((e,r)=>{let{className:n,children:s,...l}=e;return(0,t.jsx)("h3",{ref:r,className:(0,i.cn)("text-2xl font-semibold leading-none tracking-tight",n),...l,children:s})});d.displayName="CardTitle",d.propTypes={className:a().string,children:a().node};let u=s.forwardRef((e,r)=>{let{className:n,children:s,...l}=e;return(0,t.jsx)("p",{ref:r,className:(0,i.cn)("text-sm text-muted-foreground",n),...l,children:s})});u.displayName="CardDescription",u.propTypes={className:a().string,children:a().node};let f=s.forwardRef((e,r)=>{let{className:n,children:s,...l}=e;return(0,t.jsx)("div",{ref:r,className:(0,i.cn)("p-6 pt-0",n),...l,children:s})});f.displayName="CardContent",f.propTypes={className:a().string,children:a().node};let m=s.forwardRef((e,r)=>{let{className:n,children:s,...l}=e;return(0,t.jsx)("div",{ref:r,className:(0,i.cn)("flex items-center p-6 pt-0",n),...l,children:s})});m.displayName="CardFooter",m.propTypes={className:a().string,children:a().node}},2003:function(e,r,n){n.d(r,{j:function(){return a}});var t=n(512);let s=e=>"boolean"==typeof e?`${e}`:0===e?"0":e,l=t.W,a=(e,r)=>n=>{var t;if((null==r?void 0:r.variants)==null)return l(e,null==n?void 0:n.class,null==n?void 0:n.className);let{variants:a,defaultVariants:i}=r,o=Object.keys(a).map(e=>{let r=null==n?void 0:n[e],t=null==i?void 0:i[e];if(null===r)return null;let l=s(r)||s(t);return a[e][l]}),c=n&&Object.entries(n).reduce((e,r)=>{let[n,t]=r;return void 0===t||(e[n]=t),e},{});return l(e,o,null==r?void 0:null===(t=r.compoundVariants)||void 0===t?void 0:t.reduce((e,r)=>{let{class:n,className:t,...s}=r;return Object.entries(s).every(e=>{let[r,n]=e;return Array.isArray(n)?n.includes({...i,...c}[r]):({...i,...c})[r]===n})?[...e,n,t]:e},[]),null==n?void 0:n.class,null==n?void 0:n.className)}}}]);
|