@fastgpt-sdk/sandbox-adapter 0.0.40-beta.4 → 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 result = await res.json();
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
@@ -45762,6 +45782,9 @@ function joinUrlPath(url, path) {
45762
45782
  }
45763
45783
 
45764
45784
  // src/adapters/SealosDevboxAdapter/index.ts
45785
+ var GET_INFO_RETRY_TIMEOUT_MS = 30000;
45786
+ var GET_INFO_RETRY_INTERVAL_MS = 1000;
45787
+
45765
45788
  class SealosDevboxAdapter extends BaseSandboxAdapter {
45766
45789
  config;
45767
45790
  createConfig;
@@ -45832,6 +45855,33 @@ class SealosDevboxAdapter extends BaseSandboxAdapter {
45832
45855
  isNotFoundResponse(res) {
45833
45856
  return res.code === 404 || String(res.message ?? "").toLowerCase().includes("not found");
45834
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
+ }
45835
45885
  async getInfo() {
45836
45886
  try {
45837
45887
  const res = await this.api.info(this._id);
@@ -45858,7 +45908,7 @@ class SealosDevboxAdapter extends BaseSandboxAdapter {
45858
45908
  }
45859
45909
  async ensureRunning() {
45860
45910
  try {
45861
- const sandbox = await this.getInfo();
45911
+ const sandbox = await this.getInfoWithProviderRetry();
45862
45912
  if (sandbox) {
45863
45913
  const status = sandbox.status.state;
45864
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 result = await res.json();
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
@@ -45746,6 +45766,9 @@ function joinUrlPath(url, path) {
45746
45766
  }
45747
45767
 
45748
45768
  // src/adapters/SealosDevboxAdapter/index.ts
45769
+ var GET_INFO_RETRY_TIMEOUT_MS = 30000;
45770
+ var GET_INFO_RETRY_INTERVAL_MS = 1000;
45771
+
45749
45772
  class SealosDevboxAdapter extends BaseSandboxAdapter {
45750
45773
  config;
45751
45774
  createConfig;
@@ -45816,6 +45839,33 @@ class SealosDevboxAdapter extends BaseSandboxAdapter {
45816
45839
  isNotFoundResponse(res) {
45817
45840
  return res.code === 404 || String(res.message ?? "").toLowerCase().includes("not found");
45818
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
+ }
45819
45869
  async getInfo() {
45820
45870
  try {
45821
45871
  const res = await this.api.info(this._id);
@@ -45842,7 +45892,7 @@ class SealosDevboxAdapter extends BaseSandboxAdapter {
45842
45892
  }
45843
45893
  async ensureRunning() {
45844
45894
  try {
45845
- const sandbox = await this.getInfo();
45895
+ const sandbox = await this.getInfoWithProviderRetry();
45846
45896
  if (sandbox) {
45847
45897
  const status = sandbox.status.state;
45848
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.4",
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",