deepline 0.1.179 → 0.1.181
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 +131 -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 +379 -57
- package/dist/bundling-sources/apps/play-runner-workers/src/runtime/batching.ts +42 -2
- 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/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/runtime-api.ts +6 -0
- package/dist/cli/index.js +339 -14
- package/dist/cli/index.mjs +339 -14
- 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
package/dist/index.js
CHANGED
|
@@ -422,10 +422,10 @@ var SDK_RELEASE = {
|
|
|
422
422
|
// 0.1.154 removes the short-lived generated enrich StepOptions recompute
|
|
423
423
|
// fields shipped in 0.1.153.
|
|
424
424
|
// 0.1.175 ships loud `deepline enrich` empty-waterfall reporting.
|
|
425
|
-
version: "0.1.
|
|
425
|
+
version: "0.1.181",
|
|
426
426
|
apiContract: "2026-06-dataset-handle-results-hard-cutover",
|
|
427
427
|
supportPolicy: {
|
|
428
|
-
latest: "0.1.
|
|
428
|
+
latest: "0.1.181",
|
|
429
429
|
minimumSupported: "0.1.53",
|
|
430
430
|
deprecatedBelow: "0.1.53",
|
|
431
431
|
commandMinimumSupported: [
|
|
@@ -586,6 +586,58 @@ var SYNTHETIC_RUN_HEADER = "x-deepline-synthetic-run";
|
|
|
586
586
|
// src/http.ts
|
|
587
587
|
var MAX_DIAGNOSTIC_HEADER_LENGTH = 120;
|
|
588
588
|
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.";
|
|
589
|
+
var REQUEST_TIMEOUT_MARKER = /* @__PURE__ */ Symbol("deeplineRequestTimeout");
|
|
590
|
+
var REQUEST_ABORT_MARKER = /* @__PURE__ */ Symbol("deeplineRequestAbort");
|
|
591
|
+
function normalizeRequestAbortError(error, input) {
|
|
592
|
+
const normalized = error instanceof Error ? error : new Error(String(error));
|
|
593
|
+
const tagged = normalized;
|
|
594
|
+
if (input.timedOut) {
|
|
595
|
+
tagged[REQUEST_TIMEOUT_MARKER] = input.timeoutMs;
|
|
596
|
+
} else if (isAbortLikeError(normalized)) {
|
|
597
|
+
tagged[REQUEST_ABORT_MARKER] = true;
|
|
598
|
+
}
|
|
599
|
+
return normalized;
|
|
600
|
+
}
|
|
601
|
+
function isAbortLikeError(error) {
|
|
602
|
+
const name = error.name.toLowerCase();
|
|
603
|
+
const message = error.message.toLowerCase();
|
|
604
|
+
return name === "aborterror" || message.includes("operation was aborted") || message === "aborted";
|
|
605
|
+
}
|
|
606
|
+
function mapNetworkError(baseUrl, error, targetLabel) {
|
|
607
|
+
const tagged = error;
|
|
608
|
+
const timeoutMs = tagged?.[REQUEST_TIMEOUT_MARKER];
|
|
609
|
+
if (typeof timeoutMs === "number") {
|
|
610
|
+
return {
|
|
611
|
+
message: `Request to ${targetLabel} timed out after ${timeoutMs}ms while waiting for a response.`,
|
|
612
|
+
code: "NETWORK_TIMEOUT",
|
|
613
|
+
details: { timeoutMs, target: targetLabel }
|
|
614
|
+
};
|
|
615
|
+
}
|
|
616
|
+
if (tagged?.[REQUEST_ABORT_MARKER]) {
|
|
617
|
+
return {
|
|
618
|
+
message: `Request to ${targetLabel} was aborted before it completed.`,
|
|
619
|
+
code: "NETWORK_ABORTED"
|
|
620
|
+
};
|
|
621
|
+
}
|
|
622
|
+
return {
|
|
623
|
+
message: error?.message ? `Unable to connect to ${baseUrl}. ${error.message}` : `Unable to connect to ${baseUrl}. Is the computer able to access the url?`,
|
|
624
|
+
code: "NETWORK_ERROR"
|
|
625
|
+
};
|
|
626
|
+
}
|
|
627
|
+
function describeRequestTarget(path) {
|
|
628
|
+
const toolExecuteMatch = path.match(
|
|
629
|
+
/^\/api\/v2\/integrations\/([^/?#]+)\/execute(?:[/?#]|$)/
|
|
630
|
+
);
|
|
631
|
+
if (toolExecuteMatch) {
|
|
632
|
+
return providerLabelFromToolId(decodeURIComponent(toolExecuteMatch[1]));
|
|
633
|
+
}
|
|
634
|
+
return "Deepline";
|
|
635
|
+
}
|
|
636
|
+
function providerLabelFromToolId(toolId) {
|
|
637
|
+
const normalized = toolId.trim().toLowerCase();
|
|
638
|
+
const provider = normalized.split(/[_:./-]+/)[0];
|
|
639
|
+
return provider || normalized || "provider";
|
|
640
|
+
}
|
|
589
641
|
var HttpClient = class {
|
|
590
642
|
constructor(config) {
|
|
591
643
|
this.config = config;
|
|
@@ -698,10 +750,12 @@ var HttpClient = class {
|
|
|
698
750
|
}
|
|
699
751
|
for (const candidateUrl of candidateUrls) {
|
|
700
752
|
const controller = new AbortController();
|
|
701
|
-
const
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
|
|
753
|
+
const timeoutMs = options?.timeout ?? this.config.timeout;
|
|
754
|
+
let requestTimedOut = false;
|
|
755
|
+
const timeoutId = setTimeout(() => {
|
|
756
|
+
requestTimedOut = true;
|
|
757
|
+
controller.abort();
|
|
758
|
+
}, timeoutMs);
|
|
705
759
|
try {
|
|
706
760
|
const response = await fetch(candidateUrl, {
|
|
707
761
|
method,
|
|
@@ -774,7 +828,10 @@ var HttpClient = class {
|
|
|
774
828
|
lastError = error;
|
|
775
829
|
break;
|
|
776
830
|
}
|
|
777
|
-
lastError = error
|
|
831
|
+
lastError = normalizeRequestAbortError(error, {
|
|
832
|
+
timeoutMs,
|
|
833
|
+
timedOut: requestTimedOut
|
|
834
|
+
});
|
|
778
835
|
}
|
|
779
836
|
}
|
|
780
837
|
if (attempt < maxRetries) continue;
|
|
@@ -782,8 +839,17 @@ var HttpClient = class {
|
|
|
782
839
|
if (lastError instanceof DeeplineError) {
|
|
783
840
|
throw lastError;
|
|
784
841
|
}
|
|
785
|
-
const
|
|
786
|
-
|
|
842
|
+
const mappedNetworkError = mapNetworkError(
|
|
843
|
+
baseUrl,
|
|
844
|
+
lastError,
|
|
845
|
+
describeRequestTarget(path)
|
|
846
|
+
);
|
|
847
|
+
throw new DeeplineError(
|
|
848
|
+
withCoworkNetworkHint(mappedNetworkError.message),
|
|
849
|
+
void 0,
|
|
850
|
+
mappedNetworkError.code,
|
|
851
|
+
mappedNetworkError.details
|
|
852
|
+
);
|
|
787
853
|
}
|
|
788
854
|
/**
|
|
789
855
|
* Send a GET request.
|
|
@@ -2054,6 +2120,57 @@ function decodeBase64Bytes(value) {
|
|
|
2054
2120
|
}
|
|
2055
2121
|
return bytes;
|
|
2056
2122
|
}
|
|
2123
|
+
var STAGE_LEGACY_MULTIPART_MAX_BYTES = 4e6;
|
|
2124
|
+
var STAGE_DIRECT_UPLOAD_MAX_ATTEMPTS = 3;
|
|
2125
|
+
function stagedUploadIdentity(logicalPath, contentHash) {
|
|
2126
|
+
return `${contentHash}:${logicalPath}`;
|
|
2127
|
+
}
|
|
2128
|
+
function isStagedUploadMintUnsupported(error) {
|
|
2129
|
+
return error instanceof DeeplineError && error.statusCode === 404;
|
|
2130
|
+
}
|
|
2131
|
+
function formatMegabytes(bytes) {
|
|
2132
|
+
return `${(bytes / (1024 * 1024)).toFixed(1)} MB`;
|
|
2133
|
+
}
|
|
2134
|
+
async function uploadStagedFileToPresignedUrl(input) {
|
|
2135
|
+
const arrayBuffer = input.body.buffer.slice(
|
|
2136
|
+
input.body.byteOffset,
|
|
2137
|
+
input.body.byteOffset + input.body.byteLength
|
|
2138
|
+
);
|
|
2139
|
+
let lastError;
|
|
2140
|
+
for (let attempt = 1; attempt <= STAGE_DIRECT_UPLOAD_MAX_ATTEMPTS; attempt += 1) {
|
|
2141
|
+
try {
|
|
2142
|
+
const response = await fetch(input.url, {
|
|
2143
|
+
method: "PUT",
|
|
2144
|
+
headers: input.headers,
|
|
2145
|
+
body: new Blob([arrayBuffer])
|
|
2146
|
+
});
|
|
2147
|
+
if (response.ok) {
|
|
2148
|
+
return;
|
|
2149
|
+
}
|
|
2150
|
+
const text = await response.text().catch(() => "");
|
|
2151
|
+
lastError = new DeeplineError(
|
|
2152
|
+
`Direct storage upload of ${input.logicalPath} failed: ${response.status} ${text.slice(0, 200)}`,
|
|
2153
|
+
response.status,
|
|
2154
|
+
"STAGED_FILE_UPLOAD_FAILED",
|
|
2155
|
+
{ logicalPath: input.logicalPath }
|
|
2156
|
+
);
|
|
2157
|
+
if (response.status < 500 && response.status !== 429) {
|
|
2158
|
+
break;
|
|
2159
|
+
}
|
|
2160
|
+
} catch (error) {
|
|
2161
|
+
lastError = error;
|
|
2162
|
+
}
|
|
2163
|
+
if (attempt < STAGE_DIRECT_UPLOAD_MAX_ATTEMPTS) {
|
|
2164
|
+
await sleep2(250 * attempt);
|
|
2165
|
+
}
|
|
2166
|
+
}
|
|
2167
|
+
throw lastError instanceof Error ? lastError : new DeeplineError(
|
|
2168
|
+
`Direct storage upload of ${input.logicalPath} failed.`,
|
|
2169
|
+
void 0,
|
|
2170
|
+
"STAGED_FILE_UPLOAD_FAILED",
|
|
2171
|
+
{ logicalPath: input.logicalPath }
|
|
2172
|
+
);
|
|
2173
|
+
}
|
|
2057
2174
|
function readStringArray(value) {
|
|
2058
2175
|
return Array.isArray(value) ? value.filter((line) => typeof line === "string") : [];
|
|
2059
2176
|
}
|
|
@@ -2459,6 +2576,21 @@ var DeeplineClient = class {
|
|
|
2459
2576
|
...input.maxRows ? { max_rows: input.maxRows } : {}
|
|
2460
2577
|
});
|
|
2461
2578
|
}
|
|
2579
|
+
/**
|
|
2580
|
+
* Re-establish this workspace's tenant storage contract: role/DB connect
|
|
2581
|
+
* grants plus materialized table grants. Org-admin only. Use when a run fails
|
|
2582
|
+
* with WORKSPACE_STORAGE_NOT_READY.
|
|
2583
|
+
*/
|
|
2584
|
+
async repairIngestionStorage(input) {
|
|
2585
|
+
return this.http.post(
|
|
2586
|
+
"/api/v2/ingestion/repair",
|
|
2587
|
+
{
|
|
2588
|
+
...input?.provider ? { provider: input.provider } : {}
|
|
2589
|
+
},
|
|
2590
|
+
void 0,
|
|
2591
|
+
{ forbiddenAsApiError: true }
|
|
2592
|
+
);
|
|
2593
|
+
}
|
|
2462
2594
|
// ——————————————————————————————————————————————————————————
|
|
2463
2595
|
// Plays — submission and lifecycle
|
|
2464
2596
|
// ——————————————————————————————————————————————————————————
|
|
@@ -2836,6 +2968,94 @@ var DeeplineClient = class {
|
|
|
2836
2968
|
* ```
|
|
2837
2969
|
*/
|
|
2838
2970
|
async stagePlayFiles(files) {
|
|
2971
|
+
let uploads;
|
|
2972
|
+
try {
|
|
2973
|
+
uploads = await this.mintStagedPlayFileUploads(
|
|
2974
|
+
files.map((file) => ({
|
|
2975
|
+
logicalPath: file.logicalPath,
|
|
2976
|
+
contentHash: file.contentHash,
|
|
2977
|
+
contentType: file.contentType,
|
|
2978
|
+
bytes: file.bytes
|
|
2979
|
+
}))
|
|
2980
|
+
);
|
|
2981
|
+
} catch (error) {
|
|
2982
|
+
if (isStagedUploadMintUnsupported(error)) {
|
|
2983
|
+
return this.stagePlayFilesViaMultipart(files);
|
|
2984
|
+
}
|
|
2985
|
+
throw error;
|
|
2986
|
+
}
|
|
2987
|
+
const uploadByIdentity = /* @__PURE__ */ new Map();
|
|
2988
|
+
for (const upload of uploads) {
|
|
2989
|
+
uploadByIdentity.set(
|
|
2990
|
+
stagedUploadIdentity(upload.logicalPath, upload.contentHash),
|
|
2991
|
+
upload
|
|
2992
|
+
);
|
|
2993
|
+
}
|
|
2994
|
+
await Promise.all(
|
|
2995
|
+
files.map(async (file) => {
|
|
2996
|
+
const upload = uploadByIdentity.get(
|
|
2997
|
+
stagedUploadIdentity(file.logicalPath, file.contentHash)
|
|
2998
|
+
);
|
|
2999
|
+
if (!upload) {
|
|
3000
|
+
throw new DeeplineError(
|
|
3001
|
+
`The staging server did not return an upload target for ${file.logicalPath}.`,
|
|
3002
|
+
void 0,
|
|
3003
|
+
"STAGED_FILE_MINT_INCOMPLETE"
|
|
3004
|
+
);
|
|
3005
|
+
}
|
|
3006
|
+
if (upload.alreadyStaged || !upload.uploadUrl) {
|
|
3007
|
+
return;
|
|
3008
|
+
}
|
|
3009
|
+
await uploadStagedFileToPresignedUrl({
|
|
3010
|
+
url: upload.uploadUrl,
|
|
3011
|
+
headers: upload.uploadHeaders ?? {
|
|
3012
|
+
"content-type": file.contentType
|
|
3013
|
+
},
|
|
3014
|
+
body: decodeBase64Bytes(file.contentBase64),
|
|
3015
|
+
logicalPath: file.logicalPath
|
|
3016
|
+
});
|
|
3017
|
+
})
|
|
3018
|
+
);
|
|
3019
|
+
return files.map((file) => {
|
|
3020
|
+
const upload = uploadByIdentity.get(
|
|
3021
|
+
stagedUploadIdentity(file.logicalPath, file.contentHash)
|
|
3022
|
+
);
|
|
3023
|
+
if (!upload) {
|
|
3024
|
+
throw new DeeplineError(
|
|
3025
|
+
`The staging server did not return an upload target for ${file.logicalPath}.`,
|
|
3026
|
+
void 0,
|
|
3027
|
+
"STAGED_FILE_MINT_INCOMPLETE"
|
|
3028
|
+
);
|
|
3029
|
+
}
|
|
3030
|
+
return upload.ref;
|
|
3031
|
+
});
|
|
3032
|
+
}
|
|
3033
|
+
/**
|
|
3034
|
+
* Mint short-lived presigned upload targets for staged play files.
|
|
3035
|
+
*
|
|
3036
|
+
* Internal primitive used by {@link stagePlayFiles}. The server returns an
|
|
3037
|
+
* already-staged ref (no upload needed) for content-addressed files it
|
|
3038
|
+
* already holds, or a presigned PUT URL the caller uploads the body to.
|
|
3039
|
+
*/
|
|
3040
|
+
async mintStagedPlayFileUploads(files) {
|
|
3041
|
+
const response = await this.http.post("/api/v2/plays/files/stage/mint", { files });
|
|
3042
|
+
return response.uploads ?? [];
|
|
3043
|
+
}
|
|
3044
|
+
async stagePlayFilesViaMultipart(files) {
|
|
3045
|
+
for (const file of files) {
|
|
3046
|
+
if (file.bytes > STAGE_LEGACY_MULTIPART_MAX_BYTES) {
|
|
3047
|
+
throw new DeeplineError(
|
|
3048
|
+
`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.`,
|
|
3049
|
+
413,
|
|
3050
|
+
"STAGED_FILE_TOO_LARGE_FOR_LEGACY_UPLOAD",
|
|
3051
|
+
{
|
|
3052
|
+
logicalPath: file.logicalPath,
|
|
3053
|
+
bytes: file.bytes,
|
|
3054
|
+
maxBytes: STAGE_LEGACY_MULTIPART_MAX_BYTES
|
|
3055
|
+
}
|
|
3056
|
+
);
|
|
3057
|
+
}
|
|
3058
|
+
}
|
|
2839
3059
|
const buildFormData = () => {
|
|
2840
3060
|
const formData = new FormData();
|
|
2841
3061
|
formData.set(
|
package/dist/index.mjs
CHANGED
|
@@ -352,10 +352,10 @@ var SDK_RELEASE = {
|
|
|
352
352
|
// 0.1.154 removes the short-lived generated enrich StepOptions recompute
|
|
353
353
|
// fields shipped in 0.1.153.
|
|
354
354
|
// 0.1.175 ships loud `deepline enrich` empty-waterfall reporting.
|
|
355
|
-
version: "0.1.
|
|
355
|
+
version: "0.1.181",
|
|
356
356
|
apiContract: "2026-06-dataset-handle-results-hard-cutover",
|
|
357
357
|
supportPolicy: {
|
|
358
|
-
latest: "0.1.
|
|
358
|
+
latest: "0.1.181",
|
|
359
359
|
minimumSupported: "0.1.53",
|
|
360
360
|
deprecatedBelow: "0.1.53",
|
|
361
361
|
commandMinimumSupported: [
|
|
@@ -516,6 +516,58 @@ var SYNTHETIC_RUN_HEADER = "x-deepline-synthetic-run";
|
|
|
516
516
|
// src/http.ts
|
|
517
517
|
var MAX_DIAGNOSTIC_HEADER_LENGTH = 120;
|
|
518
518
|
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.";
|
|
519
|
+
var REQUEST_TIMEOUT_MARKER = /* @__PURE__ */ Symbol("deeplineRequestTimeout");
|
|
520
|
+
var REQUEST_ABORT_MARKER = /* @__PURE__ */ Symbol("deeplineRequestAbort");
|
|
521
|
+
function normalizeRequestAbortError(error, input) {
|
|
522
|
+
const normalized = error instanceof Error ? error : new Error(String(error));
|
|
523
|
+
const tagged = normalized;
|
|
524
|
+
if (input.timedOut) {
|
|
525
|
+
tagged[REQUEST_TIMEOUT_MARKER] = input.timeoutMs;
|
|
526
|
+
} else if (isAbortLikeError(normalized)) {
|
|
527
|
+
tagged[REQUEST_ABORT_MARKER] = true;
|
|
528
|
+
}
|
|
529
|
+
return normalized;
|
|
530
|
+
}
|
|
531
|
+
function isAbortLikeError(error) {
|
|
532
|
+
const name = error.name.toLowerCase();
|
|
533
|
+
const message = error.message.toLowerCase();
|
|
534
|
+
return name === "aborterror" || message.includes("operation was aborted") || message === "aborted";
|
|
535
|
+
}
|
|
536
|
+
function mapNetworkError(baseUrl, error, targetLabel) {
|
|
537
|
+
const tagged = error;
|
|
538
|
+
const timeoutMs = tagged?.[REQUEST_TIMEOUT_MARKER];
|
|
539
|
+
if (typeof timeoutMs === "number") {
|
|
540
|
+
return {
|
|
541
|
+
message: `Request to ${targetLabel} timed out after ${timeoutMs}ms while waiting for a response.`,
|
|
542
|
+
code: "NETWORK_TIMEOUT",
|
|
543
|
+
details: { timeoutMs, target: targetLabel }
|
|
544
|
+
};
|
|
545
|
+
}
|
|
546
|
+
if (tagged?.[REQUEST_ABORT_MARKER]) {
|
|
547
|
+
return {
|
|
548
|
+
message: `Request to ${targetLabel} was aborted before it completed.`,
|
|
549
|
+
code: "NETWORK_ABORTED"
|
|
550
|
+
};
|
|
551
|
+
}
|
|
552
|
+
return {
|
|
553
|
+
message: error?.message ? `Unable to connect to ${baseUrl}. ${error.message}` : `Unable to connect to ${baseUrl}. Is the computer able to access the url?`,
|
|
554
|
+
code: "NETWORK_ERROR"
|
|
555
|
+
};
|
|
556
|
+
}
|
|
557
|
+
function describeRequestTarget(path) {
|
|
558
|
+
const toolExecuteMatch = path.match(
|
|
559
|
+
/^\/api\/v2\/integrations\/([^/?#]+)\/execute(?:[/?#]|$)/
|
|
560
|
+
);
|
|
561
|
+
if (toolExecuteMatch) {
|
|
562
|
+
return providerLabelFromToolId(decodeURIComponent(toolExecuteMatch[1]));
|
|
563
|
+
}
|
|
564
|
+
return "Deepline";
|
|
565
|
+
}
|
|
566
|
+
function providerLabelFromToolId(toolId) {
|
|
567
|
+
const normalized = toolId.trim().toLowerCase();
|
|
568
|
+
const provider = normalized.split(/[_:./-]+/)[0];
|
|
569
|
+
return provider || normalized || "provider";
|
|
570
|
+
}
|
|
519
571
|
var HttpClient = class {
|
|
520
572
|
constructor(config) {
|
|
521
573
|
this.config = config;
|
|
@@ -628,10 +680,12 @@ var HttpClient = class {
|
|
|
628
680
|
}
|
|
629
681
|
for (const candidateUrl of candidateUrls) {
|
|
630
682
|
const controller = new AbortController();
|
|
631
|
-
const
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
683
|
+
const timeoutMs = options?.timeout ?? this.config.timeout;
|
|
684
|
+
let requestTimedOut = false;
|
|
685
|
+
const timeoutId = setTimeout(() => {
|
|
686
|
+
requestTimedOut = true;
|
|
687
|
+
controller.abort();
|
|
688
|
+
}, timeoutMs);
|
|
635
689
|
try {
|
|
636
690
|
const response = await fetch(candidateUrl, {
|
|
637
691
|
method,
|
|
@@ -704,7 +758,10 @@ var HttpClient = class {
|
|
|
704
758
|
lastError = error;
|
|
705
759
|
break;
|
|
706
760
|
}
|
|
707
|
-
lastError = error
|
|
761
|
+
lastError = normalizeRequestAbortError(error, {
|
|
762
|
+
timeoutMs,
|
|
763
|
+
timedOut: requestTimedOut
|
|
764
|
+
});
|
|
708
765
|
}
|
|
709
766
|
}
|
|
710
767
|
if (attempt < maxRetries) continue;
|
|
@@ -712,8 +769,17 @@ var HttpClient = class {
|
|
|
712
769
|
if (lastError instanceof DeeplineError) {
|
|
713
770
|
throw lastError;
|
|
714
771
|
}
|
|
715
|
-
const
|
|
716
|
-
|
|
772
|
+
const mappedNetworkError = mapNetworkError(
|
|
773
|
+
baseUrl,
|
|
774
|
+
lastError,
|
|
775
|
+
describeRequestTarget(path)
|
|
776
|
+
);
|
|
777
|
+
throw new DeeplineError(
|
|
778
|
+
withCoworkNetworkHint(mappedNetworkError.message),
|
|
779
|
+
void 0,
|
|
780
|
+
mappedNetworkError.code,
|
|
781
|
+
mappedNetworkError.details
|
|
782
|
+
);
|
|
717
783
|
}
|
|
718
784
|
/**
|
|
719
785
|
* Send a GET request.
|
|
@@ -1984,6 +2050,57 @@ function decodeBase64Bytes(value) {
|
|
|
1984
2050
|
}
|
|
1985
2051
|
return bytes;
|
|
1986
2052
|
}
|
|
2053
|
+
var STAGE_LEGACY_MULTIPART_MAX_BYTES = 4e6;
|
|
2054
|
+
var STAGE_DIRECT_UPLOAD_MAX_ATTEMPTS = 3;
|
|
2055
|
+
function stagedUploadIdentity(logicalPath, contentHash) {
|
|
2056
|
+
return `${contentHash}:${logicalPath}`;
|
|
2057
|
+
}
|
|
2058
|
+
function isStagedUploadMintUnsupported(error) {
|
|
2059
|
+
return error instanceof DeeplineError && error.statusCode === 404;
|
|
2060
|
+
}
|
|
2061
|
+
function formatMegabytes(bytes) {
|
|
2062
|
+
return `${(bytes / (1024 * 1024)).toFixed(1)} MB`;
|
|
2063
|
+
}
|
|
2064
|
+
async function uploadStagedFileToPresignedUrl(input) {
|
|
2065
|
+
const arrayBuffer = input.body.buffer.slice(
|
|
2066
|
+
input.body.byteOffset,
|
|
2067
|
+
input.body.byteOffset + input.body.byteLength
|
|
2068
|
+
);
|
|
2069
|
+
let lastError;
|
|
2070
|
+
for (let attempt = 1; attempt <= STAGE_DIRECT_UPLOAD_MAX_ATTEMPTS; attempt += 1) {
|
|
2071
|
+
try {
|
|
2072
|
+
const response = await fetch(input.url, {
|
|
2073
|
+
method: "PUT",
|
|
2074
|
+
headers: input.headers,
|
|
2075
|
+
body: new Blob([arrayBuffer])
|
|
2076
|
+
});
|
|
2077
|
+
if (response.ok) {
|
|
2078
|
+
return;
|
|
2079
|
+
}
|
|
2080
|
+
const text = await response.text().catch(() => "");
|
|
2081
|
+
lastError = new DeeplineError(
|
|
2082
|
+
`Direct storage upload of ${input.logicalPath} failed: ${response.status} ${text.slice(0, 200)}`,
|
|
2083
|
+
response.status,
|
|
2084
|
+
"STAGED_FILE_UPLOAD_FAILED",
|
|
2085
|
+
{ logicalPath: input.logicalPath }
|
|
2086
|
+
);
|
|
2087
|
+
if (response.status < 500 && response.status !== 429) {
|
|
2088
|
+
break;
|
|
2089
|
+
}
|
|
2090
|
+
} catch (error) {
|
|
2091
|
+
lastError = error;
|
|
2092
|
+
}
|
|
2093
|
+
if (attempt < STAGE_DIRECT_UPLOAD_MAX_ATTEMPTS) {
|
|
2094
|
+
await sleep2(250 * attempt);
|
|
2095
|
+
}
|
|
2096
|
+
}
|
|
2097
|
+
throw lastError instanceof Error ? lastError : new DeeplineError(
|
|
2098
|
+
`Direct storage upload of ${input.logicalPath} failed.`,
|
|
2099
|
+
void 0,
|
|
2100
|
+
"STAGED_FILE_UPLOAD_FAILED",
|
|
2101
|
+
{ logicalPath: input.logicalPath }
|
|
2102
|
+
);
|
|
2103
|
+
}
|
|
1987
2104
|
function readStringArray(value) {
|
|
1988
2105
|
return Array.isArray(value) ? value.filter((line) => typeof line === "string") : [];
|
|
1989
2106
|
}
|
|
@@ -2389,6 +2506,21 @@ var DeeplineClient = class {
|
|
|
2389
2506
|
...input.maxRows ? { max_rows: input.maxRows } : {}
|
|
2390
2507
|
});
|
|
2391
2508
|
}
|
|
2509
|
+
/**
|
|
2510
|
+
* Re-establish this workspace's tenant storage contract: role/DB connect
|
|
2511
|
+
* grants plus materialized table grants. Org-admin only. Use when a run fails
|
|
2512
|
+
* with WORKSPACE_STORAGE_NOT_READY.
|
|
2513
|
+
*/
|
|
2514
|
+
async repairIngestionStorage(input) {
|
|
2515
|
+
return this.http.post(
|
|
2516
|
+
"/api/v2/ingestion/repair",
|
|
2517
|
+
{
|
|
2518
|
+
...input?.provider ? { provider: input.provider } : {}
|
|
2519
|
+
},
|
|
2520
|
+
void 0,
|
|
2521
|
+
{ forbiddenAsApiError: true }
|
|
2522
|
+
);
|
|
2523
|
+
}
|
|
2392
2524
|
// ——————————————————————————————————————————————————————————
|
|
2393
2525
|
// Plays — submission and lifecycle
|
|
2394
2526
|
// ——————————————————————————————————————————————————————————
|
|
@@ -2766,6 +2898,94 @@ var DeeplineClient = class {
|
|
|
2766
2898
|
* ```
|
|
2767
2899
|
*/
|
|
2768
2900
|
async stagePlayFiles(files) {
|
|
2901
|
+
let uploads;
|
|
2902
|
+
try {
|
|
2903
|
+
uploads = await this.mintStagedPlayFileUploads(
|
|
2904
|
+
files.map((file) => ({
|
|
2905
|
+
logicalPath: file.logicalPath,
|
|
2906
|
+
contentHash: file.contentHash,
|
|
2907
|
+
contentType: file.contentType,
|
|
2908
|
+
bytes: file.bytes
|
|
2909
|
+
}))
|
|
2910
|
+
);
|
|
2911
|
+
} catch (error) {
|
|
2912
|
+
if (isStagedUploadMintUnsupported(error)) {
|
|
2913
|
+
return this.stagePlayFilesViaMultipart(files);
|
|
2914
|
+
}
|
|
2915
|
+
throw error;
|
|
2916
|
+
}
|
|
2917
|
+
const uploadByIdentity = /* @__PURE__ */ new Map();
|
|
2918
|
+
for (const upload of uploads) {
|
|
2919
|
+
uploadByIdentity.set(
|
|
2920
|
+
stagedUploadIdentity(upload.logicalPath, upload.contentHash),
|
|
2921
|
+
upload
|
|
2922
|
+
);
|
|
2923
|
+
}
|
|
2924
|
+
await Promise.all(
|
|
2925
|
+
files.map(async (file) => {
|
|
2926
|
+
const upload = uploadByIdentity.get(
|
|
2927
|
+
stagedUploadIdentity(file.logicalPath, file.contentHash)
|
|
2928
|
+
);
|
|
2929
|
+
if (!upload) {
|
|
2930
|
+
throw new DeeplineError(
|
|
2931
|
+
`The staging server did not return an upload target for ${file.logicalPath}.`,
|
|
2932
|
+
void 0,
|
|
2933
|
+
"STAGED_FILE_MINT_INCOMPLETE"
|
|
2934
|
+
);
|
|
2935
|
+
}
|
|
2936
|
+
if (upload.alreadyStaged || !upload.uploadUrl) {
|
|
2937
|
+
return;
|
|
2938
|
+
}
|
|
2939
|
+
await uploadStagedFileToPresignedUrl({
|
|
2940
|
+
url: upload.uploadUrl,
|
|
2941
|
+
headers: upload.uploadHeaders ?? {
|
|
2942
|
+
"content-type": file.contentType
|
|
2943
|
+
},
|
|
2944
|
+
body: decodeBase64Bytes(file.contentBase64),
|
|
2945
|
+
logicalPath: file.logicalPath
|
|
2946
|
+
});
|
|
2947
|
+
})
|
|
2948
|
+
);
|
|
2949
|
+
return files.map((file) => {
|
|
2950
|
+
const upload = uploadByIdentity.get(
|
|
2951
|
+
stagedUploadIdentity(file.logicalPath, file.contentHash)
|
|
2952
|
+
);
|
|
2953
|
+
if (!upload) {
|
|
2954
|
+
throw new DeeplineError(
|
|
2955
|
+
`The staging server did not return an upload target for ${file.logicalPath}.`,
|
|
2956
|
+
void 0,
|
|
2957
|
+
"STAGED_FILE_MINT_INCOMPLETE"
|
|
2958
|
+
);
|
|
2959
|
+
}
|
|
2960
|
+
return upload.ref;
|
|
2961
|
+
});
|
|
2962
|
+
}
|
|
2963
|
+
/**
|
|
2964
|
+
* Mint short-lived presigned upload targets for staged play files.
|
|
2965
|
+
*
|
|
2966
|
+
* Internal primitive used by {@link stagePlayFiles}. The server returns an
|
|
2967
|
+
* already-staged ref (no upload needed) for content-addressed files it
|
|
2968
|
+
* already holds, or a presigned PUT URL the caller uploads the body to.
|
|
2969
|
+
*/
|
|
2970
|
+
async mintStagedPlayFileUploads(files) {
|
|
2971
|
+
const response = await this.http.post("/api/v2/plays/files/stage/mint", { files });
|
|
2972
|
+
return response.uploads ?? [];
|
|
2973
|
+
}
|
|
2974
|
+
async stagePlayFilesViaMultipart(files) {
|
|
2975
|
+
for (const file of files) {
|
|
2976
|
+
if (file.bytes > STAGE_LEGACY_MULTIPART_MAX_BYTES) {
|
|
2977
|
+
throw new DeeplineError(
|
|
2978
|
+
`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.`,
|
|
2979
|
+
413,
|
|
2980
|
+
"STAGED_FILE_TOO_LARGE_FOR_LEGACY_UPLOAD",
|
|
2981
|
+
{
|
|
2982
|
+
logicalPath: file.logicalPath,
|
|
2983
|
+
bytes: file.bytes,
|
|
2984
|
+
maxBytes: STAGE_LEGACY_MULTIPART_MAX_BYTES
|
|
2985
|
+
}
|
|
2986
|
+
);
|
|
2987
|
+
}
|
|
2988
|
+
}
|
|
2769
2989
|
const buildFormData = () => {
|
|
2770
2990
|
const formData = new FormData();
|
|
2771
2991
|
formData.set(
|