deepline 0.3.0 → 0.3.1
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/plays/bundle-play-file.ts +15 -1
- package/dist/bundling-sources/sdk/src/release.ts +1 -1
- package/dist/bundling-sources/shared_libs/play-runtime/runner-backends/backends/daytona-lifecycle.ts +57 -1
- package/dist/bundling-sources/shared_libs/play-runtime/runner-backends/backends/daytona.ts +20 -19
- package/dist/bundling-sources/shared_libs/play-runtime/runner-backends/backends/modal.ts +100 -25
- package/dist/bundling-sources/shared_libs/play-runtime/runner-backends/types.ts +25 -0
- package/dist/bundling-sources/shared_libs/play-runtime/runtime-capacity-policy.ts +0 -9
- package/dist/bundling-sources/shared_libs/play-runtime/transient-service-error.ts +6 -5
- package/dist/bundling-sources/shared_libs/plays/authoring-contract.ts +1 -0
- package/dist/bundling-sources/shared_libs/plays/bundling/index.ts +5 -2
- package/dist/cli/index.js +43 -40
- package/dist/cli/index.mjs +6 -3
- package/dist/index.js +1 -1
- package/dist/index.mjs +1 -1
- package/dist/plays/bundle-play-file.mjs +15 -3
- package/package.json +1 -1
|
@@ -2,6 +2,7 @@ import { tmpdir } from 'node:os';
|
|
|
2
2
|
import { dirname, join, resolve } from 'node:path';
|
|
3
3
|
import { fileURLToPath } from 'node:url';
|
|
4
4
|
import { existsSync } from 'node:fs';
|
|
5
|
+
import { realpath } from 'node:fs/promises';
|
|
5
6
|
import {
|
|
6
7
|
bundlePlayFile as bundlePlayFileCore,
|
|
7
8
|
type BundlePlayFileOptions,
|
|
@@ -145,10 +146,23 @@ export async function bundlePlayFile(
|
|
|
145
146
|
filePath: string,
|
|
146
147
|
options: BundlePlayFileOptions = {},
|
|
147
148
|
): Promise<BundledPlayFileResult> {
|
|
149
|
+
// The SDK sends this graph to a remote checker/runtime. Its file identities
|
|
150
|
+
// therefore must describe the authoring workspace, rather than the local
|
|
151
|
+
// machine paths used while building it. This is especially important on
|
|
152
|
+
// Windows: a raw `C:\\...` key cannot be reconciled beneath `/var/task`.
|
|
153
|
+
// Match the bundler's physical-path normalization. On macOS, for example,
|
|
154
|
+
// os.tmpdir() can report /var while the source graph resolves /private/var.
|
|
155
|
+
const localWorkspaceRoot = dirname(resolve(filePath));
|
|
156
|
+
const sourceIdentityRoot = await realpath(localWorkspaceRoot).catch(
|
|
157
|
+
() => localWorkspaceRoot,
|
|
158
|
+
);
|
|
148
159
|
const result = await bundlePlayFileCore(filePath, {
|
|
149
160
|
target: options.target ?? defaultPlayBundleTarget(),
|
|
150
161
|
exportName: options.exportName,
|
|
151
|
-
adapter:
|
|
162
|
+
adapter: {
|
|
163
|
+
...createSdkPlayBundlingAdapter(),
|
|
164
|
+
sourceIdentityRoot,
|
|
165
|
+
},
|
|
152
166
|
});
|
|
153
167
|
if (result.success)
|
|
154
168
|
validatePlaySourceFilesHaveNoInlineSecrets(result.sourceFiles);
|
|
@@ -192,7 +192,7 @@ export const SDK_RELEASE = {
|
|
|
192
192
|
// 0.3.0 introduces raw-v2: complete scrubbed provider responses are
|
|
193
193
|
// available at toolResponse.rawV2 while toolResponse.raw and all declared
|
|
194
194
|
// getters keep their established compatibility behavior.
|
|
195
|
-
version: '0.3.
|
|
195
|
+
version: '0.3.1',
|
|
196
196
|
updateSummary:
|
|
197
197
|
'New raw-v2 tool responses preserve complete scrubbed provider envelopes at toolResponse.rawV2, including JSON:API included resources, links, cursors, and totals. Existing toolResponse.raw and declared getters keep working unchanged.',
|
|
198
198
|
contracts: {
|
package/dist/bundling-sources/shared_libs/play-runtime/runner-backends/backends/daytona-lifecycle.ts
CHANGED
|
@@ -51,6 +51,7 @@ export type AcquiredDaytonaSandbox = {
|
|
|
51
51
|
daytonaOrganizationId: string;
|
|
52
52
|
billingStartedAt: number;
|
|
53
53
|
billingEndedAt?: number;
|
|
54
|
+
sandboxCapacityLeaseId?: string;
|
|
54
55
|
};
|
|
55
56
|
|
|
56
57
|
export type OneShotDaytonaSandboxLifecycle = {
|
|
@@ -118,6 +119,7 @@ type DaytonaCreateResult = {
|
|
|
118
119
|
sandbox: DaytonaSandbox;
|
|
119
120
|
attempt: number;
|
|
120
121
|
attemptElapsedMs: number;
|
|
122
|
+
sandboxCapacityLeaseId?: string;
|
|
121
123
|
};
|
|
122
124
|
|
|
123
125
|
function daytonaCreateErrorClass(
|
|
@@ -133,6 +135,8 @@ function daytonaCreateErrorClass(
|
|
|
133
135
|
async function rejectAcquiredSandbox(
|
|
134
136
|
sandbox: DaytonaSandbox,
|
|
135
137
|
reason: string,
|
|
138
|
+
reservation?: { leaseId: string },
|
|
139
|
+
releaseSandboxCapacity?: (leaseId: string) => Promise<void>,
|
|
136
140
|
): Promise<never> {
|
|
137
141
|
try {
|
|
138
142
|
await sandbox.delete(30);
|
|
@@ -143,6 +147,9 @@ async function rejectAcquiredSandbox(
|
|
|
143
147
|
{ cause: error },
|
|
144
148
|
);
|
|
145
149
|
}
|
|
150
|
+
if (reservation) {
|
|
151
|
+
await releaseSandboxCapacity?.(reservation.leaseId);
|
|
152
|
+
}
|
|
146
153
|
throw new Error(reason);
|
|
147
154
|
}
|
|
148
155
|
|
|
@@ -350,6 +357,8 @@ async function createRetriedOneShotDaytonaSandbox(input: {
|
|
|
350
357
|
context: DaytonaExecutionContext;
|
|
351
358
|
emitStage: DaytonaStageEmitter;
|
|
352
359
|
observeCreateCall?: DaytonaCreateCallObserver;
|
|
360
|
+
reserveSandboxCapacity?: () => Promise<{ leaseId: string }>;
|
|
361
|
+
releaseSandboxCapacity?: (leaseId: string) => Promise<void>;
|
|
353
362
|
nextProviderAttempt: () => number;
|
|
354
363
|
startedAt: number;
|
|
355
364
|
}): Promise<DaytonaCreateResult> {
|
|
@@ -372,6 +381,7 @@ async function createRetriedOneShotDaytonaSandbox(input: {
|
|
|
372
381
|
occurredAtMs: attemptStartedAt,
|
|
373
382
|
providerAttempt: attempt,
|
|
374
383
|
});
|
|
384
|
+
const reservation = await input.reserveSandboxCapacity?.();
|
|
375
385
|
let sandbox: DaytonaSandbox;
|
|
376
386
|
try {
|
|
377
387
|
sandbox = await createOneShotDaytonaSandbox({
|
|
@@ -381,6 +391,12 @@ async function createRetriedOneShotDaytonaSandbox(input: {
|
|
|
381
391
|
sandboxName,
|
|
382
392
|
});
|
|
383
393
|
} catch (error) {
|
|
394
|
+
// A normal rejected create has no durable provider resource. A timeout
|
|
395
|
+
// can be ambiguous, so retain its lease until the crash TTL rather than
|
|
396
|
+
// admitting another sandbox against an unknown provider outcome.
|
|
397
|
+
if (reservation && !isDaytonaSandboxStartTimeout(String(error))) {
|
|
398
|
+
await input.releaseSandboxCapacity?.(reservation.leaseId);
|
|
399
|
+
}
|
|
384
400
|
await input.observeCreateCall?.({
|
|
385
401
|
type: 'daytona_create_call_failed',
|
|
386
402
|
occurredAtMs: Date.now(),
|
|
@@ -407,6 +423,13 @@ async function createRetriedOneShotDaytonaSandbox(input: {
|
|
|
407
423
|
sandboxName,
|
|
408
424
|
});
|
|
409
425
|
if (deleted) {
|
|
426
|
+
// The provider-confirmed reconciliation means this reservation can
|
|
427
|
+
// no longer represent a live sandbox. Release before returning the
|
|
428
|
+
// acquisition-unavailable result so Modal fallback is not blocked
|
|
429
|
+
// behind the unbound crash TTL.
|
|
430
|
+
if (reservation) {
|
|
431
|
+
await input.releaseSandboxCapacity?.(reservation.leaseId);
|
|
432
|
+
}
|
|
410
433
|
throw new DaytonaSandboxAcquisitionUnavailableError(
|
|
411
434
|
'daytona_sandbox_start_timeout',
|
|
412
435
|
`${message} Timed-out Daytona sandbox was reconciled and deleted before fallback.`,
|
|
@@ -448,6 +471,7 @@ async function createRetriedOneShotDaytonaSandbox(input: {
|
|
|
448
471
|
sandbox,
|
|
449
472
|
attempt,
|
|
450
473
|
attemptElapsedMs: acquiredAt - attemptStartedAt,
|
|
474
|
+
...(reservation ? { sandboxCapacityLeaseId: reservation.leaseId } : {}),
|
|
451
475
|
};
|
|
452
476
|
}
|
|
453
477
|
const message = `Daytona sandbox create failed across ${errors.length} bounded attempts: ${errors.join('; ')}`;
|
|
@@ -468,6 +492,8 @@ async function acquireOneShotDaytonaSandbox(input: {
|
|
|
468
492
|
context: DaytonaExecutionContext;
|
|
469
493
|
emitStage: DaytonaStageEmitter;
|
|
470
494
|
observeCreateCall?: DaytonaCreateCallObserver;
|
|
495
|
+
reserveSandboxCapacity?: () => Promise<{ leaseId: string }>;
|
|
496
|
+
releaseSandboxCapacity?: (leaseId: string) => Promise<void>;
|
|
471
497
|
nextProviderAttempt: () => number;
|
|
472
498
|
startedAt: number;
|
|
473
499
|
}): Promise<AcquiredDaytonaSandbox> {
|
|
@@ -493,6 +519,10 @@ async function acquireOneShotDaytonaSandbox(input: {
|
|
|
493
519
|
await rejectAcquiredSandbox(
|
|
494
520
|
result.sandbox,
|
|
495
521
|
`Daytona sandbox resource boundary mismatch: expected cpu=${limits.cpu} memoryGiB=${limits.memoryGiB} diskGiB=${limits.diskGiB} gpu=${DAYTONA_SANDBOX_GPU}, granted cpu=${granted.cpu} memoryGiB=${granted.memoryGiB} diskGiB=${granted.diskGiB} gpu=${granted.gpu}`,
|
|
522
|
+
result.sandboxCapacityLeaseId
|
|
523
|
+
? { leaseId: result.sandboxCapacityLeaseId }
|
|
524
|
+
: undefined,
|
|
525
|
+
input.releaseSandboxCapacity,
|
|
496
526
|
);
|
|
497
527
|
}
|
|
498
528
|
const configuredOrganizationId =
|
|
@@ -506,6 +536,10 @@ async function acquireOneShotDaytonaSandbox(input: {
|
|
|
506
536
|
await rejectAcquiredSandbox(
|
|
507
537
|
result.sandbox,
|
|
508
538
|
'Daytona sandbox organization routing mismatch. Refusing to run customer code in a sandbox whose observed organization differs from the configured organization.',
|
|
539
|
+
result.sandboxCapacityLeaseId
|
|
540
|
+
? { leaseId: result.sandboxCapacityLeaseId }
|
|
541
|
+
: undefined,
|
|
542
|
+
input.releaseSandboxCapacity,
|
|
509
543
|
);
|
|
510
544
|
}
|
|
511
545
|
let lookupOrganizationId: string | null = null;
|
|
@@ -528,6 +562,10 @@ async function acquireOneShotDaytonaSandbox(input: {
|
|
|
528
562
|
return await rejectAcquiredSandbox(
|
|
529
563
|
result.sandbox,
|
|
530
564
|
'Daytona sandbox organization routing identity is missing. Refusing to run customer code without a durable cleanup routing domain.',
|
|
565
|
+
result.sandboxCapacityLeaseId
|
|
566
|
+
? { leaseId: result.sandboxCapacityLeaseId }
|
|
567
|
+
: undefined,
|
|
568
|
+
input.releaseSandboxCapacity,
|
|
531
569
|
);
|
|
532
570
|
}
|
|
533
571
|
const billingStartedAt = Date.now();
|
|
@@ -541,7 +579,14 @@ async function acquireOneShotDaytonaSandbox(input: {
|
|
|
541
579
|
memoryGiB: granted.memoryGiB,
|
|
542
580
|
diskGiB: granted.diskGiB,
|
|
543
581
|
});
|
|
544
|
-
return {
|
|
582
|
+
return {
|
|
583
|
+
sandbox,
|
|
584
|
+
daytonaOrganizationId,
|
|
585
|
+
billingStartedAt,
|
|
586
|
+
...(result.sandboxCapacityLeaseId
|
|
587
|
+
? { sandboxCapacityLeaseId: result.sandboxCapacityLeaseId }
|
|
588
|
+
: {}),
|
|
589
|
+
};
|
|
545
590
|
}
|
|
546
591
|
|
|
547
592
|
export function createOneShotDaytonaSandboxLifecycle(input: {
|
|
@@ -549,6 +594,8 @@ export function createOneShotDaytonaSandboxLifecycle(input: {
|
|
|
549
594
|
context: DaytonaExecutionContext;
|
|
550
595
|
emitStage: DaytonaStageEmitter;
|
|
551
596
|
observeCreateCall?: DaytonaCreateCallObserver;
|
|
597
|
+
reserveSandboxCapacity?: () => Promise<{ leaseId: string }>;
|
|
598
|
+
releaseSandboxCapacity?: (leaseId: string) => Promise<void>;
|
|
552
599
|
startedAt?: number;
|
|
553
600
|
}): OneShotDaytonaSandboxLifecycle {
|
|
554
601
|
const orgId = validateDaytonaExecutionContext(input.context);
|
|
@@ -564,6 +611,8 @@ export function createOneShotDaytonaSandboxLifecycle(input: {
|
|
|
564
611
|
context: input.context,
|
|
565
612
|
emitStage: input.emitStage,
|
|
566
613
|
observeCreateCall: input.observeCreateCall,
|
|
614
|
+
reserveSandboxCapacity: input.reserveSandboxCapacity,
|
|
615
|
+
releaseSandboxCapacity: input.releaseSandboxCapacity,
|
|
567
616
|
nextProviderAttempt: () => {
|
|
568
617
|
providerAttempt += 1;
|
|
569
618
|
return providerAttempt;
|
|
@@ -594,6 +643,13 @@ export function createOneShotDaytonaSandboxLifecycle(input: {
|
|
|
594
643
|
try {
|
|
595
644
|
const acquired = await latestAcquiredSandboxPromise;
|
|
596
645
|
await acquired.sandbox.delete(30);
|
|
646
|
+
// `dispose` has provider-confirmed that the sandbox is gone. This is
|
|
647
|
+
// normally the narrow cancellation window before the scheduler has
|
|
648
|
+
// durably recorded the resource, so no cleanup job will release an
|
|
649
|
+
// unbound lease on our behalf.
|
|
650
|
+
if (acquired.sandboxCapacityLeaseId) {
|
|
651
|
+
await input.releaseSandboxCapacity?.(acquired.sandboxCapacityLeaseId);
|
|
652
|
+
}
|
|
597
653
|
} catch (error) {
|
|
598
654
|
console.warn('[play-runner.daytona.dispose_failed_before_acquire]', {
|
|
599
655
|
error: error instanceof Error ? error.message : String(error),
|
|
@@ -5,7 +5,10 @@ import type {
|
|
|
5
5
|
PlayRunnerPreparedExecution,
|
|
6
6
|
PlayRunnerPrepareInput,
|
|
7
7
|
} from '../types';
|
|
8
|
-
import {
|
|
8
|
+
import {
|
|
9
|
+
isRuntimeSandboxCapacityLimitError,
|
|
10
|
+
RuntimeResourceFenceLostError,
|
|
11
|
+
} from '../types';
|
|
9
12
|
import { buildPlayRunnerBundle } from '../bundle';
|
|
10
13
|
import { findPlayRunnerResult, parsePlayRunnerEvents } from '../runner-events';
|
|
11
14
|
import type {
|
|
@@ -26,6 +29,7 @@ import {
|
|
|
26
29
|
DaytonaSandboxAcquisitionUnavailableError,
|
|
27
30
|
createDaytonaSandboxCleanupManager,
|
|
28
31
|
createOneShotDaytonaSandboxLifecycle,
|
|
32
|
+
type AcquiredDaytonaSandbox,
|
|
29
33
|
type DaytonaClient,
|
|
30
34
|
type DaytonaExecutionContext,
|
|
31
35
|
type DaytonaSandbox,
|
|
@@ -945,6 +949,10 @@ function prepareDaytonaExecution(
|
|
|
945
949
|
emitStage: (stage, extra) =>
|
|
946
950
|
emitDaytonaStage(callbacks, input.context, stage, extra),
|
|
947
951
|
observeCreateCall: callbacks?.onRuntimeLifecycleEvent,
|
|
952
|
+
reserveSandboxCapacity: callbacks?.reserveSandboxCapacity
|
|
953
|
+
? () => callbacks.reserveSandboxCapacity!('daytona')
|
|
954
|
+
: undefined,
|
|
955
|
+
releaseSandboxCapacity: callbacks?.releaseSandboxCapacity,
|
|
948
956
|
});
|
|
949
957
|
return {
|
|
950
958
|
kind: 'daytona',
|
|
@@ -1055,22 +1063,12 @@ export const daytonaPlayRunnerBackend: PlayRunnerBackend = {
|
|
|
1055
1063
|
};
|
|
1056
1064
|
let cancellationRequested = false;
|
|
1057
1065
|
let cancellationCleanupStarted = false;
|
|
1058
|
-
let activeAcquiredResource:
|
|
1059
|
-
sandbox: DaytonaSandbox;
|
|
1060
|
-
daytonaOrganizationId: string;
|
|
1061
|
-
billingStartedAt: number;
|
|
1062
|
-
billingEndedAt?: number;
|
|
1063
|
-
} | null = null;
|
|
1066
|
+
let activeAcquiredResource: AcquiredDaytonaSandbox | null = null;
|
|
1064
1067
|
const uploadDeadlineBreaches: DaytonaUploadAttemptTiming[] = [];
|
|
1065
1068
|
const reportedSandboxIds = new Set<string>();
|
|
1066
1069
|
const reportedSandboxEndTimes = new Map<string, number | null>();
|
|
1067
1070
|
const runtimeResourceReportErrors = new Set<unknown>();
|
|
1068
|
-
const reportRuntimeResource = async (acquired: {
|
|
1069
|
-
sandbox: DaytonaSandbox;
|
|
1070
|
-
daytonaOrganizationId: string;
|
|
1071
|
-
billingStartedAt: number;
|
|
1072
|
-
billingEndedAt?: number;
|
|
1073
|
-
}) => {
|
|
1071
|
+
const reportRuntimeResource = async (acquired: AcquiredDaytonaSandbox) => {
|
|
1074
1072
|
const sandbox = acquired.sandbox;
|
|
1075
1073
|
const billingEndedAt = acquired.billingEndedAt ?? null;
|
|
1076
1074
|
if (
|
|
@@ -1101,6 +1099,9 @@ export const daytonaPlayRunnerBackend: PlayRunnerBackend = {
|
|
|
1101
1099
|
cpu: typeof sandbox.cpu === 'number' ? sandbox.cpu : null,
|
|
1102
1100
|
memoryGiB: typeof sandbox.memory === 'number' ? sandbox.memory : null,
|
|
1103
1101
|
diskGiB: typeof sandbox.disk === 'number' ? sandbox.disk : null,
|
|
1102
|
+
...(acquired.sandboxCapacityLeaseId
|
|
1103
|
+
? { sandboxCapacityLeaseId: acquired.sandboxCapacityLeaseId }
|
|
1104
|
+
: {}),
|
|
1104
1105
|
});
|
|
1105
1106
|
} catch (error) {
|
|
1106
1107
|
runtimeResourceReportErrors.add(error);
|
|
@@ -1109,12 +1110,9 @@ export const daytonaPlayRunnerBackend: PlayRunnerBackend = {
|
|
|
1109
1110
|
reportedSandboxIds.add(sandbox.id);
|
|
1110
1111
|
reportedSandboxEndTimes.set(sandbox.id, billingEndedAt);
|
|
1111
1112
|
};
|
|
1112
|
-
const reportRetiringRuntimeResource = async (
|
|
1113
|
-
|
|
1114
|
-
|
|
1115
|
-
billingStartedAt: number;
|
|
1116
|
-
billingEndedAt?: number;
|
|
1117
|
-
}) => {
|
|
1113
|
+
const reportRetiringRuntimeResource = async (
|
|
1114
|
+
acquired: AcquiredDaytonaSandbox,
|
|
1115
|
+
) => {
|
|
1118
1116
|
try {
|
|
1119
1117
|
await reportRuntimeResource(acquired);
|
|
1120
1118
|
} catch (error) {
|
|
@@ -1429,6 +1427,9 @@ export const daytonaPlayRunnerBackend: PlayRunnerBackend = {
|
|
|
1429
1427
|
}
|
|
1430
1428
|
throw error;
|
|
1431
1429
|
}
|
|
1430
|
+
if (isRuntimeSandboxCapacityLimitError(error)) {
|
|
1431
|
+
throw error;
|
|
1432
|
+
}
|
|
1432
1433
|
if (error instanceof DaytonaSandboxAcquisitionUnavailableError) {
|
|
1433
1434
|
throw error;
|
|
1434
1435
|
}
|
|
@@ -1,6 +1,9 @@
|
|
|
1
1
|
import type { ModalClient } from 'modal';
|
|
2
2
|
import type { PlayRunnerBackend, PlayRunnerCallbacks } from '../types';
|
|
3
|
-
import {
|
|
3
|
+
import {
|
|
4
|
+
isRuntimeSandboxCapacityLimitError,
|
|
5
|
+
RuntimeResourceFenceLostError,
|
|
6
|
+
} from '../types';
|
|
4
7
|
import { buildPlayRunnerBundle } from '../bundle';
|
|
5
8
|
import type {
|
|
6
9
|
PlayRunnerExecutionConfig,
|
|
@@ -23,6 +26,7 @@ import { stageRunnerPayload } from './daytona-payload-transport';
|
|
|
23
26
|
import { captureDetachedDaytonaRunnerReadinessBaseline } from './daytona-session-execution';
|
|
24
27
|
|
|
25
28
|
const MODAL_RUNNER_READY_TIMEOUT_MS = 30_000;
|
|
29
|
+
const MODAL_CAPACITY_RELEASE_RETRY_DELAYS_MS = [0, 100, 500, 2_000] as const;
|
|
26
30
|
type ModalSandbox = Awaited<ReturnType<ModalClient['sandboxes']['create']>>;
|
|
27
31
|
|
|
28
32
|
export function modalSandboxLifetimeMs(
|
|
@@ -99,13 +103,38 @@ async function confirmDetachedModalRunnerReady(input: {
|
|
|
99
103
|
);
|
|
100
104
|
}
|
|
101
105
|
|
|
102
|
-
async function terminateModalSandbox(sandbox: ModalSandbox): Promise<
|
|
103
|
-
|
|
106
|
+
async function terminateModalSandbox(sandbox: ModalSandbox): Promise<boolean> {
|
|
107
|
+
try {
|
|
108
|
+
// The default terminate call only acknowledges the request. Capacity may
|
|
109
|
+
// be released only after Modal confirms the sandbox itself has stopped.
|
|
110
|
+
await sandbox.terminate({ wait: true });
|
|
111
|
+
return true;
|
|
112
|
+
} catch (error) {
|
|
104
113
|
console.warn('[play-runner.modal.terminate_failed]', {
|
|
105
114
|
sandboxId: sandbox.sandboxId,
|
|
106
115
|
error: error instanceof Error ? error.message : String(error),
|
|
107
116
|
});
|
|
108
|
-
|
|
117
|
+
return false;
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
async function releaseModalSandboxCapacityAfterConfirmedTermination(input: {
|
|
122
|
+
leaseId: string;
|
|
123
|
+
release: NonNullable<PlayRunnerCallbacks['releaseSandboxCapacity']>;
|
|
124
|
+
}): Promise<void> {
|
|
125
|
+
let lastError: unknown;
|
|
126
|
+
for (const delayMs of MODAL_CAPACITY_RELEASE_RETRY_DELAYS_MS) {
|
|
127
|
+
if (delayMs > 0) {
|
|
128
|
+
await new Promise<void>((resolve) => setTimeout(resolve, delayMs));
|
|
129
|
+
}
|
|
130
|
+
try {
|
|
131
|
+
await input.release(input.leaseId);
|
|
132
|
+
return;
|
|
133
|
+
} catch (error) {
|
|
134
|
+
lastError = error;
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
throw lastError;
|
|
109
138
|
}
|
|
110
139
|
|
|
111
140
|
export const modalPlayRunnerBackend: PlayRunnerBackend = {
|
|
@@ -139,24 +168,35 @@ export const modalPlayRunnerBackend: PlayRunnerBackend = {
|
|
|
139
168
|
createIfMissing: true,
|
|
140
169
|
});
|
|
141
170
|
const image = modalConfig.client.images.fromRegistry(modalConfig.image);
|
|
142
|
-
const
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
171
|
+
const capacityReservation =
|
|
172
|
+
await callbacks?.reserveSandboxCapacity?.('modal');
|
|
173
|
+
const sandbox = await modalConfig.client.sandboxes
|
|
174
|
+
.create(app, image, {
|
|
175
|
+
cpu: MODAL_SANDBOX_CPU_CORES,
|
|
176
|
+
cpuLimit: MODAL_SANDBOX_CPU_CORES,
|
|
177
|
+
memoryMiB: MODAL_SANDBOX_MEMORY_MIB,
|
|
178
|
+
memoryLimitMiB: MODAL_SANDBOX_MEMORY_MIB,
|
|
179
|
+
timeoutMs: modalSandboxLifetimeMs(modalConfig.limits),
|
|
180
|
+
idleTimeoutMs: modalSandboxLifetimeMs(modalConfig.limits),
|
|
181
|
+
workdir: modalConfig.workdir,
|
|
182
|
+
tags: {
|
|
183
|
+
source: 'deepline-play-runner',
|
|
184
|
+
orgId: config.context.orgId ?? 'unknown',
|
|
185
|
+
workflowId: config.context.workflowId ?? 'unknown',
|
|
186
|
+
runId: config.context.runId ?? 'unknown',
|
|
187
|
+
},
|
|
188
|
+
...(modalConfig.outboundCidrAllowlist
|
|
189
|
+
? { outboundCidrAllowlist: modalConfig.outboundCidrAllowlist }
|
|
190
|
+
: {}),
|
|
191
|
+
})
|
|
192
|
+
.catch(async (error) => {
|
|
193
|
+
if (capacityReservation) {
|
|
194
|
+
await callbacks?.releaseSandboxCapacity?.(
|
|
195
|
+
capacityReservation.leaseId,
|
|
196
|
+
);
|
|
197
|
+
}
|
|
198
|
+
throw error;
|
|
199
|
+
});
|
|
160
200
|
const billingStartedAt = Date.now();
|
|
161
201
|
runtimeTiming.modalCreateMs = billingStartedAt - startedAt;
|
|
162
202
|
emitModalStage(config.context, 'create:done', {
|
|
@@ -164,6 +204,7 @@ export const modalPlayRunnerBackend: PlayRunnerBackend = {
|
|
|
164
204
|
elapsedMs: runtimeTiming.modalCreateMs,
|
|
165
205
|
});
|
|
166
206
|
let detached = false;
|
|
207
|
+
let setupError: unknown;
|
|
167
208
|
let cancelExecution!: (error: Error) => void;
|
|
168
209
|
const cancellationPromise = new Promise<never>((_resolve, reject) => {
|
|
169
210
|
cancelExecution = reject;
|
|
@@ -192,6 +233,9 @@ export const modalPlayRunnerBackend: PlayRunnerBackend = {
|
|
|
192
233
|
cpu: MODAL_SANDBOX_CPU_CORES,
|
|
193
234
|
memoryGiB: MODAL_SANDBOX_MEMORY_MIB / 1024,
|
|
194
235
|
diskGiB: 0,
|
|
236
|
+
...(capacityReservation
|
|
237
|
+
? { sandboxCapacityLeaseId: capacityReservation.leaseId }
|
|
238
|
+
: {}),
|
|
195
239
|
});
|
|
196
240
|
} catch (error) {
|
|
197
241
|
runtimeResourceRegistrationError = error;
|
|
@@ -290,14 +334,45 @@ export const modalPlayRunnerBackend: PlayRunnerBackend = {
|
|
|
290
334
|
tableNamespace: null,
|
|
291
335
|
runtimeTiming,
|
|
292
336
|
};
|
|
337
|
+
} catch (error) {
|
|
338
|
+
setupError = error;
|
|
339
|
+
throw error;
|
|
293
340
|
} finally {
|
|
294
341
|
callbacks?.cancellationSignal?.removeEventListener('abort', onCancel);
|
|
295
|
-
if (!detached
|
|
342
|
+
if (!detached && (await terminateModalSandbox(sandbox))) {
|
|
343
|
+
// This branch synchronously confirms that the just-created resource
|
|
344
|
+
// is gone. Do not keep its admission lease until the cleanup loop
|
|
345
|
+
// sees a resource that no longer exists.
|
|
346
|
+
if (capacityReservation && callbacks?.releaseSandboxCapacity) {
|
|
347
|
+
try {
|
|
348
|
+
await releaseModalSandboxCapacityAfterConfirmedTermination({
|
|
349
|
+
leaseId: capacityReservation.leaseId,
|
|
350
|
+
release: callbacks.releaseSandboxCapacity,
|
|
351
|
+
});
|
|
352
|
+
} catch (releaseError) {
|
|
353
|
+
// The scheduler fence is the primary correctness signal. Keep
|
|
354
|
+
// it intact if its best-effort early capacity release is
|
|
355
|
+
// transiently unavailable; durable cleanup will retry release.
|
|
356
|
+
if (setupError) {
|
|
357
|
+
console.error('[play-runner.modal.capacity_release_failed]', {
|
|
358
|
+
sandboxId: sandbox.sandboxId,
|
|
359
|
+
error:
|
|
360
|
+
releaseError instanceof Error
|
|
361
|
+
? releaseError.message
|
|
362
|
+
: String(releaseError),
|
|
363
|
+
});
|
|
364
|
+
} else {
|
|
365
|
+
throw releaseError;
|
|
366
|
+
}
|
|
367
|
+
}
|
|
368
|
+
}
|
|
369
|
+
}
|
|
296
370
|
}
|
|
297
371
|
} catch (error) {
|
|
298
372
|
if (
|
|
299
373
|
error === runtimeResourceRegistrationError ||
|
|
300
|
-
error instanceof RuntimeResourceFenceLostError
|
|
374
|
+
error instanceof RuntimeResourceFenceLostError ||
|
|
375
|
+
isRuntimeSandboxCapacityLimitError(error)
|
|
301
376
|
) {
|
|
302
377
|
throw error;
|
|
303
378
|
}
|
|
@@ -335,7 +410,7 @@ export async function deleteModalSandboxById(input: {
|
|
|
335
410
|
appId: expectedAppId,
|
|
336
411
|
})) {
|
|
337
412
|
if (sandbox.sandboxId !== input.sandboxId) continue;
|
|
338
|
-
await sandbox.terminate();
|
|
413
|
+
await sandbox.terminate({ wait: true });
|
|
339
414
|
return { kind: 'deleted', appId: expectedAppId };
|
|
340
415
|
}
|
|
341
416
|
// Cleanup means ensure absent. An exact app-scoped inventory proving the
|
|
@@ -28,6 +28,8 @@ export type PlayRunnerRuntimeResource = {
|
|
|
28
28
|
cpu?: number | null;
|
|
29
29
|
memoryGiB?: number | null;
|
|
30
30
|
diskGiB?: number | null;
|
|
31
|
+
/** Scheduler-only soft-capacity reservation made before provider create. */
|
|
32
|
+
sandboxCapacityLeaseId?: string;
|
|
31
33
|
};
|
|
32
34
|
|
|
33
35
|
export type RuntimeResourceTerminalReason =
|
|
@@ -64,6 +66,23 @@ export class RuntimeResourceFenceLostError extends Error {
|
|
|
64
66
|
}
|
|
65
67
|
}
|
|
66
68
|
|
|
69
|
+
/**
|
|
70
|
+
* A scheduler-owned admission result crosses the provider-backend boundary.
|
|
71
|
+
* Keep it typed by name here so the shared backend can rethrow it without an
|
|
72
|
+
* import from the app-owned scheduler implementation.
|
|
73
|
+
*/
|
|
74
|
+
export const RUNTIME_SANDBOX_CAPACITY_LIMIT_ERROR_NAME =
|
|
75
|
+
'RuntimeSandboxCapacityLimitError';
|
|
76
|
+
|
|
77
|
+
export function isRuntimeSandboxCapacityLimitError(
|
|
78
|
+
error: unknown,
|
|
79
|
+
): error is Error {
|
|
80
|
+
return (
|
|
81
|
+
error instanceof Error &&
|
|
82
|
+
error.name === RUNTIME_SANDBOX_CAPACITY_LIMIT_ERROR_NAME
|
|
83
|
+
);
|
|
84
|
+
}
|
|
85
|
+
|
|
67
86
|
export interface PlayRunnerCallbacks {
|
|
68
87
|
onLog?: (event: PlayRunnerLogEvent) => void;
|
|
69
88
|
onCheckpoint?: (checkpoint: PlayCheckpoint) => void;
|
|
@@ -72,6 +91,12 @@ export interface PlayRunnerCallbacks {
|
|
|
72
91
|
onRuntimeResourceAcquired?: (
|
|
73
92
|
resource: PlayRunnerRuntimeResource,
|
|
74
93
|
) => void | Promise<void>;
|
|
94
|
+
/** Reserve one physical-sandbox slot immediately before a provider create. */
|
|
95
|
+
reserveSandboxCapacity?: (
|
|
96
|
+
provider: 'daytona' | 'modal',
|
|
97
|
+
) => Promise<{ leaseId: string }>;
|
|
98
|
+
/** Release an unbound reservation when provider create never produced a resource. */
|
|
99
|
+
releaseSandboxCapacity?: (leaseId: string) => Promise<void>;
|
|
75
100
|
/**
|
|
76
101
|
* Durable scheduler-owned evidence for a provider create call. Backends
|
|
77
102
|
* await this callback at the create boundary; it is not best-effort logging.
|
|
@@ -13,7 +13,6 @@ export const RUNTIME_CAPACITY_POLICY = {
|
|
|
13
13
|
/** 32 Machines x 8 claim slots permits 256 concurrent launch/resume legs. */
|
|
14
14
|
maxActiveLaneMachines: 32,
|
|
15
15
|
workerSlotsPerMachine: 8,
|
|
16
|
-
perOrgConcurrency: 4,
|
|
17
16
|
/** Queue age is an escape hatch for small-but-stuck backlogs. */
|
|
18
17
|
scaleUpQueueAgeMs: 10_000,
|
|
19
18
|
/** Bound one Fly reconciliation without turning backlog into a stampede. */
|
|
@@ -113,14 +112,6 @@ export function assertRuntimeCapacityPolicy(): void {
|
|
|
113
112
|
'Receipt claim operations must leave at least five seconds for cancellation and response delivery.',
|
|
114
113
|
);
|
|
115
114
|
}
|
|
116
|
-
if (
|
|
117
|
-
RUNTIME_CAPACITY_POLICY.absurd.perOrgConcurrency >
|
|
118
|
-
ABSURD_GLOBAL_RUN_CONCURRENCY
|
|
119
|
-
) {
|
|
120
|
-
throw new Error(
|
|
121
|
-
'Per-org runtime concurrency cannot exceed global runtime concurrency.',
|
|
122
|
-
);
|
|
123
|
-
}
|
|
124
115
|
if (
|
|
125
116
|
RUNTIME_CAPACITY_POLICY.absurd.activeLaneMachines >
|
|
126
117
|
RUNTIME_CAPACITY_POLICY.absurd.maxActiveLaneMachines
|
|
@@ -8,11 +8,12 @@ export function transientServiceUnavailableDetail(
|
|
|
8
8
|
if (error instanceof TypeError && /^fetch failed$/i.test(detail.trim())) {
|
|
9
9
|
return detail;
|
|
10
10
|
}
|
|
11
|
-
// Convex uses
|
|
12
|
-
// InternalServerError envelope for short control-plane
|
|
13
|
-
// text is intentionally exact: application exceptions
|
|
14
|
-
// retryable merely because they mention an internal-server
|
|
15
|
-
|
|
11
|
+
// Convex uses explicit ServiceUnavailable and ExpiredInQueue responses, as
|
|
12
|
+
// well as this generic InternalServerError envelope for short control-plane
|
|
13
|
+
// outages. The latter text is intentionally exact: application exceptions
|
|
14
|
+
// must not become retryable merely because they mention an internal-server
|
|
15
|
+
// error.
|
|
16
|
+
return /ServiceUnavailable|Service temporarily unavailable|temporarily unavailable \(Code:\s*\d+\)|"code"\s*:\s*"ExpiredInQueue"|"code"\s*:\s*"InternalServerError"[\s\S]*(?:Your request couldn't be completed\. Try again later\.|Your request timed out performing too many system operations\.)/i.test(
|
|
16
17
|
detail,
|
|
17
18
|
)
|
|
18
19
|
? detail
|
|
@@ -71,6 +71,7 @@ export const PLAY_AUTHORING_CONTRACT_ISSUE_CODES = [
|
|
|
71
71
|
'play_authoring_fetch_key_reused_in_loop',
|
|
72
72
|
'play_authoring_binding_invalid',
|
|
73
73
|
'play_authoring_input_schema_unresolved',
|
|
74
|
+
'tool_response_raw_access',
|
|
74
75
|
'check_error',
|
|
75
76
|
'docflow_compile_error',
|
|
76
77
|
'docflow_binding_drift',
|
|
@@ -223,9 +223,12 @@ function sourceIdentityPath(
|
|
|
223
223
|
): string {
|
|
224
224
|
if (!sourceIdentityRoot) return filePath;
|
|
225
225
|
const identityRoot = resolve(sourceIdentityRoot);
|
|
226
|
-
const
|
|
226
|
+
const resolvedFilePath = resolve(filePath);
|
|
227
|
+
const logicalPath = relative(identityRoot, resolvedFilePath);
|
|
228
|
+
if (!logicalPath) {
|
|
229
|
+
return basename(resolvedFilePath);
|
|
230
|
+
}
|
|
227
231
|
if (
|
|
228
|
-
!logicalPath ||
|
|
229
232
|
logicalPath === '..' ||
|
|
230
233
|
logicalPath.startsWith(`..${sep}`) ||
|
|
231
234
|
isAbsolute(logicalPath)
|
package/dist/cli/index.js
CHANGED
|
@@ -185,7 +185,7 @@ function configureProxyFromEnv() {
|
|
|
185
185
|
configureProxyFromEnv();
|
|
186
186
|
|
|
187
187
|
// src/cli/index.ts
|
|
188
|
-
var
|
|
188
|
+
var import_promises10 = require("fs/promises");
|
|
189
189
|
var import_node_path26 = require("path");
|
|
190
190
|
var import_node_os19 = require("os");
|
|
191
191
|
var import_commander4 = require("commander");
|
|
@@ -1047,7 +1047,7 @@ var SDK_RELEASE = {
|
|
|
1047
1047
|
// 0.3.0 introduces raw-v2: complete scrubbed provider responses are
|
|
1048
1048
|
// available at toolResponse.rawV2 while toolResponse.raw and all declared
|
|
1049
1049
|
// getters keep their established compatibility behavior.
|
|
1050
|
-
version: "0.3.
|
|
1050
|
+
version: "0.3.1",
|
|
1051
1051
|
updateSummary: "New raw-v2 tool responses preserve complete scrubbed provider envelopes at toolResponse.rawV2, including JSON:API included resources, links, cursors, and totals. Existing toolResponse.raw and declared getters keep working unchanged.",
|
|
1052
1052
|
contracts: {
|
|
1053
1053
|
api: {
|
|
@@ -11068,7 +11068,7 @@ Examples:
|
|
|
11068
11068
|
}
|
|
11069
11069
|
|
|
11070
11070
|
// src/cli/commands/enrich.ts
|
|
11071
|
-
var
|
|
11071
|
+
var import_promises7 = require("fs/promises");
|
|
11072
11072
|
var import_node_os10 = require("os");
|
|
11073
11073
|
var import_node_path15 = require("path");
|
|
11074
11074
|
var import_commander2 = require("commander");
|
|
@@ -11076,7 +11076,7 @@ var import_commander2 = require("commander");
|
|
|
11076
11076
|
// src/cli/commands/play.ts
|
|
11077
11077
|
var import_node_crypto6 = require("crypto");
|
|
11078
11078
|
var import_node_fs12 = require("fs");
|
|
11079
|
-
var
|
|
11079
|
+
var import_promises6 = require("fs/promises");
|
|
11080
11080
|
var import_node_path14 = require("path");
|
|
11081
11081
|
var import_sync5 = require("csv-parse/sync");
|
|
11082
11082
|
|
|
@@ -12784,6 +12784,7 @@ var import_node_os9 = require("os");
|
|
|
12784
12784
|
var import_node_path13 = require("path");
|
|
12785
12785
|
var import_node_url = require("url");
|
|
12786
12786
|
var import_node_fs11 = require("fs");
|
|
12787
|
+
var import_promises5 = require("fs/promises");
|
|
12787
12788
|
|
|
12788
12789
|
// ../shared_libs/plays/bundling/index.ts
|
|
12789
12790
|
var import_node_crypto4 = require("crypto");
|
|
@@ -18314,7 +18315,7 @@ var PLAY_RUN_RESERVED_BOOLEAN_FLAGS = /* @__PURE__ */ new Set([
|
|
|
18314
18315
|
]);
|
|
18315
18316
|
async function pathExistsIncludingSymlink(path) {
|
|
18316
18317
|
try {
|
|
18317
|
-
await (0,
|
|
18318
|
+
await (0, import_promises6.lstat)(path);
|
|
18318
18319
|
return true;
|
|
18319
18320
|
} catch (error) {
|
|
18320
18321
|
if (error.code === "ENOENT") {
|
|
@@ -18331,7 +18332,7 @@ function runIdFileTempPath(destination) {
|
|
|
18331
18332
|
}
|
|
18332
18333
|
async function removeRunIdTempFile(path) {
|
|
18333
18334
|
try {
|
|
18334
|
-
await (0,
|
|
18335
|
+
await (0, import_promises6.unlink)(path);
|
|
18335
18336
|
} catch (error) {
|
|
18336
18337
|
if (error.code !== "ENOENT") {
|
|
18337
18338
|
throw error;
|
|
@@ -18348,7 +18349,7 @@ async function preflightRunIdFile(path) {
|
|
|
18348
18349
|
const tempPath = runIdFileTempPath(destination);
|
|
18349
18350
|
let handle = null;
|
|
18350
18351
|
try {
|
|
18351
|
-
handle = await (0,
|
|
18352
|
+
handle = await (0, import_promises6.open)(tempPath, "wx", 384);
|
|
18352
18353
|
await handle.sync();
|
|
18353
18354
|
} catch (error) {
|
|
18354
18355
|
throw new Error(
|
|
@@ -18366,7 +18367,7 @@ async function readRunIdFile(destination) {
|
|
|
18366
18367
|
}
|
|
18367
18368
|
let parsed;
|
|
18368
18369
|
try {
|
|
18369
|
-
parsed = JSON.parse(await (0,
|
|
18370
|
+
parsed = JSON.parse(await (0, import_promises6.readFile)(destination, "utf8"));
|
|
18370
18371
|
} catch (error) {
|
|
18371
18372
|
throw new Error(
|
|
18372
18373
|
`--run-id-file destination contains invalid JSON: ${destination}. Refusing to overwrite it (${error instanceof Error ? error.message : String(error)}).`
|
|
@@ -18390,7 +18391,7 @@ async function writePlayRunIdFile(destination, runId) {
|
|
|
18390
18391
|
const tempPath = runIdFileTempPath(destination);
|
|
18391
18392
|
let handle = null;
|
|
18392
18393
|
try {
|
|
18393
|
-
handle = await (0,
|
|
18394
|
+
handle = await (0, import_promises6.open)(tempPath, "wx", 384);
|
|
18394
18395
|
await handle.writeFile(
|
|
18395
18396
|
`${JSON.stringify({ version: 1, runId })}
|
|
18396
18397
|
`,
|
|
@@ -18400,8 +18401,8 @@ async function writePlayRunIdFile(destination, runId) {
|
|
|
18400
18401
|
await handle.close();
|
|
18401
18402
|
handle = null;
|
|
18402
18403
|
try {
|
|
18403
|
-
await (0,
|
|
18404
|
-
const directory = await (0,
|
|
18404
|
+
await (0, import_promises6.link)(tempPath, destination);
|
|
18405
|
+
const directory = await (0, import_promises6.open)((0, import_node_path14.dirname)(destination), "r");
|
|
18405
18406
|
try {
|
|
18406
18407
|
await directory.sync();
|
|
18407
18408
|
} finally {
|
|
@@ -27693,6 +27694,7 @@ var ENRICH_DEBUG_T0 = Date.now();
|
|
|
27693
27694
|
var GENERATED_ENRICH_ROWS_TABLE_NAMESPACE = "deepline_enrich_rows";
|
|
27694
27695
|
var SDK_ENRICH_TELEMETRY_TIMEOUT_MS = 1e4;
|
|
27695
27696
|
var SDK_ENRICH_TELEMETRY_COMMAND_MAX_LENGTH = 2e4;
|
|
27697
|
+
var ENRICH_DEPRECATION_NOTICE = "DEPRECATION: `deepline enrich` is deprecated and no longer actively maintained. Use `deepline plays` for the supported workflow.\n";
|
|
27696
27698
|
var ENRICH_AI_RUNTIME_COMPACT_TOOLS = /* @__PURE__ */ new Set([
|
|
27697
27699
|
"ai_inference",
|
|
27698
27700
|
"aiinference",
|
|
@@ -27866,7 +27868,7 @@ async function readAtFileReference(value, argumentName, strip = true) {
|
|
|
27866
27868
|
throw new Error(`Invalid ${argumentName} value: empty @file path.`);
|
|
27867
27869
|
}
|
|
27868
27870
|
try {
|
|
27869
|
-
const text = await (0,
|
|
27871
|
+
const text = await (0, import_promises7.readFile)(filePath, "utf8");
|
|
27870
27872
|
const normalized = text.replace(/^\uFEFF/, "");
|
|
27871
27873
|
return strip ? normalized.trim() : normalized;
|
|
27872
27874
|
} catch (error) {
|
|
@@ -28164,7 +28166,7 @@ async function buildPlanArgs(args) {
|
|
|
28164
28166
|
async function assertInputCsvExists(inputCsv) {
|
|
28165
28167
|
const path = (0, import_node_path15.resolve)(inputCsv);
|
|
28166
28168
|
try {
|
|
28167
|
-
const info = await (0,
|
|
28169
|
+
const info = await (0, import_promises7.stat)(path);
|
|
28168
28170
|
if (info.isFile()) {
|
|
28169
28171
|
return;
|
|
28170
28172
|
}
|
|
@@ -28185,8 +28187,8 @@ async function assertSafeOutputPath(inputCsv, outputPath) {
|
|
|
28185
28187
|
}
|
|
28186
28188
|
try {
|
|
28187
28189
|
const [inputInfo, outputInfo] = await Promise.all([
|
|
28188
|
-
(0,
|
|
28189
|
-
(0,
|
|
28190
|
+
(0, import_promises7.stat)(input2),
|
|
28191
|
+
(0, import_promises7.stat)(output2)
|
|
28190
28192
|
]);
|
|
28191
28193
|
if (inputInfo.dev === outputInfo.dev && inputInfo.ino === outputInfo.ino) {
|
|
28192
28194
|
throw new Error(
|
|
@@ -28206,7 +28208,7 @@ async function assertSafeOutputPath(inputCsv, outputPath) {
|
|
|
28206
28208
|
}
|
|
28207
28209
|
async function regularFileExists(path) {
|
|
28208
28210
|
try {
|
|
28209
|
-
const info = await (0,
|
|
28211
|
+
const info = await (0, import_promises7.stat)((0, import_node_path15.resolve)(path));
|
|
28210
28212
|
return info.isFile();
|
|
28211
28213
|
} catch (error) {
|
|
28212
28214
|
const code = error && typeof error === "object" ? error.code : void 0;
|
|
@@ -28217,7 +28219,7 @@ async function regularFileExists(path) {
|
|
|
28217
28219
|
}
|
|
28218
28220
|
}
|
|
28219
28221
|
async function readConfig(path) {
|
|
28220
|
-
const source = await (0,
|
|
28222
|
+
const source = await (0, import_promises7.readFile)((0, import_node_path15.resolve)(path), "utf8");
|
|
28221
28223
|
let parsed;
|
|
28222
28224
|
try {
|
|
28223
28225
|
parsed = JSON.parse(source);
|
|
@@ -28638,7 +28640,7 @@ async function writeOutputCsv(outputPath, status, options) {
|
|
|
28638
28640
|
]),
|
|
28639
28641
|
options?.config
|
|
28640
28642
|
);
|
|
28641
|
-
await (0,
|
|
28643
|
+
await (0, import_promises7.writeFile)(
|
|
28642
28644
|
(0, import_node_path15.resolve)(outputPath),
|
|
28643
28645
|
csvStringFromRows(merged.rows, columns),
|
|
28644
28646
|
"utf8"
|
|
@@ -30318,7 +30320,7 @@ async function persistEnrichFailureReport(input2) {
|
|
|
30318
30320
|
}
|
|
30319
30321
|
const stateDir = (0, import_node_path15.join)((0, import_node_os10.homedir)(), ".local", "deepline", "runtime", "state");
|
|
30320
30322
|
const reportPrefix = input2.jobs.length > 0 ? "run-block-failures" : "enrich-issues";
|
|
30321
|
-
await (0,
|
|
30323
|
+
await (0, import_promises7.mkdir)(stateDir, { recursive: true });
|
|
30322
30324
|
const reportPath = (0, import_node_path15.join)(
|
|
30323
30325
|
stateDir,
|
|
30324
30326
|
`${reportPrefix}-${Math.floor(Date.now() / 1e3)}-${process.pid}.json`
|
|
@@ -30344,7 +30346,7 @@ async function persistEnrichFailureReport(input2) {
|
|
|
30344
30346
|
if (input2.rows.rowStart !== null && input2.rows.rowEnd !== null) {
|
|
30345
30347
|
report.rows = { start: input2.rows.rowStart, end: input2.rows.rowEnd };
|
|
30346
30348
|
}
|
|
30347
|
-
await (0,
|
|
30349
|
+
await (0, import_promises7.writeFile)(reportPath, `${JSON.stringify(report, null, 2)}
|
|
30348
30350
|
`, "utf8");
|
|
30349
30351
|
return reportPath;
|
|
30350
30352
|
}
|
|
@@ -31162,6 +31164,7 @@ function registerEnrichCommand(program) {
|
|
|
31162
31164
|
"--max-credits-per-run <credits>",
|
|
31163
31165
|
"Set a hard Deepline-credit ceiling enforced by the runtime for this run."
|
|
31164
31166
|
).action(async (options, _command) => {
|
|
31167
|
+
process.stderr.write(ENRICH_DEPRECATION_NOTICE);
|
|
31165
31168
|
if (currentEnrichArgs().some(
|
|
31166
31169
|
(arg) => arg === "--no-open" || arg.startsWith("--no-open=")
|
|
31167
31170
|
)) {
|
|
@@ -31247,13 +31250,13 @@ function registerEnrichCommand(program) {
|
|
|
31247
31250
|
sdkEnrichTelemetryCompleted = true;
|
|
31248
31251
|
await completeSdkEnrichTelemetry(sdkEnrichTelemetry, input2);
|
|
31249
31252
|
};
|
|
31250
|
-
const tempDir = await (0,
|
|
31253
|
+
const tempDir = await (0, import_promises7.mkdtemp)((0, import_node_path15.join)((0, import_node_os10.tmpdir)(), "deepline-enrich-play-"));
|
|
31251
31254
|
await emitSdkEnrichTelemetry(sdkEnrichTelemetry, "enrich_started");
|
|
31252
31255
|
const tempPlay = (0, import_node_path15.join)(tempDir, "deepline-enrich.play.ts");
|
|
31253
31256
|
let inPlaceTempDir = null;
|
|
31254
31257
|
let inPlaceTempOutputPath = null;
|
|
31255
31258
|
const inPlaceFinalOutputPath = options.inPlace ? (0, import_node_path15.resolve)(inputCsv) : null;
|
|
31256
|
-
const inPlaceCommitOutputPath = options.inPlace ? (await (0,
|
|
31259
|
+
const inPlaceCommitOutputPath = options.inPlace ? (await (0, import_promises7.lstat)(inputCsv)).isSymbolicLink() ? await (0, import_promises7.realpath)(inputCsv) : inPlaceFinalOutputPath : null;
|
|
31257
31260
|
const failureReportOutputPath = options.inPlace ? inPlaceFinalOutputPath : null;
|
|
31258
31261
|
const enrichIssueFollowUpOutputPath = options.inPlace && inPlaceFinalOutputPath ? sidecarEnrichRowsExportPath(inPlaceFinalOutputPath) : null;
|
|
31259
31262
|
let activeRunId2 = null;
|
|
@@ -31274,16 +31277,16 @@ function registerEnrichCommand(program) {
|
|
|
31274
31277
|
return;
|
|
31275
31278
|
}
|
|
31276
31279
|
if (inPlaceTempDir) {
|
|
31277
|
-
await (0,
|
|
31280
|
+
await (0, import_promises7.rm)(inPlaceTempDir, { recursive: true, force: true });
|
|
31278
31281
|
}
|
|
31279
|
-
inPlaceTempDir = await (0,
|
|
31282
|
+
inPlaceTempDir = await (0, import_promises7.mkdtemp)(
|
|
31280
31283
|
(0, import_node_path15.join)(
|
|
31281
31284
|
(0, import_node_path15.dirname)(inPlaceCommitOutputPath ?? (0, import_node_path15.resolve)(inputCsv)),
|
|
31282
31285
|
".deepline-enrich-in-place-"
|
|
31283
31286
|
)
|
|
31284
31287
|
);
|
|
31285
31288
|
inPlaceTempOutputPath = (0, import_node_path15.join)(inPlaceTempDir, "output.csv");
|
|
31286
|
-
await (0,
|
|
31289
|
+
await (0, import_promises7.copyFile)((0, import_node_path15.resolve)(inputCsv), inPlaceTempOutputPath);
|
|
31287
31290
|
outputPath = inPlaceTempOutputPath;
|
|
31288
31291
|
};
|
|
31289
31292
|
const commitInPlaceOutput = async (exportResult) => {
|
|
@@ -31291,12 +31294,12 @@ function registerEnrichCommand(program) {
|
|
|
31291
31294
|
return exportResult;
|
|
31292
31295
|
}
|
|
31293
31296
|
const committedTempDir = inPlaceTempDir;
|
|
31294
|
-
await (0,
|
|
31297
|
+
await (0, import_promises7.rename)(inPlaceTempOutputPath, inPlaceCommitOutputPath);
|
|
31295
31298
|
inPlaceTempDir = null;
|
|
31296
31299
|
inPlaceTempOutputPath = null;
|
|
31297
31300
|
outputPath = inPlaceFinalOutputPath;
|
|
31298
31301
|
if (committedTempDir) {
|
|
31299
|
-
await (0,
|
|
31302
|
+
await (0, import_promises7.rm)(committedTempDir, { recursive: true, force: true });
|
|
31300
31303
|
}
|
|
31301
31304
|
if (!exportResult) {
|
|
31302
31305
|
return null;
|
|
@@ -31309,7 +31312,7 @@ function registerEnrichCommand(program) {
|
|
|
31309
31312
|
try {
|
|
31310
31313
|
process.once("SIGINT", onSigint);
|
|
31311
31314
|
process.once("SIGTERM", onSigterm);
|
|
31312
|
-
await (0,
|
|
31315
|
+
await (0, import_promises7.writeFile)(tempPlay, playSource, "utf8");
|
|
31313
31316
|
if (options.inPlace) {
|
|
31314
31317
|
await prepareInPlaceOutput();
|
|
31315
31318
|
}
|
|
@@ -31503,11 +31506,11 @@ function registerEnrichCommand(program) {
|
|
|
31503
31506
|
process.removeListener("SIGINT", onSigint);
|
|
31504
31507
|
process.removeListener("SIGTERM", onSigterm);
|
|
31505
31508
|
if (inPlaceTempDir) {
|
|
31506
|
-
await (0,
|
|
31509
|
+
await (0, import_promises7.rm)(inPlaceTempDir, { recursive: true, force: true });
|
|
31507
31510
|
} else if (inPlaceTempOutputPath) {
|
|
31508
|
-
await (0,
|
|
31511
|
+
await (0, import_promises7.rm)(inPlaceTempOutputPath, { force: true });
|
|
31509
31512
|
}
|
|
31510
|
-
await (0,
|
|
31513
|
+
await (0, import_promises7.rm)(tempDir, { recursive: true, force: true });
|
|
31511
31514
|
}
|
|
31512
31515
|
});
|
|
31513
31516
|
}
|
|
@@ -32399,7 +32402,7 @@ Examples:
|
|
|
32399
32402
|
|
|
32400
32403
|
// src/cli/commands/monitors.ts
|
|
32401
32404
|
var import_node_fs14 = require("fs");
|
|
32402
|
-
var
|
|
32405
|
+
var import_promises8 = require("readline/promises");
|
|
32403
32406
|
var JSON_OPTION_DESCRIPTION = "Emit JSON output. Also automatic when stdout is piped";
|
|
32404
32407
|
function withJsonOption(command) {
|
|
32405
32408
|
return command.option("--json", JSON_OPTION_DESCRIPTION);
|
|
@@ -33168,7 +33171,7 @@ async function handleMonitorsValidate(key, options) {
|
|
|
33168
33171
|
if (result.valid === false) process.exitCode = 7;
|
|
33169
33172
|
}
|
|
33170
33173
|
async function confirmMonitorDelete(key, options) {
|
|
33171
|
-
const rl = (0,
|
|
33174
|
+
const rl = (0, import_promises8.createInterface)({
|
|
33172
33175
|
input: process.stdin,
|
|
33173
33176
|
output: process.stderr
|
|
33174
33177
|
});
|
|
@@ -40185,7 +40188,7 @@ Examples:
|
|
|
40185
40188
|
}
|
|
40186
40189
|
|
|
40187
40190
|
// src/cli/commands/workflow.ts
|
|
40188
|
-
var
|
|
40191
|
+
var import_promises9 = require("fs/promises");
|
|
40189
40192
|
var import_node_path24 = require("path");
|
|
40190
40193
|
|
|
40191
40194
|
// src/cli/workflow-to-play.ts
|
|
@@ -40403,7 +40406,7 @@ function readStatus(payload) {
|
|
|
40403
40406
|
}
|
|
40404
40407
|
async function readJsonOption(payload, file) {
|
|
40405
40408
|
if (file) {
|
|
40406
|
-
const raw = await (0,
|
|
40409
|
+
const raw = await (0, import_promises9.readFile)((0, import_node_path24.resolve)(file), "utf8");
|
|
40407
40410
|
return JSON.parse(raw);
|
|
40408
40411
|
}
|
|
40409
40412
|
if (payload) {
|
|
@@ -40438,8 +40441,8 @@ async function transformOne(api, workflowId, outDir, publish) {
|
|
|
40438
40441
|
{ workflowName: workflow.name, version: revision.version }
|
|
40439
40442
|
);
|
|
40440
40443
|
const file = (0, import_node_path24.join)((0, import_node_path24.resolve)(outDir), `${compiled.playName}.play.ts`);
|
|
40441
|
-
await (0,
|
|
40442
|
-
await (0,
|
|
40444
|
+
await (0, import_promises9.mkdir)((0, import_node_path24.dirname)(file), { recursive: true });
|
|
40445
|
+
await (0, import_promises9.writeFile)(file, compiled.sourceCode, "utf8");
|
|
40443
40446
|
let published = false;
|
|
40444
40447
|
if (publish) {
|
|
40445
40448
|
const code = await handlePlayPublish([file]);
|
|
@@ -41573,10 +41576,10 @@ function topLevelCommandKnown(program, commandName) {
|
|
|
41573
41576
|
);
|
|
41574
41577
|
}
|
|
41575
41578
|
async function runPlayRunnerHealthCheck() {
|
|
41576
|
-
const dir = await (0,
|
|
41579
|
+
const dir = await (0, import_promises10.mkdtemp)((0, import_node_path26.join)((0, import_node_os19.tmpdir)(), "deepline-health-play-"));
|
|
41577
41580
|
const file = (0, import_node_path26.join)(dir, "health-check.play.ts");
|
|
41578
41581
|
try {
|
|
41579
|
-
await (0,
|
|
41582
|
+
await (0, import_promises10.writeFile)(
|
|
41580
41583
|
file,
|
|
41581
41584
|
[
|
|
41582
41585
|
"import { definePlay } from 'deepline';",
|
|
@@ -41625,7 +41628,7 @@ async function runPlayRunnerHealthCheck() {
|
|
|
41625
41628
|
}
|
|
41626
41629
|
};
|
|
41627
41630
|
} finally {
|
|
41628
|
-
await (0,
|
|
41631
|
+
await (0, import_promises10.rm)(dir, { recursive: true, force: true });
|
|
41629
41632
|
}
|
|
41630
41633
|
}
|
|
41631
41634
|
function pickString(value, ...keys) {
|
package/dist/cli/index.mjs
CHANGED
|
@@ -1033,7 +1033,7 @@ var SDK_RELEASE = {
|
|
|
1033
1033
|
// 0.3.0 introduces raw-v2: complete scrubbed provider responses are
|
|
1034
1034
|
// available at toolResponse.rawV2 while toolResponse.raw and all declared
|
|
1035
1035
|
// getters keep their established compatibility behavior.
|
|
1036
|
-
version: "0.3.
|
|
1036
|
+
version: "0.3.1",
|
|
1037
1037
|
updateSummary: "New raw-v2 tool responses preserve complete scrubbed provider envelopes at toolResponse.rawV2, including JSON:API included resources, links, cursors, and totals. Existing toolResponse.raw and declared getters keep working unchanged.",
|
|
1038
1038
|
contracts: {
|
|
1039
1039
|
api: {
|
|
@@ -11080,7 +11080,7 @@ import {
|
|
|
11080
11080
|
mkdir as mkdir4,
|
|
11081
11081
|
mkdtemp,
|
|
11082
11082
|
readFile as readFile3,
|
|
11083
|
-
realpath as
|
|
11083
|
+
realpath as realpath3,
|
|
11084
11084
|
rename,
|
|
11085
11085
|
rm,
|
|
11086
11086
|
stat as stat3,
|
|
@@ -12828,6 +12828,7 @@ import { tmpdir as tmpdir3 } from "os";
|
|
|
12828
12828
|
import { dirname as dirname8, join as join9, resolve as resolve10 } from "path";
|
|
12829
12829
|
import { fileURLToPath } from "url";
|
|
12830
12830
|
import { existsSync as existsSync8 } from "fs";
|
|
12831
|
+
import { realpath as realpath2 } from "fs/promises";
|
|
12831
12832
|
|
|
12832
12833
|
// ../shared_libs/plays/bundling/index.ts
|
|
12833
12834
|
import { createHash as createHash2 } from "crypto";
|
|
@@ -27753,6 +27754,7 @@ var ENRICH_DEBUG_T0 = Date.now();
|
|
|
27753
27754
|
var GENERATED_ENRICH_ROWS_TABLE_NAMESPACE = "deepline_enrich_rows";
|
|
27754
27755
|
var SDK_ENRICH_TELEMETRY_TIMEOUT_MS = 1e4;
|
|
27755
27756
|
var SDK_ENRICH_TELEMETRY_COMMAND_MAX_LENGTH = 2e4;
|
|
27757
|
+
var ENRICH_DEPRECATION_NOTICE = "DEPRECATION: `deepline enrich` is deprecated and no longer actively maintained. Use `deepline plays` for the supported workflow.\n";
|
|
27756
27758
|
var ENRICH_AI_RUNTIME_COMPACT_TOOLS = /* @__PURE__ */ new Set([
|
|
27757
27759
|
"ai_inference",
|
|
27758
27760
|
"aiinference",
|
|
@@ -31222,6 +31224,7 @@ function registerEnrichCommand(program) {
|
|
|
31222
31224
|
"--max-credits-per-run <credits>",
|
|
31223
31225
|
"Set a hard Deepline-credit ceiling enforced by the runtime for this run."
|
|
31224
31226
|
).action(async (options, _command) => {
|
|
31227
|
+
process.stderr.write(ENRICH_DEPRECATION_NOTICE);
|
|
31225
31228
|
if (currentEnrichArgs().some(
|
|
31226
31229
|
(arg) => arg === "--no-open" || arg.startsWith("--no-open=")
|
|
31227
31230
|
)) {
|
|
@@ -31313,7 +31316,7 @@ function registerEnrichCommand(program) {
|
|
|
31313
31316
|
let inPlaceTempDir = null;
|
|
31314
31317
|
let inPlaceTempOutputPath = null;
|
|
31315
31318
|
const inPlaceFinalOutputPath = options.inPlace ? resolve12(inputCsv) : null;
|
|
31316
|
-
const inPlaceCommitOutputPath = options.inPlace ? (await lstat2(inputCsv)).isSymbolicLink() ? await
|
|
31319
|
+
const inPlaceCommitOutputPath = options.inPlace ? (await lstat2(inputCsv)).isSymbolicLink() ? await realpath3(inputCsv) : inPlaceFinalOutputPath : null;
|
|
31317
31320
|
const failureReportOutputPath = options.inPlace ? inPlaceFinalOutputPath : null;
|
|
31318
31321
|
const enrichIssueFollowUpOutputPath = options.inPlace && inPlaceFinalOutputPath ? sidecarEnrichRowsExportPath(inPlaceFinalOutputPath) : null;
|
|
31319
31322
|
let activeRunId2 = null;
|
package/dist/index.js
CHANGED
|
@@ -783,7 +783,7 @@ var SDK_RELEASE = {
|
|
|
783
783
|
// 0.3.0 introduces raw-v2: complete scrubbed provider responses are
|
|
784
784
|
// available at toolResponse.rawV2 while toolResponse.raw and all declared
|
|
785
785
|
// getters keep their established compatibility behavior.
|
|
786
|
-
version: "0.3.
|
|
786
|
+
version: "0.3.1",
|
|
787
787
|
updateSummary: "New raw-v2 tool responses preserve complete scrubbed provider envelopes at toolResponse.rawV2, including JSON:API included resources, links, cursors, and totals. Existing toolResponse.raw and declared getters keep working unchanged.",
|
|
788
788
|
contracts: {
|
|
789
789
|
api: {
|
package/dist/index.mjs
CHANGED
|
@@ -706,7 +706,7 @@ var SDK_RELEASE = {
|
|
|
706
706
|
// 0.3.0 introduces raw-v2: complete scrubbed provider responses are
|
|
707
707
|
// available at toolResponse.rawV2 while toolResponse.raw and all declared
|
|
708
708
|
// getters keep their established compatibility behavior.
|
|
709
|
-
version: "0.3.
|
|
709
|
+
version: "0.3.1",
|
|
710
710
|
updateSummary: "New raw-v2 tool responses preserve complete scrubbed provider envelopes at toolResponse.rawV2, including JSON:API included resources, links, cursors, and totals. Existing toolResponse.raw and declared getters keep working unchanged.",
|
|
711
711
|
contracts: {
|
|
712
712
|
api: {
|
|
@@ -9,6 +9,7 @@ import { tmpdir as tmpdir2 } from "os";
|
|
|
9
9
|
import { dirname as dirname3, join as join3, resolve as resolve3 } from "path";
|
|
10
10
|
import { fileURLToPath } from "url";
|
|
11
11
|
import { existsSync as existsSync2 } from "fs";
|
|
12
|
+
import { realpath as realpath2 } from "fs/promises";
|
|
12
13
|
|
|
13
14
|
// ../shared_libs/plays/bundling/index.ts
|
|
14
15
|
import { createHash } from "crypto";
|
|
@@ -5286,8 +5287,12 @@ function sha256(value) {
|
|
|
5286
5287
|
function sourceIdentityPath(filePath, sourceIdentityRoot) {
|
|
5287
5288
|
if (!sourceIdentityRoot) return filePath;
|
|
5288
5289
|
const identityRoot = resolve(sourceIdentityRoot);
|
|
5289
|
-
const
|
|
5290
|
-
|
|
5290
|
+
const resolvedFilePath = resolve(filePath);
|
|
5291
|
+
const logicalPath = relative(identityRoot, resolvedFilePath);
|
|
5292
|
+
if (!logicalPath) {
|
|
5293
|
+
return basename(resolvedFilePath);
|
|
5294
|
+
}
|
|
5295
|
+
if (logicalPath === ".." || logicalPath.startsWith(`..${sep}`) || isAbsolute(logicalPath)) {
|
|
5291
5296
|
return filePath;
|
|
5292
5297
|
}
|
|
5293
5298
|
return logicalPath.split(/[\\/]+/).join("/");
|
|
@@ -7201,10 +7206,17 @@ function createSdkPlayBundlingAdapter() {
|
|
|
7201
7206
|
};
|
|
7202
7207
|
}
|
|
7203
7208
|
async function bundlePlayFile2(filePath, options = {}) {
|
|
7209
|
+
const localWorkspaceRoot = dirname3(resolve3(filePath));
|
|
7210
|
+
const sourceIdentityRoot = await realpath2(localWorkspaceRoot).catch(
|
|
7211
|
+
() => localWorkspaceRoot
|
|
7212
|
+
);
|
|
7204
7213
|
const result = await bundlePlayFile(filePath, {
|
|
7205
7214
|
target: options.target ?? defaultPlayBundleTarget(),
|
|
7206
7215
|
exportName: options.exportName,
|
|
7207
|
-
adapter:
|
|
7216
|
+
adapter: {
|
|
7217
|
+
...createSdkPlayBundlingAdapter(),
|
|
7218
|
+
sourceIdentityRoot
|
|
7219
|
+
}
|
|
7208
7220
|
});
|
|
7209
7221
|
if (result.success)
|
|
7210
7222
|
validatePlaySourceFilesHaveNoInlineSecrets(result.sourceFiles);
|