@fastgpt-sdk/sandbox-adapter 0.0.36 → 0.0.37

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,6 +1,6 @@
1
1
  import type { ISandbox } from '../interfaces/ISandbox';
2
2
  import { CommandPolyfillService } from '../polyfill/CommandPolyfillService';
3
- import type { ContentReplaceEntry, DirectoryEntry, ExecuteOptions, ExecuteResult, FileDeleteResult, FileInfo, FileReadResult, FileWriteEntry, FileWriteResult, MoveEntry, PermissionEntry, ReadFileOptions, SandboxId, SandboxInfo, SandboxMetrics, SandboxStatus, SearchResult, StreamHandlers } from '../types';
3
+ import type { ContentReplaceEntry, DirectoryEntry, Endpoint, ExecuteOptions, ExecuteResult, FileDeleteResult, FileInfo, FileReadResult, FileWriteEntry, FileWriteResult, MoveEntry, PermissionEntry, ReadFileOptions, SandboxEndpointSelector, SandboxId, SandboxInfo, SandboxMetrics, SandboxProxyService, SandboxProxyTarget, SandboxStatus, SearchResult, StreamHandlers } from '../types';
4
4
  /**
5
5
  * Abstract base class for all sandbox adapters.
6
6
  *
@@ -37,6 +37,8 @@ export declare abstract class BaseSandboxAdapter implements ISandbox {
37
37
  waitUntilReady(timeoutMs?: number): Promise<void>;
38
38
  waitUntilDeleted(timeoutMs?: number): Promise<void>;
39
39
  renewExpiration(_additionalSeconds: number): Promise<void>;
40
+ getEndpoint(_selector: SandboxEndpointSelector): Promise<Endpoint>;
41
+ getProxyTarget(_service?: SandboxProxyService): Promise<SandboxProxyTarget>;
40
42
  abstract execute(command: string, options?: ExecuteOptions): Promise<ExecuteResult>;
41
43
  executeStream(command: string, handlers: StreamHandlers, options?: ExecuteOptions): Promise<void>;
42
44
  executeBackground(_command: string, _options?: ExecuteOptions): Promise<{
@@ -1,4 +1,4 @@
1
- import type { Endpoint, ExecuteOptions, ExecuteResult, FileWriteEntry, FileWriteResult, SandboxId, SandboxInfo, SandboxMetrics, StreamHandlers } from '@/types';
1
+ import type { Endpoint, ExecuteOptions, ExecuteResult, FileWriteEntry, FileWriteResult, SandboxEndpointSelector, SandboxId, SandboxInfo, SandboxMetrics, SandboxProxyService, SandboxProxyTarget, StreamHandlers } from '@/types';
2
2
  import { BaseSandboxAdapter } from '../BaseSandboxAdapter';
3
3
  import type { OpenSandboxConfigType } from './type';
4
4
  export type { OpenSandboxConfigType } from './type';
@@ -23,6 +23,11 @@ export interface OpenSandboxConnectionConfig {
23
23
  debug?: boolean;
24
24
  /** Route execd traffic through the OpenSandbox server proxy */
25
25
  useServerProxy?: boolean;
26
+ /**
27
+ * Rewrite OpenSandbox local endpoint host when sandbox-proxy runs on the host
28
+ * instead of inside Docker/Kubernetes.
29
+ */
30
+ replaceDockerInternalWithLocalhost?: boolean;
26
31
  /**
27
32
  * Sandbox runtime type.
28
33
  * @default 'docker'
@@ -65,8 +70,6 @@ export declare class OpenSandboxAdapter extends BaseSandboxAdapter {
65
70
  private createConnectionConfig;
66
71
  private static readonly STATE_MAP;
67
72
  private mapStatus;
68
- private convertImageSpec;
69
- private parseImageSpec;
70
73
  private convertResourceLimits;
71
74
  private parseResourceLimits;
72
75
  private extractExitCode;
@@ -85,11 +88,12 @@ export declare class OpenSandboxAdapter extends BaseSandboxAdapter {
85
88
  */
86
89
  close(): Promise<void>;
87
90
  /**
88
- * Get endpoint information for a specific port exposed by the sandbox.
89
- * @param port The port number to get endpoint for
90
- * @returns Endpoint with host, port, protocol and url fields
91
+ * Get endpoint information for a provider endpoint or well-known service.
91
92
  */
92
- getEndpoint(port: number): Promise<Endpoint>;
93
+ getEndpoint(selector: SandboxEndpointSelector): Promise<Endpoint>;
94
+ private getOpenSandboxEndpoint;
95
+ getProxyTarget(service?: SandboxProxyService): Promise<SandboxProxyTarget>;
96
+ private getDirectEndpointOrigin;
93
97
  getInfo(): Promise<SandboxInfo | null>;
94
98
  renewExpiration(additionalSeconds: number): Promise<void>;
95
99
  writeFiles(entries: FileWriteEntry[]): Promise<FileWriteResult[]>;
@@ -1,4 +1,4 @@
1
- import { ImageSpec, NetworkPolicy, ResourceLimits } from '@/types';
1
+ import type { ImageSpec, NetworkPolicy, ResourceLimits } from '@/types';
2
2
  import type { Volume } from '@alibaba-group/opensandbox';
3
3
  /**
4
4
  * Configuration for creating a sandbox.
@@ -1,4 +1,4 @@
1
- import { type DevboxApiConfig, type DevboxApiResponse, type DevboxInfoData, type DevboxMutationData, type DownloadFileParams, type ExecRequest, type ExecResponseData, type UploadFileParams, type UploadResponseData } from './type';
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
2
  /**
3
3
  * HTTP client for the Sealos Devbox REST API.
4
4
  *
@@ -11,7 +11,7 @@ export declare class DevboxApi {
11
11
  private url;
12
12
  private request;
13
13
  /** POST /api/v1/devbox — create a devbox */
14
- create(name: string): Promise<DevboxApiResponse<DevboxMutationData>>;
14
+ create(req: DevboxCreateRequest): Promise<DevboxApiResponse<DevboxMutationData>>;
15
15
  /** GET /api/v1/devbox/{name} — query devbox info (state + SSH) */
16
16
  info(name: string): Promise<DevboxApiResponse<DevboxInfoData>>;
17
17
  /** POST /api/v1/devbox/{name}/pause */
@@ -1,4 +1,4 @@
1
- import type { ExecuteOptions, ExecuteResult, SandboxId, SandboxInfo } from '../../types';
1
+ import type { Endpoint, ExecuteOptions, ExecuteResult, ImageSpec, KubeAccessPolicy, LabelSpec, LifecyclePolicy, SandboxEndpointSelector, SandboxId, SandboxInfo, SandboxProxyService, SandboxProxyTarget } from '../../types';
2
2
  import { BaseSandboxAdapter } from '../BaseSandboxAdapter';
3
3
  /**
4
4
  * Configuration for Sealos Devbox Adapter.
@@ -9,16 +9,33 @@ export interface SealosDevboxConfig {
9
9
  /** JWT authentication token */
10
10
  token: string;
11
11
  sandboxId: string;
12
+ /**
13
+ * Optional override for the Sealos httpgate wildcard domain. When omitted,
14
+ * it is derived from gateway.url returned by Devbox Server.
15
+ */
16
+ httpgateDomain?: string;
17
+ }
18
+ export interface SealosDevboxCreateConfig {
19
+ image?: ImageSpec;
20
+ env?: Record<string, string>;
21
+ labels?: LabelSpec[];
22
+ upstreamID?: string;
23
+ kubeAccess?: KubeAccessPolicy;
24
+ lifecycle?: LifecyclePolicy;
25
+ workingDir?: string;
12
26
  }
13
27
  export declare class SealosDevboxAdapter extends BaseSandboxAdapter {
14
28
  private config;
29
+ private createConfig?;
15
30
  readonly provider: "sealosdevbox";
16
31
  get rootPath(): string;
17
32
  private api;
18
33
  private _id;
19
- constructor(config: SealosDevboxConfig);
34
+ constructor(config: SealosDevboxConfig, createConfig?: SealosDevboxCreateConfig | undefined);
20
35
  get id(): SandboxId;
21
36
  private StatusAdapt;
37
+ private buildCreateRequest;
38
+ private removeUndefined;
22
39
  getInfo(): Promise<SandboxInfo | null>;
23
40
  ensureRunning(): Promise<void>;
24
41
  create(): Promise<void>;
@@ -26,6 +43,12 @@ export declare class SealosDevboxAdapter extends BaseSandboxAdapter {
26
43
  start(): Promise<void>;
27
44
  delete(): Promise<void>;
28
45
  execute(command: string, options?: ExecuteOptions): Promise<ExecuteResult>;
46
+ getEndpoint(selector: SandboxEndpointSelector): Promise<Endpoint>;
47
+ getProxyTarget(service?: SandboxProxyService): Promise<SandboxProxyTarget>;
48
+ private getHttpgateTarget;
49
+ private getGatewayUniqueID;
50
+ private getHttpgateDomain;
51
+ private normalizeHttpgateDomain;
29
52
  /**
30
53
  * Check if the devbox is ready by querying info endpoint.
31
54
  * Ready when spec, status, and phase are all "Running".
@@ -52,6 +52,23 @@ export interface DevboxMutationData {
52
52
  state?: string;
53
53
  status?: string;
54
54
  }
55
+ /** Request body for the create endpoint. */
56
+ export interface DevboxCreateRequest {
57
+ name: string;
58
+ image?: string;
59
+ env?: Record<string, string>;
60
+ labels?: Array<{
61
+ key: string;
62
+ value: string;
63
+ }>;
64
+ upstreamID?: string;
65
+ kubeAccess?: {
66
+ enabled?: boolean;
67
+ roleTemplate?: 'view' | 'edit' | 'admin';
68
+ };
69
+ pauseAt?: string;
70
+ archiveAfterPauseTime?: string;
71
+ }
55
72
  /** SSH connection info returned by the info endpoint. */
56
73
  export interface DevboxSshInfo {
57
74
  user: string;
@@ -63,14 +80,27 @@ export interface DevboxSshInfo {
63
80
  privateKeyEncoding: string;
64
81
  privateKeyBase64: string;
65
82
  }
83
+ /** HTTP gateway info returned by the info endpoint. */
84
+ export interface DevboxGatewayInfo {
85
+ url: string;
86
+ token?: string;
87
+ port?: number;
88
+ uniqueID?: string;
89
+ }
66
90
  /** Response data from the GET info endpoint. */
67
91
  export interface DevboxInfoData {
68
92
  name: string;
93
+ image?: string;
94
+ creationTimestamp?: string;
69
95
  deletionTimestamp?: string | null;
70
96
  state: {
71
97
  phase: `${DevboxPhaseEnum}`;
72
98
  };
73
99
  ssh: DevboxSshInfo;
100
+ gateway?: DevboxGatewayInfo;
101
+ codeServerGateway?: DevboxGatewayInfo & {
102
+ password?: string;
103
+ };
74
104
  }
75
105
  /** Response data from the upload endpoint. */
76
106
  export interface UploadResponseData {
@@ -1,9 +1,9 @@
1
- import { type SealosDevboxConfig } from './SealosDevboxAdapter';
1
+ import { type SealosDevboxConfig, type SealosDevboxCreateConfig } from './SealosDevboxAdapter';
2
2
  import { type OpenSandboxConnectionConfig, type OpenSandboxConfigType } from './OpenSandboxAdapter';
3
3
  import { type E2BConfig } from './E2BAdapter';
4
4
  import { ISandbox } from '@/interfaces';
5
5
  export { SealosDevboxAdapter } from './SealosDevboxAdapter';
6
- export type { SealosDevboxConfig } from './SealosDevboxAdapter';
6
+ export type { SealosDevboxConfig, SealosDevboxCreateConfig } from './SealosDevboxAdapter';
7
7
  export { OpenSandboxAdapter } from './OpenSandboxAdapter';
8
8
  export type { OpenSandboxConfigType, OpenSandboxConnectionConfig } from './OpenSandboxAdapter';
9
9
  export type { Volume as OpenSandboxVolume } from '@alibaba-group/opensandbox';
@@ -13,7 +13,7 @@ export type SandboxProviderType = 'opensandbox' | 'sealosdevbox' | 'e2b';
13
13
  /** Maps each provider name to the ISandbox config type it exposes. */
14
14
  interface SandboxConfigMap {
15
15
  opensandbox: OpenSandboxConfigType;
16
- sealosdevbox: undefined;
16
+ sealosdevbox: SealosDevboxCreateConfig;
17
17
  e2b: undefined;
18
18
  }
19
19
  /** Resolves the concrete ISandbox type for a given provider. */
@@ -0,0 +1,7 @@
1
+ /**
2
+ * OpenSandbox exposes execd through this direct endpoint. code-server itself is
3
+ * still reached via execd path proxying at /proxy/8080.
4
+ */
5
+ export declare const OPEN_SANDBOX_EXECD_PORT = 44772;
6
+ export declare const OPEN_SANDBOX_CODE_SERVER_PORT = 8080;
7
+ export declare const SEALOS_DEVBOX_CODE_SERVER_PORT = 1318;
package/dist/index.cjs CHANGED
@@ -4,39 +4,60 @@ var __defProp = Object.defineProperty;
4
4
  var __getOwnPropNames = Object.getOwnPropertyNames;
5
5
  var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
6
6
  var __hasOwnProp = Object.prototype.hasOwnProperty;
7
+ function __accessProp(key) {
8
+ return this[key];
9
+ }
10
+ var __toESMCache_node;
11
+ var __toESMCache_esm;
7
12
  var __toESM = (mod, isNodeMode, target) => {
13
+ var canCache = mod != null && typeof mod === "object";
14
+ if (canCache) {
15
+ var cache = isNodeMode ? __toESMCache_node ??= new WeakMap : __toESMCache_esm ??= new WeakMap;
16
+ var cached = cache.get(mod);
17
+ if (cached)
18
+ return cached;
19
+ }
8
20
  target = mod != null ? __create(__getProtoOf(mod)) : {};
9
21
  const to = isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target;
10
22
  for (let key of __getOwnPropNames(mod))
11
23
  if (!__hasOwnProp.call(to, key))
12
24
  __defProp(to, key, {
13
- get: () => mod[key],
25
+ get: __accessProp.bind(mod, key),
14
26
  enumerable: true
15
27
  });
28
+ if (canCache)
29
+ cache.set(mod, to);
16
30
  return to;
17
31
  };
18
- var __moduleCache = /* @__PURE__ */ new WeakMap;
19
32
  var __toCommonJS = (from) => {
20
- var entry = __moduleCache.get(from), desc;
33
+ var entry = (__moduleCache ??= new WeakMap).get(from), desc;
21
34
  if (entry)
22
35
  return entry;
23
36
  entry = __defProp({}, "__esModule", { value: true });
24
- if (from && typeof from === "object" || typeof from === "function")
25
- __getOwnPropNames(from).map((key) => !__hasOwnProp.call(entry, key) && __defProp(entry, key, {
26
- get: () => from[key],
27
- enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable
28
- }));
37
+ if (from && typeof from === "object" || typeof from === "function") {
38
+ for (var key of __getOwnPropNames(from))
39
+ if (!__hasOwnProp.call(entry, key))
40
+ __defProp(entry, key, {
41
+ get: __accessProp.bind(from, key),
42
+ enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable
43
+ });
44
+ }
29
45
  __moduleCache.set(from, entry);
30
46
  return entry;
31
47
  };
48
+ var __moduleCache;
32
49
  var __commonJS = (cb, mod) => () => (mod || cb((mod = { exports: {} }).exports, mod), mod.exports);
50
+ var __returnValue = (v) => v;
51
+ function __exportSetter(name, newValue) {
52
+ this[name] = __returnValue.bind(null, newValue);
53
+ }
33
54
  var __export = (target, all) => {
34
55
  for (var name in all)
35
56
  __defProp(target, name, {
36
57
  get: all[name],
37
58
  enumerable: true,
38
59
  configurable: true,
39
- set: (newValue) => all[name] = () => newValue
60
+ set: __exportSetter.bind(all, name)
40
61
  });
41
62
  };
42
63
  var __esm = (fn, res) => () => (fn && (res = fn(fn = 0)), res);
@@ -19139,7 +19160,7 @@ var require_frame = __commonJS((exports2, module2) => {
19139
19160
  var BUFFER_SIZE = 8 * 1024;
19140
19161
  var buffer = null;
19141
19162
  var bufIdx = BUFFER_SIZE;
19142
- var randomFillSync = runtimeFeatures.has("crypto") ? require("node:crypto").randomFillSync : function randomFillSync(buffer2, _offset, _size) {
19163
+ var randomFillSync = runtimeFeatures.has("crypto") ? require("node:crypto").randomFillSync : function randomFillSync2(buffer2, _offset, _size) {
19143
19164
  for (let i = 0;i < buffer2.length; ++i) {
19144
19165
  buffer2[i] = Math.random() * 255 | 0;
19145
19166
  }
@@ -21083,7 +21104,7 @@ var require_eventsource = __commonJS((exports2, module2) => {
21083
21104
 
21084
21105
  // node_modules/undici/index.js
21085
21106
  var require_undici = __commonJS((exports2, module2) => {
21086
- var __filename = "/Volumes/code/dev/agent-sandbox-adaptor/node_modules/undici/index.js";
21107
+ var __filename = "/Users/sealos/Documents/GitHub/agent-sandbox-adaptor/node_modules/undici/index.js";
21087
21108
  var Client = require_client();
21088
21109
  var Dispatcher = require_dispatcher();
21089
21110
  var Pool = require_pool();
@@ -21206,7 +21227,7 @@ var require_undici = __commonJS((exports2, module2) => {
21206
21227
  err.stack = stack ? `${stack}
21207
21228
  ${captureLines}` : capture.stack;
21208
21229
  }
21209
- module2.exports.fetch = function fetch(init, options = undefined) {
21230
+ module2.exports.fetch = function fetch2(init, options = undefined) {
21210
21231
  return fetchImpl(init, options).catch((err) => {
21211
21232
  appendFetchStackTrace(err, __filename);
21212
21233
  throw err;
@@ -45388,6 +45409,12 @@ class BaseSandboxAdapter {
45388
45409
  async renewExpiration(_additionalSeconds) {
45389
45410
  throw new FeatureNotSupportedError("Sandbox expiration renewal not supported by this provider", "renewExpiration", this.provider);
45390
45411
  }
45412
+ async getEndpoint(_selector) {
45413
+ throw new FeatureNotSupportedError("Endpoint resolution not supported by this provider", "getEndpoint", this.provider);
45414
+ }
45415
+ async getProxyTarget(_service = "code-server") {
45416
+ throw new FeatureNotSupportedError("Proxy target resolution not supported by this provider", "getProxyTarget", this.provider);
45417
+ }
45391
45418
  async executeStream(command, handlers, options) {
45392
45419
  const result = await this.execute(command, options);
45393
45420
  if (handlers.onStdout && result.stdout) {
@@ -45624,10 +45651,10 @@ class DevboxApi {
45624
45651
  const result = await res.json();
45625
45652
  return result;
45626
45653
  }
45627
- async create(name) {
45654
+ async create(req) {
45628
45655
  return this.request(this.url("/api/v1/devbox"), {
45629
45656
  method: "POST",
45630
- body: JSON.stringify({ name })
45657
+ body: JSON.stringify(req)
45631
45658
  });
45632
45659
  }
45633
45660
  async info(name) {
@@ -45685,18 +45712,60 @@ class DevboxApi {
45685
45712
  }
45686
45713
  }
45687
45714
 
45715
+ // src/adapters/ports.ts
45716
+ var OPEN_SANDBOX_EXECD_PORT = 44772;
45717
+ var OPEN_SANDBOX_CODE_SERVER_PORT = 8080;
45718
+ var SEALOS_DEVBOX_CODE_SERVER_PORT = 1318;
45719
+
45720
+ // src/utils/image.ts
45721
+ function formatImageSpec(image) {
45722
+ const parts = [image.repository];
45723
+ if (image.tag)
45724
+ parts.push(":", image.tag);
45725
+ if (image.digest)
45726
+ parts.push("@", image.digest);
45727
+ return parts.join("");
45728
+ }
45729
+ function parseImageSpec(image) {
45730
+ if (!image)
45731
+ return { repository: "" };
45732
+ const atIndex = image.indexOf("@");
45733
+ if (atIndex > -1) {
45734
+ return { repository: image.slice(0, atIndex), digest: image.slice(atIndex + 1) };
45735
+ }
45736
+ const colonIndex = image.indexOf(":");
45737
+ if (colonIndex > -1) {
45738
+ return { repository: image.slice(0, colonIndex), tag: image.slice(colonIndex + 1) };
45739
+ }
45740
+ return { repository: image };
45741
+ }
45742
+
45743
+ // src/utils/url.ts
45744
+ function normalizePathPrefix(path) {
45745
+ if (!path)
45746
+ return "";
45747
+ const normalized = path.startsWith("/") ? path : `/${path}`;
45748
+ return normalized.length > 1 ? normalized.replace(/\/+$/, "") : "";
45749
+ }
45750
+ function joinUrlPath(url, path) {
45751
+ const normalizedPath = normalizePathPrefix(path);
45752
+ return normalizedPath ? `${url.replace(/\/+$/, "")}${normalizedPath}` : url.replace(/\/+$/, "");
45753
+ }
45754
+
45688
45755
  // src/adapters/SealosDevboxAdapter/index.ts
45689
45756
  class SealosDevboxAdapter extends BaseSandboxAdapter {
45690
45757
  config;
45758
+ createConfig;
45691
45759
  provider = "sealosdevbox";
45692
45760
  get rootPath() {
45693
45761
  return "/home/devbox/workspace";
45694
45762
  }
45695
45763
  api;
45696
45764
  _id;
45697
- constructor(config) {
45765
+ constructor(config, createConfig) {
45698
45766
  super();
45699
45767
  this.config = config;
45768
+ this.createConfig = createConfig;
45700
45769
  this.api = new DevboxApi({ baseUrl: config.baseUrl, token: config.token });
45701
45770
  this._id = config.sandboxId;
45702
45771
  this.polyfillService = new CommandPolyfillService(this);
@@ -45721,6 +45790,26 @@ class SealosDevboxAdapter extends BaseSandboxAdapter {
45721
45790
  return "Error";
45722
45791
  }
45723
45792
  }
45793
+ buildCreateRequest() {
45794
+ const spec = this.createConfig ?? {};
45795
+ const env = { ...spec.env ?? {} };
45796
+ if (spec.workingDir && !env.CODEX_GATEWAY_CWD) {
45797
+ env.CODEX_GATEWAY_CWD = spec.workingDir;
45798
+ }
45799
+ return this.removeUndefined({
45800
+ name: this._id,
45801
+ image: spec.image ? formatImageSpec(spec.image) : undefined,
45802
+ env: Object.keys(env).length > 0 ? env : undefined,
45803
+ labels: spec.labels,
45804
+ upstreamID: spec.upstreamID,
45805
+ kubeAccess: spec.kubeAccess,
45806
+ pauseAt: spec.lifecycle?.pauseAt,
45807
+ archiveAfterPauseTime: spec.lifecycle?.archiveAfterPauseTime
45808
+ });
45809
+ }
45810
+ removeUndefined(obj) {
45811
+ return Object.fromEntries(Object.entries(obj).filter(([, value]) => value !== undefined));
45812
+ }
45724
45813
  async getInfo() {
45725
45814
  try {
45726
45815
  const res = await this.api.info(this._id);
@@ -45732,10 +45821,10 @@ class SealosDevboxAdapter extends BaseSandboxAdapter {
45732
45821
  this._status = { state: this.StatusAdapt(data), message: res.message };
45733
45822
  return {
45734
45823
  id: data.name,
45735
- image: { repository: "" },
45824
+ image: parseImageSpec(data.image),
45736
45825
  entrypoint: [],
45737
45826
  status: this._status,
45738
- createdAt: new Date
45827
+ createdAt: data.creationTimestamp ? new Date(data.creationTimestamp) : new Date
45739
45828
  };
45740
45829
  } catch (error) {
45741
45830
  throw new CommandExecutionError(`Failed to get sandbox info`, "getInfo", error instanceof Error ? error : undefined);
@@ -45773,7 +45862,10 @@ class SealosDevboxAdapter extends BaseSandboxAdapter {
45773
45862
  async create() {
45774
45863
  try {
45775
45864
  this._status = { state: "Creating" };
45776
- await this.api.create(this._id);
45865
+ const res = await this.api.create(this.buildCreateRequest());
45866
+ if (res.code !== 200 && res.code !== 201) {
45867
+ throw new Error(res.message || `Devbox create failed with code ${res.code}`);
45868
+ }
45777
45869
  await this.waitUntilReady();
45778
45870
  await new Promise((resolve) => setTimeout(resolve, 1000));
45779
45871
  this._status = { state: "Running" };
@@ -45829,11 +45921,98 @@ class SealosDevboxAdapter extends BaseSandboxAdapter {
45829
45921
  throw new CommandExecutionError(`Command execution failed: ${error?.message || error?.code}`, command, error instanceof Error ? error : undefined);
45830
45922
  }
45831
45923
  }
45924
+ async getEndpoint(selector) {
45925
+ const port = typeof selector === "number" ? selector : SEALOS_DEVBOX_CODE_SERVER_PORT;
45926
+ const target = await this.getHttpgateTarget(port);
45927
+ const url = new URL(target.origin);
45928
+ return {
45929
+ host: url.host,
45930
+ port: target.port,
45931
+ protocol: url.protocol === "https:" ? "https" : "http",
45932
+ url: joinUrlPath(target.origin, target.basePath)
45933
+ };
45934
+ }
45935
+ async getProxyTarget(service = "code-server") {
45936
+ if (service !== "code-server") {
45937
+ throw new FeatureNotSupportedError(`Proxy service "${service}" is not supported by this provider`, "getProxyTarget", this.provider);
45938
+ }
45939
+ const target = await this.getHttpgateTarget(SEALOS_DEVBOX_CODE_SERVER_PORT);
45940
+ return {
45941
+ service,
45942
+ origin: target.origin,
45943
+ basePath: target.basePath,
45944
+ auth: "code-server",
45945
+ ...target.password ? { password: target.password } : {}
45946
+ };
45947
+ }
45948
+ async getHttpgateTarget(port) {
45949
+ const res = await this.api.info(this._id);
45950
+ if (res.code !== 200 || !res.data) {
45951
+ throw new ConnectionError(`Failed to get devbox info: ${res.message}`, this.config.baseUrl);
45952
+ }
45953
+ if (port === SEALOS_DEVBOX_CODE_SERVER_PORT) {
45954
+ const gateway2 = res.data.codeServerGateway;
45955
+ if (!gateway2?.url) {
45956
+ throw new ConnectionError("Devbox info does not include codeServerGateway.url; cannot resolve code-server endpoint", this.config.baseUrl);
45957
+ }
45958
+ const codeServerUrl = new URL(gateway2.url);
45959
+ return {
45960
+ origin: codeServerUrl.origin,
45961
+ basePath: normalizePathPrefix(codeServerUrl.pathname),
45962
+ port: gateway2.port ?? port,
45963
+ password: gateway2.password
45964
+ };
45965
+ }
45966
+ const gatewayUrl = res.data.gateway?.url;
45967
+ if (!gatewayUrl) {
45968
+ throw new ConnectionError("Devbox info does not include gateway.url; cannot derive httpgate endpoint", this.config.baseUrl);
45969
+ }
45970
+ const gateway = new URL(gatewayUrl);
45971
+ const uniqueID = this.getGatewayUniqueID(res.data, gateway);
45972
+ const domain = this.getHttpgateDomain(gateway);
45973
+ return {
45974
+ origin: `${gateway.protocol}//devbox-${uniqueID}-${port}.${domain}`,
45975
+ basePath: "",
45976
+ port
45977
+ };
45978
+ }
45979
+ getGatewayUniqueID(data, gateway) {
45980
+ if (data.gateway?.uniqueID)
45981
+ return data.gateway.uniqueID;
45982
+ const parts = gateway.pathname.split("/").filter(Boolean);
45983
+ const uniqueID = parts[parts.length - 1];
45984
+ if (!uniqueID) {
45985
+ throw new ConnectionError("Devbox gateway.url does not include uniqueID; cannot derive httpgate endpoint", this.config.baseUrl);
45986
+ }
45987
+ return uniqueID;
45988
+ }
45989
+ getHttpgateDomain(gateway) {
45990
+ if (this.config.httpgateDomain) {
45991
+ return this.normalizeHttpgateDomain(this.config.httpgateDomain);
45992
+ }
45993
+ const prefix = "devbox-gateway.";
45994
+ if (!gateway.host.startsWith(prefix)) {
45995
+ throw new ConnectionError(`Cannot derive httpgate domain from gateway host "${gateway.host}"`, this.config.baseUrl);
45996
+ }
45997
+ return gateway.host.slice(prefix.length);
45998
+ }
45999
+ normalizeHttpgateDomain(domain) {
46000
+ const trimmed = domain.trim().replace(/^\.+|\.+$/g, "");
46001
+ if (!trimmed) {
46002
+ throw new ConnectionError("httpgateDomain is empty", this.config.baseUrl);
46003
+ }
46004
+ if (trimmed.includes("://")) {
46005
+ return new URL(trimmed).host;
46006
+ }
46007
+ return trimmed;
46008
+ }
45832
46009
  async ping() {
45833
46010
  try {
45834
46011
  const res = await this.api.info(this._id);
45835
46012
  if (res.code !== 200)
45836
46013
  return false;
46014
+ if (!res.data)
46015
+ return false;
45837
46016
  return res.data.state.phase === "Running" /* Running */;
45838
46017
  } catch {
45839
46018
  return false;
@@ -47425,7 +47604,7 @@ function createNodeFetch() {
47425
47604
  const nodeFetch = async (input, init) => {
47426
47605
  dispatcherPromise ??= (async () => {
47427
47606
  try {
47428
- const mod = await Promise.resolve().then(() => __toESM(require_undici()));
47607
+ const mod = await Promise.resolve().then(() => __toESM(require_undici(), 1));
47429
47608
  const Agent = mod.Agent;
47430
47609
  if (!Agent) {
47431
47610
  return;
@@ -48085,27 +48264,6 @@ class OpenSandboxAdapter extends BaseSandboxAdapter {
48085
48264
  message: sdkStatus.message
48086
48265
  };
48087
48266
  }
48088
- convertImageSpec(image) {
48089
- const parts = [image.repository];
48090
- if (image.tag) {
48091
- parts.push(":", image.tag);
48092
- }
48093
- if (image.digest) {
48094
- parts.push("@", image.digest);
48095
- }
48096
- return parts.join("");
48097
- }
48098
- parseImageSpec(image) {
48099
- const atIndex = image.indexOf("@");
48100
- if (atIndex > -1) {
48101
- return { repository: image.slice(0, atIndex), digest: image.slice(atIndex + 1) };
48102
- }
48103
- const colonIndex = image.indexOf(":");
48104
- if (colonIndex > -1) {
48105
- return { repository: image.slice(0, colonIndex), tag: image.slice(colonIndex + 1) };
48106
- }
48107
- return { repository: image };
48108
- }
48109
48267
  convertResourceLimits(resourceLimits) {
48110
48268
  if (!resourceLimits)
48111
48269
  return;
@@ -48220,7 +48378,7 @@ class OpenSandboxAdapter extends BaseSandboxAdapter {
48220
48378
  }
48221
48379
  try {
48222
48380
  this._status = { state: "Creating" };
48223
- const image = this.convertImageSpec(cfg.image);
48381
+ const image = formatImageSpec(cfg.image);
48224
48382
  const resource = this.convertResourceLimits(cfg.resourceLimits);
48225
48383
  this.sandbox = await Sandbox.create({
48226
48384
  connectionConfig: this._connection,
@@ -48324,7 +48482,18 @@ class OpenSandboxAdapter extends BaseSandboxAdapter {
48324
48482
  async close() {
48325
48483
  await this.sandbox.close();
48326
48484
  }
48327
- async getEndpoint(port) {
48485
+ async getEndpoint(selector) {
48486
+ const port = typeof selector === "number" ? selector : OPEN_SANDBOX_EXECD_PORT;
48487
+ const endpoint = await this.getOpenSandboxEndpoint(port);
48488
+ if (selector === "code-server") {
48489
+ return {
48490
+ ...endpoint,
48491
+ url: joinUrlPath(endpoint.url, `/proxy/${OPEN_SANDBOX_CODE_SERVER_PORT}`)
48492
+ };
48493
+ }
48494
+ return endpoint;
48495
+ }
48496
+ async getOpenSandboxEndpoint(port) {
48328
48497
  const sdkEndpoint = await this.sandbox.getEndpoint(port);
48329
48498
  const raw = sdkEndpoint.endpoint;
48330
48499
  const colonIdx = raw.lastIndexOf(":");
@@ -48343,6 +48512,40 @@ class OpenSandboxAdapter extends BaseSandboxAdapter {
48343
48512
  url: `https://${raw}`
48344
48513
  };
48345
48514
  }
48515
+ async getProxyTarget(service = "code-server") {
48516
+ if (service !== "code-server") {
48517
+ throw new FeatureNotSupportedError(`Proxy service "${service}" is not supported by this provider`, "getProxyTarget", this.provider);
48518
+ }
48519
+ return {
48520
+ service,
48521
+ origin: await this.getDirectEndpointOrigin(OPEN_SANDBOX_EXECD_PORT),
48522
+ basePath: `/proxy/${OPEN_SANDBOX_CODE_SERVER_PORT}`,
48523
+ auth: "code-server"
48524
+ };
48525
+ }
48526
+ async getDirectEndpointOrigin(port) {
48527
+ if (!this.id) {
48528
+ throw new SandboxStateError("Sandbox not initialized. Call create() or connect() first.", "UnExist", "Running");
48529
+ }
48530
+ const headers = {
48531
+ ...this._connection.headers,
48532
+ Accept: "application/json"
48533
+ };
48534
+ const response = await this._connection.fetch(`${this._connection.getBaseUrl()}/sandboxes/${this.id}/endpoints/${port}?use_server_proxy=false`, { method: "GET", headers });
48535
+ if (!response.ok) {
48536
+ throw new ConnectionError(`OpenSandbox endpoint lookup failed: HTTP ${response.status}`, this.connectionConfig.baseUrl);
48537
+ }
48538
+ const data = await response.json();
48539
+ if (!data.endpoint) {
48540
+ throw new ConnectionError("OpenSandbox returned no endpoint", this.connectionConfig.baseUrl);
48541
+ }
48542
+ let hostPort = data.endpoint.replace(/\/proxy\/\d+\/?$/, "");
48543
+ if (this.connectionConfig.replaceDockerInternalWithLocalhost) {
48544
+ hostPort = hostPort.replace(/^host\.docker\.internal\b/, "localhost");
48545
+ }
48546
+ const url = new URL(/^https?:\/\//.test(hostPort) ? hostPort : `http://${hostPort}`);
48547
+ return url.origin;
48548
+ }
48346
48549
  async getInfo() {
48347
48550
  if (!this._sandbox) {
48348
48551
  return null;
@@ -48351,7 +48554,7 @@ class OpenSandboxAdapter extends BaseSandboxAdapter {
48351
48554
  const info = await this.sandbox.getInfo();
48352
48555
  return {
48353
48556
  id: info.id,
48354
- image: typeof info.image === "string" ? this.parseImageSpec(info.image) : ("uri" in info.image) ? this.parseImageSpec(info.image.uri) : info.image,
48557
+ image: typeof info.image === "string" ? parseImageSpec(info.image) : ("uri" in info.image) ? parseImageSpec(info.image.uri) : info.image,
48355
48558
  entrypoint: info.entrypoint,
48356
48559
  metadata: info.metadata,
48357
48560
  status: this.mapStatus(info.status),
@@ -48533,7 +48736,7 @@ class OpenSandboxAdapter extends BaseSandboxAdapter {
48533
48736
  }
48534
48737
 
48535
48738
  // src/adapters/E2BAdapter/index.ts
48536
- var import_code_interpreter = __toESM(require_dist3());
48739
+ var import_code_interpreter = __toESM(require_dist3(), 1);
48537
48740
  class E2BAdapter extends BaseSandboxAdapter {
48538
48741
  config;
48539
48742
  provider = "e2b";
@@ -48853,7 +49056,7 @@ function createSandbox(provider, config, createConfig) {
48853
49056
  case "opensandbox":
48854
49057
  return new OpenSandboxAdapter(config, createConfig);
48855
49058
  case "sealosdevbox":
48856
- return new SealosDevboxAdapter(config);
49059
+ return new SealosDevboxAdapter(config, createConfig);
48857
49060
  case "e2b":
48858
49061
  return new E2BAdapter(config);
48859
49062
  default:
package/dist/index.js CHANGED
@@ -5,39 +5,60 @@ var __defProp = Object.defineProperty;
5
5
  var __getOwnPropNames = Object.getOwnPropertyNames;
6
6
  var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
7
7
  var __hasOwnProp = Object.prototype.hasOwnProperty;
8
+ function __accessProp(key) {
9
+ return this[key];
10
+ }
11
+ var __toESMCache_node;
12
+ var __toESMCache_esm;
8
13
  var __toESM = (mod, isNodeMode, target) => {
14
+ var canCache = mod != null && typeof mod === "object";
15
+ if (canCache) {
16
+ var cache = isNodeMode ? __toESMCache_node ??= new WeakMap : __toESMCache_esm ??= new WeakMap;
17
+ var cached = cache.get(mod);
18
+ if (cached)
19
+ return cached;
20
+ }
9
21
  target = mod != null ? __create(__getProtoOf(mod)) : {};
10
22
  const to = isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target;
11
23
  for (let key of __getOwnPropNames(mod))
12
24
  if (!__hasOwnProp.call(to, key))
13
25
  __defProp(to, key, {
14
- get: () => mod[key],
26
+ get: __accessProp.bind(mod, key),
15
27
  enumerable: true
16
28
  });
29
+ if (canCache)
30
+ cache.set(mod, to);
17
31
  return to;
18
32
  };
19
- var __moduleCache = /* @__PURE__ */ new WeakMap;
20
33
  var __toCommonJS = (from) => {
21
- var entry = __moduleCache.get(from), desc;
34
+ var entry = (__moduleCache ??= new WeakMap).get(from), desc;
22
35
  if (entry)
23
36
  return entry;
24
37
  entry = __defProp({}, "__esModule", { value: true });
25
- if (from && typeof from === "object" || typeof from === "function")
26
- __getOwnPropNames(from).map((key) => !__hasOwnProp.call(entry, key) && __defProp(entry, key, {
27
- get: () => from[key],
28
- enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable
29
- }));
38
+ if (from && typeof from === "object" || typeof from === "function") {
39
+ for (var key of __getOwnPropNames(from))
40
+ if (!__hasOwnProp.call(entry, key))
41
+ __defProp(entry, key, {
42
+ get: __accessProp.bind(from, key),
43
+ enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable
44
+ });
45
+ }
30
46
  __moduleCache.set(from, entry);
31
47
  return entry;
32
48
  };
49
+ var __moduleCache;
33
50
  var __commonJS = (cb, mod) => () => (mod || cb((mod = { exports: {} }).exports, mod), mod.exports);
51
+ var __returnValue = (v) => v;
52
+ function __exportSetter(name, newValue) {
53
+ this[name] = __returnValue.bind(null, newValue);
54
+ }
34
55
  var __export = (target, all) => {
35
56
  for (var name in all)
36
57
  __defProp(target, name, {
37
58
  get: all[name],
38
59
  enumerable: true,
39
60
  configurable: true,
40
- set: (newValue) => all[name] = () => newValue
61
+ set: __exportSetter.bind(all, name)
41
62
  });
42
63
  };
43
64
  var __esm = (fn, res) => () => (fn && (res = fn(fn = 0)), res);
@@ -19141,7 +19162,7 @@ var require_frame = __commonJS((exports, module) => {
19141
19162
  var BUFFER_SIZE = 8 * 1024;
19142
19163
  var buffer = null;
19143
19164
  var bufIdx = BUFFER_SIZE;
19144
- var randomFillSync = runtimeFeatures.has("crypto") ? __require("node:crypto").randomFillSync : function randomFillSync(buffer2, _offset, _size) {
19165
+ var randomFillSync = runtimeFeatures.has("crypto") ? __require("node:crypto").randomFillSync : function randomFillSync2(buffer2, _offset, _size) {
19145
19166
  for (let i = 0;i < buffer2.length; ++i) {
19146
19167
  buffer2[i] = Math.random() * 255 | 0;
19147
19168
  }
@@ -21085,7 +21106,7 @@ var require_eventsource = __commonJS((exports, module) => {
21085
21106
 
21086
21107
  // node_modules/undici/index.js
21087
21108
  var require_undici = __commonJS((exports, module) => {
21088
- var __filename = "/Volumes/code/dev/agent-sandbox-adaptor/node_modules/undici/index.js";
21109
+ var __filename = "/Users/sealos/Documents/GitHub/agent-sandbox-adaptor/node_modules/undici/index.js";
21089
21110
  var Client = require_client();
21090
21111
  var Dispatcher = require_dispatcher();
21091
21112
  var Pool = require_pool();
@@ -21208,7 +21229,7 @@ var require_undici = __commonJS((exports, module) => {
21208
21229
  err.stack = stack ? `${stack}
21209
21230
  ${captureLines}` : capture.stack;
21210
21231
  }
21211
- exports.fetch = function fetch(init, options = undefined) {
21232
+ exports.fetch = function fetch2(init, options = undefined) {
21212
21233
  return fetchImpl(init, options).catch((err) => {
21213
21234
  appendFetchStackTrace(err, __filename);
21214
21235
  throw err;
@@ -45372,6 +45393,12 @@ class BaseSandboxAdapter {
45372
45393
  async renewExpiration(_additionalSeconds) {
45373
45394
  throw new FeatureNotSupportedError("Sandbox expiration renewal not supported by this provider", "renewExpiration", this.provider);
45374
45395
  }
45396
+ async getEndpoint(_selector) {
45397
+ throw new FeatureNotSupportedError("Endpoint resolution not supported by this provider", "getEndpoint", this.provider);
45398
+ }
45399
+ async getProxyTarget(_service = "code-server") {
45400
+ throw new FeatureNotSupportedError("Proxy target resolution not supported by this provider", "getProxyTarget", this.provider);
45401
+ }
45375
45402
  async executeStream(command, handlers, options) {
45376
45403
  const result = await this.execute(command, options);
45377
45404
  if (handlers.onStdout && result.stdout) {
@@ -45608,10 +45635,10 @@ class DevboxApi {
45608
45635
  const result = await res.json();
45609
45636
  return result;
45610
45637
  }
45611
- async create(name) {
45638
+ async create(req) {
45612
45639
  return this.request(this.url("/api/v1/devbox"), {
45613
45640
  method: "POST",
45614
- body: JSON.stringify({ name })
45641
+ body: JSON.stringify(req)
45615
45642
  });
45616
45643
  }
45617
45644
  async info(name) {
@@ -45669,18 +45696,60 @@ class DevboxApi {
45669
45696
  }
45670
45697
  }
45671
45698
 
45699
+ // src/adapters/ports.ts
45700
+ var OPEN_SANDBOX_EXECD_PORT = 44772;
45701
+ var OPEN_SANDBOX_CODE_SERVER_PORT = 8080;
45702
+ var SEALOS_DEVBOX_CODE_SERVER_PORT = 1318;
45703
+
45704
+ // src/utils/image.ts
45705
+ function formatImageSpec(image) {
45706
+ const parts = [image.repository];
45707
+ if (image.tag)
45708
+ parts.push(":", image.tag);
45709
+ if (image.digest)
45710
+ parts.push("@", image.digest);
45711
+ return parts.join("");
45712
+ }
45713
+ function parseImageSpec(image) {
45714
+ if (!image)
45715
+ return { repository: "" };
45716
+ const atIndex = image.indexOf("@");
45717
+ if (atIndex > -1) {
45718
+ return { repository: image.slice(0, atIndex), digest: image.slice(atIndex + 1) };
45719
+ }
45720
+ const colonIndex = image.indexOf(":");
45721
+ if (colonIndex > -1) {
45722
+ return { repository: image.slice(0, colonIndex), tag: image.slice(colonIndex + 1) };
45723
+ }
45724
+ return { repository: image };
45725
+ }
45726
+
45727
+ // src/utils/url.ts
45728
+ function normalizePathPrefix(path) {
45729
+ if (!path)
45730
+ return "";
45731
+ const normalized = path.startsWith("/") ? path : `/${path}`;
45732
+ return normalized.length > 1 ? normalized.replace(/\/+$/, "") : "";
45733
+ }
45734
+ function joinUrlPath(url, path) {
45735
+ const normalizedPath = normalizePathPrefix(path);
45736
+ return normalizedPath ? `${url.replace(/\/+$/, "")}${normalizedPath}` : url.replace(/\/+$/, "");
45737
+ }
45738
+
45672
45739
  // src/adapters/SealosDevboxAdapter/index.ts
45673
45740
  class SealosDevboxAdapter extends BaseSandboxAdapter {
45674
45741
  config;
45742
+ createConfig;
45675
45743
  provider = "sealosdevbox";
45676
45744
  get rootPath() {
45677
45745
  return "/home/devbox/workspace";
45678
45746
  }
45679
45747
  api;
45680
45748
  _id;
45681
- constructor(config) {
45749
+ constructor(config, createConfig) {
45682
45750
  super();
45683
45751
  this.config = config;
45752
+ this.createConfig = createConfig;
45684
45753
  this.api = new DevboxApi({ baseUrl: config.baseUrl, token: config.token });
45685
45754
  this._id = config.sandboxId;
45686
45755
  this.polyfillService = new CommandPolyfillService(this);
@@ -45705,6 +45774,26 @@ class SealosDevboxAdapter extends BaseSandboxAdapter {
45705
45774
  return "Error";
45706
45775
  }
45707
45776
  }
45777
+ buildCreateRequest() {
45778
+ const spec = this.createConfig ?? {};
45779
+ const env = { ...spec.env ?? {} };
45780
+ if (spec.workingDir && !env.CODEX_GATEWAY_CWD) {
45781
+ env.CODEX_GATEWAY_CWD = spec.workingDir;
45782
+ }
45783
+ return this.removeUndefined({
45784
+ name: this._id,
45785
+ image: spec.image ? formatImageSpec(spec.image) : undefined,
45786
+ env: Object.keys(env).length > 0 ? env : undefined,
45787
+ labels: spec.labels,
45788
+ upstreamID: spec.upstreamID,
45789
+ kubeAccess: spec.kubeAccess,
45790
+ pauseAt: spec.lifecycle?.pauseAt,
45791
+ archiveAfterPauseTime: spec.lifecycle?.archiveAfterPauseTime
45792
+ });
45793
+ }
45794
+ removeUndefined(obj) {
45795
+ return Object.fromEntries(Object.entries(obj).filter(([, value]) => value !== undefined));
45796
+ }
45708
45797
  async getInfo() {
45709
45798
  try {
45710
45799
  const res = await this.api.info(this._id);
@@ -45716,10 +45805,10 @@ class SealosDevboxAdapter extends BaseSandboxAdapter {
45716
45805
  this._status = { state: this.StatusAdapt(data), message: res.message };
45717
45806
  return {
45718
45807
  id: data.name,
45719
- image: { repository: "" },
45808
+ image: parseImageSpec(data.image),
45720
45809
  entrypoint: [],
45721
45810
  status: this._status,
45722
- createdAt: new Date
45811
+ createdAt: data.creationTimestamp ? new Date(data.creationTimestamp) : new Date
45723
45812
  };
45724
45813
  } catch (error) {
45725
45814
  throw new CommandExecutionError(`Failed to get sandbox info`, "getInfo", error instanceof Error ? error : undefined);
@@ -45757,7 +45846,10 @@ class SealosDevboxAdapter extends BaseSandboxAdapter {
45757
45846
  async create() {
45758
45847
  try {
45759
45848
  this._status = { state: "Creating" };
45760
- await this.api.create(this._id);
45849
+ const res = await this.api.create(this.buildCreateRequest());
45850
+ if (res.code !== 200 && res.code !== 201) {
45851
+ throw new Error(res.message || `Devbox create failed with code ${res.code}`);
45852
+ }
45761
45853
  await this.waitUntilReady();
45762
45854
  await new Promise((resolve) => setTimeout(resolve, 1000));
45763
45855
  this._status = { state: "Running" };
@@ -45813,11 +45905,98 @@ class SealosDevboxAdapter extends BaseSandboxAdapter {
45813
45905
  throw new CommandExecutionError(`Command execution failed: ${error?.message || error?.code}`, command, error instanceof Error ? error : undefined);
45814
45906
  }
45815
45907
  }
45908
+ async getEndpoint(selector) {
45909
+ const port = typeof selector === "number" ? selector : SEALOS_DEVBOX_CODE_SERVER_PORT;
45910
+ const target = await this.getHttpgateTarget(port);
45911
+ const url = new URL(target.origin);
45912
+ return {
45913
+ host: url.host,
45914
+ port: target.port,
45915
+ protocol: url.protocol === "https:" ? "https" : "http",
45916
+ url: joinUrlPath(target.origin, target.basePath)
45917
+ };
45918
+ }
45919
+ async getProxyTarget(service = "code-server") {
45920
+ if (service !== "code-server") {
45921
+ throw new FeatureNotSupportedError(`Proxy service "${service}" is not supported by this provider`, "getProxyTarget", this.provider);
45922
+ }
45923
+ const target = await this.getHttpgateTarget(SEALOS_DEVBOX_CODE_SERVER_PORT);
45924
+ return {
45925
+ service,
45926
+ origin: target.origin,
45927
+ basePath: target.basePath,
45928
+ auth: "code-server",
45929
+ ...target.password ? { password: target.password } : {}
45930
+ };
45931
+ }
45932
+ async getHttpgateTarget(port) {
45933
+ const res = await this.api.info(this._id);
45934
+ if (res.code !== 200 || !res.data) {
45935
+ throw new ConnectionError(`Failed to get devbox info: ${res.message}`, this.config.baseUrl);
45936
+ }
45937
+ if (port === SEALOS_DEVBOX_CODE_SERVER_PORT) {
45938
+ const gateway2 = res.data.codeServerGateway;
45939
+ if (!gateway2?.url) {
45940
+ throw new ConnectionError("Devbox info does not include codeServerGateway.url; cannot resolve code-server endpoint", this.config.baseUrl);
45941
+ }
45942
+ const codeServerUrl = new URL(gateway2.url);
45943
+ return {
45944
+ origin: codeServerUrl.origin,
45945
+ basePath: normalizePathPrefix(codeServerUrl.pathname),
45946
+ port: gateway2.port ?? port,
45947
+ password: gateway2.password
45948
+ };
45949
+ }
45950
+ const gatewayUrl = res.data.gateway?.url;
45951
+ if (!gatewayUrl) {
45952
+ throw new ConnectionError("Devbox info does not include gateway.url; cannot derive httpgate endpoint", this.config.baseUrl);
45953
+ }
45954
+ const gateway = new URL(gatewayUrl);
45955
+ const uniqueID = this.getGatewayUniqueID(res.data, gateway);
45956
+ const domain = this.getHttpgateDomain(gateway);
45957
+ return {
45958
+ origin: `${gateway.protocol}//devbox-${uniqueID}-${port}.${domain}`,
45959
+ basePath: "",
45960
+ port
45961
+ };
45962
+ }
45963
+ getGatewayUniqueID(data, gateway) {
45964
+ if (data.gateway?.uniqueID)
45965
+ return data.gateway.uniqueID;
45966
+ const parts = gateway.pathname.split("/").filter(Boolean);
45967
+ const uniqueID = parts[parts.length - 1];
45968
+ if (!uniqueID) {
45969
+ throw new ConnectionError("Devbox gateway.url does not include uniqueID; cannot derive httpgate endpoint", this.config.baseUrl);
45970
+ }
45971
+ return uniqueID;
45972
+ }
45973
+ getHttpgateDomain(gateway) {
45974
+ if (this.config.httpgateDomain) {
45975
+ return this.normalizeHttpgateDomain(this.config.httpgateDomain);
45976
+ }
45977
+ const prefix = "devbox-gateway.";
45978
+ if (!gateway.host.startsWith(prefix)) {
45979
+ throw new ConnectionError(`Cannot derive httpgate domain from gateway host "${gateway.host}"`, this.config.baseUrl);
45980
+ }
45981
+ return gateway.host.slice(prefix.length);
45982
+ }
45983
+ normalizeHttpgateDomain(domain) {
45984
+ const trimmed = domain.trim().replace(/^\.+|\.+$/g, "");
45985
+ if (!trimmed) {
45986
+ throw new ConnectionError("httpgateDomain is empty", this.config.baseUrl);
45987
+ }
45988
+ if (trimmed.includes("://")) {
45989
+ return new URL(trimmed).host;
45990
+ }
45991
+ return trimmed;
45992
+ }
45816
45993
  async ping() {
45817
45994
  try {
45818
45995
  const res = await this.api.info(this._id);
45819
45996
  if (res.code !== 200)
45820
45997
  return false;
45998
+ if (!res.data)
45999
+ return false;
45821
46000
  return res.data.state.phase === "Running" /* Running */;
45822
46001
  } catch {
45823
46002
  return false;
@@ -48069,27 +48248,6 @@ class OpenSandboxAdapter extends BaseSandboxAdapter {
48069
48248
  message: sdkStatus.message
48070
48249
  };
48071
48250
  }
48072
- convertImageSpec(image) {
48073
- const parts = [image.repository];
48074
- if (image.tag) {
48075
- parts.push(":", image.tag);
48076
- }
48077
- if (image.digest) {
48078
- parts.push("@", image.digest);
48079
- }
48080
- return parts.join("");
48081
- }
48082
- parseImageSpec(image) {
48083
- const atIndex = image.indexOf("@");
48084
- if (atIndex > -1) {
48085
- return { repository: image.slice(0, atIndex), digest: image.slice(atIndex + 1) };
48086
- }
48087
- const colonIndex = image.indexOf(":");
48088
- if (colonIndex > -1) {
48089
- return { repository: image.slice(0, colonIndex), tag: image.slice(colonIndex + 1) };
48090
- }
48091
- return { repository: image };
48092
- }
48093
48251
  convertResourceLimits(resourceLimits) {
48094
48252
  if (!resourceLimits)
48095
48253
  return;
@@ -48204,7 +48362,7 @@ class OpenSandboxAdapter extends BaseSandboxAdapter {
48204
48362
  }
48205
48363
  try {
48206
48364
  this._status = { state: "Creating" };
48207
- const image = this.convertImageSpec(cfg.image);
48365
+ const image = formatImageSpec(cfg.image);
48208
48366
  const resource = this.convertResourceLimits(cfg.resourceLimits);
48209
48367
  this.sandbox = await Sandbox.create({
48210
48368
  connectionConfig: this._connection,
@@ -48308,7 +48466,18 @@ class OpenSandboxAdapter extends BaseSandboxAdapter {
48308
48466
  async close() {
48309
48467
  await this.sandbox.close();
48310
48468
  }
48311
- async getEndpoint(port) {
48469
+ async getEndpoint(selector) {
48470
+ const port = typeof selector === "number" ? selector : OPEN_SANDBOX_EXECD_PORT;
48471
+ const endpoint = await this.getOpenSandboxEndpoint(port);
48472
+ if (selector === "code-server") {
48473
+ return {
48474
+ ...endpoint,
48475
+ url: joinUrlPath(endpoint.url, `/proxy/${OPEN_SANDBOX_CODE_SERVER_PORT}`)
48476
+ };
48477
+ }
48478
+ return endpoint;
48479
+ }
48480
+ async getOpenSandboxEndpoint(port) {
48312
48481
  const sdkEndpoint = await this.sandbox.getEndpoint(port);
48313
48482
  const raw = sdkEndpoint.endpoint;
48314
48483
  const colonIdx = raw.lastIndexOf(":");
@@ -48327,6 +48496,40 @@ class OpenSandboxAdapter extends BaseSandboxAdapter {
48327
48496
  url: `https://${raw}`
48328
48497
  };
48329
48498
  }
48499
+ async getProxyTarget(service = "code-server") {
48500
+ if (service !== "code-server") {
48501
+ throw new FeatureNotSupportedError(`Proxy service "${service}" is not supported by this provider`, "getProxyTarget", this.provider);
48502
+ }
48503
+ return {
48504
+ service,
48505
+ origin: await this.getDirectEndpointOrigin(OPEN_SANDBOX_EXECD_PORT),
48506
+ basePath: `/proxy/${OPEN_SANDBOX_CODE_SERVER_PORT}`,
48507
+ auth: "code-server"
48508
+ };
48509
+ }
48510
+ async getDirectEndpointOrigin(port) {
48511
+ if (!this.id) {
48512
+ throw new SandboxStateError("Sandbox not initialized. Call create() or connect() first.", "UnExist", "Running");
48513
+ }
48514
+ const headers = {
48515
+ ...this._connection.headers,
48516
+ Accept: "application/json"
48517
+ };
48518
+ const response = await this._connection.fetch(`${this._connection.getBaseUrl()}/sandboxes/${this.id}/endpoints/${port}?use_server_proxy=false`, { method: "GET", headers });
48519
+ if (!response.ok) {
48520
+ throw new ConnectionError(`OpenSandbox endpoint lookup failed: HTTP ${response.status}`, this.connectionConfig.baseUrl);
48521
+ }
48522
+ const data = await response.json();
48523
+ if (!data.endpoint) {
48524
+ throw new ConnectionError("OpenSandbox returned no endpoint", this.connectionConfig.baseUrl);
48525
+ }
48526
+ let hostPort = data.endpoint.replace(/\/proxy\/\d+\/?$/, "");
48527
+ if (this.connectionConfig.replaceDockerInternalWithLocalhost) {
48528
+ hostPort = hostPort.replace(/^host\.docker\.internal\b/, "localhost");
48529
+ }
48530
+ const url = new URL(/^https?:\/\//.test(hostPort) ? hostPort : `http://${hostPort}`);
48531
+ return url.origin;
48532
+ }
48330
48533
  async getInfo() {
48331
48534
  if (!this._sandbox) {
48332
48535
  return null;
@@ -48335,7 +48538,7 @@ class OpenSandboxAdapter extends BaseSandboxAdapter {
48335
48538
  const info = await this.sandbox.getInfo();
48336
48539
  return {
48337
48540
  id: info.id,
48338
- image: typeof info.image === "string" ? this.parseImageSpec(info.image) : ("uri" in info.image) ? this.parseImageSpec(info.image.uri) : info.image,
48541
+ image: typeof info.image === "string" ? parseImageSpec(info.image) : ("uri" in info.image) ? parseImageSpec(info.image.uri) : info.image,
48339
48542
  entrypoint: info.entrypoint,
48340
48543
  metadata: info.metadata,
48341
48544
  status: this.mapStatus(info.status),
@@ -48837,7 +49040,7 @@ function createSandbox(provider, config, createConfig) {
48837
49040
  case "opensandbox":
48838
49041
  return new OpenSandboxAdapter(config, createConfig);
48839
49042
  case "sealosdevbox":
48840
- return new SealosDevboxAdapter(config);
49043
+ return new SealosDevboxAdapter(config, createConfig);
48841
49044
  case "e2b":
48842
49045
  return new E2BAdapter(config);
48843
49046
  default:
@@ -2,6 +2,7 @@ import type { ICommandExecution } from './ICommandExecution';
2
2
  import type { IFileSystem } from './IFileSystem';
3
3
  import type { IHealthCheck } from './IHealthCheck';
4
4
  import type { ISandboxLifecycle } from './ISandboxLifecycle';
5
+ import type { Endpoint, SandboxEndpointSelector, SandboxProxyService, SandboxProxyTarget } from '../types';
5
6
  /**
6
7
  * Unified sandbox interface.
7
8
  * Composes all sandbox behaviors into a single interface.
@@ -15,4 +16,8 @@ import type { ISandboxLifecycle } from './ISandboxLifecycle';
15
16
  export interface ISandbox extends ISandboxLifecycle, ICommandExecution, IFileSystem, IHealthCheck {
16
17
  /** Provider name (e.g., 'opensandbox') */
17
18
  readonly provider: string;
19
+ /** Resolve an endpoint exposed by the sandbox provider. */
20
+ getEndpoint(selector: SandboxEndpointSelector): Promise<Endpoint>;
21
+ /** Resolve the upstream target used by FastGPT sandbox-proxy. */
22
+ getProxyTarget(service?: SandboxProxyService): Promise<SandboxProxyTarget>;
18
23
  }
@@ -1,3 +1,3 @@
1
1
  export type { BackgroundExecution, ExecuteOptions, ExecuteResult, OutputMessage, StreamHandlers } from './execution';
2
2
  export type { ContentReplaceEntry, DirectoryEntry, FileDeleteResult, FileInfo, FileReadResult, FileWriteEntry, FileWriteResult, MoveEntry, PermissionEntry, ReadFileOptions, SearchResult } from './filesystem';
3
- export type { Endpoint, ImageSpec, NetworkPolicy, ResourceLimits, SandboxId, SandboxInfo, SandboxMetrics, SandboxState, SandboxStatus } from './sandbox';
3
+ export type { Endpoint, ImageSpec, KubeAccessPolicy, LabelSpec, LifecyclePolicy, NetworkPolicy, ResourceLimits, SandboxCreateSpec, SandboxEndpointSelector, SandboxId, SandboxInfo, SandboxMetrics, SandboxProxyService, SandboxProxyTarget, SandboxState, SandboxStatus } from './sandbox';
@@ -37,6 +37,18 @@ export interface NetworkPolicy {
37
37
  allowEgress?: boolean;
38
38
  allowedHosts?: string[];
39
39
  }
40
+ export interface LabelSpec {
41
+ key: string;
42
+ value: string;
43
+ }
44
+ export interface LifecyclePolicy {
45
+ pauseAt?: string;
46
+ archiveAfterPauseTime?: string;
47
+ }
48
+ export interface KubeAccessPolicy {
49
+ enabled?: boolean;
50
+ roleTemplate?: 'view' | 'edit' | 'admin';
51
+ }
40
52
  /**
41
53
  * Information about a sandbox.
42
54
  */
@@ -69,3 +81,36 @@ export interface Endpoint {
69
81
  protocol: 'http' | 'https';
70
82
  url: string;
71
83
  }
84
+ /**
85
+ * App-facing create spec shared by callers that choose a provider at runtime.
86
+ * Individual adapters should map only the fields their backend actually supports.
87
+ */
88
+ export interface SandboxCreateSpec {
89
+ image?: ImageSpec;
90
+ entrypoint?: string[];
91
+ timeout?: number;
92
+ timeoutSeconds?: number | null;
93
+ resourceLimits?: ResourceLimits;
94
+ env?: Record<string, string>;
95
+ metadata?: Record<string, unknown>;
96
+ labels?: LabelSpec[];
97
+ lifecycle?: LifecyclePolicy;
98
+ kubeAccess?: KubeAccessPolicy;
99
+ networkPolicy?: NetworkPolicy;
100
+ volumes?: unknown[];
101
+ workingDir?: string;
102
+ upstreamID?: string;
103
+ extensions?: Record<string, unknown>;
104
+ skipHealthCheck?: boolean;
105
+ readyTimeoutSeconds?: number;
106
+ healthCheckPollingInterval?: number;
107
+ }
108
+ export type SandboxProxyService = 'code-server';
109
+ export type SandboxEndpointSelector = number | SandboxProxyService;
110
+ export interface SandboxProxyTarget {
111
+ service: SandboxProxyService;
112
+ origin: string;
113
+ basePath: string;
114
+ auth: 'code-server';
115
+ password?: string;
116
+ }
@@ -0,0 +1,3 @@
1
+ import type { ImageSpec } from '@/types';
2
+ export declare function formatImageSpec(image: ImageSpec): string;
3
+ export declare function parseImageSpec(image?: string): ImageSpec;
@@ -0,0 +1,2 @@
1
+ export declare function normalizePathPrefix(path: string): string;
2
+ export declare function joinUrlPath(url: string, path: string): string;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@fastgpt-sdk/sandbox-adapter",
3
- "version": "0.0.36",
3
+ "version": "0.0.37",
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",