deepline 0.3.0 → 0.3.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 +6 -0
- package/dist/bundling-sources/sdk/src/http.ts +32 -0
- 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 +63 -41
- package/dist/cli/index.mjs +26 -4
- package/dist/index.js +21 -2
- package/dist/index.mjs +21 -2
- package/dist/plays/bundle-play-file.mjs +15 -3
- package/package.json +1 -1
|
@@ -4656,6 +4656,12 @@ export class DeeplineClient {
|
|
|
4656
4656
|
{
|
|
4657
4657
|
method: 'POST',
|
|
4658
4658
|
body: definition,
|
|
4659
|
+
// A provider can reject a create with a 429 after Deepline has begun
|
|
4660
|
+
// the lifecycle request. Do not replay a mutation or hide the server's
|
|
4661
|
+
// monitor-specific recovery guidance behind a generic transport retry.
|
|
4662
|
+
maxRetries: 0,
|
|
4663
|
+
exactUrlOnly: true,
|
|
4664
|
+
preserveRateLimitResponse: true,
|
|
4659
4665
|
},
|
|
4660
4666
|
);
|
|
4661
4667
|
if (definition.tool !== 'deepline.analytics') return deployed;
|
|
@@ -70,6 +70,12 @@ interface RequestOptions {
|
|
|
70
70
|
maxRetries?: number;
|
|
71
71
|
/** Disable localhost/127.0.0.1 failover for non-idempotent requests. */
|
|
72
72
|
exactUrlOnly?: boolean;
|
|
73
|
+
/**
|
|
74
|
+
* Return a server-owned 429 envelope as a DeeplineError instead of replacing
|
|
75
|
+
* it with the transport's generic RateLimitError. Lifecycle mutations use
|
|
76
|
+
* this when their response explains whether the upstream change applied.
|
|
77
|
+
*/
|
|
78
|
+
preserveRateLimitResponse?: boolean;
|
|
73
79
|
/** Enables endpoint-specific structured tool failure mapping. */
|
|
74
80
|
toolId?: string;
|
|
75
81
|
}
|
|
@@ -419,6 +425,17 @@ export class HttpClient {
|
|
|
419
425
|
}
|
|
420
426
|
|
|
421
427
|
if (response.status === 429) {
|
|
428
|
+
if (
|
|
429
|
+
options?.preserveRateLimitResponse &&
|
|
430
|
+
isMonitorRateLimitEnvelope(parsed)
|
|
431
|
+
) {
|
|
432
|
+
throw new DeeplineError(
|
|
433
|
+
apiErrorMessage(parsed, response.status),
|
|
434
|
+
response.status,
|
|
435
|
+
apiErrorCodeFromResponse(parsed),
|
|
436
|
+
{ response: parsed },
|
|
437
|
+
);
|
|
438
|
+
}
|
|
422
439
|
const retryAfter = parseRetryAfter(response);
|
|
423
440
|
lastError = new RateLimitError(retryAfter);
|
|
424
441
|
if (attempt < maxRetries) {
|
|
@@ -779,6 +796,21 @@ function isProviderOriginatedHttpError(parsed: unknown): boolean {
|
|
|
779
796
|
return failureOrigin === 'provider' || code === 'UPSTREAM_BLOCKED';
|
|
780
797
|
}
|
|
781
798
|
|
|
799
|
+
/**
|
|
800
|
+
* A monitor deployment may receive a safe, actionable 429 from Deepline after
|
|
801
|
+
* an upstream lifecycle attempt. Do not treat a CDN/WAF 429 as that envelope:
|
|
802
|
+
* it can be arbitrary HTML or text and must retain normal RateLimitError
|
|
803
|
+
* behavior.
|
|
804
|
+
*/
|
|
805
|
+
function isMonitorRateLimitEnvelope(parsed: unknown): boolean {
|
|
806
|
+
const response = asRecord(parsed);
|
|
807
|
+
const error = asRecord(response?.error);
|
|
808
|
+
return (
|
|
809
|
+
error?.code === 'monitor_upstream_rate_limited' &&
|
|
810
|
+
typeof error.message === 'string'
|
|
811
|
+
);
|
|
812
|
+
}
|
|
813
|
+
|
|
782
814
|
function apiErrorCodeFromResponse(parsed: unknown): string {
|
|
783
815
|
const response = asRecord(parsed);
|
|
784
816
|
const error = asRecord(response?.error);
|
|
@@ -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.2',
|
|
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.2",
|
|
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: {
|
|
@@ -1466,6 +1466,14 @@ var HttpClient = class {
|
|
|
1466
1466
|
throw structuredToolError;
|
|
1467
1467
|
}
|
|
1468
1468
|
if (response.status === 429) {
|
|
1469
|
+
if (options?.preserveRateLimitResponse && isMonitorRateLimitEnvelope(parsed)) {
|
|
1470
|
+
throw new DeeplineError(
|
|
1471
|
+
apiErrorMessage(parsed, response.status),
|
|
1472
|
+
response.status,
|
|
1473
|
+
apiErrorCodeFromResponse(parsed),
|
|
1474
|
+
{ response: parsed }
|
|
1475
|
+
);
|
|
1476
|
+
}
|
|
1469
1477
|
const retryAfter = parseRetryAfter(response);
|
|
1470
1478
|
lastError = new RateLimitError(retryAfter);
|
|
1471
1479
|
if (attempt < maxRetries) {
|
|
@@ -1727,6 +1735,11 @@ function isProviderOriginatedHttpError(parsed) {
|
|
|
1727
1735
|
const code = error?.code ?? response?.code;
|
|
1728
1736
|
return failureOrigin === "provider" || code === "UPSTREAM_BLOCKED";
|
|
1729
1737
|
}
|
|
1738
|
+
function isMonitorRateLimitEnvelope(parsed) {
|
|
1739
|
+
const response = asRecord(parsed);
|
|
1740
|
+
const error = asRecord(response?.error);
|
|
1741
|
+
return error?.code === "monitor_upstream_rate_limited" && typeof error.message === "string";
|
|
1742
|
+
}
|
|
1730
1743
|
function apiErrorCodeFromResponse(parsed) {
|
|
1731
1744
|
const response = asRecord(parsed);
|
|
1732
1745
|
const error = asRecord(response?.error);
|
|
@@ -6503,7 +6516,13 @@ var DeeplineClient = class {
|
|
|
6503
6516
|
"/api/v2/monitors/deploy",
|
|
6504
6517
|
{
|
|
6505
6518
|
method: "POST",
|
|
6506
|
-
body: definition
|
|
6519
|
+
body: definition,
|
|
6520
|
+
// A provider can reject a create with a 429 after Deepline has begun
|
|
6521
|
+
// the lifecycle request. Do not replay a mutation or hide the server's
|
|
6522
|
+
// monitor-specific recovery guidance behind a generic transport retry.
|
|
6523
|
+
maxRetries: 0,
|
|
6524
|
+
exactUrlOnly: true,
|
|
6525
|
+
preserveRateLimitResponse: true
|
|
6507
6526
|
}
|
|
6508
6527
|
);
|
|
6509
6528
|
if (definition.tool !== "deepline.analytics") return deployed;
|
|
@@ -11068,7 +11087,7 @@ Examples:
|
|
|
11068
11087
|
}
|
|
11069
11088
|
|
|
11070
11089
|
// src/cli/commands/enrich.ts
|
|
11071
|
-
var
|
|
11090
|
+
var import_promises7 = require("fs/promises");
|
|
11072
11091
|
var import_node_os10 = require("os");
|
|
11073
11092
|
var import_node_path15 = require("path");
|
|
11074
11093
|
var import_commander2 = require("commander");
|
|
@@ -11076,7 +11095,7 @@ var import_commander2 = require("commander");
|
|
|
11076
11095
|
// src/cli/commands/play.ts
|
|
11077
11096
|
var import_node_crypto6 = require("crypto");
|
|
11078
11097
|
var import_node_fs12 = require("fs");
|
|
11079
|
-
var
|
|
11098
|
+
var import_promises6 = require("fs/promises");
|
|
11080
11099
|
var import_node_path14 = require("path");
|
|
11081
11100
|
var import_sync5 = require("csv-parse/sync");
|
|
11082
11101
|
|
|
@@ -12784,6 +12803,7 @@ var import_node_os9 = require("os");
|
|
|
12784
12803
|
var import_node_path13 = require("path");
|
|
12785
12804
|
var import_node_url = require("url");
|
|
12786
12805
|
var import_node_fs11 = require("fs");
|
|
12806
|
+
var import_promises5 = require("fs/promises");
|
|
12787
12807
|
|
|
12788
12808
|
// ../shared_libs/plays/bundling/index.ts
|
|
12789
12809
|
var import_node_crypto4 = require("crypto");
|
|
@@ -18314,7 +18334,7 @@ var PLAY_RUN_RESERVED_BOOLEAN_FLAGS = /* @__PURE__ */ new Set([
|
|
|
18314
18334
|
]);
|
|
18315
18335
|
async function pathExistsIncludingSymlink(path) {
|
|
18316
18336
|
try {
|
|
18317
|
-
await (0,
|
|
18337
|
+
await (0, import_promises6.lstat)(path);
|
|
18318
18338
|
return true;
|
|
18319
18339
|
} catch (error) {
|
|
18320
18340
|
if (error.code === "ENOENT") {
|
|
@@ -18331,7 +18351,7 @@ function runIdFileTempPath(destination) {
|
|
|
18331
18351
|
}
|
|
18332
18352
|
async function removeRunIdTempFile(path) {
|
|
18333
18353
|
try {
|
|
18334
|
-
await (0,
|
|
18354
|
+
await (0, import_promises6.unlink)(path);
|
|
18335
18355
|
} catch (error) {
|
|
18336
18356
|
if (error.code !== "ENOENT") {
|
|
18337
18357
|
throw error;
|
|
@@ -18348,7 +18368,7 @@ async function preflightRunIdFile(path) {
|
|
|
18348
18368
|
const tempPath = runIdFileTempPath(destination);
|
|
18349
18369
|
let handle = null;
|
|
18350
18370
|
try {
|
|
18351
|
-
handle = await (0,
|
|
18371
|
+
handle = await (0, import_promises6.open)(tempPath, "wx", 384);
|
|
18352
18372
|
await handle.sync();
|
|
18353
18373
|
} catch (error) {
|
|
18354
18374
|
throw new Error(
|
|
@@ -18366,7 +18386,7 @@ async function readRunIdFile(destination) {
|
|
|
18366
18386
|
}
|
|
18367
18387
|
let parsed;
|
|
18368
18388
|
try {
|
|
18369
|
-
parsed = JSON.parse(await (0,
|
|
18389
|
+
parsed = JSON.parse(await (0, import_promises6.readFile)(destination, "utf8"));
|
|
18370
18390
|
} catch (error) {
|
|
18371
18391
|
throw new Error(
|
|
18372
18392
|
`--run-id-file destination contains invalid JSON: ${destination}. Refusing to overwrite it (${error instanceof Error ? error.message : String(error)}).`
|
|
@@ -18390,7 +18410,7 @@ async function writePlayRunIdFile(destination, runId) {
|
|
|
18390
18410
|
const tempPath = runIdFileTempPath(destination);
|
|
18391
18411
|
let handle = null;
|
|
18392
18412
|
try {
|
|
18393
|
-
handle = await (0,
|
|
18413
|
+
handle = await (0, import_promises6.open)(tempPath, "wx", 384);
|
|
18394
18414
|
await handle.writeFile(
|
|
18395
18415
|
`${JSON.stringify({ version: 1, runId })}
|
|
18396
18416
|
`,
|
|
@@ -18400,8 +18420,8 @@ async function writePlayRunIdFile(destination, runId) {
|
|
|
18400
18420
|
await handle.close();
|
|
18401
18421
|
handle = null;
|
|
18402
18422
|
try {
|
|
18403
|
-
await (0,
|
|
18404
|
-
const directory = await (0,
|
|
18423
|
+
await (0, import_promises6.link)(tempPath, destination);
|
|
18424
|
+
const directory = await (0, import_promises6.open)((0, import_node_path14.dirname)(destination), "r");
|
|
18405
18425
|
try {
|
|
18406
18426
|
await directory.sync();
|
|
18407
18427
|
} finally {
|
|
@@ -27693,6 +27713,7 @@ var ENRICH_DEBUG_T0 = Date.now();
|
|
|
27693
27713
|
var GENERATED_ENRICH_ROWS_TABLE_NAMESPACE = "deepline_enrich_rows";
|
|
27694
27714
|
var SDK_ENRICH_TELEMETRY_TIMEOUT_MS = 1e4;
|
|
27695
27715
|
var SDK_ENRICH_TELEMETRY_COMMAND_MAX_LENGTH = 2e4;
|
|
27716
|
+
var ENRICH_DEPRECATION_NOTICE = "DEPRECATION: `deepline enrich` is deprecated and no longer actively maintained. Use `deepline plays` for the supported workflow.\n";
|
|
27696
27717
|
var ENRICH_AI_RUNTIME_COMPACT_TOOLS = /* @__PURE__ */ new Set([
|
|
27697
27718
|
"ai_inference",
|
|
27698
27719
|
"aiinference",
|
|
@@ -27866,7 +27887,7 @@ async function readAtFileReference(value, argumentName, strip = true) {
|
|
|
27866
27887
|
throw new Error(`Invalid ${argumentName} value: empty @file path.`);
|
|
27867
27888
|
}
|
|
27868
27889
|
try {
|
|
27869
|
-
const text = await (0,
|
|
27890
|
+
const text = await (0, import_promises7.readFile)(filePath, "utf8");
|
|
27870
27891
|
const normalized = text.replace(/^\uFEFF/, "");
|
|
27871
27892
|
return strip ? normalized.trim() : normalized;
|
|
27872
27893
|
} catch (error) {
|
|
@@ -28164,7 +28185,7 @@ async function buildPlanArgs(args) {
|
|
|
28164
28185
|
async function assertInputCsvExists(inputCsv) {
|
|
28165
28186
|
const path = (0, import_node_path15.resolve)(inputCsv);
|
|
28166
28187
|
try {
|
|
28167
|
-
const info = await (0,
|
|
28188
|
+
const info = await (0, import_promises7.stat)(path);
|
|
28168
28189
|
if (info.isFile()) {
|
|
28169
28190
|
return;
|
|
28170
28191
|
}
|
|
@@ -28185,8 +28206,8 @@ async function assertSafeOutputPath(inputCsv, outputPath) {
|
|
|
28185
28206
|
}
|
|
28186
28207
|
try {
|
|
28187
28208
|
const [inputInfo, outputInfo] = await Promise.all([
|
|
28188
|
-
(0,
|
|
28189
|
-
(0,
|
|
28209
|
+
(0, import_promises7.stat)(input2),
|
|
28210
|
+
(0, import_promises7.stat)(output2)
|
|
28190
28211
|
]);
|
|
28191
28212
|
if (inputInfo.dev === outputInfo.dev && inputInfo.ino === outputInfo.ino) {
|
|
28192
28213
|
throw new Error(
|
|
@@ -28206,7 +28227,7 @@ async function assertSafeOutputPath(inputCsv, outputPath) {
|
|
|
28206
28227
|
}
|
|
28207
28228
|
async function regularFileExists(path) {
|
|
28208
28229
|
try {
|
|
28209
|
-
const info = await (0,
|
|
28230
|
+
const info = await (0, import_promises7.stat)((0, import_node_path15.resolve)(path));
|
|
28210
28231
|
return info.isFile();
|
|
28211
28232
|
} catch (error) {
|
|
28212
28233
|
const code = error && typeof error === "object" ? error.code : void 0;
|
|
@@ -28217,7 +28238,7 @@ async function regularFileExists(path) {
|
|
|
28217
28238
|
}
|
|
28218
28239
|
}
|
|
28219
28240
|
async function readConfig(path) {
|
|
28220
|
-
const source = await (0,
|
|
28241
|
+
const source = await (0, import_promises7.readFile)((0, import_node_path15.resolve)(path), "utf8");
|
|
28221
28242
|
let parsed;
|
|
28222
28243
|
try {
|
|
28223
28244
|
parsed = JSON.parse(source);
|
|
@@ -28638,7 +28659,7 @@ async function writeOutputCsv(outputPath, status, options) {
|
|
|
28638
28659
|
]),
|
|
28639
28660
|
options?.config
|
|
28640
28661
|
);
|
|
28641
|
-
await (0,
|
|
28662
|
+
await (0, import_promises7.writeFile)(
|
|
28642
28663
|
(0, import_node_path15.resolve)(outputPath),
|
|
28643
28664
|
csvStringFromRows(merged.rows, columns),
|
|
28644
28665
|
"utf8"
|
|
@@ -30318,7 +30339,7 @@ async function persistEnrichFailureReport(input2) {
|
|
|
30318
30339
|
}
|
|
30319
30340
|
const stateDir = (0, import_node_path15.join)((0, import_node_os10.homedir)(), ".local", "deepline", "runtime", "state");
|
|
30320
30341
|
const reportPrefix = input2.jobs.length > 0 ? "run-block-failures" : "enrich-issues";
|
|
30321
|
-
await (0,
|
|
30342
|
+
await (0, import_promises7.mkdir)(stateDir, { recursive: true });
|
|
30322
30343
|
const reportPath = (0, import_node_path15.join)(
|
|
30323
30344
|
stateDir,
|
|
30324
30345
|
`${reportPrefix}-${Math.floor(Date.now() / 1e3)}-${process.pid}.json`
|
|
@@ -30344,7 +30365,7 @@ async function persistEnrichFailureReport(input2) {
|
|
|
30344
30365
|
if (input2.rows.rowStart !== null && input2.rows.rowEnd !== null) {
|
|
30345
30366
|
report.rows = { start: input2.rows.rowStart, end: input2.rows.rowEnd };
|
|
30346
30367
|
}
|
|
30347
|
-
await (0,
|
|
30368
|
+
await (0, import_promises7.writeFile)(reportPath, `${JSON.stringify(report, null, 2)}
|
|
30348
30369
|
`, "utf8");
|
|
30349
30370
|
return reportPath;
|
|
30350
30371
|
}
|
|
@@ -31162,6 +31183,7 @@ function registerEnrichCommand(program) {
|
|
|
31162
31183
|
"--max-credits-per-run <credits>",
|
|
31163
31184
|
"Set a hard Deepline-credit ceiling enforced by the runtime for this run."
|
|
31164
31185
|
).action(async (options, _command) => {
|
|
31186
|
+
process.stderr.write(ENRICH_DEPRECATION_NOTICE);
|
|
31165
31187
|
if (currentEnrichArgs().some(
|
|
31166
31188
|
(arg) => arg === "--no-open" || arg.startsWith("--no-open=")
|
|
31167
31189
|
)) {
|
|
@@ -31247,13 +31269,13 @@ function registerEnrichCommand(program) {
|
|
|
31247
31269
|
sdkEnrichTelemetryCompleted = true;
|
|
31248
31270
|
await completeSdkEnrichTelemetry(sdkEnrichTelemetry, input2);
|
|
31249
31271
|
};
|
|
31250
|
-
const tempDir = await (0,
|
|
31272
|
+
const tempDir = await (0, import_promises7.mkdtemp)((0, import_node_path15.join)((0, import_node_os10.tmpdir)(), "deepline-enrich-play-"));
|
|
31251
31273
|
await emitSdkEnrichTelemetry(sdkEnrichTelemetry, "enrich_started");
|
|
31252
31274
|
const tempPlay = (0, import_node_path15.join)(tempDir, "deepline-enrich.play.ts");
|
|
31253
31275
|
let inPlaceTempDir = null;
|
|
31254
31276
|
let inPlaceTempOutputPath = null;
|
|
31255
31277
|
const inPlaceFinalOutputPath = options.inPlace ? (0, import_node_path15.resolve)(inputCsv) : null;
|
|
31256
|
-
const inPlaceCommitOutputPath = options.inPlace ? (await (0,
|
|
31278
|
+
const inPlaceCommitOutputPath = options.inPlace ? (await (0, import_promises7.lstat)(inputCsv)).isSymbolicLink() ? await (0, import_promises7.realpath)(inputCsv) : inPlaceFinalOutputPath : null;
|
|
31257
31279
|
const failureReportOutputPath = options.inPlace ? inPlaceFinalOutputPath : null;
|
|
31258
31280
|
const enrichIssueFollowUpOutputPath = options.inPlace && inPlaceFinalOutputPath ? sidecarEnrichRowsExportPath(inPlaceFinalOutputPath) : null;
|
|
31259
31281
|
let activeRunId2 = null;
|
|
@@ -31274,16 +31296,16 @@ function registerEnrichCommand(program) {
|
|
|
31274
31296
|
return;
|
|
31275
31297
|
}
|
|
31276
31298
|
if (inPlaceTempDir) {
|
|
31277
|
-
await (0,
|
|
31299
|
+
await (0, import_promises7.rm)(inPlaceTempDir, { recursive: true, force: true });
|
|
31278
31300
|
}
|
|
31279
|
-
inPlaceTempDir = await (0,
|
|
31301
|
+
inPlaceTempDir = await (0, import_promises7.mkdtemp)(
|
|
31280
31302
|
(0, import_node_path15.join)(
|
|
31281
31303
|
(0, import_node_path15.dirname)(inPlaceCommitOutputPath ?? (0, import_node_path15.resolve)(inputCsv)),
|
|
31282
31304
|
".deepline-enrich-in-place-"
|
|
31283
31305
|
)
|
|
31284
31306
|
);
|
|
31285
31307
|
inPlaceTempOutputPath = (0, import_node_path15.join)(inPlaceTempDir, "output.csv");
|
|
31286
|
-
await (0,
|
|
31308
|
+
await (0, import_promises7.copyFile)((0, import_node_path15.resolve)(inputCsv), inPlaceTempOutputPath);
|
|
31287
31309
|
outputPath = inPlaceTempOutputPath;
|
|
31288
31310
|
};
|
|
31289
31311
|
const commitInPlaceOutput = async (exportResult) => {
|
|
@@ -31291,12 +31313,12 @@ function registerEnrichCommand(program) {
|
|
|
31291
31313
|
return exportResult;
|
|
31292
31314
|
}
|
|
31293
31315
|
const committedTempDir = inPlaceTempDir;
|
|
31294
|
-
await (0,
|
|
31316
|
+
await (0, import_promises7.rename)(inPlaceTempOutputPath, inPlaceCommitOutputPath);
|
|
31295
31317
|
inPlaceTempDir = null;
|
|
31296
31318
|
inPlaceTempOutputPath = null;
|
|
31297
31319
|
outputPath = inPlaceFinalOutputPath;
|
|
31298
31320
|
if (committedTempDir) {
|
|
31299
|
-
await (0,
|
|
31321
|
+
await (0, import_promises7.rm)(committedTempDir, { recursive: true, force: true });
|
|
31300
31322
|
}
|
|
31301
31323
|
if (!exportResult) {
|
|
31302
31324
|
return null;
|
|
@@ -31309,7 +31331,7 @@ function registerEnrichCommand(program) {
|
|
|
31309
31331
|
try {
|
|
31310
31332
|
process.once("SIGINT", onSigint);
|
|
31311
31333
|
process.once("SIGTERM", onSigterm);
|
|
31312
|
-
await (0,
|
|
31334
|
+
await (0, import_promises7.writeFile)(tempPlay, playSource, "utf8");
|
|
31313
31335
|
if (options.inPlace) {
|
|
31314
31336
|
await prepareInPlaceOutput();
|
|
31315
31337
|
}
|
|
@@ -31503,11 +31525,11 @@ function registerEnrichCommand(program) {
|
|
|
31503
31525
|
process.removeListener("SIGINT", onSigint);
|
|
31504
31526
|
process.removeListener("SIGTERM", onSigterm);
|
|
31505
31527
|
if (inPlaceTempDir) {
|
|
31506
|
-
await (0,
|
|
31528
|
+
await (0, import_promises7.rm)(inPlaceTempDir, { recursive: true, force: true });
|
|
31507
31529
|
} else if (inPlaceTempOutputPath) {
|
|
31508
|
-
await (0,
|
|
31530
|
+
await (0, import_promises7.rm)(inPlaceTempOutputPath, { force: true });
|
|
31509
31531
|
}
|
|
31510
|
-
await (0,
|
|
31532
|
+
await (0, import_promises7.rm)(tempDir, { recursive: true, force: true });
|
|
31511
31533
|
}
|
|
31512
31534
|
});
|
|
31513
31535
|
}
|
|
@@ -32399,7 +32421,7 @@ Examples:
|
|
|
32399
32421
|
|
|
32400
32422
|
// src/cli/commands/monitors.ts
|
|
32401
32423
|
var import_node_fs14 = require("fs");
|
|
32402
|
-
var
|
|
32424
|
+
var import_promises8 = require("readline/promises");
|
|
32403
32425
|
var JSON_OPTION_DESCRIPTION = "Emit JSON output. Also automatic when stdout is piped";
|
|
32404
32426
|
function withJsonOption(command) {
|
|
32405
32427
|
return command.option("--json", JSON_OPTION_DESCRIPTION);
|
|
@@ -33168,7 +33190,7 @@ async function handleMonitorsValidate(key, options) {
|
|
|
33168
33190
|
if (result.valid === false) process.exitCode = 7;
|
|
33169
33191
|
}
|
|
33170
33192
|
async function confirmMonitorDelete(key, options) {
|
|
33171
|
-
const rl = (0,
|
|
33193
|
+
const rl = (0, import_promises8.createInterface)({
|
|
33172
33194
|
input: process.stdin,
|
|
33173
33195
|
output: process.stderr
|
|
33174
33196
|
});
|
|
@@ -40185,7 +40207,7 @@ Examples:
|
|
|
40185
40207
|
}
|
|
40186
40208
|
|
|
40187
40209
|
// src/cli/commands/workflow.ts
|
|
40188
|
-
var
|
|
40210
|
+
var import_promises9 = require("fs/promises");
|
|
40189
40211
|
var import_node_path24 = require("path");
|
|
40190
40212
|
|
|
40191
40213
|
// src/cli/workflow-to-play.ts
|
|
@@ -40403,7 +40425,7 @@ function readStatus(payload) {
|
|
|
40403
40425
|
}
|
|
40404
40426
|
async function readJsonOption(payload, file) {
|
|
40405
40427
|
if (file) {
|
|
40406
|
-
const raw = await (0,
|
|
40428
|
+
const raw = await (0, import_promises9.readFile)((0, import_node_path24.resolve)(file), "utf8");
|
|
40407
40429
|
return JSON.parse(raw);
|
|
40408
40430
|
}
|
|
40409
40431
|
if (payload) {
|
|
@@ -40438,8 +40460,8 @@ async function transformOne(api, workflowId, outDir, publish) {
|
|
|
40438
40460
|
{ workflowName: workflow.name, version: revision.version }
|
|
40439
40461
|
);
|
|
40440
40462
|
const file = (0, import_node_path24.join)((0, import_node_path24.resolve)(outDir), `${compiled.playName}.play.ts`);
|
|
40441
|
-
await (0,
|
|
40442
|
-
await (0,
|
|
40463
|
+
await (0, import_promises9.mkdir)((0, import_node_path24.dirname)(file), { recursive: true });
|
|
40464
|
+
await (0, import_promises9.writeFile)(file, compiled.sourceCode, "utf8");
|
|
40443
40465
|
let published = false;
|
|
40444
40466
|
if (publish) {
|
|
40445
40467
|
const code = await handlePlayPublish([file]);
|
|
@@ -41573,10 +41595,10 @@ function topLevelCommandKnown(program, commandName) {
|
|
|
41573
41595
|
);
|
|
41574
41596
|
}
|
|
41575
41597
|
async function runPlayRunnerHealthCheck() {
|
|
41576
|
-
const dir = await (0,
|
|
41598
|
+
const dir = await (0, import_promises10.mkdtemp)((0, import_node_path26.join)((0, import_node_os19.tmpdir)(), "deepline-health-play-"));
|
|
41577
41599
|
const file = (0, import_node_path26.join)(dir, "health-check.play.ts");
|
|
41578
41600
|
try {
|
|
41579
|
-
await (0,
|
|
41601
|
+
await (0, import_promises10.writeFile)(
|
|
41580
41602
|
file,
|
|
41581
41603
|
[
|
|
41582
41604
|
"import { definePlay } from 'deepline';",
|
|
@@ -41625,7 +41647,7 @@ async function runPlayRunnerHealthCheck() {
|
|
|
41625
41647
|
}
|
|
41626
41648
|
};
|
|
41627
41649
|
} finally {
|
|
41628
|
-
await (0,
|
|
41650
|
+
await (0, import_promises10.rm)(dir, { recursive: true, force: true });
|
|
41629
41651
|
}
|
|
41630
41652
|
}
|
|
41631
41653
|
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.2",
|
|
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: {
|
|
@@ -1452,6 +1452,14 @@ var HttpClient = class {
|
|
|
1452
1452
|
throw structuredToolError;
|
|
1453
1453
|
}
|
|
1454
1454
|
if (response.status === 429) {
|
|
1455
|
+
if (options?.preserveRateLimitResponse && isMonitorRateLimitEnvelope(parsed)) {
|
|
1456
|
+
throw new DeeplineError(
|
|
1457
|
+
apiErrorMessage(parsed, response.status),
|
|
1458
|
+
response.status,
|
|
1459
|
+
apiErrorCodeFromResponse(parsed),
|
|
1460
|
+
{ response: parsed }
|
|
1461
|
+
);
|
|
1462
|
+
}
|
|
1455
1463
|
const retryAfter = parseRetryAfter(response);
|
|
1456
1464
|
lastError = new RateLimitError(retryAfter);
|
|
1457
1465
|
if (attempt < maxRetries) {
|
|
@@ -1713,6 +1721,11 @@ function isProviderOriginatedHttpError(parsed) {
|
|
|
1713
1721
|
const code = error?.code ?? response?.code;
|
|
1714
1722
|
return failureOrigin === "provider" || code === "UPSTREAM_BLOCKED";
|
|
1715
1723
|
}
|
|
1724
|
+
function isMonitorRateLimitEnvelope(parsed) {
|
|
1725
|
+
const response = asRecord(parsed);
|
|
1726
|
+
const error = asRecord(response?.error);
|
|
1727
|
+
return error?.code === "monitor_upstream_rate_limited" && typeof error.message === "string";
|
|
1728
|
+
}
|
|
1716
1729
|
function apiErrorCodeFromResponse(parsed) {
|
|
1717
1730
|
const response = asRecord(parsed);
|
|
1718
1731
|
const error = asRecord(response?.error);
|
|
@@ -6489,7 +6502,13 @@ var DeeplineClient = class {
|
|
|
6489
6502
|
"/api/v2/monitors/deploy",
|
|
6490
6503
|
{
|
|
6491
6504
|
method: "POST",
|
|
6492
|
-
body: definition
|
|
6505
|
+
body: definition,
|
|
6506
|
+
// A provider can reject a create with a 429 after Deepline has begun
|
|
6507
|
+
// the lifecycle request. Do not replay a mutation or hide the server's
|
|
6508
|
+
// monitor-specific recovery guidance behind a generic transport retry.
|
|
6509
|
+
maxRetries: 0,
|
|
6510
|
+
exactUrlOnly: true,
|
|
6511
|
+
preserveRateLimitResponse: true
|
|
6493
6512
|
}
|
|
6494
6513
|
);
|
|
6495
6514
|
if (definition.tool !== "deepline.analytics") return deployed;
|
|
@@ -11080,7 +11099,7 @@ import {
|
|
|
11080
11099
|
mkdir as mkdir4,
|
|
11081
11100
|
mkdtemp,
|
|
11082
11101
|
readFile as readFile3,
|
|
11083
|
-
realpath as
|
|
11102
|
+
realpath as realpath3,
|
|
11084
11103
|
rename,
|
|
11085
11104
|
rm,
|
|
11086
11105
|
stat as stat3,
|
|
@@ -12828,6 +12847,7 @@ import { tmpdir as tmpdir3 } from "os";
|
|
|
12828
12847
|
import { dirname as dirname8, join as join9, resolve as resolve10 } from "path";
|
|
12829
12848
|
import { fileURLToPath } from "url";
|
|
12830
12849
|
import { existsSync as existsSync8 } from "fs";
|
|
12850
|
+
import { realpath as realpath2 } from "fs/promises";
|
|
12831
12851
|
|
|
12832
12852
|
// ../shared_libs/plays/bundling/index.ts
|
|
12833
12853
|
import { createHash as createHash2 } from "crypto";
|
|
@@ -27753,6 +27773,7 @@ var ENRICH_DEBUG_T0 = Date.now();
|
|
|
27753
27773
|
var GENERATED_ENRICH_ROWS_TABLE_NAMESPACE = "deepline_enrich_rows";
|
|
27754
27774
|
var SDK_ENRICH_TELEMETRY_TIMEOUT_MS = 1e4;
|
|
27755
27775
|
var SDK_ENRICH_TELEMETRY_COMMAND_MAX_LENGTH = 2e4;
|
|
27776
|
+
var ENRICH_DEPRECATION_NOTICE = "DEPRECATION: `deepline enrich` is deprecated and no longer actively maintained. Use `deepline plays` for the supported workflow.\n";
|
|
27756
27777
|
var ENRICH_AI_RUNTIME_COMPACT_TOOLS = /* @__PURE__ */ new Set([
|
|
27757
27778
|
"ai_inference",
|
|
27758
27779
|
"aiinference",
|
|
@@ -31222,6 +31243,7 @@ function registerEnrichCommand(program) {
|
|
|
31222
31243
|
"--max-credits-per-run <credits>",
|
|
31223
31244
|
"Set a hard Deepline-credit ceiling enforced by the runtime for this run."
|
|
31224
31245
|
).action(async (options, _command) => {
|
|
31246
|
+
process.stderr.write(ENRICH_DEPRECATION_NOTICE);
|
|
31225
31247
|
if (currentEnrichArgs().some(
|
|
31226
31248
|
(arg) => arg === "--no-open" || arg.startsWith("--no-open=")
|
|
31227
31249
|
)) {
|
|
@@ -31313,7 +31335,7 @@ function registerEnrichCommand(program) {
|
|
|
31313
31335
|
let inPlaceTempDir = null;
|
|
31314
31336
|
let inPlaceTempOutputPath = null;
|
|
31315
31337
|
const inPlaceFinalOutputPath = options.inPlace ? resolve12(inputCsv) : null;
|
|
31316
|
-
const inPlaceCommitOutputPath = options.inPlace ? (await lstat2(inputCsv)).isSymbolicLink() ? await
|
|
31338
|
+
const inPlaceCommitOutputPath = options.inPlace ? (await lstat2(inputCsv)).isSymbolicLink() ? await realpath3(inputCsv) : inPlaceFinalOutputPath : null;
|
|
31317
31339
|
const failureReportOutputPath = options.inPlace ? inPlaceFinalOutputPath : null;
|
|
31318
31340
|
const enrichIssueFollowUpOutputPath = options.inPlace && inPlaceFinalOutputPath ? sidecarEnrichRowsExportPath(inPlaceFinalOutputPath) : null;
|
|
31319
31341
|
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.2",
|
|
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: {
|
|
@@ -1202,6 +1202,14 @@ var HttpClient = class {
|
|
|
1202
1202
|
throw structuredToolError;
|
|
1203
1203
|
}
|
|
1204
1204
|
if (response.status === 429) {
|
|
1205
|
+
if (options?.preserveRateLimitResponse && isMonitorRateLimitEnvelope(parsed)) {
|
|
1206
|
+
throw new DeeplineError(
|
|
1207
|
+
apiErrorMessage(parsed, response.status),
|
|
1208
|
+
response.status,
|
|
1209
|
+
apiErrorCodeFromResponse(parsed),
|
|
1210
|
+
{ response: parsed }
|
|
1211
|
+
);
|
|
1212
|
+
}
|
|
1205
1213
|
const retryAfter = parseRetryAfter(response);
|
|
1206
1214
|
lastError = new RateLimitError(retryAfter);
|
|
1207
1215
|
if (attempt < maxRetries) {
|
|
@@ -1463,6 +1471,11 @@ function isProviderOriginatedHttpError(parsed) {
|
|
|
1463
1471
|
const code = error?.code ?? response?.code;
|
|
1464
1472
|
return failureOrigin === "provider" || code === "UPSTREAM_BLOCKED";
|
|
1465
1473
|
}
|
|
1474
|
+
function isMonitorRateLimitEnvelope(parsed) {
|
|
1475
|
+
const response = asRecord(parsed);
|
|
1476
|
+
const error = asRecord(response?.error);
|
|
1477
|
+
return error?.code === "monitor_upstream_rate_limited" && typeof error.message === "string";
|
|
1478
|
+
}
|
|
1466
1479
|
function apiErrorCodeFromResponse(parsed) {
|
|
1467
1480
|
const response = asRecord(parsed);
|
|
1468
1481
|
const error = asRecord(response?.error);
|
|
@@ -6239,7 +6252,13 @@ var DeeplineClient = class {
|
|
|
6239
6252
|
"/api/v2/monitors/deploy",
|
|
6240
6253
|
{
|
|
6241
6254
|
method: "POST",
|
|
6242
|
-
body: definition
|
|
6255
|
+
body: definition,
|
|
6256
|
+
// A provider can reject a create with a 429 after Deepline has begun
|
|
6257
|
+
// the lifecycle request. Do not replay a mutation or hide the server's
|
|
6258
|
+
// monitor-specific recovery guidance behind a generic transport retry.
|
|
6259
|
+
maxRetries: 0,
|
|
6260
|
+
exactUrlOnly: true,
|
|
6261
|
+
preserveRateLimitResponse: true
|
|
6243
6262
|
}
|
|
6244
6263
|
);
|
|
6245
6264
|
if (definition.tool !== "deepline.analytics") return deployed;
|
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.2",
|
|
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: {
|
|
@@ -1125,6 +1125,14 @@ var HttpClient = class {
|
|
|
1125
1125
|
throw structuredToolError;
|
|
1126
1126
|
}
|
|
1127
1127
|
if (response.status === 429) {
|
|
1128
|
+
if (options?.preserveRateLimitResponse && isMonitorRateLimitEnvelope(parsed)) {
|
|
1129
|
+
throw new DeeplineError(
|
|
1130
|
+
apiErrorMessage(parsed, response.status),
|
|
1131
|
+
response.status,
|
|
1132
|
+
apiErrorCodeFromResponse(parsed),
|
|
1133
|
+
{ response: parsed }
|
|
1134
|
+
);
|
|
1135
|
+
}
|
|
1128
1136
|
const retryAfter = parseRetryAfter(response);
|
|
1129
1137
|
lastError = new RateLimitError(retryAfter);
|
|
1130
1138
|
if (attempt < maxRetries) {
|
|
@@ -1386,6 +1394,11 @@ function isProviderOriginatedHttpError(parsed) {
|
|
|
1386
1394
|
const code = error?.code ?? response?.code;
|
|
1387
1395
|
return failureOrigin === "provider" || code === "UPSTREAM_BLOCKED";
|
|
1388
1396
|
}
|
|
1397
|
+
function isMonitorRateLimitEnvelope(parsed) {
|
|
1398
|
+
const response = asRecord(parsed);
|
|
1399
|
+
const error = asRecord(response?.error);
|
|
1400
|
+
return error?.code === "monitor_upstream_rate_limited" && typeof error.message === "string";
|
|
1401
|
+
}
|
|
1389
1402
|
function apiErrorCodeFromResponse(parsed) {
|
|
1390
1403
|
const response = asRecord(parsed);
|
|
1391
1404
|
const error = asRecord(response?.error);
|
|
@@ -6162,7 +6175,13 @@ var DeeplineClient = class {
|
|
|
6162
6175
|
"/api/v2/monitors/deploy",
|
|
6163
6176
|
{
|
|
6164
6177
|
method: "POST",
|
|
6165
|
-
body: definition
|
|
6178
|
+
body: definition,
|
|
6179
|
+
// A provider can reject a create with a 429 after Deepline has begun
|
|
6180
|
+
// the lifecycle request. Do not replay a mutation or hide the server's
|
|
6181
|
+
// monitor-specific recovery guidance behind a generic transport retry.
|
|
6182
|
+
maxRetries: 0,
|
|
6183
|
+
exactUrlOnly: true,
|
|
6184
|
+
preserveRateLimitResponse: true
|
|
6166
6185
|
}
|
|
6167
6186
|
);
|
|
6168
6187
|
if (definition.tool !== "deepline.analytics") return deployed;
|
|
@@ -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);
|