dstack 0.19.11rc2__py3-none-any.whl → 0.19.12__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 dstack might be problematic. Click here for more details.
- dstack/_internal/cli/commands/offer.py +2 -0
- dstack/_internal/cli/services/configurators/run.py +43 -42
- dstack/_internal/cli/utils/run.py +10 -26
- dstack/_internal/core/backends/template/configurator.py.jinja +1 -6
- dstack/_internal/core/backends/template/models.py.jinja +4 -0
- dstack/_internal/core/models/configurations.py +1 -1
- dstack/_internal/core/models/fleets.py +6 -1
- dstack/_internal/core/models/profiles.py +43 -3
- dstack/_internal/core/models/repos/local.py +19 -13
- dstack/_internal/core/models/runs.py +78 -45
- dstack/_internal/server/background/tasks/process_running_jobs.py +47 -12
- dstack/_internal/server/background/tasks/process_runs.py +14 -1
- dstack/_internal/server/services/fleets.py +2 -2
- dstack/_internal/server/services/gateways/__init__.py +1 -1
- dstack/_internal/server/services/plugins.py +3 -2
- dstack/_internal/server/services/runner/client.py +4 -1
- dstack/_internal/server/services/runs.py +2 -2
- dstack/_internal/server/services/volumes.py +1 -1
- dstack/_internal/server/statics/index.html +1 -1
- dstack/_internal/server/statics/{main-5b9786c955b42bf93581.js → main-b0e80f8e26a168c129e9.js} +72 -25
- dstack/_internal/server/statics/{main-5b9786c955b42bf93581.js.map → main-b0e80f8e26a168c129e9.js.map} +1 -1
- dstack/_internal/server/testing/common.py +2 -1
- dstack/_internal/utils/common.py +4 -0
- dstack/api/server/_fleets.py +5 -1
- dstack/api/server/_runs.py +8 -0
- dstack/version.py +1 -1
- {dstack-0.19.11rc2.dist-info → dstack-0.19.12.dist-info}/METADATA +2 -1
- {dstack-0.19.11rc2.dist-info → dstack-0.19.12.dist-info}/RECORD +31 -32
- dstack/_internal/utils/ignore.py +0 -92
- {dstack-0.19.11rc2.dist-info → dstack-0.19.12.dist-info}/WHEEL +0 -0
- {dstack-0.19.11rc2.dist-info → dstack-0.19.12.dist-info}/entry_points.txt +0 -0
- {dstack-0.19.11rc2.dist-info → dstack-0.19.12.dist-info}/licenses/LICENSE.md +0 -0
|
@@ -18,6 +18,7 @@ from dstack._internal.core.models.instances import (
|
|
|
18
18
|
SSHConnectionParams,
|
|
19
19
|
)
|
|
20
20
|
from dstack._internal.core.models.metrics import Metric
|
|
21
|
+
from dstack._internal.core.models.profiles import StartupOrder
|
|
21
22
|
from dstack._internal.core.models.repos import RemoteRepoCreds
|
|
22
23
|
from dstack._internal.core.models.runs import (
|
|
23
24
|
ClusterInfo,
|
|
@@ -184,18 +185,10 @@ async def _process_running_job(session: AsyncSession, job_model: JobModel):
|
|
|
184
185
|
if job_provisioning_data.hostname is None:
|
|
185
186
|
await _wait_for_instance_provisioning_data(job_model=job_model)
|
|
186
187
|
else:
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
other_job.job_spec.replica_num == job.job_spec.replica_num
|
|
192
|
-
and other_job.job_submissions[-1].status == JobStatus.PROVISIONING
|
|
193
|
-
and other_job.job_submissions[-1].job_provisioning_data is not None
|
|
194
|
-
and other_job.job_submissions[-1].job_provisioning_data.hostname is None
|
|
195
|
-
):
|
|
196
|
-
job_model.last_processed_at = common_utils.get_current_datetime()
|
|
197
|
-
await session.commit()
|
|
198
|
-
return
|
|
188
|
+
if _should_wait_for_other_nodes(run, job, job_model):
|
|
189
|
+
job_model.last_processed_at = common_utils.get_current_datetime()
|
|
190
|
+
await session.commit()
|
|
191
|
+
return
|
|
199
192
|
|
|
200
193
|
# fails are acceptable until timeout is exceeded
|
|
201
194
|
if job_provisioning_data.dockerized:
|
|
@@ -406,6 +399,48 @@ async def _wait_for_instance_provisioning_data(job_model: JobModel):
|
|
|
406
399
|
job_model.job_provisioning_data = job_model.instance.job_provisioning_data
|
|
407
400
|
|
|
408
401
|
|
|
402
|
+
def _should_wait_for_other_nodes(run: Run, job: Job, job_model: JobModel) -> bool:
|
|
403
|
+
for other_job in run.jobs:
|
|
404
|
+
if (
|
|
405
|
+
other_job.job_spec.replica_num == job.job_spec.replica_num
|
|
406
|
+
and other_job.job_submissions[-1].status == JobStatus.PROVISIONING
|
|
407
|
+
and other_job.job_submissions[-1].job_provisioning_data is not None
|
|
408
|
+
and other_job.job_submissions[-1].job_provisioning_data.hostname is None
|
|
409
|
+
):
|
|
410
|
+
logger.debug(
|
|
411
|
+
"%s: waiting for other job to have IP assigned",
|
|
412
|
+
fmt(job_model),
|
|
413
|
+
)
|
|
414
|
+
return True
|
|
415
|
+
master_job = find_job(run.jobs, job.job_spec.replica_num, 0)
|
|
416
|
+
if (
|
|
417
|
+
job.job_spec.job_num != 0
|
|
418
|
+
and run.run_spec.merged_profile.startup_order == StartupOrder.MASTER_FIRST
|
|
419
|
+
and master_job.job_submissions[-1].status != JobStatus.RUNNING
|
|
420
|
+
):
|
|
421
|
+
logger.debug(
|
|
422
|
+
"%s: waiting for master job to become running",
|
|
423
|
+
fmt(job_model),
|
|
424
|
+
)
|
|
425
|
+
return True
|
|
426
|
+
if (
|
|
427
|
+
job.job_spec.job_num == 0
|
|
428
|
+
and run.run_spec.merged_profile.startup_order == StartupOrder.WORKERS_FIRST
|
|
429
|
+
):
|
|
430
|
+
for other_job in run.jobs:
|
|
431
|
+
if (
|
|
432
|
+
other_job.job_spec.replica_num == job.job_spec.replica_num
|
|
433
|
+
and other_job.job_spec.job_num != job.job_spec.job_num
|
|
434
|
+
and other_job.job_submissions[-1].status != JobStatus.RUNNING
|
|
435
|
+
):
|
|
436
|
+
logger.debug(
|
|
437
|
+
"%s: waiting for worker job to become running",
|
|
438
|
+
fmt(job_model),
|
|
439
|
+
)
|
|
440
|
+
return True
|
|
441
|
+
return False
|
|
442
|
+
|
|
443
|
+
|
|
409
444
|
@runner_ssh_tunnel(ports=[DSTACK_SHIM_HTTP_PORT], retries=1)
|
|
410
445
|
def _process_provisioning_with_shim(
|
|
411
446
|
ports: Dict[int, int],
|
|
@@ -10,7 +10,7 @@ from sqlalchemy.orm import joinedload, selectinload
|
|
|
10
10
|
import dstack._internal.server.services.gateways as gateways
|
|
11
11
|
import dstack._internal.server.services.services.autoscalers as autoscalers
|
|
12
12
|
from dstack._internal.core.errors import ServerError
|
|
13
|
-
from dstack._internal.core.models.profiles import RetryEvent
|
|
13
|
+
from dstack._internal.core.models.profiles import RetryEvent, StopCriteria
|
|
14
14
|
from dstack._internal.core.models.runs import (
|
|
15
15
|
Job,
|
|
16
16
|
JobStatus,
|
|
@@ -313,6 +313,10 @@ async def _process_active_run(session: AsyncSession, run_model: RunModel):
|
|
|
313
313
|
termination_reason = RunTerminationReason.RETRY_LIMIT_EXCEEDED
|
|
314
314
|
else:
|
|
315
315
|
raise ValueError(f"Unexpected termination reason {run_termination_reasons}")
|
|
316
|
+
elif _should_stop_on_master_done(run):
|
|
317
|
+
new_status = RunStatus.TERMINATING
|
|
318
|
+
# ALL_JOBS_DONE is used for all DONE reasons including master-done
|
|
319
|
+
termination_reason = RunTerminationReason.ALL_JOBS_DONE
|
|
316
320
|
elif RunStatus.RUNNING in run_statuses:
|
|
317
321
|
new_status = RunStatus.RUNNING
|
|
318
322
|
elif RunStatus.PROVISIONING in run_statuses:
|
|
@@ -434,3 +438,12 @@ def _can_retry_single_job(run_spec: RunSpec) -> bool:
|
|
|
434
438
|
# We could make partial retry in some multi-node cases.
|
|
435
439
|
# E.g. restarting a worker node, independent jobs.
|
|
436
440
|
return False
|
|
441
|
+
|
|
442
|
+
|
|
443
|
+
def _should_stop_on_master_done(run: Run) -> bool:
|
|
444
|
+
if run.run_spec.merged_profile.stop_criteria != StopCriteria.MASTER_DONE:
|
|
445
|
+
return False
|
|
446
|
+
for job in run.jobs:
|
|
447
|
+
if job.job_spec.job_num == 0 and job.job_submissions[-1].status == JobStatus.DONE:
|
|
448
|
+
return True
|
|
449
|
+
return False
|
|
@@ -237,7 +237,7 @@ async def get_plan(
|
|
|
237
237
|
) -> FleetPlan:
|
|
238
238
|
# Spec must be copied by parsing to calculate merged_profile
|
|
239
239
|
effective_spec = FleetSpec.parse_obj(spec.dict())
|
|
240
|
-
effective_spec = apply_plugin_policies(
|
|
240
|
+
effective_spec = await apply_plugin_policies(
|
|
241
241
|
user=user.name,
|
|
242
242
|
project=project.name,
|
|
243
243
|
spec=effective_spec,
|
|
@@ -342,7 +342,7 @@ async def create_fleet(
|
|
|
342
342
|
spec: FleetSpec,
|
|
343
343
|
) -> Fleet:
|
|
344
344
|
# Spec must be copied by parsing to calculate merged_profile
|
|
345
|
-
spec = apply_plugin_policies(
|
|
345
|
+
spec = await apply_plugin_policies(
|
|
346
346
|
user=user.name,
|
|
347
347
|
project=project.name,
|
|
348
348
|
spec=spec,
|
|
@@ -140,7 +140,7 @@ async def create_gateway(
|
|
|
140
140
|
project: ProjectModel,
|
|
141
141
|
configuration: GatewayConfiguration,
|
|
142
142
|
) -> Gateway:
|
|
143
|
-
spec = apply_plugin_policies(
|
|
143
|
+
spec = await apply_plugin_policies(
|
|
144
144
|
user=user.name,
|
|
145
145
|
project=project.name,
|
|
146
146
|
# Create pseudo spec until the gateway API is updated to accept spec
|
|
@@ -5,6 +5,7 @@ from typing import Dict
|
|
|
5
5
|
from backports.entry_points_selectable import entry_points # backport for Python 3.9
|
|
6
6
|
|
|
7
7
|
from dstack._internal.core.errors import ServerClientError
|
|
8
|
+
from dstack._internal.utils.common import run_async
|
|
8
9
|
from dstack._internal.utils.logging import get_logger
|
|
9
10
|
from dstack.plugins import ApplyPolicy, ApplySpec, Plugin
|
|
10
11
|
|
|
@@ -91,11 +92,11 @@ def load_plugins(enabled_plugins: list[str]):
|
|
|
91
92
|
logger.warning("Enabled plugins not found: %s", plugins_to_load)
|
|
92
93
|
|
|
93
94
|
|
|
94
|
-
def apply_plugin_policies(user: str, project: str, spec: ApplySpec) -> ApplySpec:
|
|
95
|
+
async def apply_plugin_policies(user: str, project: str, spec: ApplySpec) -> ApplySpec:
|
|
95
96
|
policies = _get_apply_policies()
|
|
96
97
|
for policy in policies:
|
|
97
98
|
try:
|
|
98
|
-
spec = policy.on_apply
|
|
99
|
+
spec = await run_async(policy.on_apply, user=user, project=project, spec=spec)
|
|
99
100
|
except ValueError as e:
|
|
100
101
|
msg = None
|
|
101
102
|
if len(e.args) > 0:
|
|
@@ -32,6 +32,7 @@ from dstack._internal.utils.common import get_or_error
|
|
|
32
32
|
from dstack._internal.utils.logging import get_logger
|
|
33
33
|
|
|
34
34
|
REQUEST_TIMEOUT = 9
|
|
35
|
+
UPLOAD_CODE_REQUEST_TIMEOUT = 60
|
|
35
36
|
|
|
36
37
|
logger = get_logger(__name__)
|
|
37
38
|
|
|
@@ -109,7 +110,9 @@ class RunnerClient:
|
|
|
109
110
|
resp.raise_for_status()
|
|
110
111
|
|
|
111
112
|
def upload_code(self, file: Union[BinaryIO, bytes]):
|
|
112
|
-
resp = requests.post(
|
|
113
|
+
resp = requests.post(
|
|
114
|
+
self._url("/api/upload_code"), data=file, timeout=UPLOAD_CODE_REQUEST_TIMEOUT
|
|
115
|
+
)
|
|
113
116
|
resp.raise_for_status()
|
|
114
117
|
|
|
115
118
|
def run_job(self):
|
|
@@ -283,7 +283,7 @@ async def get_plan(
|
|
|
283
283
|
) -> RunPlan:
|
|
284
284
|
# Spec must be copied by parsing to calculate merged_profile
|
|
285
285
|
effective_run_spec = RunSpec.parse_obj(run_spec.dict())
|
|
286
|
-
effective_run_spec = apply_plugin_policies(
|
|
286
|
+
effective_run_spec = await apply_plugin_policies(
|
|
287
287
|
user=user.name,
|
|
288
288
|
project=project.name,
|
|
289
289
|
spec=effective_run_spec,
|
|
@@ -382,7 +382,7 @@ async def apply_plan(
|
|
|
382
382
|
force: bool,
|
|
383
383
|
) -> Run:
|
|
384
384
|
run_spec = plan.run_spec
|
|
385
|
-
run_spec = apply_plugin_policies(
|
|
385
|
+
run_spec = await apply_plugin_policies(
|
|
386
386
|
user=user.name,
|
|
387
387
|
project=project.name,
|
|
388
388
|
spec=run_spec,
|
|
@@ -205,7 +205,7 @@ async def create_volume(
|
|
|
205
205
|
user: UserModel,
|
|
206
206
|
configuration: VolumeConfiguration,
|
|
207
207
|
) -> Volume:
|
|
208
|
-
spec = apply_plugin_policies(
|
|
208
|
+
spec = await apply_plugin_policies(
|
|
209
209
|
user=user.name,
|
|
210
210
|
project=project.name,
|
|
211
211
|
# Create pseudo spec until the volume API is updated to accept spec
|
|
@@ -1,3 +1,3 @@
|
|
|
1
1
|
<!doctype html><html lang="en"><head><meta charset="utf-8"/><meta name="viewport" content="width=device-width,initial-scale=1"/><meta name="theme-color" content="#000000"/><title>dstack</title><meta name="description" content="Get GPUs at the best prices and availability from a wide range of providers. No cloud account of your own is required.
|
|
2
2
|
"/><link rel="preconnect" href="https://fonts.googleapis.com"><link rel="preconnect" href="https://fonts.gstatic.com" crossorigin><link href="https://fonts.googleapis.com/css2?family=Roboto:ital,wght@0,100;0,300;0,400;0,500;0,700;0,900;1,100;1,300;1,400;1,500;1,700;1,900&display=swap" rel="stylesheet"><meta name="og:title" content="dstack"><meta name="og:type" content="article"><meta name="og:image" content="/splash_thumbnail.png"><meta name="og:description" content="Get GPUs at the best prices and availability from a wide range of providers. No cloud account of your own is required.
|
|
3
|
-
"><link rel="icon" type="image/x-icon" href="/assets/favicon.ico"><link rel="icon" type="image/png" sizes="16x16" href="/assets/favicon-16x16.png"><link rel="icon" type="image/png" sizes="32x32" href="/assets/favicon-32x32.png"><link rel="icon" type="image/png" sizes="48x48" href="/assets/favicon-48x48.png"><link rel="manifest" href="/assets/manifest.webmanifest"><meta name="mobile-web-app-capable" content="yes"><meta name="theme-color" content="#fff"><meta name="application-name" content="dstackai"><link rel="apple-touch-icon" sizes="57x57" href="/assets/apple-touch-icon-57x57.png"><link rel="apple-touch-icon" sizes="60x60" href="/assets/apple-touch-icon-60x60.png"><link rel="apple-touch-icon" sizes="72x72" href="/assets/apple-touch-icon-72x72.png"><link rel="apple-touch-icon" sizes="76x76" href="/assets/apple-touch-icon-76x76.png"><link rel="apple-touch-icon" sizes="114x114" href="/assets/apple-touch-icon-114x114.png"><link rel="apple-touch-icon" sizes="120x120" href="/assets/apple-touch-icon-120x120.png"><link rel="apple-touch-icon" sizes="144x144" href="/assets/apple-touch-icon-144x144.png"><link rel="apple-touch-icon" sizes="152x152" href="/assets/apple-touch-icon-152x152.png"><link rel="apple-touch-icon" sizes="167x167" href="/assets/apple-touch-icon-167x167.png"><link rel="apple-touch-icon" sizes="180x180" href="/assets/apple-touch-icon-180x180.png"><link rel="apple-touch-icon" sizes="1024x1024" href="/assets/apple-touch-icon-1024x1024.png"><meta name="apple-mobile-web-app-capable" content="yes"><meta name="apple-mobile-web-app-status-bar-style" content="black-translucent"><meta name="apple-mobile-web-app-title" content="dstackai"><link rel="apple-touch-startup-image" media="(device-width: 320px) and (device-height: 568px) and (-webkit-device-pixel-ratio: 2) and (orientation: portrait)" href="/assets/apple-touch-startup-image-640x1136.png"><link rel="apple-touch-startup-image" media="(device-width: 320px) and (device-height: 568px) and (-webkit-device-pixel-ratio: 2) and (orientation: landscape)" href="/assets/apple-touch-startup-image-1136x640.png"><link rel="apple-touch-startup-image" media="(device-width: 375px) and (device-height: 667px) and (-webkit-device-pixel-ratio: 2) and (orientation: portrait)" href="/assets/apple-touch-startup-image-750x1334.png"><link rel="apple-touch-startup-image" media="(device-width: 375px) and (device-height: 667px) and (-webkit-device-pixel-ratio: 2) and (orientation: landscape)" href="/assets/apple-touch-startup-image-1334x750.png"><link rel="apple-touch-startup-image" media="(device-width: 375px) and (device-height: 812px) and (-webkit-device-pixel-ratio: 3) and (orientation: portrait)" href="/assets/apple-touch-startup-image-1125x2436.png"><link rel="apple-touch-startup-image" media="(device-width: 375px) and (device-height: 812px) and (-webkit-device-pixel-ratio: 3) and (orientation: landscape)" href="/assets/apple-touch-startup-image-2436x1125.png"><link rel="apple-touch-startup-image" media="(device-width: 390px) and (device-height: 844px) and (-webkit-device-pixel-ratio: 3) and (orientation: portrait)" href="/assets/apple-touch-startup-image-1170x2532.png"><link rel="apple-touch-startup-image" media="(device-width: 390px) and (device-height: 844px) and (-webkit-device-pixel-ratio: 3) and (orientation: landscape)" href="/assets/apple-touch-startup-image-2532x1170.png"><link rel="apple-touch-startup-image" media="(device-width: 393px) and (device-height: 852px) and (-webkit-device-pixel-ratio: 3) and (orientation: portrait)" href="/assets/apple-touch-startup-image-1179x2556.png"><link rel="apple-touch-startup-image" media="(device-width: 393px) and (device-height: 852px) and (-webkit-device-pixel-ratio: 3) and (orientation: landscape)" href="/assets/apple-touch-startup-image-2556x1179.png"><link rel="apple-touch-startup-image" media="(device-width: 414px) and (device-height: 896px) and (-webkit-device-pixel-ratio: 2) and (orientation: portrait)" href="/assets/apple-touch-startup-image-828x1792.png"><link rel="apple-touch-startup-image" media="(device-width: 414px) and (device-height: 896px) and (-webkit-device-pixel-ratio: 2) and (orientation: landscape)" href="/assets/apple-touch-startup-image-1792x828.png"><link rel="apple-touch-startup-image" media="(device-width: 414px) and (device-height: 896px) and (-webkit-device-pixel-ratio: 3) and (orientation: portrait)" href="/assets/apple-touch-startup-image-1242x2688.png"><link rel="apple-touch-startup-image" media="(device-width: 414px) and (device-height: 896px) and (-webkit-device-pixel-ratio: 3) and (orientation: landscape)" href="/assets/apple-touch-startup-image-2688x1242.png"><link rel="apple-touch-startup-image" media="(device-width: 414px) and (device-height: 736px) and (-webkit-device-pixel-ratio: 3) and (orientation: portrait)" href="/assets/apple-touch-startup-image-1242x2208.png"><link rel="apple-touch-startup-image" media="(device-width: 414px) and (device-height: 736px) and (-webkit-device-pixel-ratio: 3) and (orientation: landscape)" href="/assets/apple-touch-startup-image-2208x1242.png"><link rel="apple-touch-startup-image" media="(device-width: 428px) and (device-height: 926px) and (-webkit-device-pixel-ratio: 3) and (orientation: portrait)" href="/assets/apple-touch-startup-image-1284x2778.png"><link rel="apple-touch-startup-image" media="(device-width: 428px) and (device-height: 926px) and (-webkit-device-pixel-ratio: 3) and (orientation: landscape)" href="/assets/apple-touch-startup-image-2778x1284.png"><link rel="apple-touch-startup-image" media="(device-width: 430px) and (device-height: 932px) and (-webkit-device-pixel-ratio: 3) and (orientation: portrait)" href="/assets/apple-touch-startup-image-1290x2796.png"><link rel="apple-touch-startup-image" media="(device-width: 430px) and (device-height: 932px) and (-webkit-device-pixel-ratio: 3) and (orientation: landscape)" href="/assets/apple-touch-startup-image-2796x1290.png"><link rel="apple-touch-startup-image" media="(device-width: 744px) and (device-height: 1133px) and (-webkit-device-pixel-ratio: 2) and (orientation: portrait)" href="/assets/apple-touch-startup-image-1488x2266.png"><link rel="apple-touch-startup-image" media="(device-width: 744px) and (device-height: 1133px) and (-webkit-device-pixel-ratio: 2) and (orientation: landscape)" href="/assets/apple-touch-startup-image-2266x1488.png"><link rel="apple-touch-startup-image" media="(device-width: 768px) and (device-height: 1024px) and (-webkit-device-pixel-ratio: 2) and (orientation: portrait)" href="/assets/apple-touch-startup-image-1536x2048.png"><link rel="apple-touch-startup-image" media="(device-width: 768px) and (device-height: 1024px) and (-webkit-device-pixel-ratio: 2) and (orientation: landscape)" href="/assets/apple-touch-startup-image-2048x1536.png"><link rel="apple-touch-startup-image" media="(device-width: 810px) and (device-height: 1080px) and (-webkit-device-pixel-ratio: 2) and (orientation: portrait)" href="/assets/apple-touch-startup-image-1620x2160.png"><link rel="apple-touch-startup-image" media="(device-width: 810px) and (device-height: 1080px) and (-webkit-device-pixel-ratio: 2) and (orientation: landscape)" href="/assets/apple-touch-startup-image-2160x1620.png"><link rel="apple-touch-startup-image" media="(device-width: 820px) and (device-height: 1080px) and (-webkit-device-pixel-ratio: 2) and (orientation: portrait)" href="/assets/apple-touch-startup-image-1640x2160.png"><link rel="apple-touch-startup-image" media="(device-width: 820px) and (device-height: 1080px) and (-webkit-device-pixel-ratio: 2) and (orientation: landscape)" href="/assets/apple-touch-startup-image-2160x1640.png"><link rel="apple-touch-startup-image" media="(device-width: 834px) and (device-height: 1194px) and (-webkit-device-pixel-ratio: 2) and (orientation: portrait)" href="/assets/apple-touch-startup-image-1668x2388.png"><link rel="apple-touch-startup-image" media="(device-width: 834px) and (device-height: 1194px) and (-webkit-device-pixel-ratio: 2) and (orientation: landscape)" href="/assets/apple-touch-startup-image-2388x1668.png"><link rel="apple-touch-startup-image" media="(device-width: 834px) and (device-height: 1112px) and (-webkit-device-pixel-ratio: 2) and (orientation: portrait)" href="/assets/apple-touch-startup-image-1668x2224.png"><link rel="apple-touch-startup-image" media="(device-width: 834px) and (device-height: 1112px) and (-webkit-device-pixel-ratio: 2) and (orientation: landscape)" href="/assets/apple-touch-startup-image-2224x1668.png"><link rel="apple-touch-startup-image" media="(device-width: 1024px) and (device-height: 1366px) and (-webkit-device-pixel-ratio: 2) and (orientation: portrait)" href="/assets/apple-touch-startup-image-2048x2732.png"><link rel="apple-touch-startup-image" media="(device-width: 1024px) and (device-height: 1366px) and (-webkit-device-pixel-ratio: 2) and (orientation: landscape)" href="/assets/apple-touch-startup-image-2732x2048.png"><meta name="msapplication-TileColor" content="#fff"><meta name="msapplication-TileImage" content="/assets/mstile-144x144.png"><meta name="msapplication-config" content="/assets/browserconfig.xml"><link rel="yandex-tableau-widget" href="/assets/yandex-browser-manifest.json"><script defer="defer" src="/main-
|
|
3
|
+
"><link rel="icon" type="image/x-icon" href="/assets/favicon.ico"><link rel="icon" type="image/png" sizes="16x16" href="/assets/favicon-16x16.png"><link rel="icon" type="image/png" sizes="32x32" href="/assets/favicon-32x32.png"><link rel="icon" type="image/png" sizes="48x48" href="/assets/favicon-48x48.png"><link rel="manifest" href="/assets/manifest.webmanifest"><meta name="mobile-web-app-capable" content="yes"><meta name="theme-color" content="#fff"><meta name="application-name" content="dstackai"><link rel="apple-touch-icon" sizes="57x57" href="/assets/apple-touch-icon-57x57.png"><link rel="apple-touch-icon" sizes="60x60" href="/assets/apple-touch-icon-60x60.png"><link rel="apple-touch-icon" sizes="72x72" href="/assets/apple-touch-icon-72x72.png"><link rel="apple-touch-icon" sizes="76x76" href="/assets/apple-touch-icon-76x76.png"><link rel="apple-touch-icon" sizes="114x114" href="/assets/apple-touch-icon-114x114.png"><link rel="apple-touch-icon" sizes="120x120" href="/assets/apple-touch-icon-120x120.png"><link rel="apple-touch-icon" sizes="144x144" href="/assets/apple-touch-icon-144x144.png"><link rel="apple-touch-icon" sizes="152x152" href="/assets/apple-touch-icon-152x152.png"><link rel="apple-touch-icon" sizes="167x167" href="/assets/apple-touch-icon-167x167.png"><link rel="apple-touch-icon" sizes="180x180" href="/assets/apple-touch-icon-180x180.png"><link rel="apple-touch-icon" sizes="1024x1024" href="/assets/apple-touch-icon-1024x1024.png"><meta name="apple-mobile-web-app-capable" content="yes"><meta name="apple-mobile-web-app-status-bar-style" content="black-translucent"><meta name="apple-mobile-web-app-title" content="dstackai"><link rel="apple-touch-startup-image" media="(device-width: 320px) and (device-height: 568px) and (-webkit-device-pixel-ratio: 2) and (orientation: portrait)" href="/assets/apple-touch-startup-image-640x1136.png"><link rel="apple-touch-startup-image" media="(device-width: 320px) and (device-height: 568px) and (-webkit-device-pixel-ratio: 2) and (orientation: landscape)" href="/assets/apple-touch-startup-image-1136x640.png"><link rel="apple-touch-startup-image" media="(device-width: 375px) and (device-height: 667px) and (-webkit-device-pixel-ratio: 2) and (orientation: portrait)" href="/assets/apple-touch-startup-image-750x1334.png"><link rel="apple-touch-startup-image" media="(device-width: 375px) and (device-height: 667px) and (-webkit-device-pixel-ratio: 2) and (orientation: landscape)" href="/assets/apple-touch-startup-image-1334x750.png"><link rel="apple-touch-startup-image" media="(device-width: 375px) and (device-height: 812px) and (-webkit-device-pixel-ratio: 3) and (orientation: portrait)" href="/assets/apple-touch-startup-image-1125x2436.png"><link rel="apple-touch-startup-image" media="(device-width: 375px) and (device-height: 812px) and (-webkit-device-pixel-ratio: 3) and (orientation: landscape)" href="/assets/apple-touch-startup-image-2436x1125.png"><link rel="apple-touch-startup-image" media="(device-width: 390px) and (device-height: 844px) and (-webkit-device-pixel-ratio: 3) and (orientation: portrait)" href="/assets/apple-touch-startup-image-1170x2532.png"><link rel="apple-touch-startup-image" media="(device-width: 390px) and (device-height: 844px) and (-webkit-device-pixel-ratio: 3) and (orientation: landscape)" href="/assets/apple-touch-startup-image-2532x1170.png"><link rel="apple-touch-startup-image" media="(device-width: 393px) and (device-height: 852px) and (-webkit-device-pixel-ratio: 3) and (orientation: portrait)" href="/assets/apple-touch-startup-image-1179x2556.png"><link rel="apple-touch-startup-image" media="(device-width: 393px) and (device-height: 852px) and (-webkit-device-pixel-ratio: 3) and (orientation: landscape)" href="/assets/apple-touch-startup-image-2556x1179.png"><link rel="apple-touch-startup-image" media="(device-width: 414px) and (device-height: 896px) and (-webkit-device-pixel-ratio: 2) and (orientation: portrait)" href="/assets/apple-touch-startup-image-828x1792.png"><link rel="apple-touch-startup-image" media="(device-width: 414px) and (device-height: 896px) and (-webkit-device-pixel-ratio: 2) and (orientation: landscape)" href="/assets/apple-touch-startup-image-1792x828.png"><link rel="apple-touch-startup-image" media="(device-width: 414px) and (device-height: 896px) and (-webkit-device-pixel-ratio: 3) and (orientation: portrait)" href="/assets/apple-touch-startup-image-1242x2688.png"><link rel="apple-touch-startup-image" media="(device-width: 414px) and (device-height: 896px) and (-webkit-device-pixel-ratio: 3) and (orientation: landscape)" href="/assets/apple-touch-startup-image-2688x1242.png"><link rel="apple-touch-startup-image" media="(device-width: 414px) and (device-height: 736px) and (-webkit-device-pixel-ratio: 3) and (orientation: portrait)" href="/assets/apple-touch-startup-image-1242x2208.png"><link rel="apple-touch-startup-image" media="(device-width: 414px) and (device-height: 736px) and (-webkit-device-pixel-ratio: 3) and (orientation: landscape)" href="/assets/apple-touch-startup-image-2208x1242.png"><link rel="apple-touch-startup-image" media="(device-width: 428px) and (device-height: 926px) and (-webkit-device-pixel-ratio: 3) and (orientation: portrait)" href="/assets/apple-touch-startup-image-1284x2778.png"><link rel="apple-touch-startup-image" media="(device-width: 428px) and (device-height: 926px) and (-webkit-device-pixel-ratio: 3) and (orientation: landscape)" href="/assets/apple-touch-startup-image-2778x1284.png"><link rel="apple-touch-startup-image" media="(device-width: 430px) and (device-height: 932px) and (-webkit-device-pixel-ratio: 3) and (orientation: portrait)" href="/assets/apple-touch-startup-image-1290x2796.png"><link rel="apple-touch-startup-image" media="(device-width: 430px) and (device-height: 932px) and (-webkit-device-pixel-ratio: 3) and (orientation: landscape)" href="/assets/apple-touch-startup-image-2796x1290.png"><link rel="apple-touch-startup-image" media="(device-width: 744px) and (device-height: 1133px) and (-webkit-device-pixel-ratio: 2) and (orientation: portrait)" href="/assets/apple-touch-startup-image-1488x2266.png"><link rel="apple-touch-startup-image" media="(device-width: 744px) and (device-height: 1133px) and (-webkit-device-pixel-ratio: 2) and (orientation: landscape)" href="/assets/apple-touch-startup-image-2266x1488.png"><link rel="apple-touch-startup-image" media="(device-width: 768px) and (device-height: 1024px) and (-webkit-device-pixel-ratio: 2) and (orientation: portrait)" href="/assets/apple-touch-startup-image-1536x2048.png"><link rel="apple-touch-startup-image" media="(device-width: 768px) and (device-height: 1024px) and (-webkit-device-pixel-ratio: 2) and (orientation: landscape)" href="/assets/apple-touch-startup-image-2048x1536.png"><link rel="apple-touch-startup-image" media="(device-width: 810px) and (device-height: 1080px) and (-webkit-device-pixel-ratio: 2) and (orientation: portrait)" href="/assets/apple-touch-startup-image-1620x2160.png"><link rel="apple-touch-startup-image" media="(device-width: 810px) and (device-height: 1080px) and (-webkit-device-pixel-ratio: 2) and (orientation: landscape)" href="/assets/apple-touch-startup-image-2160x1620.png"><link rel="apple-touch-startup-image" media="(device-width: 820px) and (device-height: 1080px) and (-webkit-device-pixel-ratio: 2) and (orientation: portrait)" href="/assets/apple-touch-startup-image-1640x2160.png"><link rel="apple-touch-startup-image" media="(device-width: 820px) and (device-height: 1080px) and (-webkit-device-pixel-ratio: 2) and (orientation: landscape)" href="/assets/apple-touch-startup-image-2160x1640.png"><link rel="apple-touch-startup-image" media="(device-width: 834px) and (device-height: 1194px) and (-webkit-device-pixel-ratio: 2) and (orientation: portrait)" href="/assets/apple-touch-startup-image-1668x2388.png"><link rel="apple-touch-startup-image" media="(device-width: 834px) and (device-height: 1194px) and (-webkit-device-pixel-ratio: 2) and (orientation: landscape)" href="/assets/apple-touch-startup-image-2388x1668.png"><link rel="apple-touch-startup-image" media="(device-width: 834px) and (device-height: 1112px) and (-webkit-device-pixel-ratio: 2) and (orientation: portrait)" href="/assets/apple-touch-startup-image-1668x2224.png"><link rel="apple-touch-startup-image" media="(device-width: 834px) and (device-height: 1112px) and (-webkit-device-pixel-ratio: 2) and (orientation: landscape)" href="/assets/apple-touch-startup-image-2224x1668.png"><link rel="apple-touch-startup-image" media="(device-width: 1024px) and (device-height: 1366px) and (-webkit-device-pixel-ratio: 2) and (orientation: portrait)" href="/assets/apple-touch-startup-image-2048x2732.png"><link rel="apple-touch-startup-image" media="(device-width: 1024px) and (device-height: 1366px) and (-webkit-device-pixel-ratio: 2) and (orientation: landscape)" href="/assets/apple-touch-startup-image-2732x2048.png"><meta name="msapplication-TileColor" content="#fff"><meta name="msapplication-TileImage" content="/assets/mstile-144x144.png"><meta name="msapplication-config" content="/assets/browserconfig.xml"><link rel="yandex-tableau-widget" href="/assets/yandex-browser-manifest.json"><script defer="defer" src="/main-b0e80f8e26a168c129e9.js"></script><link href="/main-8f9c66f404e9c7e7e020.css" rel="stylesheet"></head><body><noscript>You need to enable JavaScript to run this app.</noscript><div class="b-page-header" id="header"></div><div id="root"></div></body></html>
|