@fastgpt-sdk/sandbox-adapter 0.0.40-beta.3 → 0.0.40-beta.5
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.
|
@@ -1,4 +1,10 @@
|
|
|
1
1
|
import { type DevboxApiConfig, type DevboxApiResponse, type DevboxCreateRequest, type DevboxInfoData, type DevboxMutationData, type DownloadFileParams, type ExecRequest, type ExecResponseData, type UploadFileParams, type UploadResponseData } from './type';
|
|
2
|
+
export declare class DevboxApiError extends Error {
|
|
3
|
+
readonly status: number;
|
|
4
|
+
readonly rawBody: string;
|
|
5
|
+
readonly url: string;
|
|
6
|
+
constructor(message: string, status: number, rawBody: string, url: string);
|
|
7
|
+
}
|
|
2
8
|
/**
|
|
3
9
|
* HTTP client for the Sealos Devbox REST API.
|
|
4
10
|
*
|
|
@@ -38,6 +38,13 @@ export declare class SealosDevboxAdapter extends BaseSandboxAdapter {
|
|
|
38
38
|
private removeUndefined;
|
|
39
39
|
private assertMutationSuccess;
|
|
40
40
|
private isNotFoundResponse;
|
|
41
|
+
private isRetryableGetInfoError;
|
|
42
|
+
/**
|
|
43
|
+
* Devbox gateway can briefly return 502/503/504 during restart before the sandbox
|
|
44
|
+
* status is readable. Limit retry to the initial info probe to avoid repeating
|
|
45
|
+
* lifecycle mutations such as create/resume/delete.
|
|
46
|
+
*/
|
|
47
|
+
private getInfoWithProviderRetry;
|
|
41
48
|
getInfo(): Promise<SandboxInfo | null>;
|
|
42
49
|
ensureRunning(): Promise<void>;
|
|
43
50
|
create(): Promise<void>;
|
package/dist/index.cjs
CHANGED
|
@@ -45625,6 +45625,20 @@ class BaseSandboxAdapter {
|
|
|
45625
45625
|
}
|
|
45626
45626
|
|
|
45627
45627
|
// src/adapters/SealosDevboxAdapter/api.ts
|
|
45628
|
+
class DevboxApiError extends Error {
|
|
45629
|
+
status;
|
|
45630
|
+
rawBody;
|
|
45631
|
+
url;
|
|
45632
|
+
constructor(message, status, rawBody, url) {
|
|
45633
|
+
super(message);
|
|
45634
|
+
this.status = status;
|
|
45635
|
+
this.rawBody = rawBody;
|
|
45636
|
+
this.url = url;
|
|
45637
|
+
this.name = "DevboxApiError";
|
|
45638
|
+
Object.setPrototypeOf(this, DevboxApiError.prototype);
|
|
45639
|
+
}
|
|
45640
|
+
}
|
|
45641
|
+
|
|
45628
45642
|
class DevboxApi {
|
|
45629
45643
|
baseUrl;
|
|
45630
45644
|
token;
|
|
@@ -45648,7 +45662,13 @@ class DevboxApi {
|
|
|
45648
45662
|
headers.set("Content-Type", "application/json");
|
|
45649
45663
|
}
|
|
45650
45664
|
const res = await fetch(input, { ...init, headers });
|
|
45651
|
-
const
|
|
45665
|
+
const rawBody = typeof res.text === "function" ? await res.text() : JSON.stringify(await res.json());
|
|
45666
|
+
let result;
|
|
45667
|
+
try {
|
|
45668
|
+
result = JSON.parse(rawBody);
|
|
45669
|
+
} catch {
|
|
45670
|
+
throw new DevboxApiError(`Devbox API returned non-JSON response (${res.status}): ${rawBody || res.statusText || res.status}`, res.status, rawBody, input);
|
|
45671
|
+
}
|
|
45652
45672
|
return {
|
|
45653
45673
|
...result,
|
|
45654
45674
|
code: result.code ?? res.status
|
|
@@ -45741,8 +45761,9 @@ function parseImageSpec(image) {
|
|
|
45741
45761
|
if (atIndex > -1) {
|
|
45742
45762
|
return { repository: image.slice(0, atIndex), digest: image.slice(atIndex + 1) };
|
|
45743
45763
|
}
|
|
45744
|
-
const
|
|
45745
|
-
|
|
45764
|
+
const slashIndex = image.lastIndexOf("/");
|
|
45765
|
+
const colonIndex = image.lastIndexOf(":");
|
|
45766
|
+
if (colonIndex > slashIndex) {
|
|
45746
45767
|
return { repository: image.slice(0, colonIndex), tag: image.slice(colonIndex + 1) };
|
|
45747
45768
|
}
|
|
45748
45769
|
return { repository: image };
|
|
@@ -45761,6 +45782,9 @@ function joinUrlPath(url, path) {
|
|
|
45761
45782
|
}
|
|
45762
45783
|
|
|
45763
45784
|
// src/adapters/SealosDevboxAdapter/index.ts
|
|
45785
|
+
var GET_INFO_RETRY_TIMEOUT_MS = 30000;
|
|
45786
|
+
var GET_INFO_RETRY_INTERVAL_MS = 1000;
|
|
45787
|
+
|
|
45764
45788
|
class SealosDevboxAdapter extends BaseSandboxAdapter {
|
|
45765
45789
|
config;
|
|
45766
45790
|
createConfig;
|
|
@@ -45831,6 +45855,33 @@ class SealosDevboxAdapter extends BaseSandboxAdapter {
|
|
|
45831
45855
|
isNotFoundResponse(res) {
|
|
45832
45856
|
return res.code === 404 || String(res.message ?? "").toLowerCase().includes("not found");
|
|
45833
45857
|
}
|
|
45858
|
+
isRetryableGetInfoError(error) {
|
|
45859
|
+
let current = error;
|
|
45860
|
+
while (current instanceof Error) {
|
|
45861
|
+
if (current instanceof DevboxApiError) {
|
|
45862
|
+
const rawBody = current.rawBody.toLowerCase();
|
|
45863
|
+
return [502, 503, 504].includes(current.status) || rawBody.includes("no healthy upstream");
|
|
45864
|
+
}
|
|
45865
|
+
current = current.cause;
|
|
45866
|
+
}
|
|
45867
|
+
return false;
|
|
45868
|
+
}
|
|
45869
|
+
async getInfoWithProviderRetry(timeoutMs = GET_INFO_RETRY_TIMEOUT_MS, intervalMs = GET_INFO_RETRY_INTERVAL_MS) {
|
|
45870
|
+
const startTime = Date.now();
|
|
45871
|
+
let lastError;
|
|
45872
|
+
while (Date.now() - startTime < timeoutMs) {
|
|
45873
|
+
try {
|
|
45874
|
+
return await this.getInfo();
|
|
45875
|
+
} catch (error) {
|
|
45876
|
+
lastError = error;
|
|
45877
|
+
if (!this.isRetryableGetInfoError(error)) {
|
|
45878
|
+
throw error;
|
|
45879
|
+
}
|
|
45880
|
+
await this.sleep(intervalMs);
|
|
45881
|
+
}
|
|
45882
|
+
}
|
|
45883
|
+
throw lastError;
|
|
45884
|
+
}
|
|
45834
45885
|
async getInfo() {
|
|
45835
45886
|
try {
|
|
45836
45887
|
const res = await this.api.info(this._id);
|
|
@@ -45857,7 +45908,7 @@ class SealosDevboxAdapter extends BaseSandboxAdapter {
|
|
|
45857
45908
|
}
|
|
45858
45909
|
async ensureRunning() {
|
|
45859
45910
|
try {
|
|
45860
|
-
const sandbox = await this.
|
|
45911
|
+
const sandbox = await this.getInfoWithProviderRetry();
|
|
45861
45912
|
if (sandbox) {
|
|
45862
45913
|
const status = sandbox.status.state;
|
|
45863
45914
|
switch (status) {
|
package/dist/index.js
CHANGED
|
@@ -45609,6 +45609,20 @@ class BaseSandboxAdapter {
|
|
|
45609
45609
|
}
|
|
45610
45610
|
|
|
45611
45611
|
// src/adapters/SealosDevboxAdapter/api.ts
|
|
45612
|
+
class DevboxApiError extends Error {
|
|
45613
|
+
status;
|
|
45614
|
+
rawBody;
|
|
45615
|
+
url;
|
|
45616
|
+
constructor(message, status, rawBody, url) {
|
|
45617
|
+
super(message);
|
|
45618
|
+
this.status = status;
|
|
45619
|
+
this.rawBody = rawBody;
|
|
45620
|
+
this.url = url;
|
|
45621
|
+
this.name = "DevboxApiError";
|
|
45622
|
+
Object.setPrototypeOf(this, DevboxApiError.prototype);
|
|
45623
|
+
}
|
|
45624
|
+
}
|
|
45625
|
+
|
|
45612
45626
|
class DevboxApi {
|
|
45613
45627
|
baseUrl;
|
|
45614
45628
|
token;
|
|
@@ -45632,7 +45646,13 @@ class DevboxApi {
|
|
|
45632
45646
|
headers.set("Content-Type", "application/json");
|
|
45633
45647
|
}
|
|
45634
45648
|
const res = await fetch(input, { ...init, headers });
|
|
45635
|
-
const
|
|
45649
|
+
const rawBody = typeof res.text === "function" ? await res.text() : JSON.stringify(await res.json());
|
|
45650
|
+
let result;
|
|
45651
|
+
try {
|
|
45652
|
+
result = JSON.parse(rawBody);
|
|
45653
|
+
} catch {
|
|
45654
|
+
throw new DevboxApiError(`Devbox API returned non-JSON response (${res.status}): ${rawBody || res.statusText || res.status}`, res.status, rawBody, input);
|
|
45655
|
+
}
|
|
45636
45656
|
return {
|
|
45637
45657
|
...result,
|
|
45638
45658
|
code: result.code ?? res.status
|
|
@@ -45725,8 +45745,9 @@ function parseImageSpec(image) {
|
|
|
45725
45745
|
if (atIndex > -1) {
|
|
45726
45746
|
return { repository: image.slice(0, atIndex), digest: image.slice(atIndex + 1) };
|
|
45727
45747
|
}
|
|
45728
|
-
const
|
|
45729
|
-
|
|
45748
|
+
const slashIndex = image.lastIndexOf("/");
|
|
45749
|
+
const colonIndex = image.lastIndexOf(":");
|
|
45750
|
+
if (colonIndex > slashIndex) {
|
|
45730
45751
|
return { repository: image.slice(0, colonIndex), tag: image.slice(colonIndex + 1) };
|
|
45731
45752
|
}
|
|
45732
45753
|
return { repository: image };
|
|
@@ -45745,6 +45766,9 @@ function joinUrlPath(url, path) {
|
|
|
45745
45766
|
}
|
|
45746
45767
|
|
|
45747
45768
|
// src/adapters/SealosDevboxAdapter/index.ts
|
|
45769
|
+
var GET_INFO_RETRY_TIMEOUT_MS = 30000;
|
|
45770
|
+
var GET_INFO_RETRY_INTERVAL_MS = 1000;
|
|
45771
|
+
|
|
45748
45772
|
class SealosDevboxAdapter extends BaseSandboxAdapter {
|
|
45749
45773
|
config;
|
|
45750
45774
|
createConfig;
|
|
@@ -45815,6 +45839,33 @@ class SealosDevboxAdapter extends BaseSandboxAdapter {
|
|
|
45815
45839
|
isNotFoundResponse(res) {
|
|
45816
45840
|
return res.code === 404 || String(res.message ?? "").toLowerCase().includes("not found");
|
|
45817
45841
|
}
|
|
45842
|
+
isRetryableGetInfoError(error) {
|
|
45843
|
+
let current = error;
|
|
45844
|
+
while (current instanceof Error) {
|
|
45845
|
+
if (current instanceof DevboxApiError) {
|
|
45846
|
+
const rawBody = current.rawBody.toLowerCase();
|
|
45847
|
+
return [502, 503, 504].includes(current.status) || rawBody.includes("no healthy upstream");
|
|
45848
|
+
}
|
|
45849
|
+
current = current.cause;
|
|
45850
|
+
}
|
|
45851
|
+
return false;
|
|
45852
|
+
}
|
|
45853
|
+
async getInfoWithProviderRetry(timeoutMs = GET_INFO_RETRY_TIMEOUT_MS, intervalMs = GET_INFO_RETRY_INTERVAL_MS) {
|
|
45854
|
+
const startTime = Date.now();
|
|
45855
|
+
let lastError;
|
|
45856
|
+
while (Date.now() - startTime < timeoutMs) {
|
|
45857
|
+
try {
|
|
45858
|
+
return await this.getInfo();
|
|
45859
|
+
} catch (error) {
|
|
45860
|
+
lastError = error;
|
|
45861
|
+
if (!this.isRetryableGetInfoError(error)) {
|
|
45862
|
+
throw error;
|
|
45863
|
+
}
|
|
45864
|
+
await this.sleep(intervalMs);
|
|
45865
|
+
}
|
|
45866
|
+
}
|
|
45867
|
+
throw lastError;
|
|
45868
|
+
}
|
|
45818
45869
|
async getInfo() {
|
|
45819
45870
|
try {
|
|
45820
45871
|
const res = await this.api.info(this._id);
|
|
@@ -45841,7 +45892,7 @@ class SealosDevboxAdapter extends BaseSandboxAdapter {
|
|
|
45841
45892
|
}
|
|
45842
45893
|
async ensureRunning() {
|
|
45843
45894
|
try {
|
|
45844
|
-
const sandbox = await this.
|
|
45895
|
+
const sandbox = await this.getInfoWithProviderRetry();
|
|
45845
45896
|
if (sandbox) {
|
|
45846
45897
|
const status = sandbox.status.state;
|
|
45847
45898
|
switch (status) {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@fastgpt-sdk/sandbox-adapter",
|
|
3
|
-
"version": "0.0.40-beta.
|
|
3
|
+
"version": "0.0.40-beta.5",
|
|
4
4
|
"description": "Unified abstraction layer for cloud sandbox providers with adapter pattern and feature polyfilling",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.cjs",
|