deepline 0.2.0 → 0.2.2
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.
- package/dist/bundling-sources/sdk/src/client.ts +17 -3
- package/dist/bundling-sources/sdk/src/release.ts +1 -1
- package/dist/bundling-sources/shared_libs/observability/scheduled-job-errors.ts +95 -0
- package/dist/bundling-sources/shared_libs/play-runtime/backend.ts +19 -0
- package/dist/bundling-sources/shared_libs/play-runtime/modal-runtime-config.ts +104 -0
- package/dist/bundling-sources/shared_libs/play-runtime/protocol.ts +4 -1
- package/dist/bundling-sources/shared_libs/play-runtime/runner-backends/backends/daytona-lifecycle.ts +177 -10
- package/dist/bundling-sources/shared_libs/play-runtime/runner-backends/backends/daytona-modal-fallback.ts +218 -0
- package/dist/bundling-sources/shared_libs/play-runtime/runner-backends/backends/daytona-payload-transport.ts +26 -7
- package/dist/bundling-sources/shared_libs/play-runtime/runner-backends/backends/daytona.ts +34 -0
- package/dist/bundling-sources/shared_libs/play-runtime/runner-backends/backends/modal.ts +380 -0
- package/dist/bundling-sources/shared_libs/play-runtime/runner-backends/index.ts +28 -3
- package/dist/bundling-sources/shared_libs/play-runtime/runner-backends/runtime-sandbox-reconciliation.ts +240 -0
- package/dist/bundling-sources/shared_libs/play-runtime/runner-backends/types.ts +28 -1
- package/dist/bundling-sources/shared_libs/play-runtime/runtime-environment.ts +17 -2
- package/dist/bundling-sources/shared_libs/play-runtime/runtime-sandbox-placement-policy.ts +188 -0
- package/dist/bundling-sources/shared_libs/play-runtime/sandbox-compute-usage.ts +77 -0
- package/dist/bundling-sources/shared_libs/play-runtime/scheduler-backend.ts +7 -0
- package/dist/bundling-sources/shared_libs/play-runtime/suspension.ts +10 -0
- package/dist/bundling-sources/shared_libs/play-runtime/worker-api-types.ts +39 -0
- package/dist/cli/index.js +50 -26
- package/dist/cli/index.mjs +50 -26
- package/dist/index.d.mts +5 -3
- package/dist/index.d.ts +5 -3
- package/dist/index.js +50 -5
- package/dist/index.mjs +50 -5
- package/dist/plays/bundle-play-file.d.mts +2 -2
- package/dist/plays/bundle-play-file.d.ts +2 -2
- package/dist/plays/bundle-play-file.mjs +7 -1
- package/dist/{tool-execution-error-YDz7UMl-.d.mts → tool-execution-error-4-rhemLQ.d.mts} +7 -1
- package/dist/{tool-execution-error-YDz7UMl-.d.ts → tool-execution-error-4-rhemLQ.d.ts} +7 -1
- package/package.json +1 -1
|
@@ -0,0 +1,218 @@
|
|
|
1
|
+
import type {
|
|
2
|
+
PlayRunnerBackend,
|
|
3
|
+
PlayRunnerCallbacks,
|
|
4
|
+
PlayRunnerPreparedExecution,
|
|
5
|
+
PlayRunnerPrepareInput,
|
|
6
|
+
} from '../types';
|
|
7
|
+
import type {
|
|
8
|
+
PlayRunnerExecutionConfig,
|
|
9
|
+
PlayRunnerResult,
|
|
10
|
+
} from '@shared_libs/play-runtime/protocol';
|
|
11
|
+
import { daytonaPlayRunnerBackend } from './daytona';
|
|
12
|
+
import { DaytonaSandboxAcquisitionUnavailableError } from './daytona-lifecycle';
|
|
13
|
+
import { modalPlayRunnerBackend } from './modal';
|
|
14
|
+
import {
|
|
15
|
+
canTransitionRuntimeSandboxPlacement,
|
|
16
|
+
resolveRuntimeSandboxPlacementPolicy,
|
|
17
|
+
RUNTIME_SANDBOX_PLACEMENT_POLICIES,
|
|
18
|
+
type RuntimeSandboxPlacementPolicyId,
|
|
19
|
+
type RuntimeSandboxPlacementFailureReason,
|
|
20
|
+
type RuntimeSandboxPlacementPolicy,
|
|
21
|
+
type RuntimeSandboxProvider,
|
|
22
|
+
} from '@shared_libs/play-runtime/runtime-sandbox-placement-policy';
|
|
23
|
+
|
|
24
|
+
type DaytonaFallbackPreparedExecution = PlayRunnerPreparedExecution & {
|
|
25
|
+
kind: 'runtime_sandbox_placement';
|
|
26
|
+
policyId: string;
|
|
27
|
+
provider: RuntimeSandboxProvider;
|
|
28
|
+
providerPrepared: PlayRunnerPreparedExecution | undefined;
|
|
29
|
+
};
|
|
30
|
+
|
|
31
|
+
function isPrepared(
|
|
32
|
+
prepared: PlayRunnerPreparedExecution | undefined,
|
|
33
|
+
): prepared is DaytonaFallbackPreparedExecution {
|
|
34
|
+
return prepared?.kind === 'runtime_sandbox_placement';
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
type RuntimeSandboxProviderAdapter = {
|
|
38
|
+
backend: PlayRunnerBackend;
|
|
39
|
+
classifyAcquisitionFailure: (
|
|
40
|
+
error: unknown,
|
|
41
|
+
) => RuntimeSandboxPlacementFailureReason | null;
|
|
42
|
+
};
|
|
43
|
+
|
|
44
|
+
function classifyDaytonaAcquisitionFailure(
|
|
45
|
+
error: unknown,
|
|
46
|
+
): RuntimeSandboxPlacementFailureReason | null {
|
|
47
|
+
if (!(error instanceof DaytonaSandboxAcquisitionUnavailableError)) {
|
|
48
|
+
return null;
|
|
49
|
+
}
|
|
50
|
+
if (error.reason === 'daytona_total_cpu_limit_exceeded') {
|
|
51
|
+
return 'provider_capacity_exhausted';
|
|
52
|
+
}
|
|
53
|
+
if (error.reason === 'daytona_sandbox_start_timeout') {
|
|
54
|
+
return 'provider_start_timeout_before_execution';
|
|
55
|
+
}
|
|
56
|
+
return null;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* Deep placement Implementation. Provider order alone never permits a retry:
|
|
61
|
+
* the policy must also name the exact pre-execution transition and the source
|
|
62
|
+
* Adapter must classify the thrown failure into that provider-neutral reason.
|
|
63
|
+
*/
|
|
64
|
+
export function createRuntimeSandboxPlacementBackend(input: {
|
|
65
|
+
policy: RuntimeSandboxPlacementPolicy;
|
|
66
|
+
adapters: Readonly<
|
|
67
|
+
Partial<Record<RuntimeSandboxProvider, RuntimeSandboxProviderAdapter>>
|
|
68
|
+
>;
|
|
69
|
+
}): PlayRunnerBackend {
|
|
70
|
+
const primaryProvider = input.policy.providers[0];
|
|
71
|
+
const primaryAdapter = input.adapters[primaryProvider];
|
|
72
|
+
if (!primaryAdapter) {
|
|
73
|
+
throw new Error(
|
|
74
|
+
`Runtime sandbox placement policy ${input.policy.id} has no Adapter for ${primaryProvider}.`,
|
|
75
|
+
);
|
|
76
|
+
}
|
|
77
|
+
return {
|
|
78
|
+
async prepare(
|
|
79
|
+
prepareInput: PlayRunnerPrepareInput,
|
|
80
|
+
callbacks?: PlayRunnerCallbacks,
|
|
81
|
+
): Promise<PlayRunnerPreparedExecution> {
|
|
82
|
+
const providerPrepared = await primaryAdapter.backend.prepare?.(
|
|
83
|
+
prepareInput,
|
|
84
|
+
callbacks,
|
|
85
|
+
);
|
|
86
|
+
const prepared: DaytonaFallbackPreparedExecution = {
|
|
87
|
+
kind: 'runtime_sandbox_placement',
|
|
88
|
+
policyId: input.policy.id,
|
|
89
|
+
provider: primaryProvider,
|
|
90
|
+
providerPrepared,
|
|
91
|
+
dispose: async () => await providerPrepared?.dispose?.(),
|
|
92
|
+
};
|
|
93
|
+
return prepared;
|
|
94
|
+
},
|
|
95
|
+
|
|
96
|
+
async execute(
|
|
97
|
+
config: PlayRunnerExecutionConfig,
|
|
98
|
+
callbacks?: PlayRunnerCallbacks,
|
|
99
|
+
prepared?: PlayRunnerPreparedExecution,
|
|
100
|
+
): Promise<PlayRunnerResult> {
|
|
101
|
+
const policyPrepared =
|
|
102
|
+
isPrepared(prepared) && prepared.policyId === input.policy.id
|
|
103
|
+
? prepared
|
|
104
|
+
: null;
|
|
105
|
+
for (const [index, provider] of input.policy.providers.entries()) {
|
|
106
|
+
const adapter = input.adapters[provider];
|
|
107
|
+
if (!adapter) {
|
|
108
|
+
throw new Error(
|
|
109
|
+
`Runtime sandbox placement policy ${input.policy.id} has no Adapter for ${provider}.`,
|
|
110
|
+
);
|
|
111
|
+
}
|
|
112
|
+
try {
|
|
113
|
+
return await adapter.backend.execute(
|
|
114
|
+
config,
|
|
115
|
+
callbacks,
|
|
116
|
+
index === 0 && policyPrepared?.provider === provider
|
|
117
|
+
? policyPrepared.providerPrepared
|
|
118
|
+
: undefined,
|
|
119
|
+
);
|
|
120
|
+
} catch (error) {
|
|
121
|
+
const nextProvider = input.policy.providers[index + 1];
|
|
122
|
+
const reason = adapter.classifyAcquisitionFailure(error);
|
|
123
|
+
if (
|
|
124
|
+
!nextProvider ||
|
|
125
|
+
!reason ||
|
|
126
|
+
callbacks?.cancellationSignal?.aborted ||
|
|
127
|
+
!canTransitionRuntimeSandboxPlacement({
|
|
128
|
+
policy: input.policy,
|
|
129
|
+
from: provider,
|
|
130
|
+
to: nextProvider,
|
|
131
|
+
stage: 'acquisition',
|
|
132
|
+
reason,
|
|
133
|
+
})
|
|
134
|
+
) {
|
|
135
|
+
throw error;
|
|
136
|
+
}
|
|
137
|
+
console.warn('[play-runner.sandbox_provider_fallback]', {
|
|
138
|
+
runId: config.context.runId ?? null,
|
|
139
|
+
workflowId: config.context.workflowId ?? null,
|
|
140
|
+
policyId: input.policy.id,
|
|
141
|
+
from: provider,
|
|
142
|
+
to: nextProvider,
|
|
143
|
+
stage: 'acquisition',
|
|
144
|
+
reason,
|
|
145
|
+
});
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
throw new Error(
|
|
149
|
+
`Runtime sandbox placement policy ${input.policy.id} exhausted without a result.`,
|
|
150
|
+
);
|
|
151
|
+
},
|
|
152
|
+
};
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
export function createRuntimeSandboxPlacementBackendForPolicy(
|
|
156
|
+
policyId: RuntimeSandboxPlacementPolicyId | string,
|
|
157
|
+
overrides: {
|
|
158
|
+
daytona?: PlayRunnerBackend;
|
|
159
|
+
modal?: PlayRunnerBackend;
|
|
160
|
+
} = {},
|
|
161
|
+
): PlayRunnerBackend {
|
|
162
|
+
return createRuntimeSandboxPlacementBackend({
|
|
163
|
+
policy: resolveRuntimeSandboxPlacementPolicy(policyId),
|
|
164
|
+
adapters: {
|
|
165
|
+
daytona: {
|
|
166
|
+
backend: overrides.daytona ?? daytonaPlayRunnerBackend,
|
|
167
|
+
classifyAcquisitionFailure: classifyDaytonaAcquisitionFailure,
|
|
168
|
+
},
|
|
169
|
+
modal: {
|
|
170
|
+
backend: overrides.modal ?? modalPlayRunnerBackend,
|
|
171
|
+
classifyAcquisitionFailure: () => null,
|
|
172
|
+
},
|
|
173
|
+
},
|
|
174
|
+
});
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
/** Compatibility factory retained for existing imports and tests. */
|
|
178
|
+
export function createDaytonaModalFallbackBackend(
|
|
179
|
+
input: {
|
|
180
|
+
daytona?: PlayRunnerBackend;
|
|
181
|
+
modal?: PlayRunnerBackend;
|
|
182
|
+
} = {},
|
|
183
|
+
): PlayRunnerBackend {
|
|
184
|
+
return createRuntimeSandboxPlacementBackendForPolicy(
|
|
185
|
+
RUNTIME_SANDBOX_PLACEMENT_POLICIES.daytonaThenModalV1,
|
|
186
|
+
input,
|
|
187
|
+
);
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
export const daytonaModalFallbackPlayRunnerBackend =
|
|
191
|
+
createDaytonaModalFallbackBackend();
|
|
192
|
+
|
|
193
|
+
export const daytonaOnlyPlayRunnerBackend =
|
|
194
|
+
createRuntimeSandboxPlacementBackendForPolicy(
|
|
195
|
+
RUNTIME_SANDBOX_PLACEMENT_POLICIES.daytonaOnlyV1,
|
|
196
|
+
);
|
|
197
|
+
|
|
198
|
+
export const modalOnlyPlayRunnerBackend =
|
|
199
|
+
createRuntimeSandboxPlacementBackendForPolicy(
|
|
200
|
+
RUNTIME_SANDBOX_PLACEMENT_POLICIES.modalOnlyV1,
|
|
201
|
+
);
|
|
202
|
+
|
|
203
|
+
const DEFAULT_POLICY_BACKENDS: Readonly<
|
|
204
|
+
Record<RuntimeSandboxPlacementPolicyId, PlayRunnerBackend>
|
|
205
|
+
> = {
|
|
206
|
+
[RUNTIME_SANDBOX_PLACEMENT_POLICIES.daytonaOnlyV1]:
|
|
207
|
+
daytonaOnlyPlayRunnerBackend,
|
|
208
|
+
[RUNTIME_SANDBOX_PLACEMENT_POLICIES.daytonaThenModalV1]:
|
|
209
|
+
daytonaModalFallbackPlayRunnerBackend,
|
|
210
|
+
[RUNTIME_SANDBOX_PLACEMENT_POLICIES.modalOnlyV1]: modalOnlyPlayRunnerBackend,
|
|
211
|
+
};
|
|
212
|
+
|
|
213
|
+
export function resolveDefaultRuntimeSandboxPlacementBackend(
|
|
214
|
+
policyId: RuntimeSandboxPlacementPolicyId | string,
|
|
215
|
+
): PlayRunnerBackend {
|
|
216
|
+
const policy = resolveRuntimeSandboxPlacementPolicy(policyId);
|
|
217
|
+
return DEFAULT_POLICY_BACKENDS[policy.id];
|
|
218
|
+
}
|
|
@@ -65,6 +65,11 @@ export type StagedDaytonaPayload = {
|
|
|
65
65
|
progressEventPath: string;
|
|
66
66
|
};
|
|
67
67
|
|
|
68
|
+
export type RemoteRunnerPayloadSandbox = {
|
|
69
|
+
id: string;
|
|
70
|
+
uploadFile(content: Buffer, path: string): Promise<void>;
|
|
71
|
+
};
|
|
72
|
+
|
|
68
73
|
function shellQuote(value: string): string {
|
|
69
74
|
return `'${value.replace(/'/g, `'\\''`)}'`;
|
|
70
75
|
}
|
|
@@ -390,23 +395,23 @@ function remoteRuntimeContextForDaytona(
|
|
|
390
395
|
};
|
|
391
396
|
}
|
|
392
397
|
|
|
393
|
-
async function
|
|
394
|
-
sandbox:
|
|
398
|
+
async function timedRunnerPayloadUpload(input: {
|
|
399
|
+
sandbox: RemoteRunnerPayloadSandbox;
|
|
395
400
|
emitStage: DaytonaStageEmitter;
|
|
396
401
|
label: string;
|
|
397
402
|
path: string;
|
|
398
403
|
content: Buffer;
|
|
399
404
|
}) {
|
|
400
405
|
const startedAt = Date.now();
|
|
401
|
-
await input.sandbox.
|
|
406
|
+
await input.sandbox.uploadFile(input.content, input.path);
|
|
402
407
|
input.emitStage(`upload:${input.label}:done`, {
|
|
403
408
|
bytes: input.content.byteLength,
|
|
404
409
|
elapsedMs: Date.now() - startedAt,
|
|
405
410
|
});
|
|
406
411
|
}
|
|
407
412
|
|
|
408
|
-
export async function
|
|
409
|
-
sandbox:
|
|
413
|
+
export async function stageRunnerPayload(input: {
|
|
414
|
+
sandbox: RemoteRunnerPayloadSandbox;
|
|
410
415
|
bundlePromise: Promise<string>;
|
|
411
416
|
config: PlayRunnerExecutionConfig;
|
|
412
417
|
workDir: string;
|
|
@@ -458,7 +463,7 @@ export async function stageDaytonaRunnerPayload(input: {
|
|
|
458
463
|
|
|
459
464
|
await Promise.all(
|
|
460
465
|
[...uniqueLocalPaths.entries()].map(async ([localPath, remotePath]) => {
|
|
461
|
-
await
|
|
466
|
+
await timedRunnerPayloadUpload({
|
|
462
467
|
sandbox: input.sandbox,
|
|
463
468
|
emitStage: input.emitStage,
|
|
464
469
|
label:
|
|
@@ -508,7 +513,7 @@ export async function stageDaytonaRunnerPayload(input: {
|
|
|
508
513
|
STANDARD_PLAY_RUNTIME_LIMIT_SECONDS;
|
|
509
514
|
|
|
510
515
|
const envelopeUpload = input.bundlePromise.then((bundle) =>
|
|
511
|
-
|
|
516
|
+
timedRunnerPayloadUpload({
|
|
512
517
|
sandbox: input.sandbox,
|
|
513
518
|
emitStage: input.emitStage,
|
|
514
519
|
label: 'payload',
|
|
@@ -583,3 +588,17 @@ export async function stageDaytonaRunnerPayload(input: {
|
|
|
583
588
|
progressEventPath,
|
|
584
589
|
};
|
|
585
590
|
}
|
|
591
|
+
|
|
592
|
+
export async function stageDaytonaRunnerPayload(
|
|
593
|
+
input: Omit<Parameters<typeof stageRunnerPayload>[0], 'sandbox'> & {
|
|
594
|
+
sandbox: DaytonaSandbox;
|
|
595
|
+
},
|
|
596
|
+
): Promise<StagedDaytonaPayload> {
|
|
597
|
+
return await stageRunnerPayload({
|
|
598
|
+
...input,
|
|
599
|
+
sandbox: {
|
|
600
|
+
id: input.sandbox.id,
|
|
601
|
+
uploadFile: (content, path) => input.sandbox.fs.uploadFile(content, path),
|
|
602
|
+
},
|
|
603
|
+
});
|
|
604
|
+
}
|
|
@@ -24,6 +24,7 @@ import {
|
|
|
24
24
|
} from '@shared_libs/play-runtime/daytona-runtime-config';
|
|
25
25
|
import {
|
|
26
26
|
DAYTONA_CANCELLED_ERROR,
|
|
27
|
+
DaytonaSandboxAcquisitionUnavailableError,
|
|
27
28
|
createDaytonaSandboxCleanupManager,
|
|
28
29
|
createOneShotDaytonaSandboxLifecycle,
|
|
29
30
|
type DaytonaClient,
|
|
@@ -290,12 +291,29 @@ export async function inspectDetachedDaytonaRunner(input: {
|
|
|
290
291
|
export async function readDetachedDaytonaRuntimeCompletion(input: {
|
|
291
292
|
sandboxId: string;
|
|
292
293
|
runtimeCompletedPath: string;
|
|
294
|
+
expectedOrganizationId?: string | null;
|
|
293
295
|
}): Promise<number | null> {
|
|
294
296
|
try {
|
|
295
297
|
const { clientOptions } = loadDaytonaRequiredConfig();
|
|
296
298
|
const sandbox = (await daytonaSdkClientFactory
|
|
297
299
|
.createFull(clientOptions)
|
|
298
300
|
.get(input.sandboxId)) as DaytonaSandbox;
|
|
301
|
+
const expectedOrganizationId = input.expectedOrganizationId?.trim() || null;
|
|
302
|
+
const observedOrganizationId = sandbox.organizationId?.trim() || null;
|
|
303
|
+
if (
|
|
304
|
+
expectedOrganizationId &&
|
|
305
|
+
observedOrganizationId !== expectedOrganizationId
|
|
306
|
+
) {
|
|
307
|
+
console.warn(
|
|
308
|
+
'[play-runner.daytona.runtime_completion_wrong_routing_domain]',
|
|
309
|
+
{
|
|
310
|
+
sandboxId: input.sandboxId,
|
|
311
|
+
expectedOrganizationId,
|
|
312
|
+
observedOrganizationId,
|
|
313
|
+
},
|
|
314
|
+
);
|
|
315
|
+
return null;
|
|
316
|
+
}
|
|
299
317
|
const marker = JSON.parse(
|
|
300
318
|
(await sandbox.fs.downloadFile(input.runtimeCompletedPath, 5)).toString(
|
|
301
319
|
'utf-8',
|
|
@@ -652,6 +670,7 @@ function prepareDaytonaExecution(
|
|
|
652
670
|
context: input.context,
|
|
653
671
|
emitStage: (stage, extra) =>
|
|
654
672
|
emitDaytonaStage(callbacks, input.context, stage, extra),
|
|
673
|
+
observeCreateCall: callbacks?.onRuntimeLifecycleEvent,
|
|
655
674
|
});
|
|
656
675
|
return {
|
|
657
676
|
kind: 'daytona',
|
|
@@ -790,6 +809,10 @@ export const daytonaPlayRunnerBackend: PlayRunnerBackend = {
|
|
|
790
809
|
await callbacks?.onRuntimeResourceAcquired?.({
|
|
791
810
|
kind: 'daytona_sandbox',
|
|
792
811
|
sandboxId: sandbox.id,
|
|
812
|
+
runtimeEnvironment:
|
|
813
|
+
process.env.DEEPLINE_RUNTIME_ENVIRONMENT === 'preview'
|
|
814
|
+
? 'preview'
|
|
815
|
+
: 'production',
|
|
793
816
|
daytonaEnvironment:
|
|
794
817
|
process.env.DEEPLINE_RUNTIME_ENVIRONMENT === 'preview'
|
|
795
818
|
? 'preview'
|
|
@@ -1031,6 +1054,14 @@ export const daytonaPlayRunnerBackend: PlayRunnerBackend = {
|
|
|
1031
1054
|
kind: 'detached_runner',
|
|
1032
1055
|
boundaryId: `detached-runner:${push.runId}:${runnerAttempt}`,
|
|
1033
1056
|
runnerAttempt,
|
|
1057
|
+
sandboxProvider: 'daytona',
|
|
1058
|
+
runtimeSandboxRef: {
|
|
1059
|
+
schemaVersion: 1,
|
|
1060
|
+
provider: 'daytona',
|
|
1061
|
+
resourceId: sandbox.id,
|
|
1062
|
+
routingDomain:
|
|
1063
|
+
activeAcquiredResource?.daytonaOrganizationId ?? null,
|
|
1064
|
+
},
|
|
1034
1065
|
sandboxId: sandbox.id,
|
|
1035
1066
|
sessionId: start.sessionId,
|
|
1036
1067
|
cmdId: start.cmdId,
|
|
@@ -1121,6 +1152,9 @@ export const daytonaPlayRunnerBackend: PlayRunnerBackend = {
|
|
|
1121
1152
|
}
|
|
1122
1153
|
throw error;
|
|
1123
1154
|
}
|
|
1155
|
+
if (error instanceof DaytonaSandboxAcquisitionUnavailableError) {
|
|
1156
|
+
throw error;
|
|
1157
|
+
}
|
|
1124
1158
|
emitDaytonaStage(callbacks, config.context, 'execute:error', {
|
|
1125
1159
|
sandboxId: sandboxCleanup.currentSandbox()?.id ?? null,
|
|
1126
1160
|
error: formatDaytonaError(error),
|