skypilot-nightly 1.0.0.dev20250523__py3-none-any.whl → 1.0.0.dev20250526__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/backends/backend_utils.py +62 -45
- sky/backends/cloud_vm_ray_backend.py +3 -1
- sky/check.py +335 -170
- sky/cli.py +56 -13
- sky/client/cli.py +56 -13
- sky/client/sdk.py +54 -10
- sky/clouds/gcp.py +19 -3
- sky/core.py +5 -2
- sky/dashboard/out/404.html +1 -1
- sky/dashboard/out/_next/static/7GEgRyZKRaSnYZCV1Jwol/_buildManifest.js +1 -0
- sky/dashboard/out/_next/static/chunks/25-062253ea41fb8eec.js +6 -0
- sky/dashboard/out/_next/static/chunks/480-5a0de8b6570ea105.js +1 -0
- sky/dashboard/out/_next/static/chunks/488-50d843fdb5396d32.js +15 -0
- sky/dashboard/out/_next/static/chunks/498-d7722313e5e5b4e6.js +21 -0
- sky/dashboard/out/_next/static/chunks/573-f17bd89d9f9118b3.js +66 -0
- sky/dashboard/out/_next/static/chunks/578-d351125af46c293f.js +6 -0
- sky/dashboard/out/_next/static/chunks/734-a6e01d7f98904741.js +1 -0
- sky/dashboard/out/_next/static/chunks/937.f97f83652028e944.js +1 -0
- sky/dashboard/out/_next/static/chunks/938-59956af3950b02ed.js +1 -0
- sky/dashboard/out/_next/static/chunks/9f96d65d-5a3e4af68c26849e.js +1 -0
- sky/dashboard/out/_next/static/chunks/pages/_app-96a715a6fb01e228.js +1 -0
- sky/dashboard/out/_next/static/chunks/pages/clusters/[cluster]/[job]-3b5aad09a25f64b7.js +1 -0
- sky/dashboard/out/_next/static/chunks/pages/clusters/[cluster]-9529d9e882a0e75c.js +16 -0
- sky/dashboard/out/_next/static/chunks/pages/clusters-9e6d1ec6e1ac5b29.js +1 -0
- sky/dashboard/out/_next/static/chunks/pages/infra-abb7d744ecf15109.js +1 -0
- sky/dashboard/out/_next/static/chunks/pages/jobs/[job]-48dc8d67d4b60be1.js +1 -0
- sky/dashboard/out/_next/static/chunks/pages/jobs-73d5e0c369d00346.js +16 -0
- sky/dashboard/out/_next/static/chunks/pages/users-b8acf6e6735323a2.js +1 -0
- sky/dashboard/out/_next/static/chunks/pages/workspace/new-bbf436f41381e169.js +1 -0
- sky/dashboard/out/_next/static/chunks/pages/workspaces/[name]-7733c960685b4385.js +1 -0
- sky/dashboard/out/_next/static/chunks/pages/workspaces-5ed48b3201b998c8.js +1 -0
- sky/dashboard/out/_next/static/chunks/webpack-deda68c926e8d0bc.js +1 -0
- sky/dashboard/out/_next/static/css/28558d57108b05ae.css +3 -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/index.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 -0
- sky/dashboard/out/workspace/new.html +1 -0
- sky/dashboard/out/workspaces/[name].html +1 -0
- sky/dashboard/out/workspaces.html +1 -0
- sky/data/storage.py +1 -1
- sky/global_user_state.py +606 -543
- sky/jobs/constants.py +1 -1
- sky/jobs/server/core.py +72 -56
- sky/jobs/state.py +26 -5
- sky/jobs/utils.py +65 -13
- sky/optimizer.py +6 -3
- sky/provision/fluidstack/instance.py +1 -0
- sky/serve/server/core.py +9 -6
- sky/server/html/token_page.html +6 -1
- sky/server/requests/executor.py +1 -0
- sky/server/requests/payloads.py +28 -0
- sky/server/server.py +59 -5
- sky/setup_files/dependencies.py +1 -0
- sky/skylet/constants.py +4 -1
- sky/skypilot_config.py +107 -11
- sky/utils/cli_utils/status_utils.py +18 -8
- sky/utils/db_utils.py +53 -0
- sky/utils/kubernetes/config_map_utils.py +133 -0
- sky/utils/kubernetes/deploy_remote_cluster.py +166 -147
- sky/utils/kubernetes/kubernetes_deploy_utils.py +49 -5
- sky/utils/kubernetes/ssh-tunnel.sh +20 -28
- sky/utils/log_utils.py +4 -0
- sky/utils/schemas.py +54 -0
- sky/workspaces/__init__.py +0 -0
- sky/workspaces/core.py +295 -0
- sky/workspaces/server.py +62 -0
- {skypilot_nightly-1.0.0.dev20250523.dist-info → skypilot_nightly-1.0.0.dev20250526.dist-info}/METADATA +2 -1
- {skypilot_nightly-1.0.0.dev20250523.dist-info → skypilot_nightly-1.0.0.dev20250526.dist-info}/RECORD +79 -63
- sky/dashboard/out/_next/static/ECKwDNS9v9y3_IKFZ2lpp/_buildManifest.js +0 -1
- sky/dashboard/out/_next/static/chunks/236-1a3a9440417720eb.js +0 -6
- sky/dashboard/out/_next/static/chunks/312-c3c8845990db8ffc.js +0 -15
- sky/dashboard/out/_next/static/chunks/37-d584022b0da4ac3b.js +0 -6
- sky/dashboard/out/_next/static/chunks/393-e1eaa440481337ec.js +0 -1
- sky/dashboard/out/_next/static/chunks/480-f28cd152a98997de.js +0 -1
- sky/dashboard/out/_next/static/chunks/582-683f4f27b81996dc.js +0 -59
- sky/dashboard/out/_next/static/chunks/pages/_app-8cfab319f9fb3ae8.js +0 -1
- sky/dashboard/out/_next/static/chunks/pages/clusters/[cluster]/[job]-33bc2bec322249b1.js +0 -1
- sky/dashboard/out/_next/static/chunks/pages/clusters/[cluster]-e2fc2dd1955e6c36.js +0 -1
- sky/dashboard/out/_next/static/chunks/pages/clusters-3a748bd76e5c2984.js +0 -1
- sky/dashboard/out/_next/static/chunks/pages/infra-abf08c4384190a39.js +0 -1
- sky/dashboard/out/_next/static/chunks/pages/jobs/[job]-70756c2dad850a7e.js +0 -1
- sky/dashboard/out/_next/static/chunks/pages/jobs-ecd804b9272f4a7c.js +0 -1
- sky/dashboard/out/_next/static/chunks/webpack-830f59b8404e96b8.js +0 -1
- sky/dashboard/out/_next/static/css/7e7ce4ff31d3977b.css +0 -3
- /sky/dashboard/out/_next/static/{ECKwDNS9v9y3_IKFZ2lpp → 7GEgRyZKRaSnYZCV1Jwol}/_ssgManifest.js +0 -0
- {skypilot_nightly-1.0.0.dev20250523.dist-info → skypilot_nightly-1.0.0.dev20250526.dist-info}/WHEEL +0 -0
- {skypilot_nightly-1.0.0.dev20250523.dist-info → skypilot_nightly-1.0.0.dev20250526.dist-info}/entry_points.txt +0 -0
- {skypilot_nightly-1.0.0.dev20250523.dist-info → skypilot_nightly-1.0.0.dev20250526.dist-info}/licenses/LICENSE +0 -0
- {skypilot_nightly-1.0.0.dev20250523.dist-info → skypilot_nightly-1.0.0.dev20250526.dist-info}/top_level.txt +0 -0
sky/cli.py
CHANGED
@@ -353,12 +353,12 @@ _TASK_OPTIONS = [
|
|
353
353
|
type=str,
|
354
354
|
help='Infrastructure to use. '
|
355
355
|
'Format: cloud, cloud/region, cloud/region/zone, '
|
356
|
-
'or
|
356
|
+
'k8s/context-name, or ssh/node-pool-name. '
|
357
357
|
'Examples: aws, aws/us-east-1, aws/us-east-1/us-east-1a, '
|
358
358
|
# TODO(zhwu): we have to use `\*` to make sure the docs build
|
359
359
|
# not complaining about the `*`, but this will cause `--help`
|
360
360
|
# to show `\*` instead of `*`.
|
361
|
-
'aws/\\*/us-east-1a,
|
361
|
+
'aws/\\*/us-east-1a, k8s/my-context, ssh/my-nodes.'),
|
362
362
|
click.option(
|
363
363
|
'--cloud',
|
364
364
|
required=False,
|
@@ -1781,9 +1781,13 @@ def _show_endpoint(query_clusters: Optional[List[str]],
|
|
1781
1781
|
return
|
1782
1782
|
|
1783
1783
|
|
1784
|
-
def _show_enabled_infra():
|
1784
|
+
def _show_enabled_infra(active_workspace: str, show_workspace: bool):
|
1785
1785
|
"""Show the enabled infrastructure."""
|
1786
|
-
|
1786
|
+
workspace_str = ''
|
1787
|
+
if show_workspace:
|
1788
|
+
workspace_str = f' (workspace: {active_workspace!r})'
|
1789
|
+
title = (f'{colorama.Fore.CYAN}{colorama.Style.BRIGHT}Enabled Infra'
|
1790
|
+
f'{workspace_str}:'
|
1787
1791
|
f'{colorama.Style.RESET_ALL} ')
|
1788
1792
|
enabled_clouds = sdk.get(sdk.enabled_clouds())
|
1789
1793
|
enabled_ssh_infras = []
|
@@ -1954,6 +1958,7 @@ def status(verbose: bool, refresh: bool, ip: bool, endpoints: bool,
|
|
1954
1958
|
# status query.
|
1955
1959
|
service_status_request_id = serve_lib.status(service_names=None)
|
1956
1960
|
|
1961
|
+
workspace_request_id = None
|
1957
1962
|
if ip or show_endpoints:
|
1958
1963
|
if refresh:
|
1959
1964
|
raise click.UsageError(
|
@@ -1988,9 +1993,18 @@ def status(verbose: bool, refresh: bool, ip: bool, endpoints: bool,
|
|
1988
1993
|
('endpoint port'
|
1989
1994
|
if show_single_endpoint else 'endpoints')))
|
1990
1995
|
else:
|
1991
|
-
|
1992
|
-
|
1993
|
-
|
1996
|
+
try:
|
1997
|
+
workspace_request_id = sdk.workspaces()
|
1998
|
+
except RuntimeError:
|
1999
|
+
# Backward compatibility for API server before #5660.
|
2000
|
+
# TODO(zhwu): remove this after 0.12.0.
|
2001
|
+
logger.warning(f'{colorama.Style.DIM}SkyPilot API server is '
|
2002
|
+
'in an old version, and may miss feature: '
|
2003
|
+
'workspaces. Update with: sky api stop; '
|
2004
|
+
'sky api start'
|
2005
|
+
f'{colorama.Style.RESET_ALL}')
|
2006
|
+
workspace_request_id = None
|
2007
|
+
|
1994
2008
|
query_clusters: Optional[List[str]] = None if not clusters else clusters
|
1995
2009
|
refresh_mode = common.StatusRefreshMode.NONE
|
1996
2010
|
if refresh:
|
@@ -2013,9 +2027,20 @@ def status(verbose: bool, refresh: bool, ip: bool, endpoints: bool,
|
|
2013
2027
|
else:
|
2014
2028
|
normal_clusters.append(cluster_record)
|
2015
2029
|
|
2030
|
+
if workspace_request_id is not None:
|
2031
|
+
all_workspaces = sdk.get(workspace_request_id)
|
2032
|
+
else:
|
2033
|
+
all_workspaces = [constants.SKYPILOT_DEFAULT_WORKSPACE]
|
2034
|
+
active_workspace = skypilot_config.get_active_workspace()
|
2035
|
+
show_workspace = len(all_workspaces) > 1
|
2036
|
+
_show_enabled_infra(active_workspace, show_workspace)
|
2037
|
+
click.echo(f'{colorama.Fore.CYAN}{colorama.Style.BRIGHT}Clusters'
|
2038
|
+
f'{colorama.Style.RESET_ALL}')
|
2039
|
+
|
2016
2040
|
num_pending_autostop = 0
|
2017
2041
|
num_pending_autostop += status_utils.show_status_table(
|
2018
|
-
normal_clusters + controllers, verbose, all_users, query_clusters
|
2042
|
+
normal_clusters + controllers, verbose, all_users, query_clusters,
|
2043
|
+
show_workspace)
|
2019
2044
|
|
2020
2045
|
managed_jobs_query_interrupted = False
|
2021
2046
|
if show_managed_jobs:
|
@@ -3345,9 +3370,16 @@ def _down_or_stop_clusters(
|
|
3345
3370
|
is_flag=True,
|
3346
3371
|
default=False,
|
3347
3372
|
help='Show the activated account for each cloud.')
|
3373
|
+
@click.option(
|
3374
|
+
'--workspace',
|
3375
|
+
'-w',
|
3376
|
+
type=str,
|
3377
|
+
help='The workspace to check. If None, all workspaces will be checked.')
|
3348
3378
|
@usage_lib.entrypoint
|
3349
3379
|
# pylint: disable=redefined-outer-name
|
3350
|
-
def check(infra_list: Tuple[str],
|
3380
|
+
def check(infra_list: Tuple[str],
|
3381
|
+
verbose: bool,
|
3382
|
+
workspace: Optional[str] = None):
|
3351
3383
|
"""Check which clouds are available to use.
|
3352
3384
|
|
3353
3385
|
This checks access credentials for all clouds supported by SkyPilot. If a
|
@@ -3370,7 +3402,9 @@ def check(infra_list: Tuple[str], verbose: bool):
|
|
3370
3402
|
sky check aws gcp
|
3371
3403
|
"""
|
3372
3404
|
infra_arg = infra_list if len(infra_list) > 0 else None
|
3373
|
-
request_id = sdk.check(infra_list=infra_arg,
|
3405
|
+
request_id = sdk.check(infra_list=infra_arg,
|
3406
|
+
verbose=verbose,
|
3407
|
+
workspace=workspace)
|
3374
3408
|
sdk.stream_and_get(request_id)
|
3375
3409
|
api_server_url = server_common.get_server_url()
|
3376
3410
|
click.echo()
|
@@ -4447,7 +4481,8 @@ def jobs_cancel(name: Optional[str], job_ids: Tuple[int], all: bool, yes: bool,
|
|
4447
4481
|
f'Provided {" ".join(arguments)!r}.')
|
4448
4482
|
|
4449
4483
|
if not yes:
|
4450
|
-
|
4484
|
+
plural = 's' if len(job_ids) > 1 else ''
|
4485
|
+
job_identity_str = (f'managed job{plural} with ID{plural} {job_id_str}'
|
4451
4486
|
if job_ids else repr(name))
|
4452
4487
|
if all_users:
|
4453
4488
|
job_identity_str = 'all managed jobs FOR ALL USERS'
|
@@ -6169,10 +6204,14 @@ def api_status(request_ids: Optional[List[str]], all_status: bool,
|
|
6169
6204
|
'-e',
|
6170
6205
|
required=False,
|
6171
6206
|
help='The SkyPilot API server endpoint.')
|
6207
|
+
@click.option('--get-token',
|
6208
|
+
is_flag=True,
|
6209
|
+
default=False,
|
6210
|
+
help='Force token-based login.')
|
6172
6211
|
@usage_lib.entrypoint
|
6173
|
-
def api_login(endpoint: Optional[str]):
|
6212
|
+
def api_login(endpoint: Optional[str], get_token: bool):
|
6174
6213
|
"""Logs into a SkyPilot API server."""
|
6175
|
-
sdk.api_login(endpoint)
|
6214
|
+
sdk.api_login(endpoint, get_token)
|
6176
6215
|
|
6177
6216
|
|
6178
6217
|
@api.command('info', cls=_DocumentedCodeCommand)
|
@@ -6184,6 +6223,10 @@ def api_info():
|
|
6184
6223
|
api_server_info = sdk.api_info()
|
6185
6224
|
user_name = os.getenv(constants.USER_ENV_VAR, getpass.getuser())
|
6186
6225
|
user_hash = common_utils.get_user_hash()
|
6226
|
+
api_server_user = api_server_info.get('user')
|
6227
|
+
if api_server_user is not None:
|
6228
|
+
user_name = api_server_user['name']
|
6229
|
+
user_hash = api_server_user['id']
|
6187
6230
|
dashboard_url = server_common.get_dashboard_url(url)
|
6188
6231
|
click.echo(f'Using SkyPilot API server: {url}\n'
|
6189
6232
|
f'{ux_utils.INDENT_SYMBOL}Status: {api_server_info["status"]}, '
|
sky/client/cli.py
CHANGED
@@ -353,12 +353,12 @@ _TASK_OPTIONS = [
|
|
353
353
|
type=str,
|
354
354
|
help='Infrastructure to use. '
|
355
355
|
'Format: cloud, cloud/region, cloud/region/zone, '
|
356
|
-
'or
|
356
|
+
'k8s/context-name, or ssh/node-pool-name. '
|
357
357
|
'Examples: aws, aws/us-east-1, aws/us-east-1/us-east-1a, '
|
358
358
|
# TODO(zhwu): we have to use `\*` to make sure the docs build
|
359
359
|
# not complaining about the `*`, but this will cause `--help`
|
360
360
|
# to show `\*` instead of `*`.
|
361
|
-
'aws/\\*/us-east-1a,
|
361
|
+
'aws/\\*/us-east-1a, k8s/my-context, ssh/my-nodes.'),
|
362
362
|
click.option(
|
363
363
|
'--cloud',
|
364
364
|
required=False,
|
@@ -1781,9 +1781,13 @@ def _show_endpoint(query_clusters: Optional[List[str]],
|
|
1781
1781
|
return
|
1782
1782
|
|
1783
1783
|
|
1784
|
-
def _show_enabled_infra():
|
1784
|
+
def _show_enabled_infra(active_workspace: str, show_workspace: bool):
|
1785
1785
|
"""Show the enabled infrastructure."""
|
1786
|
-
|
1786
|
+
workspace_str = ''
|
1787
|
+
if show_workspace:
|
1788
|
+
workspace_str = f' (workspace: {active_workspace!r})'
|
1789
|
+
title = (f'{colorama.Fore.CYAN}{colorama.Style.BRIGHT}Enabled Infra'
|
1790
|
+
f'{workspace_str}:'
|
1787
1791
|
f'{colorama.Style.RESET_ALL} ')
|
1788
1792
|
enabled_clouds = sdk.get(sdk.enabled_clouds())
|
1789
1793
|
enabled_ssh_infras = []
|
@@ -1954,6 +1958,7 @@ def status(verbose: bool, refresh: bool, ip: bool, endpoints: bool,
|
|
1954
1958
|
# status query.
|
1955
1959
|
service_status_request_id = serve_lib.status(service_names=None)
|
1956
1960
|
|
1961
|
+
workspace_request_id = None
|
1957
1962
|
if ip or show_endpoints:
|
1958
1963
|
if refresh:
|
1959
1964
|
raise click.UsageError(
|
@@ -1988,9 +1993,18 @@ def status(verbose: bool, refresh: bool, ip: bool, endpoints: bool,
|
|
1988
1993
|
('endpoint port'
|
1989
1994
|
if show_single_endpoint else 'endpoints')))
|
1990
1995
|
else:
|
1991
|
-
|
1992
|
-
|
1993
|
-
|
1996
|
+
try:
|
1997
|
+
workspace_request_id = sdk.workspaces()
|
1998
|
+
except RuntimeError:
|
1999
|
+
# Backward compatibility for API server before #5660.
|
2000
|
+
# TODO(zhwu): remove this after 0.12.0.
|
2001
|
+
logger.warning(f'{colorama.Style.DIM}SkyPilot API server is '
|
2002
|
+
'in an old version, and may miss feature: '
|
2003
|
+
'workspaces. Update with: sky api stop; '
|
2004
|
+
'sky api start'
|
2005
|
+
f'{colorama.Style.RESET_ALL}')
|
2006
|
+
workspace_request_id = None
|
2007
|
+
|
1994
2008
|
query_clusters: Optional[List[str]] = None if not clusters else clusters
|
1995
2009
|
refresh_mode = common.StatusRefreshMode.NONE
|
1996
2010
|
if refresh:
|
@@ -2013,9 +2027,20 @@ def status(verbose: bool, refresh: bool, ip: bool, endpoints: bool,
|
|
2013
2027
|
else:
|
2014
2028
|
normal_clusters.append(cluster_record)
|
2015
2029
|
|
2030
|
+
if workspace_request_id is not None:
|
2031
|
+
all_workspaces = sdk.get(workspace_request_id)
|
2032
|
+
else:
|
2033
|
+
all_workspaces = [constants.SKYPILOT_DEFAULT_WORKSPACE]
|
2034
|
+
active_workspace = skypilot_config.get_active_workspace()
|
2035
|
+
show_workspace = len(all_workspaces) > 1
|
2036
|
+
_show_enabled_infra(active_workspace, show_workspace)
|
2037
|
+
click.echo(f'{colorama.Fore.CYAN}{colorama.Style.BRIGHT}Clusters'
|
2038
|
+
f'{colorama.Style.RESET_ALL}')
|
2039
|
+
|
2016
2040
|
num_pending_autostop = 0
|
2017
2041
|
num_pending_autostop += status_utils.show_status_table(
|
2018
|
-
normal_clusters + controllers, verbose, all_users, query_clusters
|
2042
|
+
normal_clusters + controllers, verbose, all_users, query_clusters,
|
2043
|
+
show_workspace)
|
2019
2044
|
|
2020
2045
|
managed_jobs_query_interrupted = False
|
2021
2046
|
if show_managed_jobs:
|
@@ -3345,9 +3370,16 @@ def _down_or_stop_clusters(
|
|
3345
3370
|
is_flag=True,
|
3346
3371
|
default=False,
|
3347
3372
|
help='Show the activated account for each cloud.')
|
3373
|
+
@click.option(
|
3374
|
+
'--workspace',
|
3375
|
+
'-w',
|
3376
|
+
type=str,
|
3377
|
+
help='The workspace to check. If None, all workspaces will be checked.')
|
3348
3378
|
@usage_lib.entrypoint
|
3349
3379
|
# pylint: disable=redefined-outer-name
|
3350
|
-
def check(infra_list: Tuple[str],
|
3380
|
+
def check(infra_list: Tuple[str],
|
3381
|
+
verbose: bool,
|
3382
|
+
workspace: Optional[str] = None):
|
3351
3383
|
"""Check which clouds are available to use.
|
3352
3384
|
|
3353
3385
|
This checks access credentials for all clouds supported by SkyPilot. If a
|
@@ -3370,7 +3402,9 @@ def check(infra_list: Tuple[str], verbose: bool):
|
|
3370
3402
|
sky check aws gcp
|
3371
3403
|
"""
|
3372
3404
|
infra_arg = infra_list if len(infra_list) > 0 else None
|
3373
|
-
request_id = sdk.check(infra_list=infra_arg,
|
3405
|
+
request_id = sdk.check(infra_list=infra_arg,
|
3406
|
+
verbose=verbose,
|
3407
|
+
workspace=workspace)
|
3374
3408
|
sdk.stream_and_get(request_id)
|
3375
3409
|
api_server_url = server_common.get_server_url()
|
3376
3410
|
click.echo()
|
@@ -4447,7 +4481,8 @@ def jobs_cancel(name: Optional[str], job_ids: Tuple[int], all: bool, yes: bool,
|
|
4447
4481
|
f'Provided {" ".join(arguments)!r}.')
|
4448
4482
|
|
4449
4483
|
if not yes:
|
4450
|
-
|
4484
|
+
plural = 's' if len(job_ids) > 1 else ''
|
4485
|
+
job_identity_str = (f'managed job{plural} with ID{plural} {job_id_str}'
|
4451
4486
|
if job_ids else repr(name))
|
4452
4487
|
if all_users:
|
4453
4488
|
job_identity_str = 'all managed jobs FOR ALL USERS'
|
@@ -6169,10 +6204,14 @@ def api_status(request_ids: Optional[List[str]], all_status: bool,
|
|
6169
6204
|
'-e',
|
6170
6205
|
required=False,
|
6171
6206
|
help='The SkyPilot API server endpoint.')
|
6207
|
+
@click.option('--get-token',
|
6208
|
+
is_flag=True,
|
6209
|
+
default=False,
|
6210
|
+
help='Force token-based login.')
|
6172
6211
|
@usage_lib.entrypoint
|
6173
|
-
def api_login(endpoint: Optional[str]):
|
6212
|
+
def api_login(endpoint: Optional[str], get_token: bool):
|
6174
6213
|
"""Logs into a SkyPilot API server."""
|
6175
|
-
sdk.api_login(endpoint)
|
6214
|
+
sdk.api_login(endpoint, get_token)
|
6176
6215
|
|
6177
6216
|
|
6178
6217
|
@api.command('info', cls=_DocumentedCodeCommand)
|
@@ -6184,6 +6223,10 @@ def api_info():
|
|
6184
6223
|
api_server_info = sdk.api_info()
|
6185
6224
|
user_name = os.getenv(constants.USER_ENV_VAR, getpass.getuser())
|
6186
6225
|
user_hash = common_utils.get_user_hash()
|
6226
|
+
api_server_user = api_server_info.get('user')
|
6227
|
+
if api_server_user is not None:
|
6228
|
+
user_name = api_server_user['name']
|
6229
|
+
user_hash = api_server_user['id']
|
6187
6230
|
dashboard_url = server_common.get_dashboard_url(url)
|
6188
6231
|
click.echo(f'Using SkyPilot API server: {url}\n'
|
6189
6232
|
f'{ux_utils.INDENT_SYMBOL}Status: {api_server_info["status"]}, '
|
sky/client/sdk.py
CHANGED
@@ -94,12 +94,15 @@ def stream_response(request_id: Optional[str],
|
|
94
94
|
@server_common.check_server_healthy_or_start
|
95
95
|
@annotations.client_api
|
96
96
|
def check(infra_list: Optional[Tuple[str, ...]],
|
97
|
-
verbose: bool
|
97
|
+
verbose: bool,
|
98
|
+
workspace: Optional[str] = None) -> server_common.RequestId:
|
98
99
|
"""Checks the credentials to enable clouds.
|
99
100
|
|
100
101
|
Args:
|
101
102
|
infra: The infra to check.
|
102
103
|
verbose: Whether to show verbose output.
|
104
|
+
workspace: The workspace to check. If None, all workspaces will be
|
105
|
+
checked.
|
103
106
|
|
104
107
|
Returns:
|
105
108
|
The request ID of the check request.
|
@@ -123,7 +126,9 @@ def check(infra_list: Optional[Tuple[str, ...]],
|
|
123
126
|
f'ignoring {region_zone}')
|
124
127
|
specified_clouds.append(infra.cloud)
|
125
128
|
clouds = tuple(specified_clouds)
|
126
|
-
body = payloads.CheckBody(clouds=clouds,
|
129
|
+
body = payloads.CheckBody(clouds=clouds,
|
130
|
+
verbose=verbose,
|
131
|
+
workspace=workspace)
|
127
132
|
response = requests.post(f'{server_common.get_server_url()}/check',
|
128
133
|
json=json.loads(body.model_dump_json()),
|
129
134
|
cookies=server_common.get_api_cookie_jar())
|
@@ -133,16 +138,23 @@ def check(infra_list: Optional[Tuple[str, ...]],
|
|
133
138
|
@usage_lib.entrypoint
|
134
139
|
@server_common.check_server_healthy_or_start
|
135
140
|
@annotations.client_api
|
136
|
-
def enabled_clouds() -> server_common.RequestId:
|
141
|
+
def enabled_clouds(workspace: Optional[str] = None) -> server_common.RequestId:
|
137
142
|
"""Gets the enabled clouds.
|
138
143
|
|
144
|
+
Args:
|
145
|
+
workspace: The workspace to get the enabled clouds for. If None, the
|
146
|
+
active workspace will be used.
|
147
|
+
|
139
148
|
Returns:
|
140
149
|
The request ID of the enabled clouds request.
|
141
150
|
|
142
151
|
Request Returns:
|
143
152
|
A list of enabled clouds in string format.
|
144
153
|
"""
|
145
|
-
|
154
|
+
if workspace is None:
|
155
|
+
workspace = skypilot_config.get_active_workspace()
|
156
|
+
response = requests.get((f'{server_common.get_server_url()}/enabled_clouds?'
|
157
|
+
f'workspace={workspace}'),
|
146
158
|
cookies=server_common.get_api_cookie_jar())
|
147
159
|
return server_common.get_request_id(response)
|
148
160
|
|
@@ -278,6 +290,13 @@ def optimize(
|
|
278
290
|
return server_common.get_request_id(response)
|
279
291
|
|
280
292
|
|
293
|
+
def workspaces() -> server_common.RequestId:
|
294
|
+
"""Gets the workspaces."""
|
295
|
+
response = requests.get(f'{server_common.get_server_url()}/workspaces',
|
296
|
+
cookies=server_common.get_api_cookie_jar())
|
297
|
+
return server_common.get_request_id(response)
|
298
|
+
|
299
|
+
|
281
300
|
@usage_lib.entrypoint
|
282
301
|
@server_common.check_server_healthy_or_start
|
283
302
|
@annotations.client_api
|
@@ -1731,7 +1750,7 @@ def api_status(
|
|
1731
1750
|
@usage_lib.entrypoint
|
1732
1751
|
@server_common.check_server_healthy_or_start
|
1733
1752
|
@annotations.client_api
|
1734
|
-
def api_info() -> Dict[str,
|
1753
|
+
def api_info() -> Dict[str, Any]:
|
1735
1754
|
"""Gets the server's status, commit and version.
|
1736
1755
|
|
1737
1756
|
Returns:
|
@@ -1744,8 +1763,15 @@ def api_info() -> Dict[str, str]:
|
|
1744
1763
|
'api_version': '1',
|
1745
1764
|
'commit': 'abc1234567890',
|
1746
1765
|
'version': '1.0.0',
|
1766
|
+
'version_on_disk': '1.0.0',
|
1767
|
+
'user': {
|
1768
|
+
'name': 'test@example.com',
|
1769
|
+
'id': '12345abcd',
|
1770
|
+
},
|
1747
1771
|
}
|
1748
1772
|
|
1773
|
+
Note that user may be None if we are not using an auth proxy.
|
1774
|
+
|
1749
1775
|
"""
|
1750
1776
|
response = requests.get(f'{server_common.get_server_url()}/api/health',
|
1751
1777
|
cookies=server_common.get_api_cookie_jar())
|
@@ -1868,7 +1894,7 @@ def api_server_logs(follow: bool = True, tail: Optional[int] = None) -> None:
|
|
1868
1894
|
|
1869
1895
|
@usage_lib.entrypoint
|
1870
1896
|
@annotations.client_api
|
1871
|
-
def api_login(endpoint: Optional[str] = None) -> None:
|
1897
|
+
def api_login(endpoint: Optional[str] = None, get_token: bool = False) -> None:
|
1872
1898
|
"""Logs into a SkyPilot API server.
|
1873
1899
|
|
1874
1900
|
This sets the endpoint globally, i.e., all SkyPilot CLI and SDK calls will
|
@@ -1895,7 +1921,7 @@ def api_login(endpoint: Optional[str] = None) -> None:
|
|
1895
1921
|
raise click.BadParameter('Endpoint must be a valid URL.')
|
1896
1922
|
|
1897
1923
|
server_status = server_common.check_server_healthy(endpoint)
|
1898
|
-
if server_status == server_common.ApiServerStatus.NEEDS_AUTH:
|
1924
|
+
if server_status == server_common.ApiServerStatus.NEEDS_AUTH or get_token:
|
1899
1925
|
# We detected an auth proxy, so go through the auth proxy cookie flow.
|
1900
1926
|
parsed_url = urlparse.urlparse(endpoint)
|
1901
1927
|
token_url = f'{endpoint}/token'
|
@@ -1915,11 +1941,20 @@ def api_login(endpoint: Optional[str] = None) -> None:
|
|
1915
1941
|
raise ValueError(f'Malformed token: {token}') from e
|
1916
1942
|
logger.debug(f'Token data: {data!r}')
|
1917
1943
|
try:
|
1918
|
-
|
1944
|
+
json_data = json.loads(data)
|
1919
1945
|
except (json.JSONDecodeError, UnicodeDecodeError) as e:
|
1920
1946
|
raise ValueError(f'Malformed token data: {data!r}') from e
|
1921
|
-
if not isinstance(
|
1922
|
-
raise ValueError(f'Malformed token JSON: {
|
1947
|
+
if not isinstance(json_data, dict):
|
1948
|
+
raise ValueError(f'Malformed token JSON: {json_data}')
|
1949
|
+
|
1950
|
+
if json_data.get('v') == 1:
|
1951
|
+
user_hash = json_data.get('user')
|
1952
|
+
cookie_dict = json_data['cookies']
|
1953
|
+
elif 'v' not in json_data:
|
1954
|
+
user_hash = None
|
1955
|
+
cookie_dict = json_data
|
1956
|
+
else:
|
1957
|
+
raise ValueError(f'Unsupported token version: {json_data.get("v")}')
|
1923
1958
|
|
1924
1959
|
cookie_jar = cookiejar.MozillaCookieJar()
|
1925
1960
|
for (name, value) in cookie_dict.items():
|
@@ -1962,6 +1997,15 @@ def api_login(endpoint: Optional[str] = None) -> None:
|
|
1962
1997
|
server_common.get_api_cookie_jar_path())
|
1963
1998
|
cookie_jar.save(cookie_jar_path)
|
1964
1999
|
|
2000
|
+
# If we have a user_hash, save it to the local file
|
2001
|
+
if user_hash is not None:
|
2002
|
+
if not common_utils.is_valid_user_hash(user_hash):
|
2003
|
+
raise ValueError(f'Invalid user hash: {user_hash}')
|
2004
|
+
with open(os.path.expanduser('~/.sky/user_hash'),
|
2005
|
+
'w',
|
2006
|
+
encoding='utf-8') as f:
|
2007
|
+
f.write(user_hash)
|
2008
|
+
|
1965
2009
|
# Set the endpoint in the config file
|
1966
2010
|
config_path = pathlib.Path(
|
1967
2011
|
skypilot_config.get_user_config_path()).expanduser()
|
sky/clouds/gcp.py
CHANGED
@@ -997,10 +997,21 @@ class GCP(clouds.Cloud):
|
|
997
997
|
return GCPIdentityType.SHARED_CREDENTIALS_FILE
|
998
998
|
|
999
999
|
@classmethod
|
1000
|
-
@annotations.lru_cache(scope='request',
|
1001
|
-
maxsize=1) # Cache since getting identity is slow.
|
1002
1000
|
def get_user_identities(cls) -> List[List[str]]:
|
1003
1001
|
"""Returns the email address + project id of the active user."""
|
1002
|
+
gcp_workspace_config = json.dumps(
|
1003
|
+
skypilot_config.get_workspace_cloud('gcp'), sort_keys=True)
|
1004
|
+
return cls._get_user_identities(gcp_workspace_config)
|
1005
|
+
|
1006
|
+
@classmethod
|
1007
|
+
@annotations.lru_cache(scope='request', maxsize=5)
|
1008
|
+
def _get_user_identities(
|
1009
|
+
cls, workspace_config: Optional[str]) -> List[List[str]]:
|
1010
|
+
# We add workspace_config in args to avoid caching the GCP identity
|
1011
|
+
# for when different workspace configs are used. Use json.dumps to
|
1012
|
+
# ensure the config is hashable.
|
1013
|
+
del workspace_config # Unused
|
1014
|
+
|
1004
1015
|
try:
|
1005
1016
|
account = _run_output('gcloud auth list --filter=status:ACTIVE '
|
1006
1017
|
'--format="value(account)"')
|
@@ -1031,7 +1042,8 @@ class GCP(clouds.Cloud):
|
|
1031
1042
|
f'{common_utils.format_exception(e, use_bracket=True)}'
|
1032
1043
|
) from e
|
1033
1044
|
# TODO: Return a list of identities in the profile when we support
|
1034
|
-
#
|
1045
|
+
# automatic switching for GCP. Currently we only support one
|
1046
|
+
# identity.
|
1035
1047
|
return [[f'{account} [project_id={project_id}]']]
|
1036
1048
|
|
1037
1049
|
@classmethod
|
@@ -1061,6 +1073,10 @@ class GCP(clouds.Cloud):
|
|
1061
1073
|
return 'dryrun-project-id'
|
1062
1074
|
# pylint: disable=import-outside-toplevel
|
1063
1075
|
from google import auth # type: ignore
|
1076
|
+
config_project_id = skypilot_config.get_workspace_cloud('gcp').get(
|
1077
|
+
'project_id', None)
|
1078
|
+
if config_project_id:
|
1079
|
+
return config_project_id
|
1064
1080
|
_, project_id = auth.default()
|
1065
1081
|
if project_id is None:
|
1066
1082
|
raise exceptions.CloudUserIdentityError(
|
sky/core.py
CHANGED
@@ -17,6 +17,7 @@ from sky import global_user_state
|
|
17
17
|
from sky import models
|
18
18
|
from sky import optimizer
|
19
19
|
from sky import sky_logging
|
20
|
+
from sky import skypilot_config
|
20
21
|
from sky import task as task_lib
|
21
22
|
from sky.backends import backend_utils
|
22
23
|
from sky.clouds import cloud as sky_cloud
|
@@ -1009,9 +1010,11 @@ def storage_delete(name: str) -> None:
|
|
1009
1010
|
# = Catalog Observe =
|
1010
1011
|
# ===================
|
1011
1012
|
@usage_lib.entrypoint
|
1012
|
-
def enabled_clouds() -> List[clouds.Cloud]:
|
1013
|
+
def enabled_clouds(workspace: Optional[str] = None) -> List[clouds.Cloud]:
|
1014
|
+
if workspace is None:
|
1015
|
+
workspace = skypilot_config.get_active_workspace()
|
1013
1016
|
return global_user_state.get_cached_enabled_clouds(
|
1014
|
-
sky_cloud.CloudCapability.COMPUTE)
|
1017
|
+
sky_cloud.CloudCapability.COMPUTE, workspace=workspace)
|
1015
1018
|
|
1016
1019
|
|
1017
1020
|
@usage_lib.entrypoint
|
sky/dashboard/out/404.html
CHANGED
@@ -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/
|
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,"Segoe UI",Roboto,Helvetica,Arial,sans-serif,"Apple Color Emoji","Segoe UI Emoji";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>
|
@@ -0,0 +1 @@
|
|
1
|
+
self.__BUILD_MANIFEST=function(s,e,c,a,t,r,u,b,n){return{__rewrites:{afterFiles:[],beforeFiles:[],fallback:[]},"/":["static/chunks/pages/index-6b0d9e5031b70c58.js"],"/_error":["static/chunks/pages/_error-1be831200e60c5c0.js"],"/clusters":[s,c,e,a,b,"static/chunks/pages/clusters-9e6d1ec6e1ac5b29.js"],"/clusters/[cluster]":[s,c,e,a,t,b,"static/chunks/pages/clusters/[cluster]-9529d9e882a0e75c.js"],"/clusters/[cluster]/[job]":[s,e,"static/chunks/pages/clusters/[cluster]/[job]-3b5aad09a25f64b7.js"],"/infra":[s,e,"static/chunks/pages/infra-abb7d744ecf15109.js"],"/jobs":[s,c,e,a,t,"static/chunks/pages/jobs-73d5e0c369d00346.js"],"/jobs/[job]":[s,e,"static/chunks/pages/jobs/[job]-48dc8d67d4b60be1.js"],"/users":[s,e,"static/chunks/pages/users-b8acf6e6735323a2.js"],"/workspace/new":[r,s,c,u,e,a,t,n,"static/chunks/pages/workspace/new-bbf436f41381e169.js"],"/workspaces":[r,s,c,u,e,a,t,"static/chunks/pages/workspaces-5ed48b3201b998c8.js"],"/workspaces/[name]":[r,s,c,u,e,a,t,n,"static/chunks/pages/workspaces/[name]-7733c960685b4385.js"],sortedPages:["/","/_app","/_error","/clusters","/clusters/[cluster]","/clusters/[cluster]/[job]","/infra","/jobs","/jobs/[job]","/users","/workspace/new","/workspaces","/workspaces/[name]"]}}("static/chunks/573-f17bd89d9f9118b3.js","static/chunks/480-5a0de8b6570ea105.js","static/chunks/488-50d843fdb5396d32.js","static/chunks/734-a6e01d7f98904741.js","static/chunks/938-59956af3950b02ed.js","static/chunks/9f96d65d-5a3e4af68c26849e.js","static/chunks/498-d7722313e5e5b4e6.js","static/chunks/578-d351125af46c293f.js","static/chunks/25-062253ea41fb8eec.js"),self.__BUILD_MANIFEST_CB&&self.__BUILD_MANIFEST_CB();
|
@@ -0,0 +1,6 @@
|
|
1
|
+
(self.webpackChunk_N_E=self.webpackChunk_N_E||[]).push([[25],{1812:function(e,s,l){"use strict";l.d(s,{X:function(){return n}});var a=l(5893),t=l(7294);let r=e=>{if(!(null==e?void 0:e.message))return"An unexpected error occurred.";let s=e.message;return s.includes("failed:")&&(s=s.split("failed:")[1].trim()),s.charAt(0).toUpperCase()+s.slice(1)},n=e=>{let{error:s,title:l="Error",onDismiss:n}=e,[c,i]=(0,t.useState)(!1);if((0,t.useEffect)(()=>{s&&i(!1)},[s]),!s||c)return null;let o="string"==typeof s?s:r(s);return(0,a.jsx)("div",{className:"bg-red-50 border border-red-200 rounded-md p-3 mb-4",children:(0,a.jsxs)("div",{className:"flex items-center justify-between",children:[(0,a.jsxs)("div",{className:"flex",children:[(0,a.jsx)("div",{className:"flex-shrink-0",children:(0,a.jsx)("svg",{className:"h-5 w-5 text-red-400",viewBox:"0 0 20 20",fill:"currentColor",children:(0,a.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,a.jsx)("div",{className:"ml-3",children:(0,a.jsxs)("div",{className:"text-sm text-red-800",children:[(0,a.jsxs)("strong",{children:[l,":"]})," ",o]})})]}),(0,a.jsx)("button",{onClick:()=>{i(!0),n&&n()},className:"flex-shrink-0 ml-4 text-red-400 hover:text-red-600 focus:outline-none focus:ring-2 focus:ring-red-500 focus:ring-offset-2 focus:ring-offset-red-50 rounded","aria-label":"Dismiss error",children:(0,a.jsx)("svg",{className:"h-4 w-4",viewBox:"0 0 20 20",fill:"currentColor",children:(0,a.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"})})})]})})}},3025:function(e,s,l){"use strict";l.d(s,{I:function(){return O}});var a=l(5893),t=l(7294),r=l(1163),n=l(7324),c=l(3266),i=l(8969),o=l(9470),d=l(1664),u=l.n(d),x=l(9008),m=l.n(x),h=l(7673),f=l(803),p=l(2350);let g=t.forwardRef((e,s)=>{let{className:l,...t}=e;return(0,a.jsx)("textarea",{className:(0,p.cn)("flex min-h-[80px] w-full rounded-md border border-input bg-background px-3 py-2 text-sm ring-offset-background placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50",l),ref:s,...t})});g.displayName="Textarea";var j=l(8799),b=l(282),y=l(3626),N=l(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 k=(0,N.Z)("Trash",[["path",{d:"M3 6h18",key:"d0wm0j"}],["path",{d:"M19 6v14c0 1-1 2-2 2H7c-1 0-2-1-2-2V6",key:"4alrt4"}],["path",{d:"M8 6V4c0-1 1-2 2-2h4c1 0 2 1 2 2v2",key:"v07s0e"}]]),v=(0,N.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"}]]);var w=l(326);let C=e=>{let{className:s="",variant:l="default",children:t,...r}=e;return(0,a.jsx)("div",{role:"alert",className:"".concat("relative w-full rounded-lg border p-4 flex items-start space-x-2"," ").concat({default:"bg-blue-50 border-blue-200 text-blue-800",destructive:"bg-red-50 border-red-200 text-red-800"}[l]," ").concat(s),...r,children:t})},L=e=>{let{className:s="",children:l,...t}=e;return(0,a.jsx)("div",{className:"text-sm leading-relaxed ".concat(s),...t,children:l})};var E=l(3850),S=l(1812),D=l(938),M=l(3320),A=l.n(M),W=l(3225);let z=e=>{let{message:s}=e;return s?(0,a.jsxs)(C,{className:"border-green-200 bg-green-50",children:[(0,a.jsx)(b.Z,{className:"h-4 w-4 text-green-600"}),(0,a.jsx)(L,{className:"text-green-800",children:s})]}):null},R=e=>{let{workspaceName:s,config:l}=e;if(!l)return null;let t="default"===s,r=0===Object.keys(l).length;if(t&&r)return(0,a.jsx)("div",{className:"text-sm text-gray-500 mb-3 italic p-3 bg-sky-50 rounded border border-sky-200",children:"Workspace 'default' can use all accessible infrastructure."});let n=[],c=[];Object.entries(l).forEach(e=>{let[s,l]=e,t=W.MN[s.toLowerCase()];if((null==l?void 0:l.disabled)===!0)c.push(t);else if(l&&Object.keys(l).length>0){let e="";"gcp"===s.toLowerCase()&&l.project_id?e=" (Project ID: ".concat(l.project_id,")"):"aws"===s.toLowerCase()&&l.region&&(e=" (Region: ".concat(l.region,")")),n.push((0,a.jsxs)("span",{className:"block",children:[t,e," is enabled."]},"".concat(s,"-enabled")))}else n.push((0,a.jsxs)("span",{className:"block",children:[t," is enabled (using default settings)."]},"".concat(s,"-default-enabled")))});let i=[];if(c.length>0){let e=c.join(" and ");i.push((0,a.jsxs)("span",{className:"block",children:[e," ",1===c.length?"is":"are"," explicitly disabled."]},"disabled-clouds"))}return(i.push(...n),i.length>0)?(0,a.jsx)("div",{className:"text-sm text-gray-700 mb-3 p-3 bg-sky-50 rounded border border-sky-200",children:i}):!t&&r?(0,a.jsx)("div",{className:"text-sm text-gray-500 mb-3 italic p-3 bg-sky-50 rounded border border-sky-200",children:"This workspace has no specific cloud resource configurations and can use all accessible infrastructure."}):null};function O(e){let{workspaceName:s,isNewWorkspace:l=!1}=e,d=(0,r.useRouter)(),[x,p]=(0,t.useState)({}),[b,N]=(0,t.useState)({}),[C,L]=(0,t.useState)(""),[M,W]=(0,t.useState)(!0),[O,_]=(0,t.useState)(!1),[Z,T]=(0,t.useState)(!1),[Y,I]=(0,t.useState)(null),[P,V]=(0,t.useState)(null),[J,H]=(0,t.useState)(null),[X,B]=(0,t.useState)({showDialog:!1,deleting:!1,error:null}),[F,U]=(0,t.useState)({totalClusterCount:0,runningClusterCount:0,managedJobsCount:0,clouds:[]}),[G,q]=(0,t.useState)(!1),K=(0,t.useCallback)(async()=>{W(!0),I(null);try{let e;let l=(await (0,n.fX)())[s]||{};p(l),N(l),e=0===Object.keys(l).length?"".concat(s,":\n # Empty workspace configuration - uses all accessible infrastructure\n"):A().dump({[s]:l},{indent:2,lineWidth:-1,noRefs:!0,skipInvalid:!0,flowLevel:-1}),L(e)}catch(e){console.error("Error fetching workspace config:",e),I(e)}finally{W(!1)}},[s]),Q=(0,t.useCallback)(async()=>{if(!l){q(!0);try{let[e,l,a]=await Promise.all([(0,c.zd)(),(0,i.Vp)(),(0,n.yz)(s)]),t=e.filter(e=>(e.workspace||"default")===s),r=t.filter(e=>"RUNNING"===e.status||"LAUNCHING"===e.status),o={};e.forEach(e=>{o[e.cluster]=e.workspace||"default"});let d=l.jobs||[],u=new Set(D.x2.active),x=0;d.forEach(e=>{let l=e.cluster_name||e.resources&&e.resources.cluster_name;l&&o[l]===s&&u.has(e.status)&&x++}),U({totalClusterCount:t.length,runningClusterCount:r.length,managedJobsCount:x,clouds:Array.isArray(a)?a.sort():[]})}catch(e){console.error("Failed to fetch workspace stats:",e)}finally{q(!1)}}},[s,l]);(0,t.useEffect)(()=>{l?(W(!1),L("".concat(s,":\n # New workspace configuration\n # Leave empty to use all accessible infrastructure\n"))):(K(),Q())},[s,l,K,Q]),(0,t.useEffect)(()=>{T(JSON.stringify(x)!==JSON.stringify(b))},[x,b]);let $=e=>{L(e),H(null);try{let l=A().load(e)||{},a=Object.keys(l);if(0===a.length)p({});else if(1===a.length){let e=a[0];if(e!==s){H('Workspace name cannot be changed. Expected "'.concat(s,'" but found "').concat(e,'".'));return}let t=l[s]||{};p(t)}else H("Configuration must contain only one workspace. Found: ".concat(a.join(", ")))}catch(e){H("Invalid YAML: ".concat(e.message))}},ee=async()=>{_(!0),I(null),V(null);try{if(J)throw Error("Please fix YAML errors before saving");let e=A().load(C)||{},a=Object.keys(e);if(a.length>0&&a[0]!==s)throw Error('Workspace name cannot be changed. Expected "'.concat(s,'".'));l?(await (0,n.MB)(s,x),V("Workspace created successfully!"),setTimeout(()=>{d.push("/workspaces/".concat(s))},1500)):(await (0,n.eA)(s,x),V("Workspace updated successfully!"),N(x),Q())}catch(e){console.error("Error saving workspace:",e),I(e)}finally{_(!1)}},es=async()=>{B(e=>({...e,deleting:!0,error:null}));try{await (0,n.zl)(s),V("Workspace deleted successfully!"),setTimeout(()=>{d.push("/workspaces")},1500)}catch(e){console.error("Error deleting workspace:",e),B(s=>({...s,deleting:!1,error:e}))}},el=()=>{B({showDialog:!1,deleting:!1,error:null})},ea=async()=>{await Promise.all([K(),Q()])};if(!d.isReady)return(0,a.jsx)("div",{children:"Loading..."});let et=l?"Create New Workspace | SkyPilot Dashboard":"Workspace: ".concat(s," | SkyPilot Dashboard");return(0,a.jsxs)(a.Fragment,{children:[(0,a.jsx)(m(),{children:(0,a.jsx)("title",{children:et})}),(0,a.jsxs)(o.A,{highlighted:"workspaces",children:[(0,a.jsxs)("div",{className:"flex items-center justify-between mb-4 h-5",children:[(0,a.jsxs)("div",{className:"text-base flex items-center",children:[(0,a.jsx)(u(),{href:"/workspaces",className:"text-sky-blue hover:underline",children:"Workspaces"}),(0,a.jsx)("span",{className:"mx-2 text-gray-500",children:"›"}),(0,a.jsx)(u(),{href:l?"/workspace/new":"/workspaces/".concat(s),className:"text-sky-blue hover:underline",children:l?"New Workspace":s}),Z&&(0,a.jsx)("span",{className:"ml-3 px-2 py-1 bg-yellow-100 text-yellow-800 text-xs rounded",children:"Unsaved changes"})]}),(0,a.jsxs)("div",{className:"text-sm flex items-center",children:[(M||O||G)&&(0,a.jsxs)("div",{className:"flex items-center mr-4",children:[(0,a.jsx)(j.Z,{size:15,className:"mt-0"}),(0,a.jsx)("span",{className:"ml-2 text-gray-500",children:O?"Saving...":"Loading..."})]}),(0,a.jsxs)("div",{className:"flex items-center space-x-4",children:[!l&&(0,a.jsxs)("button",{onClick:ea,disabled:M||O||G,className:"text-sky-blue hover:text-sky-blue-bright font-medium inline-flex items-center",children:[(0,a.jsx)(y.Z,{className:"w-4 h-4 mr-1.5"}),"Refresh"]}),!l&&"default"!==s&&(0,a.jsxs)("button",{onClick:()=>B({...X,showDialog:!0}),disabled:X.deleting||O,className:"text-red-600 hover:text-red-700 font-medium inline-flex items-center",children:[(0,a.jsx)(k,{className:"w-4 h-4 mr-1.5"}),"Delete"]})]})]})]}),M?(0,a.jsxs)("div",{className:"flex justify-center items-center py-12",children:[(0,a.jsx)(j.Z,{size:24,className:"mr-2"}),(0,a.jsx)("span",{className:"text-gray-500",children:"Loading workspace configuration..."})]}):(0,a.jsxs)("div",{className:"space-y-6",children:[(0,a.jsx)(S.X,{error:Y,title:"Error",onDismiss:()=>I(null)}),(0,a.jsx)(z,{message:P}),(0,a.jsxs)("div",{className:"grid grid-cols-1 lg:grid-cols-3 gap-6",children:[!l&&(0,a.jsx)("div",{className:"lg:col-span-1",children:(0,a.jsxs)(h.Zb,{className:"h-full",children:[(0,a.jsx)(h.Ol,{children:(0,a.jsxs)(h.ll,{className:"text-base font-normal",children:[(0,a.jsx)("span",{className:"font-semibold",children:"Workspace:"})," ",s]})}),(0,a.jsxs)(h.aY,{className:"text-sm pb-2 flex-1",children:[(0,a.jsxs)("div",{className:"py-2 flex items-center justify-between",children:[(0,a.jsxs)("div",{className:"flex items-center text-gray-600",children:[(0,a.jsx)(E.QT,{className:"w-4 h-4 mr-2 text-gray-500"}),(0,a.jsx)("span",{children:"Clusters (Running / Total)"})]}),(0,a.jsx)("span",{className:"font-normal text-gray-800",children:G?"...":"".concat(F.runningClusterCount," / ").concat(F.totalClusterCount)})]}),(0,a.jsxs)("div",{className:"py-2 flex items-center justify-between border-t border-gray-100",children:[(0,a.jsxs)("div",{className:"flex items-center text-gray-600",children:[(0,a.jsx)(E.Vp,{className:"w-4 h-4 mr-2 text-gray-500"}),(0,a.jsx)("span",{children:"Managed Jobs"})]}),(0,a.jsx)("span",{className:"font-normal text-gray-800",children:G?"...":F.managedJobsCount})]})]}),(0,a.jsxs)("div",{className:"px-6 pb-6 text-sm pt-3",children:[(0,a.jsx)("h4",{className:"mb-2 text-xs text-gray-500 tracking-wider",children:"Enabled Infra"}),(0,a.jsx)("div",{className:"flex flex-wrap gap-x-4 gap-y-1",children:G?(0,a.jsx)("span",{className:"text-gray-500",children:"Loading..."}):F.clouds.length>0?F.clouds.map(e=>(0,a.jsxs)("div",{className:"flex items-center text-gray-700",children:[(0,a.jsx)(E.Ye,{className:"w-3.5 h-3.5 mr-1.5 text-green-500"}),(0,a.jsx)("span",{children:e})]},e)):(0,a.jsx)("span",{className:"text-gray-500 italic",children:"No enabled infrastructure"})}),(0,a.jsx)("div",{className:"mt-4",children:(0,a.jsx)(R,{workspaceName:s,config:b})})]})]})}),(0,a.jsx)("div",{className:l?"lg:col-span-3":"lg:col-span-2",children:(0,a.jsxs)(h.Zb,{className:"h-full flex flex-col",children:[(0,a.jsx)(h.Ol,{children:(0,a.jsx)(h.ll,{className:"text-base font-normal",children:l?"New Workspace YAML":"Edit Workspace YAML"})}),(0,a.jsx)(h.aY,{className:"flex-1 flex flex-col",children:(0,a.jsxs)("div",{className:"space-y-4 flex-1 flex flex-col",children:[J&&(0,a.jsx)(S.X,{error:J,onDismiss:()=>H(null)}),(0,a.jsxs)("div",{className:"flex-1 flex flex-col",children:[(0,a.jsxs)("p",{className:"text-sm text-gray-600 mb-3",children:["Configure infra-specific settings for this workspace. Leave empty to use all accessible infrastructure. Refer to"," ",(0,a.jsx)("a",{href:"https://docs.skypilot.co/en/latest/admin/workspaces.html#configuration",target:"_blank",rel:"noopener noreferrer",className:"text-blue-600",children:"SkyPilot Docs"})," ","for more details."]}),(0,a.jsxs)("div",{className:"mb-4",children:[(0,a.jsx)("h4",{className:"text-sm font-medium text-gray-700 mb-2",children:"Example configuration:"}),(0,a.jsx)("div",{className:"p-3 bg-gray-50 border rounded-lg",children:(0,a.jsx)("pre",{className:"text-xs font-mono text-gray-600 whitespace-pre-wrap",children:"".concat(s||"my-workspace",":\n gcp:\n project_id: xxx\n aws:\n disabled: true")})})]}),(0,a.jsx)(g,{value:C,onChange:e=>$(e.target.value),className:"font-mono text-sm flex-1 resize-none",style:{minHeight:"350px"},spellCheck:!1,placeholder:"# Enter workspace configuration in YAML format"}),(0,a.jsx)("div",{className:"flex justify-end space-x-3 pt-3 border-gray-200",children:(0,a.jsxs)(f.z,{onClick:ee,disabled:O||J||M,className:"inline-flex items-center bg-blue-600 hover:bg-blue-700 text-white",children:[(0,a.jsx)(v,{className:"w-4 h-4 mr-1.5"}),O?"Applying...":"Apply"]})})]})]})})]})})]})]}),(0,a.jsx)(w.Vq,{open:X.showDialog,onOpenChange:el,children:(0,a.jsxs)(w.cZ,{className:"sm:max-w-md",children:[(0,a.jsxs)(w.fK,{className:"",children:[(0,a.jsx)(w.$N,{children:"Delete Workspace"}),(0,a.jsxs)(w.Be,{children:['Are you sure you want to delete workspace "',s,'"? This action cannot be undone.']})]}),X.error&&(0,a.jsx)(S.X,{error:X.error,title:"Deletion Failed",onDismiss:()=>B(e=>({...e,error:null}))}),(0,a.jsxs)(w.cN,{className:"",children:[(0,a.jsx)(f.z,{variant:"outline",onClick:el,disabled:X.deleting,children:"Cancel"}),(0,a.jsx)(f.z,{variant:"destructive",onClick:es,disabled:X.deleting,children:X.deleting?"Deleting...":"Delete"})]})]})})]})]})}},9008:function(e,s,l){e.exports=l(7219)}}]);
|