skypilot-nightly 1.0.0.dev20250806__py3-none-any.whl → 1.0.0.dev20250808__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.
Potentially problematic release.
This version of skypilot-nightly might be problematic. Click here for more details.
- sky/__init__.py +2 -2
- sky/backends/backend_utils.py +20 -1
- sky/backends/cloud_vm_ray_backend.py +42 -6
- sky/check.py +11 -1
- sky/client/cli/command.py +248 -119
- sky/client/sdk.py +146 -66
- sky/client/sdk_async.py +5 -1
- sky/core.py +5 -2
- sky/dashboard/out/404.html +1 -1
- sky/dashboard/out/_next/static/-DXZksWqf2waNHeU9YTQe/_buildManifest.js +1 -0
- sky/dashboard/out/_next/static/chunks/1141-a8a8f1adba34c892.js +11 -0
- sky/dashboard/out/_next/static/chunks/1871-980a395e92633a5c.js +6 -0
- sky/dashboard/out/_next/static/chunks/3785.6003d293cb83eab4.js +1 -0
- sky/dashboard/out/_next/static/chunks/3850-ff4a9a69d978632b.js +1 -0
- sky/dashboard/out/_next/static/chunks/4725.29550342bd53afd8.js +1 -0
- sky/dashboard/out/_next/static/chunks/{4937.d6bf67771e353356.js → 4937.a2baa2df5572a276.js} +1 -1
- sky/dashboard/out/_next/static/chunks/6130-2be46d70a38f1e82.js +1 -0
- sky/dashboard/out/_next/static/chunks/6601-06114c982db410b6.js +1 -0
- sky/dashboard/out/_next/static/chunks/{691.6d99cbfba347cebf.js → 691.5eeedf82cc243343.js} +1 -1
- sky/dashboard/out/_next/static/chunks/6989-6129c1cfbcf51063.js +1 -0
- sky/dashboard/out/_next/static/chunks/6990-0f886f16e0d55ff8.js +1 -0
- sky/dashboard/out/_next/static/chunks/8056-34d27f51e6d1c631.js +1 -0
- sky/dashboard/out/_next/static/chunks/8252.62b0d23aed618bb2.js +16 -0
- sky/dashboard/out/_next/static/chunks/8969-c9686994ddafcf01.js +1 -0
- sky/dashboard/out/_next/static/chunks/9025.a1bef12d672bb66d.js +6 -0
- sky/dashboard/out/_next/static/chunks/9159-11421c0f2909236f.js +1 -0
- sky/dashboard/out/_next/static/chunks/9360.85b0b1b4054574dd.js +31 -0
- sky/dashboard/out/_next/static/chunks/9666.cd4273f2a5c5802c.js +1 -0
- sky/dashboard/out/_next/static/chunks/{9847.4c46c5e229c78704.js → 9847.757720f3b40c0aa5.js} +1 -1
- sky/dashboard/out/_next/static/chunks/pages/{_app-2a43ea3241bbdacd.js → _app-491a4d699d95e808.js} +1 -1
- sky/dashboard/out/_next/static/chunks/pages/clusters/[cluster]/[job]-ae17cec0fc6483d9.js +11 -0
- sky/dashboard/out/_next/static/chunks/pages/clusters/[cluster]-155d477a6c3e04e2.js +1 -0
- sky/dashboard/out/_next/static/chunks/pages/{clusters-47f1ddae13a2f8e4.js → clusters-b30460f683e6ba96.js} +1 -1
- sky/dashboard/out/_next/static/chunks/pages/config-dfb9bf07b13045f4.js +1 -0
- sky/dashboard/out/_next/static/chunks/pages/infra/{[context]-2a44e70b500b6b70.js → [context]-13d53fffc03ccb52.js} +1 -1
- sky/dashboard/out/_next/static/chunks/pages/{infra-22faac9325016d83.js → infra-fc9222e26c8e2f0d.js} +1 -1
- sky/dashboard/out/_next/static/chunks/pages/jobs/[job]-154f55cf8af55be5.js +11 -0
- sky/dashboard/out/_next/static/chunks/pages/jobs/pools/[pool]-f5ccf5d39d87aebe.js +21 -0
- sky/dashboard/out/_next/static/chunks/pages/jobs-cdc60fb5d371e16a.js +1 -0
- sky/dashboard/out/_next/static/chunks/pages/{users-b90c865a690bfe84.js → users-7ed36e44e779d5c7.js} +1 -1
- sky/dashboard/out/_next/static/chunks/pages/{volumes-7af733f5d7b6ed1c.js → volumes-c9695d657f78b5dc.js} +1 -1
- sky/dashboard/out/_next/static/chunks/pages/workspace/new-3f88a1c7e86a3f86.js +1 -0
- sky/dashboard/out/_next/static/chunks/pages/workspaces/[name]-f72f73bcef9541dc.js +1 -0
- sky/dashboard/out/_next/static/chunks/pages/workspaces-8f67be60165724cc.js +1 -0
- sky/dashboard/out/_next/static/chunks/webpack-339efec49c0cc7d0.js +1 -0
- sky/dashboard/out/_next/static/css/4614e06482d7309e.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/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/pools/[pool].html +1 -0
- sky/dashboard/out/jobs.html +1 -1
- sky/dashboard/out/users.html +1 -1
- sky/dashboard/out/volumes.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/execution.py +6 -4
- sky/global_user_state.py +22 -3
- sky/jobs/__init__.py +2 -0
- sky/jobs/client/sdk.py +67 -19
- sky/jobs/controller.py +2 -1
- sky/jobs/server/core.py +48 -1
- sky/jobs/server/server.py +52 -3
- sky/jobs/state.py +5 -1
- sky/schemas/db/global_user_state/002_add_workspace_to_cluster_history.py +35 -0
- sky/schemas/db/global_user_state/003_fix_initial_revision.py +61 -0
- sky/schemas/db/global_user_state/004_is_managed.py +34 -0
- sky/schemas/db/serve_state/001_initial_schema.py +67 -0
- sky/schemas/db/spot_jobs/003_pool_hash.py +34 -0
- sky/serve/client/impl.py +93 -6
- sky/serve/client/sdk.py +22 -53
- sky/serve/constants.py +2 -1
- sky/serve/controller.py +4 -2
- sky/serve/serve_state.py +444 -324
- sky/serve/serve_utils.py +77 -46
- sky/serve/server/core.py +13 -197
- sky/serve/server/impl.py +239 -2
- sky/serve/service.py +8 -3
- sky/server/common.py +18 -7
- sky/server/constants.py +1 -1
- sky/server/requests/executor.py +5 -3
- sky/server/requests/payloads.py +19 -0
- sky/setup_files/alembic.ini +4 -0
- sky/task.py +18 -11
- sky/templates/kubernetes-ray.yml.j2 +5 -0
- sky/templates/sky-serve-controller.yaml.j2 +1 -0
- sky/usage/usage_lib.py +8 -6
- sky/utils/annotations.py +8 -3
- sky/utils/cli_utils/status_utils.py +1 -1
- sky/utils/common_utils.py +11 -1
- sky/utils/db/db_utils.py +31 -0
- sky/utils/db/migration_utils.py +6 -2
- sky/utils/kubernetes/deploy_remote_cluster.py +3 -1
- sky/utils/resource_checker.py +162 -21
- sky/volumes/client/sdk.py +4 -4
- sky/workspaces/core.py +210 -6
- {skypilot_nightly-1.0.0.dev20250806.dist-info → skypilot_nightly-1.0.0.dev20250808.dist-info}/METADATA +19 -14
- {skypilot_nightly-1.0.0.dev20250806.dist-info → skypilot_nightly-1.0.0.dev20250808.dist-info}/RECORD +109 -103
- sky/client/sdk.pyi +0 -301
- sky/dashboard/out/_next/static/Gelsd19kVxXcX7aQQGsGu/_buildManifest.js +0 -1
- sky/dashboard/out/_next/static/chunks/1043-75af48ca5d5aaf57.js +0 -1
- sky/dashboard/out/_next/static/chunks/1141-8678a9102cc5f67e.js +0 -11
- sky/dashboard/out/_next/static/chunks/1664-22b00e32c9ff96a4.js +0 -1
- sky/dashboard/out/_next/static/chunks/1871-ced1c14230cad6e1.js +0 -6
- sky/dashboard/out/_next/static/chunks/2003.f90b06bb1f914295.js +0 -1
- sky/dashboard/out/_next/static/chunks/2350.fab69e61bac57b23.js +0 -1
- sky/dashboard/out/_next/static/chunks/2622-951867535095b0eb.js +0 -1
- sky/dashboard/out/_next/static/chunks/3785.0a173cd4393f0fef.js +0 -1
- sky/dashboard/out/_next/static/chunks/4725.42f21f250f91f65b.js +0 -1
- sky/dashboard/out/_next/static/chunks/4869.18e6a4361a380763.js +0 -16
- sky/dashboard/out/_next/static/chunks/5230-f3bb2663e442e86c.js +0 -1
- sky/dashboard/out/_next/static/chunks/6601-2109d22e7861861c.js +0 -1
- sky/dashboard/out/_next/static/chunks/6990-08b2a1cae076a943.js +0 -1
- sky/dashboard/out/_next/static/chunks/8969-9a8cca241b30db83.js +0 -1
- sky/dashboard/out/_next/static/chunks/9025.99f29acb7617963e.js +0 -6
- sky/dashboard/out/_next/static/chunks/938-bda2685db5eae6cf.js +0 -1
- sky/dashboard/out/_next/static/chunks/pages/clusters/[cluster]/[job]-7cb24da04ca00956.js +0 -11
- sky/dashboard/out/_next/static/chunks/pages/clusters/[cluster]-1e95993124dbfc57.js +0 -1
- sky/dashboard/out/_next/static/chunks/pages/config-d56e64f30db7b42e.js +0 -1
- sky/dashboard/out/_next/static/chunks/pages/jobs/[job]-90693cb88b5599a7.js +0 -11
- sky/dashboard/out/_next/static/chunks/pages/jobs-ab318e52eb4424a7.js +0 -1
- sky/dashboard/out/_next/static/chunks/pages/workspace/new-92f741084a89e27b.js +0 -1
- sky/dashboard/out/_next/static/chunks/pages/workspaces/[name]-35e0de5bca55e594.js +0 -1
- sky/dashboard/out/_next/static/chunks/pages/workspaces-062525fb5462acb6.js +0 -1
- sky/dashboard/out/_next/static/chunks/webpack-387626669badf82e.js +0 -1
- sky/dashboard/out/_next/static/css/b3227360726f12eb.css +0 -3
- /sky/dashboard/out/_next/static/{Gelsd19kVxXcX7aQQGsGu → -DXZksWqf2waNHeU9YTQe}/_ssgManifest.js +0 -0
- /sky/dashboard/out/_next/static/chunks/{6135-2d7ed3350659d073.js → 6135-85426374db04811e.js} +0 -0
- {skypilot_nightly-1.0.0.dev20250806.dist-info → skypilot_nightly-1.0.0.dev20250808.dist-info}/WHEEL +0 -0
- {skypilot_nightly-1.0.0.dev20250806.dist-info → skypilot_nightly-1.0.0.dev20250808.dist-info}/entry_points.txt +0 -0
- {skypilot_nightly-1.0.0.dev20250806.dist-info → skypilot_nightly-1.0.0.dev20250808.dist-info}/licenses/LICENSE +0 -0
- {skypilot_nightly-1.0.0.dev20250806.dist-info → skypilot_nightly-1.0.0.dev20250808.dist-info}/top_level.txt +0 -0
sky/client/sdk.py
CHANGED
|
@@ -16,7 +16,7 @@ import logging
|
|
|
16
16
|
import os
|
|
17
17
|
import subprocess
|
|
18
18
|
import typing
|
|
19
|
-
from typing import Any, Dict, List, Optional, Tuple, Union
|
|
19
|
+
from typing import Any, Dict, List, Optional, Tuple, TypeVar, Union
|
|
20
20
|
from urllib import parse as urlparse
|
|
21
21
|
|
|
22
22
|
import click
|
|
@@ -66,6 +66,10 @@ if typing.TYPE_CHECKING:
|
|
|
66
66
|
|
|
67
67
|
import sky
|
|
68
68
|
from sky import backends
|
|
69
|
+
from sky import models
|
|
70
|
+
import sky.catalog
|
|
71
|
+
from sky.provision.kubernetes import utils as kubernetes_utils
|
|
72
|
+
from sky.skylet import job_lib
|
|
69
73
|
else:
|
|
70
74
|
# only used in api_login()
|
|
71
75
|
base64 = adaptors_common.LazyImport('base64')
|
|
@@ -82,16 +86,34 @@ logging.getLogger('httpx').setLevel(logging.CRITICAL)
|
|
|
82
86
|
|
|
83
87
|
_LINE_PROCESSED_KEY = 'line_processed'
|
|
84
88
|
|
|
89
|
+
T = TypeVar('T')
|
|
90
|
+
|
|
85
91
|
|
|
86
92
|
def reload_config() -> None:
|
|
87
93
|
"""Reloads the client-side config."""
|
|
88
94
|
skypilot_config.safe_reload_config()
|
|
89
95
|
|
|
90
96
|
|
|
91
|
-
|
|
97
|
+
@typing.overload
|
|
98
|
+
def stream_response(request_id: None,
|
|
99
|
+
response: 'requests.Response',
|
|
100
|
+
output_stream: Optional['io.TextIOBase'] = None,
|
|
101
|
+
resumable: bool = False) -> None:
|
|
102
|
+
...
|
|
103
|
+
|
|
104
|
+
|
|
105
|
+
@typing.overload
|
|
106
|
+
def stream_response(request_id: server_common.RequestId[T],
|
|
107
|
+
response: 'requests.Response',
|
|
108
|
+
output_stream: Optional['io.TextIOBase'] = None,
|
|
109
|
+
resumable: bool = False) -> T:
|
|
110
|
+
...
|
|
111
|
+
|
|
112
|
+
|
|
113
|
+
def stream_response(request_id: Optional[server_common.RequestId[T]],
|
|
92
114
|
response: 'requests.Response',
|
|
93
115
|
output_stream: Optional['io.TextIOBase'] = None,
|
|
94
|
-
resumable: bool = False) ->
|
|
116
|
+
resumable: bool = False) -> Optional[T]:
|
|
95
117
|
"""Streams the response to the console.
|
|
96
118
|
|
|
97
119
|
Args:
|
|
@@ -121,6 +143,8 @@ def stream_response(request_id: Optional[server_common.RequestId],
|
|
|
121
143
|
retry_context.line_processed = line_count
|
|
122
144
|
if request_id is not None:
|
|
123
145
|
return get(request_id)
|
|
146
|
+
else:
|
|
147
|
+
return None
|
|
124
148
|
except Exception: # pylint: disable=broad-except
|
|
125
149
|
logger.debug(f'To stream request logs: sky api logs {request_id}')
|
|
126
150
|
raise
|
|
@@ -129,9 +153,11 @@ def stream_response(request_id: Optional[server_common.RequestId],
|
|
|
129
153
|
@usage_lib.entrypoint
|
|
130
154
|
@server_common.check_server_healthy_or_start
|
|
131
155
|
@annotations.client_api
|
|
132
|
-
def check(
|
|
133
|
-
|
|
134
|
-
|
|
156
|
+
def check(
|
|
157
|
+
infra_list: Optional[Tuple[str, ...]],
|
|
158
|
+
verbose: bool,
|
|
159
|
+
workspace: Optional[str] = None
|
|
160
|
+
) -> server_common.RequestId[Dict[str, List[str]]]:
|
|
135
161
|
"""Checks the credentials to enable clouds.
|
|
136
162
|
|
|
137
163
|
Args:
|
|
@@ -174,7 +200,7 @@ def check(infra_list: Optional[Tuple[str, ...]],
|
|
|
174
200
|
@server_common.check_server_healthy_or_start
|
|
175
201
|
@annotations.client_api
|
|
176
202
|
def enabled_clouds(workspace: Optional[str] = None,
|
|
177
|
-
expand: bool = False) -> server_common.RequestId:
|
|
203
|
+
expand: bool = False) -> server_common.RequestId[List[str]]:
|
|
178
204
|
"""Gets the enabled clouds.
|
|
179
205
|
|
|
180
206
|
Args:
|
|
@@ -198,14 +224,17 @@ def enabled_clouds(workspace: Optional[str] = None,
|
|
|
198
224
|
@usage_lib.entrypoint
|
|
199
225
|
@server_common.check_server_healthy_or_start
|
|
200
226
|
@annotations.client_api
|
|
201
|
-
def list_accelerators(
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
227
|
+
def list_accelerators(
|
|
228
|
+
gpus_only: bool = True,
|
|
229
|
+
name_filter: Optional[str] = None,
|
|
230
|
+
region_filter: Optional[str] = None,
|
|
231
|
+
quantity_filter: Optional[int] = None,
|
|
232
|
+
clouds: Optional[Union[List[str], str]] = None,
|
|
233
|
+
all_regions: bool = False,
|
|
234
|
+
require_price: bool = True,
|
|
235
|
+
case_sensitive: bool = True
|
|
236
|
+
) -> server_common.RequestId[Dict[str,
|
|
237
|
+
List['sky.catalog.common.InstanceTypeInfo']]]:
|
|
209
238
|
"""Lists the names of all accelerators offered by Sky.
|
|
210
239
|
|
|
211
240
|
This will include all accelerators offered by Sky, including those
|
|
@@ -248,12 +277,12 @@ def list_accelerators(gpus_only: bool = True,
|
|
|
248
277
|
@server_common.check_server_healthy_or_start
|
|
249
278
|
@annotations.client_api
|
|
250
279
|
def list_accelerator_counts(
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
280
|
+
gpus_only: bool = True,
|
|
281
|
+
name_filter: Optional[str] = None,
|
|
282
|
+
region_filter: Optional[str] = None,
|
|
283
|
+
quantity_filter: Optional[int] = None,
|
|
284
|
+
clouds: Optional[Union[List[str], str]] = None
|
|
285
|
+
) -> server_common.RequestId[Dict[str, List[float]]]:
|
|
257
286
|
"""Lists all accelerators offered by Sky and available counts.
|
|
258
287
|
|
|
259
288
|
Args:
|
|
@@ -292,7 +321,7 @@ def optimize(
|
|
|
292
321
|
dag: 'sky.Dag',
|
|
293
322
|
minimize: common.OptimizeTarget = common.OptimizeTarget.COST,
|
|
294
323
|
admin_policy_request_options: Optional[admin_policy.RequestOptions] = None
|
|
295
|
-
) -> server_common.RequestId:
|
|
324
|
+
) -> server_common.RequestId['sky.Dag']:
|
|
296
325
|
"""Finds the best execution plan for the given DAG.
|
|
297
326
|
|
|
298
327
|
Args:
|
|
@@ -323,7 +352,7 @@ def optimize(
|
|
|
323
352
|
return server_common.get_request_id(response)
|
|
324
353
|
|
|
325
354
|
|
|
326
|
-
def workspaces() -> server_common.RequestId:
|
|
355
|
+
def workspaces() -> server_common.RequestId[Dict[str, Any]]:
|
|
327
356
|
"""Gets the workspaces."""
|
|
328
357
|
response = server_common.make_authenticated_request('GET', '/workspaces')
|
|
329
358
|
return server_common.get_request_id(response)
|
|
@@ -352,7 +381,14 @@ def validate(
|
|
|
352
381
|
validation. This is only required when a admin policy is in use,
|
|
353
382
|
see: https://docs.skypilot.co/en/latest/cloud-setup/policy.html
|
|
354
383
|
"""
|
|
384
|
+
remote_api_version = versions.get_remote_api_version()
|
|
385
|
+
# TODO(kevin): remove this in v0.13.0
|
|
386
|
+
omit_user_specified_yaml = (remote_api_version is None or
|
|
387
|
+
remote_api_version < 15)
|
|
355
388
|
for task in dag.tasks:
|
|
389
|
+
if omit_user_specified_yaml:
|
|
390
|
+
# pylint: disable=protected-access
|
|
391
|
+
task._user_specified_yaml = None
|
|
356
392
|
task.expand_and_validate_workdir()
|
|
357
393
|
if not workdir_only:
|
|
358
394
|
task.expand_and_validate_file_mounts()
|
|
@@ -402,7 +438,8 @@ def launch(
|
|
|
402
438
|
_is_launched_by_jobs_controller: bool = False,
|
|
403
439
|
_is_launched_by_sky_serve_controller: bool = False,
|
|
404
440
|
_disable_controller_check: bool = False,
|
|
405
|
-
) -> server_common.RequestId
|
|
441
|
+
) -> server_common.RequestId[Tuple[Optional[int],
|
|
442
|
+
Optional['backends.ResourceHandle']]]:
|
|
406
443
|
"""Launches a cluster or task.
|
|
407
444
|
|
|
408
445
|
The task's setup and run commands are executed under the task's workdir
|
|
@@ -584,7 +621,8 @@ def _launch(
|
|
|
584
621
|
_is_launched_by_jobs_controller: bool = False,
|
|
585
622
|
_is_launched_by_sky_serve_controller: bool = False,
|
|
586
623
|
_disable_controller_check: bool = False,
|
|
587
|
-
) -> server_common.RequestId
|
|
624
|
+
) -> server_common.RequestId[Tuple[Optional[int],
|
|
625
|
+
Optional['backends.ResourceHandle']]]:
|
|
588
626
|
"""Auxiliary function for launch(), refer to launch() for details."""
|
|
589
627
|
|
|
590
628
|
validate(dag, admin_policy_request_options=request_options)
|
|
@@ -597,8 +635,8 @@ def _launch(
|
|
|
597
635
|
if _need_confirmation:
|
|
598
636
|
cluster_status = None
|
|
599
637
|
# TODO(SKY-998): we should reduce RTTs before launching the cluster.
|
|
600
|
-
|
|
601
|
-
clusters = get(
|
|
638
|
+
status_request_id = status([cluster_name], all_users=True)
|
|
639
|
+
clusters = get(status_request_id)
|
|
602
640
|
cluster_user_hash = common_utils.get_user_hash()
|
|
603
641
|
cluster_user_hash_str = ''
|
|
604
642
|
current_user = common_utils.get_current_user_name()
|
|
@@ -606,9 +644,9 @@ def _launch(
|
|
|
606
644
|
if not clusters:
|
|
607
645
|
# Show the optimize log before the prompt if the cluster does not
|
|
608
646
|
# exist.
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
stream_and_get(
|
|
647
|
+
optimize_request_id = optimize(
|
|
648
|
+
dag, admin_policy_request_options=request_options)
|
|
649
|
+
stream_and_get(optimize_request_id)
|
|
612
650
|
else:
|
|
613
651
|
cluster_record = clusters[0]
|
|
614
652
|
cluster_status = cluster_record['status']
|
|
@@ -683,7 +721,8 @@ def exec( # pylint: disable=redefined-builtin
|
|
|
683
721
|
dryrun: bool = False,
|
|
684
722
|
down: bool = False, # pylint: disable=redefined-outer-name
|
|
685
723
|
backend: Optional['backends.Backend'] = None,
|
|
686
|
-
) -> server_common.RequestId
|
|
724
|
+
) -> server_common.RequestId[Tuple[Optional[int],
|
|
725
|
+
Optional['backends.ResourceHandle']]]:
|
|
687
726
|
"""Executes a task on an existing cluster.
|
|
688
727
|
|
|
689
728
|
This function performs two actions:
|
|
@@ -805,7 +844,8 @@ def tail_logs(cluster_name: str,
|
|
|
805
844
|
stream=True,
|
|
806
845
|
timeout=(client_common.API_SERVER_REQUEST_CONNECTION_TIMEOUT_SECONDS,
|
|
807
846
|
None))
|
|
808
|
-
request_id = server_common.get_request_id(
|
|
847
|
+
request_id: server_common.RequestId[int] = server_common.get_request_id(
|
|
848
|
+
response)
|
|
809
849
|
# Log request is idempotent when tail is 0, thus can resume previous
|
|
810
850
|
# streaming point on retry.
|
|
811
851
|
return stream_response(request_id=request_id,
|
|
@@ -847,8 +887,9 @@ def download_logs(cluster_name: str,
|
|
|
847
887
|
)
|
|
848
888
|
response = server_common.make_authenticated_request(
|
|
849
889
|
'POST', '/download_logs', json=json.loads(body.model_dump_json()))
|
|
850
|
-
|
|
851
|
-
server_common.get_request_id(response)
|
|
890
|
+
request_id: server_common.RequestId[Dict[
|
|
891
|
+
str, str]] = server_common.get_request_id(response)
|
|
892
|
+
job_id_remote_path_dict = stream_and_get(request_id)
|
|
852
893
|
remote2local_path_dict = client_common.download_logs_from_api_server(
|
|
853
894
|
job_id_remote_path_dict.values())
|
|
854
895
|
return {
|
|
@@ -867,7 +908,7 @@ def start(
|
|
|
867
908
|
retry_until_up: bool = False,
|
|
868
909
|
down: bool = False, # pylint: disable=redefined-outer-name
|
|
869
910
|
force: bool = False,
|
|
870
|
-
) -> server_common.RequestId:
|
|
911
|
+
) -> server_common.RequestId['backends.CloudVmRayResourceHandle']:
|
|
871
912
|
"""Restart a cluster.
|
|
872
913
|
|
|
873
914
|
If a cluster is previously stopped (status is STOPPED) or failed in
|
|
@@ -949,7 +990,8 @@ def start(
|
|
|
949
990
|
@usage_lib.entrypoint
|
|
950
991
|
@server_common.check_server_healthy_or_start
|
|
951
992
|
@annotations.client_api
|
|
952
|
-
def down(cluster_name: str,
|
|
993
|
+
def down(cluster_name: str,
|
|
994
|
+
purge: bool = False) -> server_common.RequestId[None]:
|
|
953
995
|
"""Tears down a cluster.
|
|
954
996
|
|
|
955
997
|
Tearing down a cluster will delete all associated resources (all billing
|
|
@@ -991,7 +1033,8 @@ def down(cluster_name: str, purge: bool = False) -> server_common.RequestId:
|
|
|
991
1033
|
@usage_lib.entrypoint
|
|
992
1034
|
@server_common.check_server_healthy_or_start
|
|
993
1035
|
@annotations.client_api
|
|
994
|
-
def stop(cluster_name: str,
|
|
1036
|
+
def stop(cluster_name: str,
|
|
1037
|
+
purge: bool = False) -> server_common.RequestId[None]:
|
|
995
1038
|
"""Stops a cluster.
|
|
996
1039
|
|
|
997
1040
|
Data on attached disks is not lost when a cluster is stopped. Billing for
|
|
@@ -1041,7 +1084,7 @@ def autostop(
|
|
|
1041
1084
|
idle_minutes: int,
|
|
1042
1085
|
wait_for: Optional[autostop_lib.AutostopWaitFor] = None,
|
|
1043
1086
|
down: bool = False, # pylint: disable=redefined-outer-name
|
|
1044
|
-
) -> server_common.RequestId:
|
|
1087
|
+
) -> server_common.RequestId[None]:
|
|
1045
1088
|
"""Schedules an autostop/autodown for a cluster.
|
|
1046
1089
|
|
|
1047
1090
|
Autostop/autodown will automatically stop or teardown a cluster when it
|
|
@@ -1121,7 +1164,7 @@ def autostop(
|
|
|
1121
1164
|
@annotations.client_api
|
|
1122
1165
|
def queue(cluster_name: str,
|
|
1123
1166
|
skip_finished: bool = False,
|
|
1124
|
-
all_users: bool = False) -> server_common.RequestId:
|
|
1167
|
+
all_users: bool = False) -> server_common.RequestId[List[dict]]:
|
|
1125
1168
|
"""Gets the job queue of a cluster.
|
|
1126
1169
|
|
|
1127
1170
|
Args:
|
|
@@ -1178,8 +1221,11 @@ def queue(cluster_name: str,
|
|
|
1178
1221
|
@usage_lib.entrypoint
|
|
1179
1222
|
@server_common.check_server_healthy_or_start
|
|
1180
1223
|
@annotations.client_api
|
|
1181
|
-
def job_status(
|
|
1182
|
-
|
|
1224
|
+
def job_status(
|
|
1225
|
+
cluster_name: str,
|
|
1226
|
+
job_ids: Optional[List[int]] = None
|
|
1227
|
+
) -> server_common.RequestId[Dict[Optional[int],
|
|
1228
|
+
Optional['job_lib.JobStatus']]]:
|
|
1183
1229
|
"""Gets the status of jobs on a cluster.
|
|
1184
1230
|
|
|
1185
1231
|
Args:
|
|
@@ -1226,7 +1272,7 @@ def cancel(
|
|
|
1226
1272
|
job_ids: Optional[List[int]] = None,
|
|
1227
1273
|
# pylint: disable=invalid-name
|
|
1228
1274
|
_try_cancel_if_cluster_is_init: bool = False
|
|
1229
|
-
) -> server_common.RequestId:
|
|
1275
|
+
) -> server_common.RequestId[None]:
|
|
1230
1276
|
"""Cancels jobs on a cluster.
|
|
1231
1277
|
|
|
1232
1278
|
Args:
|
|
@@ -1276,7 +1322,7 @@ def status(
|
|
|
1276
1322
|
cluster_names: Optional[List[str]] = None,
|
|
1277
1323
|
refresh: common.StatusRefreshMode = common.StatusRefreshMode.NONE,
|
|
1278
1324
|
all_users: bool = False,
|
|
1279
|
-
) -> server_common.RequestId:
|
|
1325
|
+
) -> server_common.RequestId[List[Dict[str, Any]]]:
|
|
1280
1326
|
"""Gets cluster statuses.
|
|
1281
1327
|
|
|
1282
1328
|
If cluster_names is given, return those clusters. Otherwise, return all
|
|
@@ -1368,8 +1414,9 @@ def status(
|
|
|
1368
1414
|
@server_common.check_server_healthy_or_start
|
|
1369
1415
|
@annotations.client_api
|
|
1370
1416
|
def endpoints(
|
|
1371
|
-
|
|
1372
|
-
|
|
1417
|
+
cluster: str,
|
|
1418
|
+
port: Optional[Union[int, str]] = None
|
|
1419
|
+
) -> server_common.RequestId[Dict[str, str]]:
|
|
1373
1420
|
"""Gets the endpoint for a given cluster and port number (endpoint).
|
|
1374
1421
|
|
|
1375
1422
|
Args:
|
|
@@ -1401,7 +1448,9 @@ def endpoints(
|
|
|
1401
1448
|
@usage_lib.entrypoint
|
|
1402
1449
|
@server_common.check_server_healthy_or_start
|
|
1403
1450
|
@annotations.client_api
|
|
1404
|
-
def cost_report(
|
|
1451
|
+
def cost_report(
|
|
1452
|
+
days: Optional[int] = None
|
|
1453
|
+
) -> server_common.RequestId[List[Dict[str, Any]]]: # pylint: disable=redefined-builtin
|
|
1405
1454
|
"""Gets all cluster cost reports, including those that have been downed.
|
|
1406
1455
|
|
|
1407
1456
|
The estimated cost column indicates price for the cluster based on the type
|
|
@@ -1446,7 +1495,7 @@ def cost_report(days: Optional[int] = None) -> server_common.RequestId: # pylin
|
|
|
1446
1495
|
@usage_lib.entrypoint
|
|
1447
1496
|
@server_common.check_server_healthy_or_start
|
|
1448
1497
|
@annotations.client_api
|
|
1449
|
-
def storage_ls() -> server_common.RequestId:
|
|
1498
|
+
def storage_ls() -> server_common.RequestId[List[Dict[str, Any]]]:
|
|
1450
1499
|
"""Gets the storages.
|
|
1451
1500
|
|
|
1452
1501
|
Returns:
|
|
@@ -1474,7 +1523,7 @@ def storage_ls() -> server_common.RequestId:
|
|
|
1474
1523
|
@usage_lib.entrypoint
|
|
1475
1524
|
@server_common.check_server_healthy_or_start
|
|
1476
1525
|
@annotations.client_api
|
|
1477
|
-
def storage_delete(name: str) -> server_common.RequestId:
|
|
1526
|
+
def storage_delete(name: str) -> server_common.RequestId[None]:
|
|
1478
1527
|
"""Deletes a storage.
|
|
1479
1528
|
|
|
1480
1529
|
Args:
|
|
@@ -1507,7 +1556,7 @@ def local_up(gpus: bool,
|
|
|
1507
1556
|
ssh_key: Optional[str],
|
|
1508
1557
|
cleanup: bool,
|
|
1509
1558
|
context_name: Optional[str] = None,
|
|
1510
|
-
password: Optional[str] = None) -> server_common.RequestId:
|
|
1559
|
+
password: Optional[str] = None) -> server_common.RequestId[None]:
|
|
1511
1560
|
"""Launches a Kubernetes cluster on local machines.
|
|
1512
1561
|
|
|
1513
1562
|
Returns:
|
|
@@ -1536,7 +1585,7 @@ def local_up(gpus: bool,
|
|
|
1536
1585
|
@usage_lib.entrypoint
|
|
1537
1586
|
@server_common.check_server_healthy_or_start
|
|
1538
1587
|
@annotations.client_api
|
|
1539
|
-
def local_down() -> server_common.RequestId:
|
|
1588
|
+
def local_down() -> server_common.RequestId[None]:
|
|
1540
1589
|
"""Tears down the Kubernetes cluster started by local_up."""
|
|
1541
1590
|
# We do not allow local up when the API server is running remotely since it
|
|
1542
1591
|
# will modify the kubeconfig.
|
|
@@ -1611,7 +1660,7 @@ def _upload_ssh_key_and_wait(key_name: str, key_file_path: str) -> str:
|
|
|
1611
1660
|
@server_common.check_server_healthy_or_start
|
|
1612
1661
|
@annotations.client_api
|
|
1613
1662
|
def ssh_up(infra: Optional[str] = None,
|
|
1614
|
-
file: Optional[str] = None) -> server_common.RequestId:
|
|
1663
|
+
file: Optional[str] = None) -> server_common.RequestId[None]:
|
|
1615
1664
|
"""Deploys the SSH Node Pools defined in ~/.sky/ssh_targets.yaml.
|
|
1616
1665
|
|
|
1617
1666
|
Args:
|
|
@@ -1644,7 +1693,7 @@ def ssh_up(infra: Optional[str] = None,
|
|
|
1644
1693
|
@usage_lib.entrypoint
|
|
1645
1694
|
@server_common.check_server_healthy_or_start
|
|
1646
1695
|
@annotations.client_api
|
|
1647
|
-
def ssh_down(infra: Optional[str] = None) -> server_common.RequestId:
|
|
1696
|
+
def ssh_down(infra: Optional[str] = None) -> server_common.RequestId[None]:
|
|
1648
1697
|
"""Tears down a Kubernetes cluster on SSH targets.
|
|
1649
1698
|
|
|
1650
1699
|
Args:
|
|
@@ -1673,10 +1722,12 @@ def ssh_down(infra: Optional[str] = None) -> server_common.RequestId:
|
|
|
1673
1722
|
@server_common.check_server_healthy_or_start
|
|
1674
1723
|
@annotations.client_api
|
|
1675
1724
|
def realtime_kubernetes_gpu_availability(
|
|
1676
|
-
|
|
1677
|
-
|
|
1678
|
-
|
|
1679
|
-
|
|
1725
|
+
context: Optional[str] = None,
|
|
1726
|
+
name_filter: Optional[str] = None,
|
|
1727
|
+
quantity_filter: Optional[int] = None,
|
|
1728
|
+
is_ssh: Optional[bool] = None
|
|
1729
|
+
) -> server_common.RequestId[List[Tuple[
|
|
1730
|
+
str, List['models.RealtimeGpuAvailability']]]]:
|
|
1680
1731
|
"""Gets the real-time Kubernetes GPU availability.
|
|
1681
1732
|
|
|
1682
1733
|
Returns:
|
|
@@ -1699,7 +1750,8 @@ def realtime_kubernetes_gpu_availability(
|
|
|
1699
1750
|
@server_common.check_server_healthy_or_start
|
|
1700
1751
|
@annotations.client_api
|
|
1701
1752
|
def kubernetes_node_info(
|
|
1702
|
-
|
|
1753
|
+
context: Optional[str] = None
|
|
1754
|
+
) -> server_common.RequestId['models.KubernetesNodesInfo']:
|
|
1703
1755
|
"""Gets the resource information for all the nodes in the cluster.
|
|
1704
1756
|
|
|
1705
1757
|
Currently only GPU resources are supported. The function returns the total
|
|
@@ -1730,7 +1782,10 @@ def kubernetes_node_info(
|
|
|
1730
1782
|
@usage_lib.entrypoint
|
|
1731
1783
|
@server_common.check_server_healthy_or_start
|
|
1732
1784
|
@annotations.client_api
|
|
1733
|
-
def status_kubernetes() -> server_common.RequestId
|
|
1785
|
+
def status_kubernetes() -> server_common.RequestId[Tuple[
|
|
1786
|
+
List['kubernetes_utils.KubernetesSkyPilotClusterInfoPayload'],
|
|
1787
|
+
List['kubernetes_utils.KubernetesSkyPilotClusterInfoPayload'], List[Dict[
|
|
1788
|
+
str, Any]], Optional[str]]]:
|
|
1734
1789
|
"""Gets all SkyPilot clusters and jobs in the Kubernetes cluster.
|
|
1735
1790
|
|
|
1736
1791
|
Managed jobs and services are also included in the clusters returned.
|
|
@@ -1759,7 +1814,7 @@ def status_kubernetes() -> server_common.RequestId:
|
|
|
1759
1814
|
# === API request APIs ===
|
|
1760
1815
|
@usage_lib.entrypoint
|
|
1761
1816
|
@annotations.client_api
|
|
1762
|
-
def get(request_id: server_common.RequestId) ->
|
|
1817
|
+
def get(request_id: server_common.RequestId[T]) -> T:
|
|
1763
1818
|
"""Waits for and gets the result of a request.
|
|
1764
1819
|
|
|
1765
1820
|
This function will not check the server health since /api/get is typically
|
|
@@ -1818,16 +1873,34 @@ def get(request_id: server_common.RequestId) -> Any:
|
|
|
1818
1873
|
return request_task.get_return_value()
|
|
1819
1874
|
|
|
1820
1875
|
|
|
1876
|
+
@typing.overload
|
|
1877
|
+
def stream_and_get(request_id: server_common.RequestId[T],
|
|
1878
|
+
log_path: Optional[str] = None,
|
|
1879
|
+
tail: Optional[int] = None,
|
|
1880
|
+
follow: bool = True,
|
|
1881
|
+
output_stream: Optional['io.TextIOBase'] = None) -> T:
|
|
1882
|
+
...
|
|
1883
|
+
|
|
1884
|
+
|
|
1885
|
+
@typing.overload
|
|
1886
|
+
def stream_and_get(request_id: None = None,
|
|
1887
|
+
log_path: Optional[str] = None,
|
|
1888
|
+
tail: Optional[int] = None,
|
|
1889
|
+
follow: bool = True,
|
|
1890
|
+
output_stream: Optional['io.TextIOBase'] = None) -> None:
|
|
1891
|
+
...
|
|
1892
|
+
|
|
1893
|
+
|
|
1821
1894
|
@usage_lib.entrypoint
|
|
1822
1895
|
@server_common.check_server_healthy_or_start
|
|
1823
1896
|
@annotations.client_api
|
|
1824
1897
|
def stream_and_get(
|
|
1825
|
-
request_id: Optional[server_common.RequestId] = None,
|
|
1898
|
+
request_id: Optional[server_common.RequestId[T]] = None,
|
|
1826
1899
|
log_path: Optional[str] = None,
|
|
1827
1900
|
tail: Optional[int] = None,
|
|
1828
1901
|
follow: bool = True,
|
|
1829
1902
|
output_stream: Optional['io.TextIOBase'] = None,
|
|
1830
|
-
) ->
|
|
1903
|
+
) -> Optional[T]:
|
|
1831
1904
|
"""Streams the logs of a request or a log file and gets the final result.
|
|
1832
1905
|
|
|
1833
1906
|
This will block until the request is finished. The request id can be a
|
|
@@ -1874,16 +1947,21 @@ def stream_and_get(
|
|
|
1874
1947
|
with ux_utils.print_exception_no_traceback():
|
|
1875
1948
|
raise RuntimeError(f'Failed to stream logs: {detail}')
|
|
1876
1949
|
elif response.status_code != 200:
|
|
1950
|
+
# TODO(syang): handle the case where the requestID is not provided
|
|
1951
|
+
# see https://github.com/skypilot-org/skypilot/issues/6549
|
|
1952
|
+
if request_id is None:
|
|
1953
|
+
return None
|
|
1877
1954
|
return get(request_id)
|
|
1878
1955
|
return stream_response(request_id, response, output_stream)
|
|
1879
1956
|
|
|
1880
1957
|
|
|
1881
1958
|
@usage_lib.entrypoint
|
|
1882
1959
|
@annotations.client_api
|
|
1883
|
-
def api_cancel(request_ids: Optional[Union[
|
|
1884
|
-
|
|
1960
|
+
def api_cancel(request_ids: Optional[Union[server_common.RequestId[T],
|
|
1961
|
+
List[server_common.RequestId[T]],
|
|
1962
|
+
str, List[str]]] = None,
|
|
1885
1963
|
all_users: bool = False,
|
|
1886
|
-
silent: bool = False) -> server_common.RequestId:
|
|
1964
|
+
silent: bool = False) -> server_common.RequestId[List[str]]:
|
|
1887
1965
|
"""Aborts a request or all requests.
|
|
1888
1966
|
|
|
1889
1967
|
Args:
|
|
@@ -1946,7 +2024,7 @@ def _local_api_server_running(kill: bool = False) -> bool:
|
|
|
1946
2024
|
@usage_lib.entrypoint
|
|
1947
2025
|
@annotations.client_api
|
|
1948
2026
|
def api_status(
|
|
1949
|
-
request_ids: Optional[List[server_common.RequestId]] = None,
|
|
2027
|
+
request_ids: Optional[List[Union[server_common.RequestId[T], str]]] = None,
|
|
1950
2028
|
# pylint: disable=redefined-builtin
|
|
1951
2029
|
all_status: bool = False
|
|
1952
2030
|
) -> List[payloads.RequestPayload]:
|
|
@@ -2420,7 +2498,9 @@ def api_login(endpoint: Optional[str] = None,
|
|
|
2420
2498
|
_save_config_updates(endpoint=endpoint)
|
|
2421
2499
|
dashboard_url = server_common.get_dashboard_url(endpoint)
|
|
2422
2500
|
|
|
2423
|
-
|
|
2501
|
+
# see https://github.com/python/mypy/issues/5107 on why
|
|
2502
|
+
# typing is disabled on this line
|
|
2503
|
+
server_common.get_api_server_status.cache_clear() # type: ignore
|
|
2424
2504
|
# After successful authentication, check server health again to get user
|
|
2425
2505
|
# identity
|
|
2426
2506
|
server_status, final_api_server_info = server_common.check_server_healthy(
|
sky/client/sdk_async.py
CHANGED
|
@@ -244,6 +244,10 @@ async def stream_and_get(
|
|
|
244
244
|
with ux_utils.print_exception_no_traceback():
|
|
245
245
|
raise RuntimeError(f'Failed to stream logs: {detail}')
|
|
246
246
|
elif response.status != 200:
|
|
247
|
+
# TODO(syang): handle the case where the requestID is not
|
|
248
|
+
# provided. https://github.com/skypilot-org/skypilot/issues/6549
|
|
249
|
+
if request_id is None:
|
|
250
|
+
return None
|
|
247
251
|
return await get(request_id)
|
|
248
252
|
|
|
249
253
|
return await stream_response_async(request_id, response,
|
|
@@ -583,7 +587,7 @@ async def endpoints(
|
|
|
583
587
|
cluster: str,
|
|
584
588
|
port: Optional[Union[int, str]] = None,
|
|
585
589
|
stream_logs: Optional[StreamConfig] = DEFAULT_STREAM_CONFIG
|
|
586
|
-
) -> Dict[
|
|
590
|
+
) -> Dict[str, str]:
|
|
587
591
|
"""Async version of endpoints() that gets the endpoint for a given cluster
|
|
588
592
|
and port number."""
|
|
589
593
|
request_id = await context_utils.to_thread(sdk.endpoints, cluster, port)
|
sky/core.py
CHANGED
|
@@ -256,7 +256,7 @@ all_clusters, unmanaged_clusters, all_jobs, context
|
|
|
256
256
|
|
|
257
257
|
|
|
258
258
|
def endpoints(cluster: str,
|
|
259
|
-
port: Optional[Union[int, str]] = None) -> Dict[
|
|
259
|
+
port: Optional[Union[int, str]] = None) -> Dict[str, str]:
|
|
260
260
|
"""Gets the endpoint for a given cluster and port number (endpoint).
|
|
261
261
|
|
|
262
262
|
Args:
|
|
@@ -275,7 +275,10 @@ def endpoints(cluster: str,
|
|
|
275
275
|
with rich_utils.safe_status(
|
|
276
276
|
ux_utils.spinner_message(
|
|
277
277
|
f'Fetching endpoints for cluster {cluster}')):
|
|
278
|
-
|
|
278
|
+
result = backend_utils.get_endpoints(cluster=cluster, port=port)
|
|
279
|
+
# Convert int keys to str keys.
|
|
280
|
+
# In JSON serialization, each key must be a string.
|
|
281
|
+
return {str(k): v for k, v in result.items()}
|
|
279
282
|
|
|
280
283
|
|
|
281
284
|
@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"/><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/4614e06482d7309e.css" as="style"/><link rel="stylesheet" href="/dashboard/_next/static/css/4614e06482d7309e.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-339efec49c0cc7d0.js" defer=""></script><script src="/dashboard/_next/static/chunks/framework-cf60a09ccd051a10.js" defer=""></script><script src="/dashboard/_next/static/chunks/main-f15ccb73239a3bf1.js" defer=""></script><script src="/dashboard/_next/static/chunks/pages/_app-491a4d699d95e808.js" defer=""></script><script src="/dashboard/_next/static/chunks/pages/_error-c66a4e8afc46f17b.js" defer=""></script><script src="/dashboard/_next/static/-DXZksWqf2waNHeU9YTQe/_buildManifest.js" defer=""></script><script src="/dashboard/_next/static/-DXZksWqf2waNHeU9YTQe/_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":"-DXZksWqf2waNHeU9YTQe","assetPrefix":"/dashboard","nextExport":true,"isFallback":false,"gip":true,"scriptLoader":[]}</script></body></html>
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
self.__BUILD_MANIFEST=function(s,c,e,a,t,f,u,n,o,r,j,i,b,k,h){return{__rewrites:{afterFiles:[],beforeFiles:[],fallback:[]},"/":["static/chunks/pages/index-444f1804401f04ea.js"],"/_error":["static/chunks/pages/_error-c66a4e8afc46f17b.js"],"/clusters":["static/chunks/pages/clusters-b30460f683e6ba96.js"],"/clusters/[cluster]":[s,c,e,f,u,j,r,a,t,n,i,b,o,k,h,"static/chunks/1871-980a395e92633a5c.js","static/chunks/pages/clusters/[cluster]-155d477a6c3e04e2.js"],"/clusters/[cluster]/[job]":[s,c,e,f,a,t,o,"static/chunks/pages/clusters/[cluster]/[job]-ae17cec0fc6483d9.js"],"/config":["static/chunks/pages/config-dfb9bf07b13045f4.js"],"/infra":["static/chunks/pages/infra-fc9222e26c8e2f0d.js"],"/infra/[context]":["static/chunks/pages/infra/[context]-13d53fffc03ccb52.js"],"/jobs":["static/chunks/pages/jobs-cdc60fb5d371e16a.js"],"/jobs/pools/[pool]":[s,c,e,u,r,a,t,n,"static/chunks/pages/jobs/pools/[pool]-f5ccf5d39d87aebe.js"],"/jobs/[job]":[s,c,e,f,u,r,a,t,n,o,"static/chunks/pages/jobs/[job]-154f55cf8af55be5.js"],"/users":["static/chunks/pages/users-7ed36e44e779d5c7.js"],"/volumes":["static/chunks/pages/volumes-c9695d657f78b5dc.js"],"/workspace/new":["static/chunks/pages/workspace/new-3f88a1c7e86a3f86.js"],"/workspaces":["static/chunks/pages/workspaces-8f67be60165724cc.js"],"/workspaces/[name]":[s,c,e,f,u,j,a,t,n,i,b,o,k,h,"static/chunks/1141-a8a8f1adba34c892.js","static/chunks/pages/workspaces/[name]-f72f73bcef9541dc.js"],sortedPages:["/","/_app","/_error","/clusters","/clusters/[cluster]","/clusters/[cluster]/[job]","/config","/infra","/infra/[context]","/jobs","/jobs/pools/[pool]","/jobs/[job]","/users","/volumes","/workspace/new","/workspaces","/workspaces/[name]"]}}("static/chunks/616-3d59f75e2ccf9321.js","static/chunks/6130-2be46d70a38f1e82.js","static/chunks/5739-d67458fcb1386c92.js","static/chunks/6989-6129c1cfbcf51063.js","static/chunks/3850-ff4a9a69d978632b.js","static/chunks/7411-b15471acd2cba716.js","static/chunks/1272-1ef0bf0237faccdb.js","static/chunks/8969-c9686994ddafcf01.js","static/chunks/6135-85426374db04811e.js","static/chunks/6212-7bd06f60ba693125.js","static/chunks/1559-6c00e20454194859.js","static/chunks/6990-0f886f16e0d55ff8.js","static/chunks/8056-34d27f51e6d1c631.js","static/chunks/6601-06114c982db410b6.js","static/chunks/9159-11421c0f2909236f.js"),self.__BUILD_MANIFEST_CB&&self.__BUILD_MANIFEST_CB();
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
"use strict";(self.webpackChunk_N_E=self.webpackChunk_N_E||[]).push([[1141],{99333:function(e,s,r){r.d(s,{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,r(60998).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"}]])},98418:function(e,s,r){r.d(s,{Z:function(){return t}});/**
|
|
7
|
+
* @license lucide-react v0.407.0 - ISC
|
|
8
|
+
*
|
|
9
|
+
* This source code is licensed under the ISC license.
|
|
10
|
+
* See the LICENSE file in the root directory of this source tree.
|
|
11
|
+
*/let t=(0,r(60998).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"}]])},1812:function(e,s,r){r.d(s,{X:function(){return n}});var t=r(85893),a=r(67294);let l=e=>{if(!(null==e?void 0:e.message))return"An unexpected error occurred.";let s=e.message;return s.includes("failed:")&&(s=s.split("failed:")[1].trim()),s},n=e=>{let{error:s,title:r="Error",onDismiss:n}=e,[c,i]=(0,a.useState)(!1);if((0,a.useEffect)(()=>{s&&i(!1)},[s]),!s||c)return null;let o="string"==typeof s?s:l(s);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:[r,":"]})," ",o]})})]}),(0,t.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,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"})})})]})})}},69123:function(e,s,r){r.d(s,{g:function(){return n}});var t=r(85893),a=r(67294),l=r(32350);let n=a.forwardRef((e,s)=>{let{className:r,...a}=e;return(0,t.jsx)("textarea",{className:(0,l.cn)("flex min-h-[80px] w-full rounded-md border border-input bg-background px-3 py-2 text-sm ring-offset-background placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50",r),ref:s,...a})});n.displayName="Textarea"},11141:function(e,s,r){r.r(s),r.d(s,{WorkspaceEditor:function(){return R}});var t=r(85893),a=r(67294),l=r(11163),n=r(17324),c=r(23266),i=r(68969);r(6135);var o=r(41664),d=r.n(o),u=r(9008),x=r.n(u),m=r(37673),h=r(30803),f=r(69123),g=r(55739),p=r(70282),b=r(6021),j=r(13626),y=r(98418),N=r(99333),v=r(50326);let w=e=>{let{className:s="",variant:r="default",children:a,...l}=e;return(0,t.jsx)("div",{role:"alert",className:"".concat("relative w-full rounded-lg border p-4 flex items-start space-x-2"," ").concat({default:"bg-blue-50 border-blue-200 text-blue-800",destructive:"bg-red-50 border-red-200 text-red-800"}[r]," ").concat(s),...l,children:a})},k=e=>{let{className:s="",children:r,...a}=e;return(0,t.jsx)("div",{className:"text-sm leading-relaxed ".concat(s),...a,children:r})};var C=r(53850),L=r(1812),S=r(9159),E=r(1272),A=r(93225),W=r(53081);let Z=e=>{let{message:s}=e;return s?(0,t.jsxs)(w,{className:"border-green-200 bg-green-50",children:[(0,t.jsx)(p.Z,{className:"h-4 w-4 text-green-600"}),(0,t.jsx)(k,{className:"text-green-800",children:s})]}):null},D=e=>{let{workspaceName:s,config:r,enabledClouds:a=[]}=e;if(!r)return null;let l="default"===s,n=0===Object.keys(r).length;if(l&&n)return(0,t.jsx)("div",{className:"text-sm text-gray-500 mb-3 italic p-3 bg-sky-50 rounded border border-sky-200",children:"Workspace 'default' can use all accessible infrastructure."});let c=[],i=[],o=[],d=new Set(a.map(e=>e.toLowerCase()));Object.entries(r).forEach(e=>{let[s,r]=e;if("private"===s||"allowed_users"===s)return;let a=A.Z2[s.toLowerCase()]||s.toUpperCase(),l=null==a?void 0:a.toLowerCase(),n=d.has(l)||Array.from(d).some(e=>e.startsWith(l+"/")),u=()=>"kubernetes"===s.toLowerCase()?Array.from(d).filter(e=>e.startsWith(l+"/")).map(e=>e.split("/")[1]):[];if((null==r?void 0:r.disabled)===!0)i.push(a);else if(r&&Object.keys(r).length>0){let e="";if("gcp"===s.toLowerCase()&&r.project_id)e=" (Project ID: ".concat(r.project_id,")");else if("aws"===s.toLowerCase()&&r.region)e=" (Region: ".concat(r.region,")");else if("kubernetes"===s.toLowerCase()){let s=u();s.length>0&&(e=" (Contexts: ".concat(s.join(", "),")"))}n?c.push((0,t.jsxs)("span",{className:"block",children:[a,e," is enabled."]},"".concat(s,"-enabled"))):o.push((0,t.jsxs)("span",{className:"block text-amber-700",children:[a,e," is configured but not currently available."]},"".concat(s,"-configured-not-enabled")))}else if(n){let e="";if("kubernetes"===s.toLowerCase()){let s=u();s.length>0&&(e=" (Contexts: ".concat(s.join(", "),")"))}c.push((0,t.jsxs)("span",{className:"block",children:[a,e," is enabled (using default settings)."]},"".concat(s,"-default-enabled")))}else o.push((0,t.jsxs)("span",{className:"block text-amber-700",children:[a," is configured but not currently available."]},"".concat(s,"-default-not-enabled")))});let u=[];if(i.length>0){let e=i.join(" and ");u.push((0,t.jsxs)("span",{className:"block",children:[e," ",1===i.length?"is":"are"," explicitly disabled."]},"disabled-clouds"))}return(u.push(...c),u.push(...o),u.length>0)?(0,t.jsx)("div",{className:"text-sm text-gray-700 mb-3 p-3 bg-sky-50 rounded border border-sky-200",children:u}):!l&&n?(0,t.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},M=e=>{let{isPrivate:s}=e;return s?(0,t.jsx)("span",{className:"inline-flex items-center px-2 py-1 rounded-full text-xs font-medium bg-gray-100 text-gray-700 border border-gray-300",children:"Private"}):(0,t.jsx)("span",{className:"inline-flex items-center px-2 py-1 rounded-full text-xs font-medium bg-green-100 text-green-700 border border-green-300",children:"Public"})},P=e=>{let{workspaceConfig:s,allUsers:r}=e;if(!s.private)return null;let a=s.allowed_users||[],l=(r||[]).filter(e=>"admin"===e.role).map(e=>e.username),n=[...new Set([...a,...l])];return 0===n.length?(0,t.jsxs)("div",{className:"mt-4",children:[(0,t.jsx)("h4",{className:"mb-2 text-xs text-gray-500 tracking-wider",children:"Allowed Users (0)"}),(0,t.jsx)("div",{className:"text-amber-600 text-xs italic p-2 bg-amber-50 rounded border border-amber-200",children:"No users configured (workspace may be inaccessible)"})]}):(0,t.jsxs)("div",{className:"mt-4",children:[(0,t.jsxs)("h4",{className:"mb-2 text-xs text-gray-500 tracking-wider",children:["Allowed Users (",n.length,")"]}),(0,t.jsx)("div",{className:"space-y-1 max-h-48 overflow-y-auto border border-gray-200 rounded",children:n.map(e=>{let s=l.includes(e);return(0,t.jsxs)("div",{className:"flex items-center justify-between text-xs p-2 bg-gray-50 hover:bg-gray-100 border-b border-gray-100 last:border-b-0",children:[(0,t.jsx)("span",{className:"font-medium text-gray-700",children:e}),s?(0,t.jsxs)("span",{className:"inline-flex items-center text-blue-600",children:[(0,t.jsx)(C.r7,{className:"w-3 h-3 mr-1"}),"Admin"]}):(0,t.jsxs)("span",{className:"inline-flex items-center text-gray-600",children:[(0,t.jsx)(b.Z,{className:"w-3 h-3 mr-1"}),"User"]})]},e)})})]})};function R(e){let{workspaceName:s,isNewWorkspace:r=!1}=e,o=(0,l.useRouter)(),[u,p]=(0,a.useState)({}),[b,w]=(0,a.useState)({}),[k,A]=(0,a.useState)(""),[R,_]=(0,a.useState)(!0),[z,O]=(0,a.useState)(!1),[T,U]=(0,a.useState)(!1),[Y,J]=(0,a.useState)(null),[I,V]=(0,a.useState)(null),[F,H]=(0,a.useState)(null),[X,B]=(0,a.useState)([]),[G,q]=(0,a.useState)({showDialog:!1,deleting:!1,error:null}),[K,Q]=(0,a.useState)({totalClusterCount:0,runningClusterCount:0,managedJobsCount:0,clouds:[]}),[$,ee]=(0,a.useState)(!1),es=(0,a.useCallback)(async()=>{_(!0),J(null);try{let e;let[r,t]=await Promise.all([(0,n.fX)(),(0,W.R)()]),a=r[s]||{};p(a),w(a),B(t||[]),e=0===Object.keys(a).length?"".concat(s,":\n # Empty workspace configuration - uses all accessible infrastructure\n"):E.ZP.dump({[s]:a},{indent:2,lineWidth:-1,noRefs:!0,skipInvalid:!0,flowLevel:-1}),A(e)}catch(e){console.error("Error fetching workspace config:",e),J(e)}finally{_(!1)}},[s]),er=(0,a.useCallback)(async()=>{if(!r){ee(!0);try{let[e,r,t]=await Promise.all([(0,c.getClusters)(),(0,i.getManagedJobs)(),(0,n.yz)(s,!0)]),a=e.filter(e=>(e.workspace||"default")===s),l=a.filter(e=>"RUNNING"===e.status||"LAUNCHING"===e.status),o={};e.forEach(e=>{o[e.cluster]=e.workspace||"default"});let d=r.jobs||[],u=new Set(S.statusGroups.active),x=0;d.forEach(e=>{let r=e.cluster_name||e.resources&&e.resources.cluster_name;r&&o[r]===s&&u.has(e.status)&&x++}),Q({totalClusterCount:a.length,runningClusterCount:l.length,managedJobsCount:x,clouds:Array.isArray(t)?t:[]})}catch(e){console.error("Failed to fetch workspace stats:",e)}finally{ee(!1)}}},[s,r]);(0,a.useEffect)(()=>{r?(_(!1),A("".concat(s,":\n # New workspace configuration\n # Leave empty to use all accessible infrastructure\n"))):(es(),er())},[s,r,es,er]),(0,a.useEffect)(()=>{U(JSON.stringify(u)!==JSON.stringify(b))},[u,b]);let et=e=>{A(e),H(null);try{let r=E.ZP.load(e)||{},t=Object.keys(r);if(0===t.length)p({});else if(1===t.length){let e=t[0];if(e!==s){H('Workspace name cannot be changed. Expected "'.concat(s,'" but found "').concat(e,'".'));return}let a=r[s]||{};p(a)}else H("Configuration must contain only one workspace. Found: ".concat(t.join(", ")))}catch(e){H("Invalid YAML: ".concat(e.message))}},ea=async()=>{O(!0),J(null),V(null);try{if(F)throw Error("Please fix YAML errors before saving");let e=E.ZP.load(k)||{},t=Object.keys(e);if(t.length>0&&t[0]!==s)throw Error('Workspace name cannot be changed. Expected "'.concat(s,'".'));r?(await (0,n.MB)(s,u),V("Workspace created successfully!"),setTimeout(()=>{o.push("/workspaces/".concat(s))},1500)):(await (0,n.eA)(s,u),V("Workspace updated successfully!"),w(u),er())}catch(e){console.error("Error saving workspace:",e),J(e)}finally{O(!1)}},el=async()=>{q(e=>({...e,deleting:!0,error:null}));try{await (0,n.zl)(s),V("Workspace deleted successfully!"),setTimeout(()=>{o.push("/workspaces")},1500)}catch(e){console.error("Error deleting workspace:",e),q(s=>({...s,deleting:!1,error:e}))}},en=()=>{q({showDialog:!1,deleting:!1,error:null})},ec=async()=>{await Promise.all([es(),er()])};if(!o.isReady)return(0,t.jsx)("div",{children:"Loading..."});let ei=r?"Create New Workspace | SkyPilot Dashboard":"Workspace: ".concat(s," | SkyPilot Dashboard");return(0,t.jsxs)(t.Fragment,{children:[(0,t.jsx)(x(),{children:(0,t.jsx)("title",{children:ei})}),(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)(d(),{href:"/workspaces",className:"text-sky-blue hover:underline",children:"Workspaces"}),(0,t.jsx)("span",{className:"mx-2 text-gray-500",children:"›"}),(0,t.jsx)(d(),{href:r?"/workspace/new":"/workspaces/".concat(s),className:"text-sky-blue hover:underline",children:r?"New Workspace":s}),T&&(0,t.jsx)("span",{className:"ml-3 px-2 py-1 bg-yellow-100 text-yellow-800 text-xs rounded",children:"Unsaved changes"})]}),(0,t.jsxs)("div",{className:"text-sm flex items-center",children:[(R||z||$)&&(0,t.jsxs)("div",{className:"flex items-center mr-4",children:[(0,t.jsx)(g.Z,{size:15,className:"mt-0"}),(0,t.jsx)("span",{className:"ml-2 text-gray-500",children:z?"Saving...":"Loading..."})]}),(0,t.jsxs)("div",{className:"flex items-center space-x-4",children:[!r&&(0,t.jsxs)("button",{onClick:ec,disabled:R||z||$,className:"text-sky-blue hover:text-sky-blue-bright font-medium inline-flex items-center",children:[(0,t.jsx)(j.Z,{className:"w-4 h-4 mr-1.5"}),"Refresh"]}),!r&&"default"!==s&&(0,t.jsxs)("button",{onClick:()=>q({...G,showDialog:!0}),disabled:G.deleting||z,className:"text-red-600 hover:text-red-700 font-medium inline-flex items-center",children:[(0,t.jsx)(y.Z,{className:"w-4 h-4 mr-1.5"}),"Delete"]})]})]})]}),R?(0,t.jsxs)("div",{className:"flex justify-center items-center py-12",children:[(0,t.jsx)(g.Z,{size:24,className:"mr-2"}),(0,t.jsx)("span",{className:"text-gray-500",children:"Loading workspace configuration..."})]}):(0,t.jsxs)("div",{className:"space-y-6",children:[(0,t.jsx)(L.X,{error:Y,title:"Error",onDismiss:()=>J(null)}),(0,t.jsx)(Z,{message:I}),(0,t.jsxs)("div",{className:"grid grid-cols-1 lg:grid-cols-3 gap-6",children:[!r&&(0,t.jsx)("div",{className:"lg:col-span-1",children:(0,t.jsxs)(m.Zb,{className:"h-full",children:[(0,t.jsx)(m.Ol,{children:(0,t.jsx)(m.ll,{className:"text-base font-normal",children:(0,t.jsxs)("div",{className:"flex items-center justify-between",children:[(0,t.jsxs)("div",{children:[(0,t.jsx)("span",{className:"font-semibold",children:"Workspace:"})," ",s]}),(0,t.jsx)(M,{isPrivate:!0===b.private})]})})}),(0,t.jsxs)(m.aY,{className:"text-sm pb-2 flex-1",children:[(0,t.jsxs)("div",{className:"py-2 flex items-center justify-between",children:[(0,t.jsxs)("div",{className:"flex items-center text-gray-600",children:[(0,t.jsx)(C.QT,{className:"w-4 h-4 mr-2 text-gray-500"}),(0,t.jsx)("span",{children:"Clusters (Running / Total)"})]}),(0,t.jsx)("span",{className:"font-normal text-gray-800",children:$?"...":"".concat(K.runningClusterCount," / ").concat(K.totalClusterCount)})]}),(0,t.jsxs)("div",{className:"py-2 flex items-center justify-between border-t border-gray-100",children:[(0,t.jsxs)("div",{className:"flex items-center text-gray-600",children:[(0,t.jsx)(C.Vp,{className:"w-4 h-4 mr-2 text-gray-500"}),(0,t.jsx)("span",{children:"Managed Jobs"})]}),(0,t.jsx)("span",{className:"font-normal text-gray-800",children:$?"...":K.managedJobsCount})]})]}),(0,t.jsxs)("div",{className:"px-6 pb-6 text-sm pt-3",children:[(0,t.jsx)("h4",{className:"mb-2 text-xs text-gray-500 tracking-wider",children:"Enabled Infra"}),(0,t.jsx)("div",{className:"flex flex-wrap gap-x-4 gap-y-1",children:$?(0,t.jsx)("span",{className:"text-gray-500",children:"Loading..."}):K.clouds.length>0?K.clouds.map(e=>(0,t.jsxs)("div",{className:"flex items-center text-gray-700",children:[(0,t.jsx)(C.Ye,{className:"w-3.5 h-3.5 mr-1.5 text-green-500"}),(0,t.jsx)("span",{children:e})]},e)):(0,t.jsx)("span",{className:"text-gray-500 italic",children:"No enabled infrastructure"})}),(0,t.jsx)("div",{className:"mt-4",children:(0,t.jsx)(D,{workspaceName:s,config:b,enabledClouds:K.clouds})}),(0,t.jsx)(P,{workspaceConfig:b,allUsers:X})]})]})}),(0,t.jsx)("div",{className:r?"lg:col-span-3":"lg:col-span-2",children:(0,t.jsxs)(m.Zb,{className:"h-full flex flex-col",children:[(0,t.jsx)(m.Ol,{children:(0,t.jsx)(m.ll,{className:"text-base font-normal",children:r?"New Workspace YAML":"Edit Workspace YAML"})}),(0,t.jsx)(m.aY,{className:"flex-1 flex flex-col",children:(0,t.jsxs)("div",{className:"space-y-4 flex-1 flex flex-col",children:[F&&(0,t.jsx)(L.X,{error:F,onDismiss:()=>H(null)}),(0,t.jsxs)("div",{className:"flex-1 flex flex-col",children:[(0,t.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,t.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,t.jsxs)("div",{className:"mb-4",children:[(0,t.jsx)("h4",{className:"text-sm font-medium text-gray-700 mb-2",children:"Example configuration:"}),(0,t.jsx)("div",{className:"p-3 bg-gray-50 border rounded-lg",children:(0,t.jsx)("pre",{className:"text-xs font-mono text-gray-600 whitespace-pre-wrap",children:"".concat(s||"my-workspace",":\n private: true\n allowed_users:\n - user1@mydomain.com\n - user2@mydomain.com\n gcp:\n project_id: xxx\n disabled: false\n kubernetes:\n allowed_contexts:\n - context-1")})})]}),(0,t.jsx)(f.g,{value:k,onChange:e=>et(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,t.jsx)("div",{className:"flex justify-end space-x-3 pt-3 border-gray-200",children:(0,t.jsxs)(h.z,{onClick:ea,disabled:z||F||R,className:"inline-flex items-center bg-sky-600 hover:bg-sky-700 text-white",children:[(0,t.jsx)(N.Z,{className:"w-4 h-4 mr-1.5"}),z?"Applying...":"Apply"]})})]})]})})]})})]})]}),(0,t.jsx)(v.Vq,{open:G.showDialog,onOpenChange:en,children:(0,t.jsxs)(v.cZ,{className:"sm:max-w-md",children:[(0,t.jsxs)(v.fK,{className:"",children:[(0,t.jsx)(v.$N,{children:"Delete Workspace"}),(0,t.jsxs)(v.Be,{children:['Are you sure you want to delete workspace "',s,'"? This action cannot be undone.']})]}),G.error&&(0,t.jsx)(L.X,{error:G.error,title:"Deletion Failed",onDismiss:()=>q(e=>({...e,error:null}))}),(0,t.jsxs)(v.cN,{className:"",children:[(0,t.jsx)(h.z,{variant:"outline",onClick:en,disabled:G.deleting,children:"Cancel"}),(0,t.jsx)(h.z,{variant:"destructive",onClick:el,disabled:G.deleting,children:G.deleting?"Deleting...":"Delete"})]})]})})]})]})}}}]);
|