deepline 0.1.178 → 0.1.180
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/apps/play-runner-workers/src/coordinator-entry.ts +132 -7
- package/dist/bundling-sources/apps/play-runner-workers/src/dedup-do.ts +25 -8
- package/dist/bundling-sources/apps/play-runner-workers/src/durable-object-deploy-handoff.ts +8 -2
- package/dist/bundling-sources/apps/play-runner-workers/src/entry.ts +414 -196
- package/dist/bundling-sources/apps/play-runner-workers/src/runtime/batching.ts +42 -2
- package/dist/bundling-sources/apps/play-runner-workers/src/runtime/coordinator-progress.ts +289 -0
- package/dist/bundling-sources/apps/play-runner-workers/src/runtime/tool-receipts.ts +112 -1
- package/dist/bundling-sources/sdk/src/client.ts +237 -0
- package/dist/bundling-sources/sdk/src/http.ts +100 -9
- package/dist/bundling-sources/sdk/src/release.ts +2 -2
- package/dist/bundling-sources/shared_libs/play-runtime/app-runtime-api.ts +10 -2
- package/dist/bundling-sources/shared_libs/play-runtime/batch-runtime.ts +58 -2
- package/dist/bundling-sources/shared_libs/play-runtime/context.ts +99 -25
- package/dist/bundling-sources/shared_libs/play-runtime/db-session.ts +7 -2
- package/dist/bundling-sources/shared_libs/play-runtime/persistence-latch.ts +167 -0
- package/dist/bundling-sources/shared_libs/play-runtime/receipt-salvage.ts +158 -0
- package/dist/bundling-sources/shared_libs/play-runtime/run-failure.ts +44 -0
- package/dist/bundling-sources/shared_libs/play-runtime/run-ledger.ts +74 -30
- package/dist/bundling-sources/shared_libs/play-runtime/runtime-api.ts +6 -0
- package/dist/cli/index.js +338 -13
- package/dist/cli/index.mjs +338 -13
- package/dist/index.d.mts +44 -0
- package/dist/index.d.ts +44 -0
- package/dist/index.js +229 -9
- package/dist/index.mjs +229 -9
- package/package.json +1 -1
|
@@ -1,4 +1,7 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import {
|
|
2
|
+
normalizePlayRunFailure,
|
|
3
|
+
PLATFORM_DEPLOY_INTERRUPTED_MESSAGE,
|
|
4
|
+
} from './run-failure';
|
|
2
5
|
|
|
3
6
|
export type PlayRunLedgerStatus =
|
|
4
7
|
| 'queued'
|
|
@@ -530,6 +533,25 @@ const TERMINAL_STATUS_BY_EVENT_TYPE = {
|
|
|
530
533
|
'run.cancelled': 'cancelled',
|
|
531
534
|
} as const satisfies Partial<Record<string, PlayRunLedgerStatus>>;
|
|
532
535
|
|
|
536
|
+
function isRawPlatformDeployResetText(
|
|
537
|
+
error: string | null | undefined,
|
|
538
|
+
): boolean {
|
|
539
|
+
return Boolean(
|
|
540
|
+
error &&
|
|
541
|
+
normalizePlayRunFailure(error).code === 'PLATFORM_DEPLOY_INTERRUPTED',
|
|
542
|
+
);
|
|
543
|
+
}
|
|
544
|
+
|
|
545
|
+
function isPlatformDeployFailureText(
|
|
546
|
+
error: string | null | undefined,
|
|
547
|
+
): boolean {
|
|
548
|
+
return Boolean(
|
|
549
|
+
error &&
|
|
550
|
+
(isRawPlatformDeployResetText(error) ||
|
|
551
|
+
error.includes(PLATFORM_DEPLOY_INTERRUPTED_MESSAGE)),
|
|
552
|
+
);
|
|
553
|
+
}
|
|
554
|
+
|
|
533
555
|
/**
|
|
534
556
|
* Append already-deduplicated log lines to the snapshot tail.
|
|
535
557
|
*
|
|
@@ -566,31 +588,29 @@ function appendLogLines(
|
|
|
566
588
|
}
|
|
567
589
|
|
|
568
590
|
/**
|
|
569
|
-
* Terminal-status precedence.
|
|
570
|
-
*
|
|
571
|
-
*
|
|
572
|
-
*
|
|
573
|
-
*
|
|
574
|
-
*
|
|
575
|
-
*
|
|
591
|
+
* Terminal-status precedence. Conflicting terminal events use
|
|
592
|
+
* newest-terminal-wins by event time: older events are ignored and logged,
|
|
593
|
+
* newer events reconcile the snapshot. Same-status terminal events continue to
|
|
594
|
+
* the reducer so recovery/transport paths can fill in canonical payloads after
|
|
595
|
+
* an earlier partial terminal snapshot. Retryable platform-deploy failures are
|
|
596
|
+
* the narrow exception: they are never allowed to replace a real terminal
|
|
597
|
+
* result from the runtime.
|
|
576
598
|
*/
|
|
577
599
|
function conflictingTerminalSnapshot(
|
|
578
600
|
base: PlayRunLedgerSnapshot,
|
|
579
601
|
eventType: keyof typeof TERMINAL_STATUS_BY_EVENT_TYPE,
|
|
580
602
|
occurredAt: number,
|
|
581
603
|
eventError?: string | null,
|
|
604
|
+
options?: { allowStaleSameStatusPayloadMerge?: boolean },
|
|
582
605
|
): PlayRunLedgerSnapshot | null {
|
|
583
606
|
if (!isTerminalPlayRunLedgerStatus(base.status)) {
|
|
584
607
|
return null;
|
|
585
608
|
}
|
|
586
|
-
|
|
587
|
-
return null;
|
|
588
|
-
}
|
|
609
|
+
const terminalAt = base.finishedAt ?? base.updatedAt ?? 0;
|
|
589
610
|
if (
|
|
590
611
|
base.status === 'completed' &&
|
|
591
612
|
eventType === 'run.failed' &&
|
|
592
|
-
eventError
|
|
593
|
-
normalizePlayRunFailure(eventError).code === 'PLATFORM_DEPLOY_INTERRUPTED'
|
|
613
|
+
isPlatformDeployFailureText(eventError)
|
|
594
614
|
) {
|
|
595
615
|
return withTiming(
|
|
596
616
|
appendLogLines(base, [
|
|
@@ -598,7 +618,22 @@ function conflictingTerminalSnapshot(
|
|
|
598
618
|
]),
|
|
599
619
|
);
|
|
600
620
|
}
|
|
601
|
-
|
|
621
|
+
if (
|
|
622
|
+
base.status === 'failed' &&
|
|
623
|
+
eventType === 'run.failed' &&
|
|
624
|
+
isPlatformDeployFailureText(eventError)
|
|
625
|
+
) {
|
|
626
|
+
return withTiming(base);
|
|
627
|
+
}
|
|
628
|
+
if (TERMINAL_STATUS_BY_EVENT_TYPE[eventType] === base.status) {
|
|
629
|
+
if (
|
|
630
|
+
options?.allowStaleSameStatusPayloadMerge !== true ||
|
|
631
|
+
occurredAt < terminalAt
|
|
632
|
+
) {
|
|
633
|
+
return withTiming(base);
|
|
634
|
+
}
|
|
635
|
+
return null;
|
|
636
|
+
}
|
|
602
637
|
if (occurredAt > terminalAt) {
|
|
603
638
|
// Newer terminal evidence reconciles the run. This covers replay/receipt
|
|
604
639
|
// races where an earlier attempt failed but a later attempt recovered and
|
|
@@ -619,7 +654,7 @@ function retryablePlatformDeployFailureSnapshot(
|
|
|
619
654
|
if (
|
|
620
655
|
isTerminalPlayRunLedgerStatus(base.status) ||
|
|
621
656
|
!eventError ||
|
|
622
|
-
|
|
657
|
+
!isRawPlatformDeployResetText(eventError)
|
|
623
658
|
) {
|
|
624
659
|
return null;
|
|
625
660
|
}
|
|
@@ -717,7 +752,12 @@ export function reducePlayRunLedgerEvent(
|
|
|
717
752
|
});
|
|
718
753
|
case 'run.completed':
|
|
719
754
|
return (
|
|
720
|
-
conflictingTerminalSnapshot(base, event.type, occurredAt
|
|
755
|
+
conflictingTerminalSnapshot(base, event.type, occurredAt, null, {
|
|
756
|
+
allowStaleSameStatusPayloadMerge:
|
|
757
|
+
(base.result === undefined && event.result !== undefined) ||
|
|
758
|
+
(base.resultSummary === undefined &&
|
|
759
|
+
event.resultSummary !== undefined),
|
|
760
|
+
}) ??
|
|
721
761
|
withTiming({
|
|
722
762
|
...settleRunningStepsOnTerminal(base, 'completed', occurredAt),
|
|
723
763
|
status: 'completed',
|
|
@@ -733,16 +773,18 @@ export function reducePlayRunLedgerEvent(
|
|
|
733
773
|
case 'run.failed':
|
|
734
774
|
return (
|
|
735
775
|
retryablePlatformDeployFailureSnapshot(base, event.error) ??
|
|
736
|
-
conflictingTerminalSnapshot(
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
|
|
740
|
-
|
|
741
|
-
) ??
|
|
776
|
+
conflictingTerminalSnapshot(base, event.type, occurredAt, event.error, {
|
|
777
|
+
allowStaleSameStatusPayloadMerge:
|
|
778
|
+
(base.error == null && event.error != null) ||
|
|
779
|
+
(base.result === undefined && event.result !== undefined),
|
|
780
|
+
}) ??
|
|
742
781
|
withTiming({
|
|
743
782
|
...settleRunningStepsOnTerminal(base, 'failed', occurredAt),
|
|
744
783
|
status: 'failed',
|
|
745
|
-
error:
|
|
784
|
+
error:
|
|
785
|
+
base.status === 'failed'
|
|
786
|
+
? (base.error ?? event.error ?? null)
|
|
787
|
+
: (event.error ?? base.error ?? null),
|
|
746
788
|
startedAt: base.startedAt ?? occurredAt,
|
|
747
789
|
finishedAt: terminalFinishedAt(base, event.type, occurredAt),
|
|
748
790
|
activeStepId: null,
|
|
@@ -752,16 +794,18 @@ export function reducePlayRunLedgerEvent(
|
|
|
752
794
|
);
|
|
753
795
|
case 'run.cancelled':
|
|
754
796
|
return (
|
|
755
|
-
conflictingTerminalSnapshot(
|
|
756
|
-
|
|
757
|
-
|
|
758
|
-
|
|
759
|
-
|
|
760
|
-
) ??
|
|
797
|
+
conflictingTerminalSnapshot(base, event.type, occurredAt, event.error, {
|
|
798
|
+
allowStaleSameStatusPayloadMerge:
|
|
799
|
+
(base.error == null && event.error != null) ||
|
|
800
|
+
(base.result === undefined && event.result !== undefined),
|
|
801
|
+
}) ??
|
|
761
802
|
withTiming({
|
|
762
803
|
...settleRunningStepsOnTerminal(base, 'failed', occurredAt),
|
|
763
804
|
status: 'cancelled',
|
|
764
|
-
error:
|
|
805
|
+
error:
|
|
806
|
+
base.status === 'cancelled'
|
|
807
|
+
? (base.error ?? event.error ?? null)
|
|
808
|
+
: (event.error ?? base.error ?? null),
|
|
765
809
|
startedAt: base.startedAt ?? occurredAt,
|
|
766
810
|
finishedAt: terminalFinishedAt(base, event.type, occurredAt),
|
|
767
811
|
activeStepId: null,
|
|
@@ -112,6 +112,12 @@ const runtimeSheetEnsureCache = new Map<
|
|
|
112
112
|
const DIRECT_POSTGRES_BATCH_SIZE = 10_000;
|
|
113
113
|
const RUNTIME_DB_SESSION_ROW_LIMIT_FLOOR = DIRECT_POSTGRES_BATCH_SIZE;
|
|
114
114
|
const APPEND_KEY_SUFFIX_LENGTH = 12;
|
|
115
|
+
// On-demand (create_db_session) mint TTL. Deliberately NOT aligned with
|
|
116
|
+
// DB_SESSION_DEFAULT_TTL_SECONDS (60 min): on-demand sessions are isolate-
|
|
117
|
+
// cached and re-minted through sessionHasRenewalWindow, never stored in the
|
|
118
|
+
// dedup DO, so they are not subject to the 410 expired-retention race the
|
|
119
|
+
// 60-min alignment fixed. A shorter TTL only causes periodic re-mints (cheap;
|
|
120
|
+
// the tenant runtime role is long-lived), not run failures.
|
|
115
121
|
const RUNTIME_DB_SESSION_TTL_SECONDS = 10 * 60;
|
|
116
122
|
const RUNTIME_SHEET_ENSURE_CACHE_TTL_MS = 10 * 60_000;
|
|
117
123
|
const RUNTIME_SHEET_READY_AFTER_ENSURE_RETRY_DELAYS_MS = [
|
package/dist/cli/index.js
CHANGED
|
@@ -623,10 +623,10 @@ var SDK_RELEASE = {
|
|
|
623
623
|
// 0.1.154 removes the short-lived generated enrich StepOptions recompute
|
|
624
624
|
// fields shipped in 0.1.153.
|
|
625
625
|
// 0.1.175 ships loud `deepline enrich` empty-waterfall reporting.
|
|
626
|
-
version: "0.1.
|
|
626
|
+
version: "0.1.180",
|
|
627
627
|
apiContract: "2026-06-dataset-handle-results-hard-cutover",
|
|
628
628
|
supportPolicy: {
|
|
629
|
-
latest: "0.1.
|
|
629
|
+
latest: "0.1.180",
|
|
630
630
|
minimumSupported: "0.1.53",
|
|
631
631
|
deprecatedBelow: "0.1.53",
|
|
632
632
|
commandMinimumSupported: [
|
|
@@ -787,6 +787,58 @@ var SYNTHETIC_RUN_HEADER = "x-deepline-synthetic-run";
|
|
|
787
787
|
// src/http.ts
|
|
788
788
|
var MAX_DIAGNOSTIC_HEADER_LENGTH = 120;
|
|
789
789
|
var COWORK_NETWORK_HINT = "Claude Cowork appears to be running Deepline in a network-restricted sandbox. In Claude Desktop, open Settings > Capabilities, turn on Allow network egress, and set Domain allowlist to All domains for the Cowork session.";
|
|
790
|
+
var REQUEST_TIMEOUT_MARKER = /* @__PURE__ */ Symbol("deeplineRequestTimeout");
|
|
791
|
+
var REQUEST_ABORT_MARKER = /* @__PURE__ */ Symbol("deeplineRequestAbort");
|
|
792
|
+
function normalizeRequestAbortError(error, input2) {
|
|
793
|
+
const normalized = error instanceof Error ? error : new Error(String(error));
|
|
794
|
+
const tagged = normalized;
|
|
795
|
+
if (input2.timedOut) {
|
|
796
|
+
tagged[REQUEST_TIMEOUT_MARKER] = input2.timeoutMs;
|
|
797
|
+
} else if (isAbortLikeError(normalized)) {
|
|
798
|
+
tagged[REQUEST_ABORT_MARKER] = true;
|
|
799
|
+
}
|
|
800
|
+
return normalized;
|
|
801
|
+
}
|
|
802
|
+
function isAbortLikeError(error) {
|
|
803
|
+
const name = error.name.toLowerCase();
|
|
804
|
+
const message = error.message.toLowerCase();
|
|
805
|
+
return name === "aborterror" || message.includes("operation was aborted") || message === "aborted";
|
|
806
|
+
}
|
|
807
|
+
function mapNetworkError(baseUrl, error, targetLabel) {
|
|
808
|
+
const tagged = error;
|
|
809
|
+
const timeoutMs = tagged?.[REQUEST_TIMEOUT_MARKER];
|
|
810
|
+
if (typeof timeoutMs === "number") {
|
|
811
|
+
return {
|
|
812
|
+
message: `Request to ${targetLabel} timed out after ${timeoutMs}ms while waiting for a response.`,
|
|
813
|
+
code: "NETWORK_TIMEOUT",
|
|
814
|
+
details: { timeoutMs, target: targetLabel }
|
|
815
|
+
};
|
|
816
|
+
}
|
|
817
|
+
if (tagged?.[REQUEST_ABORT_MARKER]) {
|
|
818
|
+
return {
|
|
819
|
+
message: `Request to ${targetLabel} was aborted before it completed.`,
|
|
820
|
+
code: "NETWORK_ABORTED"
|
|
821
|
+
};
|
|
822
|
+
}
|
|
823
|
+
return {
|
|
824
|
+
message: error?.message ? `Unable to connect to ${baseUrl}. ${error.message}` : `Unable to connect to ${baseUrl}. Is the computer able to access the url?`,
|
|
825
|
+
code: "NETWORK_ERROR"
|
|
826
|
+
};
|
|
827
|
+
}
|
|
828
|
+
function describeRequestTarget(path) {
|
|
829
|
+
const toolExecuteMatch = path.match(
|
|
830
|
+
/^\/api\/v2\/integrations\/([^/?#]+)\/execute(?:[/?#]|$)/
|
|
831
|
+
);
|
|
832
|
+
if (toolExecuteMatch) {
|
|
833
|
+
return providerLabelFromToolId(decodeURIComponent(toolExecuteMatch[1]));
|
|
834
|
+
}
|
|
835
|
+
return "Deepline";
|
|
836
|
+
}
|
|
837
|
+
function providerLabelFromToolId(toolId) {
|
|
838
|
+
const normalized = toolId.trim().toLowerCase();
|
|
839
|
+
const provider = normalized.split(/[_:./-]+/)[0];
|
|
840
|
+
return provider || normalized || "provider";
|
|
841
|
+
}
|
|
790
842
|
var HttpClient = class {
|
|
791
843
|
constructor(config) {
|
|
792
844
|
this.config = config;
|
|
@@ -899,10 +951,12 @@ var HttpClient = class {
|
|
|
899
951
|
}
|
|
900
952
|
for (const candidateUrl of candidateUrls) {
|
|
901
953
|
const controller = new AbortController();
|
|
902
|
-
const
|
|
903
|
-
|
|
904
|
-
|
|
905
|
-
|
|
954
|
+
const timeoutMs = options?.timeout ?? this.config.timeout;
|
|
955
|
+
let requestTimedOut = false;
|
|
956
|
+
const timeoutId = setTimeout(() => {
|
|
957
|
+
requestTimedOut = true;
|
|
958
|
+
controller.abort();
|
|
959
|
+
}, timeoutMs);
|
|
906
960
|
try {
|
|
907
961
|
const response = await fetch(candidateUrl, {
|
|
908
962
|
method,
|
|
@@ -975,7 +1029,10 @@ var HttpClient = class {
|
|
|
975
1029
|
lastError = error;
|
|
976
1030
|
break;
|
|
977
1031
|
}
|
|
978
|
-
lastError = error
|
|
1032
|
+
lastError = normalizeRequestAbortError(error, {
|
|
1033
|
+
timeoutMs,
|
|
1034
|
+
timedOut: requestTimedOut
|
|
1035
|
+
});
|
|
979
1036
|
}
|
|
980
1037
|
}
|
|
981
1038
|
if (attempt < maxRetries) continue;
|
|
@@ -983,8 +1040,17 @@ var HttpClient = class {
|
|
|
983
1040
|
if (lastError instanceof DeeplineError) {
|
|
984
1041
|
throw lastError;
|
|
985
1042
|
}
|
|
986
|
-
const
|
|
987
|
-
|
|
1043
|
+
const mappedNetworkError = mapNetworkError(
|
|
1044
|
+
baseUrl,
|
|
1045
|
+
lastError,
|
|
1046
|
+
describeRequestTarget(path)
|
|
1047
|
+
);
|
|
1048
|
+
throw new DeeplineError(
|
|
1049
|
+
withCoworkNetworkHint(mappedNetworkError.message),
|
|
1050
|
+
void 0,
|
|
1051
|
+
mappedNetworkError.code,
|
|
1052
|
+
mappedNetworkError.details
|
|
1053
|
+
);
|
|
988
1054
|
}
|
|
989
1055
|
/**
|
|
990
1056
|
* Send a GET request.
|
|
@@ -2255,6 +2321,57 @@ function decodeBase64Bytes(value) {
|
|
|
2255
2321
|
}
|
|
2256
2322
|
return bytes;
|
|
2257
2323
|
}
|
|
2324
|
+
var STAGE_LEGACY_MULTIPART_MAX_BYTES = 4e6;
|
|
2325
|
+
var STAGE_DIRECT_UPLOAD_MAX_ATTEMPTS = 3;
|
|
2326
|
+
function stagedUploadIdentity(logicalPath, contentHash) {
|
|
2327
|
+
return `${contentHash}:${logicalPath}`;
|
|
2328
|
+
}
|
|
2329
|
+
function isStagedUploadMintUnsupported(error) {
|
|
2330
|
+
return error instanceof DeeplineError && error.statusCode === 404;
|
|
2331
|
+
}
|
|
2332
|
+
function formatMegabytes(bytes) {
|
|
2333
|
+
return `${(bytes / (1024 * 1024)).toFixed(1)} MB`;
|
|
2334
|
+
}
|
|
2335
|
+
async function uploadStagedFileToPresignedUrl(input2) {
|
|
2336
|
+
const arrayBuffer = input2.body.buffer.slice(
|
|
2337
|
+
input2.body.byteOffset,
|
|
2338
|
+
input2.body.byteOffset + input2.body.byteLength
|
|
2339
|
+
);
|
|
2340
|
+
let lastError;
|
|
2341
|
+
for (let attempt = 1; attempt <= STAGE_DIRECT_UPLOAD_MAX_ATTEMPTS; attempt += 1) {
|
|
2342
|
+
try {
|
|
2343
|
+
const response = await fetch(input2.url, {
|
|
2344
|
+
method: "PUT",
|
|
2345
|
+
headers: input2.headers,
|
|
2346
|
+
body: new Blob([arrayBuffer])
|
|
2347
|
+
});
|
|
2348
|
+
if (response.ok) {
|
|
2349
|
+
return;
|
|
2350
|
+
}
|
|
2351
|
+
const text = await response.text().catch(() => "");
|
|
2352
|
+
lastError = new DeeplineError(
|
|
2353
|
+
`Direct storage upload of ${input2.logicalPath} failed: ${response.status} ${text.slice(0, 200)}`,
|
|
2354
|
+
response.status,
|
|
2355
|
+
"STAGED_FILE_UPLOAD_FAILED",
|
|
2356
|
+
{ logicalPath: input2.logicalPath }
|
|
2357
|
+
);
|
|
2358
|
+
if (response.status < 500 && response.status !== 429) {
|
|
2359
|
+
break;
|
|
2360
|
+
}
|
|
2361
|
+
} catch (error) {
|
|
2362
|
+
lastError = error;
|
|
2363
|
+
}
|
|
2364
|
+
if (attempt < STAGE_DIRECT_UPLOAD_MAX_ATTEMPTS) {
|
|
2365
|
+
await sleep2(250 * attempt);
|
|
2366
|
+
}
|
|
2367
|
+
}
|
|
2368
|
+
throw lastError instanceof Error ? lastError : new DeeplineError(
|
|
2369
|
+
`Direct storage upload of ${input2.logicalPath} failed.`,
|
|
2370
|
+
void 0,
|
|
2371
|
+
"STAGED_FILE_UPLOAD_FAILED",
|
|
2372
|
+
{ logicalPath: input2.logicalPath }
|
|
2373
|
+
);
|
|
2374
|
+
}
|
|
2258
2375
|
function readStringArray(value) {
|
|
2259
2376
|
return Array.isArray(value) ? value.filter((line) => typeof line === "string") : [];
|
|
2260
2377
|
}
|
|
@@ -2660,6 +2777,21 @@ var DeeplineClient = class {
|
|
|
2660
2777
|
...input2.maxRows ? { max_rows: input2.maxRows } : {}
|
|
2661
2778
|
});
|
|
2662
2779
|
}
|
|
2780
|
+
/**
|
|
2781
|
+
* Re-establish this workspace's tenant storage contract: role/DB connect
|
|
2782
|
+
* grants plus materialized table grants. Org-admin only. Use when a run fails
|
|
2783
|
+
* with WORKSPACE_STORAGE_NOT_READY.
|
|
2784
|
+
*/
|
|
2785
|
+
async repairIngestionStorage(input2) {
|
|
2786
|
+
return this.http.post(
|
|
2787
|
+
"/api/v2/ingestion/repair",
|
|
2788
|
+
{
|
|
2789
|
+
...input2?.provider ? { provider: input2.provider } : {}
|
|
2790
|
+
},
|
|
2791
|
+
void 0,
|
|
2792
|
+
{ forbiddenAsApiError: true }
|
|
2793
|
+
);
|
|
2794
|
+
}
|
|
2663
2795
|
// ——————————————————————————————————————————————————————————
|
|
2664
2796
|
// Plays — submission and lifecycle
|
|
2665
2797
|
// ——————————————————————————————————————————————————————————
|
|
@@ -3037,6 +3169,94 @@ var DeeplineClient = class {
|
|
|
3037
3169
|
* ```
|
|
3038
3170
|
*/
|
|
3039
3171
|
async stagePlayFiles(files) {
|
|
3172
|
+
let uploads;
|
|
3173
|
+
try {
|
|
3174
|
+
uploads = await this.mintStagedPlayFileUploads(
|
|
3175
|
+
files.map((file) => ({
|
|
3176
|
+
logicalPath: file.logicalPath,
|
|
3177
|
+
contentHash: file.contentHash,
|
|
3178
|
+
contentType: file.contentType,
|
|
3179
|
+
bytes: file.bytes
|
|
3180
|
+
}))
|
|
3181
|
+
);
|
|
3182
|
+
} catch (error) {
|
|
3183
|
+
if (isStagedUploadMintUnsupported(error)) {
|
|
3184
|
+
return this.stagePlayFilesViaMultipart(files);
|
|
3185
|
+
}
|
|
3186
|
+
throw error;
|
|
3187
|
+
}
|
|
3188
|
+
const uploadByIdentity = /* @__PURE__ */ new Map();
|
|
3189
|
+
for (const upload of uploads) {
|
|
3190
|
+
uploadByIdentity.set(
|
|
3191
|
+
stagedUploadIdentity(upload.logicalPath, upload.contentHash),
|
|
3192
|
+
upload
|
|
3193
|
+
);
|
|
3194
|
+
}
|
|
3195
|
+
await Promise.all(
|
|
3196
|
+
files.map(async (file) => {
|
|
3197
|
+
const upload = uploadByIdentity.get(
|
|
3198
|
+
stagedUploadIdentity(file.logicalPath, file.contentHash)
|
|
3199
|
+
);
|
|
3200
|
+
if (!upload) {
|
|
3201
|
+
throw new DeeplineError(
|
|
3202
|
+
`The staging server did not return an upload target for ${file.logicalPath}.`,
|
|
3203
|
+
void 0,
|
|
3204
|
+
"STAGED_FILE_MINT_INCOMPLETE"
|
|
3205
|
+
);
|
|
3206
|
+
}
|
|
3207
|
+
if (upload.alreadyStaged || !upload.uploadUrl) {
|
|
3208
|
+
return;
|
|
3209
|
+
}
|
|
3210
|
+
await uploadStagedFileToPresignedUrl({
|
|
3211
|
+
url: upload.uploadUrl,
|
|
3212
|
+
headers: upload.uploadHeaders ?? {
|
|
3213
|
+
"content-type": file.contentType
|
|
3214
|
+
},
|
|
3215
|
+
body: decodeBase64Bytes(file.contentBase64),
|
|
3216
|
+
logicalPath: file.logicalPath
|
|
3217
|
+
});
|
|
3218
|
+
})
|
|
3219
|
+
);
|
|
3220
|
+
return files.map((file) => {
|
|
3221
|
+
const upload = uploadByIdentity.get(
|
|
3222
|
+
stagedUploadIdentity(file.logicalPath, file.contentHash)
|
|
3223
|
+
);
|
|
3224
|
+
if (!upload) {
|
|
3225
|
+
throw new DeeplineError(
|
|
3226
|
+
`The staging server did not return an upload target for ${file.logicalPath}.`,
|
|
3227
|
+
void 0,
|
|
3228
|
+
"STAGED_FILE_MINT_INCOMPLETE"
|
|
3229
|
+
);
|
|
3230
|
+
}
|
|
3231
|
+
return upload.ref;
|
|
3232
|
+
});
|
|
3233
|
+
}
|
|
3234
|
+
/**
|
|
3235
|
+
* Mint short-lived presigned upload targets for staged play files.
|
|
3236
|
+
*
|
|
3237
|
+
* Internal primitive used by {@link stagePlayFiles}. The server returns an
|
|
3238
|
+
* already-staged ref (no upload needed) for content-addressed files it
|
|
3239
|
+
* already holds, or a presigned PUT URL the caller uploads the body to.
|
|
3240
|
+
*/
|
|
3241
|
+
async mintStagedPlayFileUploads(files) {
|
|
3242
|
+
const response = await this.http.post("/api/v2/plays/files/stage/mint", { files });
|
|
3243
|
+
return response.uploads ?? [];
|
|
3244
|
+
}
|
|
3245
|
+
async stagePlayFilesViaMultipart(files) {
|
|
3246
|
+
for (const file of files) {
|
|
3247
|
+
if (file.bytes > STAGE_LEGACY_MULTIPART_MAX_BYTES) {
|
|
3248
|
+
throw new DeeplineError(
|
|
3249
|
+
`Cannot stage ${file.logicalPath} (${formatMegabytes(file.bytes)}): the connected Deepline server does not support direct-to-storage uploads, and this file exceeds the ~4.5MB request-body limit the legacy upload path is subject to. Update the Deepline server (or target code.deepline.com) to stage files this large.`,
|
|
3250
|
+
413,
|
|
3251
|
+
"STAGED_FILE_TOO_LARGE_FOR_LEGACY_UPLOAD",
|
|
3252
|
+
{
|
|
3253
|
+
logicalPath: file.logicalPath,
|
|
3254
|
+
bytes: file.bytes,
|
|
3255
|
+
maxBytes: STAGE_LEGACY_MULTIPART_MAX_BYTES
|
|
3256
|
+
}
|
|
3257
|
+
);
|
|
3258
|
+
}
|
|
3259
|
+
}
|
|
3040
3260
|
const buildFormData = () => {
|
|
3041
3261
|
const formData = new FormData();
|
|
3042
3262
|
formData.set(
|
|
@@ -7670,6 +7890,53 @@ async function handleDbQuery(args) {
|
|
|
7670
7890
|
);
|
|
7671
7891
|
return 0;
|
|
7672
7892
|
}
|
|
7893
|
+
async function handleDbRepair(args) {
|
|
7894
|
+
const providerIndex = args.indexOf("--provider");
|
|
7895
|
+
const provider = providerIndex >= 0 ? args[providerIndex + 1]?.trim() : void 0;
|
|
7896
|
+
const jsonOutput = argsWantJson(args);
|
|
7897
|
+
const client2 = new DeeplineClient();
|
|
7898
|
+
let result;
|
|
7899
|
+
try {
|
|
7900
|
+
result = await client2.repairIngestionStorage(
|
|
7901
|
+
provider ? { provider } : void 0
|
|
7902
|
+
);
|
|
7903
|
+
} catch (error) {
|
|
7904
|
+
if (error instanceof DeeplineError && error.statusCode === 403) {
|
|
7905
|
+
console.error(
|
|
7906
|
+
"deepline db repair requires org admin. Ask an organization admin to run it."
|
|
7907
|
+
);
|
|
7908
|
+
return 1;
|
|
7909
|
+
}
|
|
7910
|
+
console.error(errorMessage(error));
|
|
7911
|
+
return 1;
|
|
7912
|
+
}
|
|
7913
|
+
printCommandEnvelope(
|
|
7914
|
+
{
|
|
7915
|
+
command: "db repair",
|
|
7916
|
+
repaired: result.repaired,
|
|
7917
|
+
count: result.count,
|
|
7918
|
+
connection_grants: result.connection_grants,
|
|
7919
|
+
render: {
|
|
7920
|
+
sections: [
|
|
7921
|
+
{
|
|
7922
|
+
title: "storage contract repair",
|
|
7923
|
+
lines: [
|
|
7924
|
+
`Repaired ${result.count} table(s).`,
|
|
7925
|
+
`Runtime role: ${result.connection_grants.runtime_role}`,
|
|
7926
|
+
`Customer DB role: ${result.connection_grants.customer_db_role}`
|
|
7927
|
+
]
|
|
7928
|
+
}
|
|
7929
|
+
]
|
|
7930
|
+
}
|
|
7931
|
+
},
|
|
7932
|
+
{
|
|
7933
|
+
json: jsonOutput,
|
|
7934
|
+
text: `Repaired ${result.count} table(s). Storage grants re-established.
|
|
7935
|
+
`
|
|
7936
|
+
}
|
|
7937
|
+
);
|
|
7938
|
+
return 0;
|
|
7939
|
+
}
|
|
7673
7940
|
function registerDbCommands(program) {
|
|
7674
7941
|
const db = program.command("db").description("Query the tenant customer database.").addHelpText(
|
|
7675
7942
|
"after",
|
|
@@ -7725,6 +7992,26 @@ Examples:
|
|
|
7725
7992
|
...options.json ? ["--json"] : []
|
|
7726
7993
|
]);
|
|
7727
7994
|
});
|
|
7995
|
+
db.command("repair").description(
|
|
7996
|
+
"Repair this workspace storage contract (role/DB grants). Org admin only."
|
|
7997
|
+
).addHelpText(
|
|
7998
|
+
"after",
|
|
7999
|
+
`
|
|
8000
|
+
Notes:
|
|
8001
|
+
Re-establishes tenant runtime and customer-db role grants plus materialized
|
|
8002
|
+
table grants for the active workspace. Run this after a play run fails with
|
|
8003
|
+
WORKSPACE_STORAGE_NOT_READY, then re-run the play. Requires org admin.
|
|
8004
|
+
|
|
8005
|
+
Examples:
|
|
8006
|
+
deepline db repair
|
|
8007
|
+
deepline db repair --json
|
|
8008
|
+
`
|
|
8009
|
+
).option("--provider <provider>", "Limit materialized-table repair to one provider").option("--json", "Emit raw JSON response. Also automatic when stdout is piped").action(async (options) => {
|
|
8010
|
+
process.exitCode = await handleDbRepair([
|
|
8011
|
+
...options.provider ? ["--provider", options.provider] : [],
|
|
8012
|
+
...options.json ? ["--json"] : []
|
|
8013
|
+
]);
|
|
8014
|
+
});
|
|
7728
8015
|
}
|
|
7729
8016
|
|
|
7730
8017
|
// src/cli/commands/enrich.ts
|
|
@@ -12531,6 +12818,26 @@ function resolveDatasetByName(available, datasetPath) {
|
|
|
12531
12818
|
}
|
|
12532
12819
|
return null;
|
|
12533
12820
|
}
|
|
12821
|
+
function assertDatasetHasExportableRows(input2) {
|
|
12822
|
+
if (input2.rowsInfo.rows.length > 0) {
|
|
12823
|
+
return input2.rowsInfo;
|
|
12824
|
+
}
|
|
12825
|
+
const datasetLabel = input2.datasetPath ?? input2.rowsInfo.tableNamespace ?? input2.rowsInfo.source ?? "result.rows";
|
|
12826
|
+
const available = input2.availableRows.map((info) => info.tableNamespace ?? info.source).filter((name) => typeof name === "string");
|
|
12827
|
+
const runStatus = String(input2.status.status ?? "").toLowerCase();
|
|
12828
|
+
const failedBeforePersisting = runStatus !== "completed";
|
|
12829
|
+
throw new DeeplineError(
|
|
12830
|
+
`Run ${input2.status.runId} has zero rows to export from dataset '${datasetLabel}'.` + (failedBeforePersisting ? ` The run status is '${runStatus || "unknown"}', so it likely failed before persisting rows to this dataset \u2014 provider calls may have been billed without a durable row. Fix the run's failure cause and re-run to resume.` : " The run completed but this dataset materialized no rows.") + (available.length > 0 ? ` Available datasets: ${available.join(", ")}.` : " No datasets were materialized for this run."),
|
|
12831
|
+
void 0,
|
|
12832
|
+
"RUN_EXPORT_EMPTY",
|
|
12833
|
+
{
|
|
12834
|
+
runId: input2.status.runId,
|
|
12835
|
+
dataset: datasetLabel,
|
|
12836
|
+
available,
|
|
12837
|
+
runStatus: runStatus || null
|
|
12838
|
+
}
|
|
12839
|
+
);
|
|
12840
|
+
}
|
|
12534
12841
|
async function exportPlayStatusRows(client2, status, outPath, options = {}) {
|
|
12535
12842
|
if (!outPath) {
|
|
12536
12843
|
return null;
|
|
@@ -12576,13 +12883,25 @@ async function exportPlayStatusRows(client2, status, outPath, options = {}) {
|
|
|
12576
12883
|
}
|
|
12577
12884
|
}
|
|
12578
12885
|
if (fetchedRowsInfo?.complete) {
|
|
12886
|
+
const guarded2 = assertDatasetHasExportableRows({
|
|
12887
|
+
rowsInfo: fetchedRowsInfo,
|
|
12888
|
+
status,
|
|
12889
|
+
availableRows,
|
|
12890
|
+
datasetPath: options.datasetPath
|
|
12891
|
+
});
|
|
12579
12892
|
return {
|
|
12580
|
-
path: writeCanonicalRowsCsv(
|
|
12581
|
-
rowsInfo:
|
|
12893
|
+
path: writeCanonicalRowsCsv(guarded2, outPath),
|
|
12894
|
+
rowsInfo: guarded2
|
|
12582
12895
|
};
|
|
12583
12896
|
}
|
|
12584
12897
|
if (rowsInfo.complete) {
|
|
12585
|
-
|
|
12898
|
+
const guarded2 = assertDatasetHasExportableRows({
|
|
12899
|
+
rowsInfo,
|
|
12900
|
+
status,
|
|
12901
|
+
availableRows,
|
|
12902
|
+
datasetPath: options.datasetPath
|
|
12903
|
+
});
|
|
12904
|
+
return { path: writeCanonicalRowsCsv(guarded2, outPath), rowsInfo: guarded2 };
|
|
12586
12905
|
}
|
|
12587
12906
|
if (attempt < attempts && retryDelayMs > 0) {
|
|
12588
12907
|
await sleep5(retryDelayMs);
|
|
@@ -12608,7 +12927,13 @@ async function exportPlayStatusRows(client2, status, outPath, options = {}) {
|
|
|
12608
12927
|
}
|
|
12609
12928
|
);
|
|
12610
12929
|
}
|
|
12611
|
-
|
|
12930
|
+
const guarded = assertDatasetHasExportableRows({
|
|
12931
|
+
rowsInfo,
|
|
12932
|
+
status,
|
|
12933
|
+
availableRows,
|
|
12934
|
+
datasetPath: options.datasetPath
|
|
12935
|
+
});
|
|
12936
|
+
return { path: writeCanonicalRowsCsv(guarded, outPath), rowsInfo: guarded };
|
|
12612
12937
|
}
|
|
12613
12938
|
function extractActiveRunsFromError(error) {
|
|
12614
12939
|
if (!(error instanceof DeeplineError)) {
|