@fastgpt-sdk/sandbox-adapter 0.0.36 → 0.0.38-beta.0
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/adapters/BaseSandboxAdapter.d.ts +3 -1
- package/dist/adapters/OpenSandboxAdapter/index.d.ts +11 -7
- package/dist/adapters/OpenSandboxAdapter/type.d.ts +1 -1
- package/dist/adapters/SealosDevboxAdapter/api.d.ts +4 -2
- package/dist/adapters/SealosDevboxAdapter/index.d.ts +26 -2
- package/dist/adapters/SealosDevboxAdapter/type.d.ts +32 -1
- package/dist/adapters/index.d.ts +3 -3
- package/dist/adapters/ports.d.ts +7 -0
- package/dist/index.cjs +282 -47
- package/dist/index.js +280 -45
- package/dist/interfaces/ISandbox.d.ts +5 -0
- package/dist/types/index.d.ts +1 -1
- package/dist/types/sandbox.d.ts +45 -0
- package/dist/utils/image.d.ts +3 -0
- package/dist/utils/url.d.ts +2 -0
- package/package.json +1 -1
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: (
|
|
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
|
-
|
|
27
|
-
|
|
28
|
-
|
|
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: (
|
|
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
|
|
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 = "/
|
|
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
|
|
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(
|
|
45638
|
+
async create(req) {
|
|
45612
45639
|
return this.request(this.url("/api/v1/devbox"), {
|
|
45613
45640
|
method: "POST",
|
|
45614
|
-
body: JSON.stringify(
|
|
45641
|
+
body: JSON.stringify(req)
|
|
45615
45642
|
});
|
|
45616
45643
|
}
|
|
45617
45644
|
async info(name) {
|
|
@@ -45624,6 +45651,11 @@ class DevboxApi {
|
|
|
45624
45651
|
method: "POST"
|
|
45625
45652
|
});
|
|
45626
45653
|
}
|
|
45654
|
+
async stop(name) {
|
|
45655
|
+
return this.request(this.url(`/api/v1/devbox/${name}/stop`), {
|
|
45656
|
+
method: "POST"
|
|
45657
|
+
});
|
|
45658
|
+
}
|
|
45627
45659
|
async resume(name) {
|
|
45628
45660
|
return this.request(this.url(`/api/v1/devbox/${name}/resume`), {
|
|
45629
45661
|
method: "POST"
|
|
@@ -45669,18 +45701,60 @@ class DevboxApi {
|
|
|
45669
45701
|
}
|
|
45670
45702
|
}
|
|
45671
45703
|
|
|
45704
|
+
// src/adapters/ports.ts
|
|
45705
|
+
var OPEN_SANDBOX_EXECD_PORT = 44772;
|
|
45706
|
+
var OPEN_SANDBOX_CODE_SERVER_PORT = 8080;
|
|
45707
|
+
var SEALOS_DEVBOX_CODE_SERVER_PORT = 1318;
|
|
45708
|
+
|
|
45709
|
+
// src/utils/image.ts
|
|
45710
|
+
function formatImageSpec(image) {
|
|
45711
|
+
const parts = [image.repository];
|
|
45712
|
+
if (image.tag)
|
|
45713
|
+
parts.push(":", image.tag);
|
|
45714
|
+
if (image.digest)
|
|
45715
|
+
parts.push("@", image.digest);
|
|
45716
|
+
return parts.join("");
|
|
45717
|
+
}
|
|
45718
|
+
function parseImageSpec(image) {
|
|
45719
|
+
if (!image)
|
|
45720
|
+
return { repository: "" };
|
|
45721
|
+
const atIndex = image.indexOf("@");
|
|
45722
|
+
if (atIndex > -1) {
|
|
45723
|
+
return { repository: image.slice(0, atIndex), digest: image.slice(atIndex + 1) };
|
|
45724
|
+
}
|
|
45725
|
+
const colonIndex = image.indexOf(":");
|
|
45726
|
+
if (colonIndex > -1) {
|
|
45727
|
+
return { repository: image.slice(0, colonIndex), tag: image.slice(colonIndex + 1) };
|
|
45728
|
+
}
|
|
45729
|
+
return { repository: image };
|
|
45730
|
+
}
|
|
45731
|
+
|
|
45732
|
+
// src/utils/url.ts
|
|
45733
|
+
function normalizePathPrefix(path) {
|
|
45734
|
+
if (!path)
|
|
45735
|
+
return "";
|
|
45736
|
+
const normalized = path.startsWith("/") ? path : `/${path}`;
|
|
45737
|
+
return normalized.length > 1 ? normalized.replace(/\/+$/, "") : "";
|
|
45738
|
+
}
|
|
45739
|
+
function joinUrlPath(url, path) {
|
|
45740
|
+
const normalizedPath = normalizePathPrefix(path);
|
|
45741
|
+
return normalizedPath ? `${url.replace(/\/+$/, "")}${normalizedPath}` : url.replace(/\/+$/, "");
|
|
45742
|
+
}
|
|
45743
|
+
|
|
45672
45744
|
// src/adapters/SealosDevboxAdapter/index.ts
|
|
45673
45745
|
class SealosDevboxAdapter extends BaseSandboxAdapter {
|
|
45674
45746
|
config;
|
|
45747
|
+
createConfig;
|
|
45675
45748
|
provider = "sealosdevbox";
|
|
45676
45749
|
get rootPath() {
|
|
45677
45750
|
return "/home/devbox/workspace";
|
|
45678
45751
|
}
|
|
45679
45752
|
api;
|
|
45680
45753
|
_id;
|
|
45681
|
-
constructor(config) {
|
|
45754
|
+
constructor(config, createConfig) {
|
|
45682
45755
|
super();
|
|
45683
45756
|
this.config = config;
|
|
45757
|
+
this.createConfig = createConfig;
|
|
45684
45758
|
this.api = new DevboxApi({ baseUrl: config.baseUrl, token: config.token });
|
|
45685
45759
|
this._id = config.sandboxId;
|
|
45686
45760
|
this.polyfillService = new CommandPolyfillService(this);
|
|
@@ -45705,6 +45779,26 @@ class SealosDevboxAdapter extends BaseSandboxAdapter {
|
|
|
45705
45779
|
return "Error";
|
|
45706
45780
|
}
|
|
45707
45781
|
}
|
|
45782
|
+
buildCreateRequest() {
|
|
45783
|
+
const spec = this.createConfig ?? {};
|
|
45784
|
+
const env = { ...spec.env ?? {} };
|
|
45785
|
+
if (spec.workingDir && !env.CODEX_GATEWAY_CWD) {
|
|
45786
|
+
env.CODEX_GATEWAY_CWD = spec.workingDir;
|
|
45787
|
+
}
|
|
45788
|
+
return this.removeUndefined({
|
|
45789
|
+
name: this._id,
|
|
45790
|
+
image: spec.image ? formatImageSpec(spec.image) : undefined,
|
|
45791
|
+
env: Object.keys(env).length > 0 ? env : undefined,
|
|
45792
|
+
labels: spec.labels,
|
|
45793
|
+
upstreamID: spec.upstreamID,
|
|
45794
|
+
kubeAccess: spec.kubeAccess,
|
|
45795
|
+
pauseAt: spec.lifecycle?.pauseAt,
|
|
45796
|
+
archiveAfterPauseTime: spec.lifecycle?.archiveAfterPauseTime
|
|
45797
|
+
});
|
|
45798
|
+
}
|
|
45799
|
+
removeUndefined(obj) {
|
|
45800
|
+
return Object.fromEntries(Object.entries(obj).filter(([, value]) => value !== undefined));
|
|
45801
|
+
}
|
|
45708
45802
|
async getInfo() {
|
|
45709
45803
|
try {
|
|
45710
45804
|
const res = await this.api.info(this._id);
|
|
@@ -45716,10 +45810,10 @@ class SealosDevboxAdapter extends BaseSandboxAdapter {
|
|
|
45716
45810
|
this._status = { state: this.StatusAdapt(data), message: res.message };
|
|
45717
45811
|
return {
|
|
45718
45812
|
id: data.name,
|
|
45719
|
-
image:
|
|
45813
|
+
image: parseImageSpec(data.image),
|
|
45720
45814
|
entrypoint: [],
|
|
45721
45815
|
status: this._status,
|
|
45722
|
-
createdAt: new Date
|
|
45816
|
+
createdAt: data.creationTimestamp ? new Date(data.creationTimestamp) : new Date
|
|
45723
45817
|
};
|
|
45724
45818
|
} catch (error) {
|
|
45725
45819
|
throw new CommandExecutionError(`Failed to get sandbox info`, "getInfo", error instanceof Error ? error : undefined);
|
|
@@ -45757,7 +45851,10 @@ class SealosDevboxAdapter extends BaseSandboxAdapter {
|
|
|
45757
45851
|
async create() {
|
|
45758
45852
|
try {
|
|
45759
45853
|
this._status = { state: "Creating" };
|
|
45760
|
-
await this.api.create(this.
|
|
45854
|
+
const res = await this.api.create(this.buildCreateRequest());
|
|
45855
|
+
if (res.code !== 200 && res.code !== 201) {
|
|
45856
|
+
throw new Error(res.message || `Devbox create failed with code ${res.code}`);
|
|
45857
|
+
}
|
|
45761
45858
|
await this.waitUntilReady();
|
|
45762
45859
|
await new Promise((resolve) => setTimeout(resolve, 1000));
|
|
45763
45860
|
this._status = { state: "Running" };
|
|
@@ -45768,10 +45865,10 @@ class SealosDevboxAdapter extends BaseSandboxAdapter {
|
|
|
45768
45865
|
async stop() {
|
|
45769
45866
|
try {
|
|
45770
45867
|
this._status = { state: "Stopping" };
|
|
45771
|
-
await this.api.
|
|
45868
|
+
await this.api.stop(this._id);
|
|
45772
45869
|
this._status = { state: "Stopped" };
|
|
45773
45870
|
} catch (error) {
|
|
45774
|
-
throw new CommandExecutionError("Failed to
|
|
45871
|
+
throw new CommandExecutionError("Failed to stop sandbox", "stop", error instanceof Error ? error : undefined);
|
|
45775
45872
|
}
|
|
45776
45873
|
}
|
|
45777
45874
|
async start() {
|
|
@@ -45813,11 +45910,125 @@ class SealosDevboxAdapter extends BaseSandboxAdapter {
|
|
|
45813
45910
|
throw new CommandExecutionError(`Command execution failed: ${error?.message || error?.code}`, command, error instanceof Error ? error : undefined);
|
|
45814
45911
|
}
|
|
45815
45912
|
}
|
|
45913
|
+
async getEndpoint(selector) {
|
|
45914
|
+
const port = typeof selector === "number" ? selector : SEALOS_DEVBOX_CODE_SERVER_PORT;
|
|
45915
|
+
const target = await this.getHttpgateTarget(port);
|
|
45916
|
+
if (selector === "code-server") {
|
|
45917
|
+
await this.waitForCodeServerHealthz(target);
|
|
45918
|
+
}
|
|
45919
|
+
const url = new URL(target.origin);
|
|
45920
|
+
return {
|
|
45921
|
+
host: url.host,
|
|
45922
|
+
port: target.port,
|
|
45923
|
+
protocol: url.protocol === "https:" ? "https" : "http",
|
|
45924
|
+
url: joinUrlPath(target.origin, target.basePath)
|
|
45925
|
+
};
|
|
45926
|
+
}
|
|
45927
|
+
async getProxyTarget(service = "code-server") {
|
|
45928
|
+
if (service !== "code-server") {
|
|
45929
|
+
throw new FeatureNotSupportedError(`Proxy service "${service}" is not supported by this provider`, "getProxyTarget", this.provider);
|
|
45930
|
+
}
|
|
45931
|
+
const target = await this.getHttpgateTarget(SEALOS_DEVBOX_CODE_SERVER_PORT);
|
|
45932
|
+
return {
|
|
45933
|
+
service,
|
|
45934
|
+
origin: target.origin,
|
|
45935
|
+
basePath: target.basePath,
|
|
45936
|
+
auth: "code-server",
|
|
45937
|
+
...target.password ? { password: target.password } : {}
|
|
45938
|
+
};
|
|
45939
|
+
}
|
|
45940
|
+
async waitForCodeServerHealthz(target) {
|
|
45941
|
+
const healthUrl = joinUrlPath(joinUrlPath(target.origin, target.basePath), "/healthz");
|
|
45942
|
+
const timeoutMs = 60000;
|
|
45943
|
+
const intervalMs = 500;
|
|
45944
|
+
const requestTimeoutMs = 3000;
|
|
45945
|
+
const deadline = Date.now() + timeoutMs;
|
|
45946
|
+
let lastResult = "not checked";
|
|
45947
|
+
while (Date.now() < deadline) {
|
|
45948
|
+
try {
|
|
45949
|
+
const res = await fetch(healthUrl, {
|
|
45950
|
+
method: "GET",
|
|
45951
|
+
signal: AbortSignal.timeout(requestTimeoutMs)
|
|
45952
|
+
});
|
|
45953
|
+
if (res.status >= 200 && res.status < 400) {
|
|
45954
|
+
return;
|
|
45955
|
+
}
|
|
45956
|
+
lastResult = `status ${res.status}`;
|
|
45957
|
+
} catch (error) {
|
|
45958
|
+
lastResult = error instanceof Error ? error.message : String(error);
|
|
45959
|
+
}
|
|
45960
|
+
await this.sleep(intervalMs);
|
|
45961
|
+
}
|
|
45962
|
+
throw new ConnectionError(`Devbox code-server health check ${healthUrl} did not become ready within ${timeoutMs}ms. Last result: ${lastResult}`, this.config.baseUrl);
|
|
45963
|
+
}
|
|
45964
|
+
async getHttpgateTarget(port) {
|
|
45965
|
+
const res = await this.api.info(this._id);
|
|
45966
|
+
if (res.code !== 200 || !res.data) {
|
|
45967
|
+
throw new ConnectionError(`Failed to get devbox info: ${res.message}`, this.config.baseUrl);
|
|
45968
|
+
}
|
|
45969
|
+
if (port === SEALOS_DEVBOX_CODE_SERVER_PORT) {
|
|
45970
|
+
const gateway2 = res.data.codeServerGateway;
|
|
45971
|
+
if (!gateway2?.url) {
|
|
45972
|
+
throw new ConnectionError("Devbox info does not include codeServerGateway.url; cannot resolve code-server endpoint", this.config.baseUrl);
|
|
45973
|
+
}
|
|
45974
|
+
const codeServerUrl = new URL(gateway2.url);
|
|
45975
|
+
return {
|
|
45976
|
+
origin: codeServerUrl.origin,
|
|
45977
|
+
basePath: normalizePathPrefix(codeServerUrl.pathname),
|
|
45978
|
+
port: gateway2.port ?? port,
|
|
45979
|
+
password: gateway2.password
|
|
45980
|
+
};
|
|
45981
|
+
}
|
|
45982
|
+
const gatewayUrl = res.data.gateway?.url;
|
|
45983
|
+
if (!gatewayUrl) {
|
|
45984
|
+
throw new ConnectionError("Devbox info does not include gateway.url; cannot derive httpgate endpoint", this.config.baseUrl);
|
|
45985
|
+
}
|
|
45986
|
+
const gateway = new URL(gatewayUrl);
|
|
45987
|
+
const uniqueID = this.getGatewayUniqueID(res.data, gateway);
|
|
45988
|
+
const domain = this.getHttpgateDomain(gateway);
|
|
45989
|
+
return {
|
|
45990
|
+
origin: `${gateway.protocol}//devbox-${uniqueID}-${port}.${domain}`,
|
|
45991
|
+
basePath: "",
|
|
45992
|
+
port
|
|
45993
|
+
};
|
|
45994
|
+
}
|
|
45995
|
+
getGatewayUniqueID(data, gateway) {
|
|
45996
|
+
if (data.gateway?.uniqueID)
|
|
45997
|
+
return data.gateway.uniqueID;
|
|
45998
|
+
const parts = gateway.pathname.split("/").filter(Boolean);
|
|
45999
|
+
const uniqueID = parts[parts.length - 1];
|
|
46000
|
+
if (!uniqueID) {
|
|
46001
|
+
throw new ConnectionError("Devbox gateway.url does not include uniqueID; cannot derive httpgate endpoint", this.config.baseUrl);
|
|
46002
|
+
}
|
|
46003
|
+
return uniqueID;
|
|
46004
|
+
}
|
|
46005
|
+
getHttpgateDomain(gateway) {
|
|
46006
|
+
if (this.config.httpgateDomain) {
|
|
46007
|
+
return this.normalizeHttpgateDomain(this.config.httpgateDomain);
|
|
46008
|
+
}
|
|
46009
|
+
const prefix = "devbox-gateway.";
|
|
46010
|
+
if (!gateway.host.startsWith(prefix)) {
|
|
46011
|
+
throw new ConnectionError(`Cannot derive httpgate domain from gateway host "${gateway.host}"`, this.config.baseUrl);
|
|
46012
|
+
}
|
|
46013
|
+
return gateway.host.slice(prefix.length);
|
|
46014
|
+
}
|
|
46015
|
+
normalizeHttpgateDomain(domain) {
|
|
46016
|
+
const trimmed = domain.trim().replace(/^\.+|\.+$/g, "");
|
|
46017
|
+
if (!trimmed) {
|
|
46018
|
+
throw new ConnectionError("httpgateDomain is empty", this.config.baseUrl);
|
|
46019
|
+
}
|
|
46020
|
+
if (trimmed.includes("://")) {
|
|
46021
|
+
return new URL(trimmed).host;
|
|
46022
|
+
}
|
|
46023
|
+
return trimmed;
|
|
46024
|
+
}
|
|
45816
46025
|
async ping() {
|
|
45817
46026
|
try {
|
|
45818
46027
|
const res = await this.api.info(this._id);
|
|
45819
46028
|
if (res.code !== 200)
|
|
45820
46029
|
return false;
|
|
46030
|
+
if (!res.data)
|
|
46031
|
+
return false;
|
|
45821
46032
|
return res.data.state.phase === "Running" /* Running */;
|
|
45822
46033
|
} catch {
|
|
45823
46034
|
return false;
|
|
@@ -48069,27 +48280,6 @@ class OpenSandboxAdapter extends BaseSandboxAdapter {
|
|
|
48069
48280
|
message: sdkStatus.message
|
|
48070
48281
|
};
|
|
48071
48282
|
}
|
|
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
48283
|
convertResourceLimits(resourceLimits) {
|
|
48094
48284
|
if (!resourceLimits)
|
|
48095
48285
|
return;
|
|
@@ -48204,7 +48394,7 @@ class OpenSandboxAdapter extends BaseSandboxAdapter {
|
|
|
48204
48394
|
}
|
|
48205
48395
|
try {
|
|
48206
48396
|
this._status = { state: "Creating" };
|
|
48207
|
-
const image =
|
|
48397
|
+
const image = formatImageSpec(cfg.image);
|
|
48208
48398
|
const resource = this.convertResourceLimits(cfg.resourceLimits);
|
|
48209
48399
|
this.sandbox = await Sandbox.create({
|
|
48210
48400
|
connectionConfig: this._connection,
|
|
@@ -48308,7 +48498,18 @@ class OpenSandboxAdapter extends BaseSandboxAdapter {
|
|
|
48308
48498
|
async close() {
|
|
48309
48499
|
await this.sandbox.close();
|
|
48310
48500
|
}
|
|
48311
|
-
async getEndpoint(
|
|
48501
|
+
async getEndpoint(selector) {
|
|
48502
|
+
const port = typeof selector === "number" ? selector : OPEN_SANDBOX_EXECD_PORT;
|
|
48503
|
+
const endpoint = await this.getOpenSandboxEndpoint(port);
|
|
48504
|
+
if (selector === "code-server") {
|
|
48505
|
+
return {
|
|
48506
|
+
...endpoint,
|
|
48507
|
+
url: joinUrlPath(endpoint.url, `/proxy/${OPEN_SANDBOX_CODE_SERVER_PORT}`)
|
|
48508
|
+
};
|
|
48509
|
+
}
|
|
48510
|
+
return endpoint;
|
|
48511
|
+
}
|
|
48512
|
+
async getOpenSandboxEndpoint(port) {
|
|
48312
48513
|
const sdkEndpoint = await this.sandbox.getEndpoint(port);
|
|
48313
48514
|
const raw = sdkEndpoint.endpoint;
|
|
48314
48515
|
const colonIdx = raw.lastIndexOf(":");
|
|
@@ -48327,6 +48528,40 @@ class OpenSandboxAdapter extends BaseSandboxAdapter {
|
|
|
48327
48528
|
url: `https://${raw}`
|
|
48328
48529
|
};
|
|
48329
48530
|
}
|
|
48531
|
+
async getProxyTarget(service = "code-server") {
|
|
48532
|
+
if (service !== "code-server") {
|
|
48533
|
+
throw new FeatureNotSupportedError(`Proxy service "${service}" is not supported by this provider`, "getProxyTarget", this.provider);
|
|
48534
|
+
}
|
|
48535
|
+
return {
|
|
48536
|
+
service,
|
|
48537
|
+
origin: await this.getDirectEndpointOrigin(OPEN_SANDBOX_EXECD_PORT),
|
|
48538
|
+
basePath: `/proxy/${OPEN_SANDBOX_CODE_SERVER_PORT}`,
|
|
48539
|
+
auth: "code-server"
|
|
48540
|
+
};
|
|
48541
|
+
}
|
|
48542
|
+
async getDirectEndpointOrigin(port) {
|
|
48543
|
+
if (!this.id) {
|
|
48544
|
+
throw new SandboxStateError("Sandbox not initialized. Call create() or connect() first.", "UnExist", "Running");
|
|
48545
|
+
}
|
|
48546
|
+
const headers = {
|
|
48547
|
+
...this._connection.headers,
|
|
48548
|
+
Accept: "application/json"
|
|
48549
|
+
};
|
|
48550
|
+
const response = await this._connection.fetch(`${this._connection.getBaseUrl()}/sandboxes/${this.id}/endpoints/${port}?use_server_proxy=false`, { method: "GET", headers });
|
|
48551
|
+
if (!response.ok) {
|
|
48552
|
+
throw new ConnectionError(`OpenSandbox endpoint lookup failed: HTTP ${response.status}`, this.connectionConfig.baseUrl);
|
|
48553
|
+
}
|
|
48554
|
+
const data = await response.json();
|
|
48555
|
+
if (!data.endpoint) {
|
|
48556
|
+
throw new ConnectionError("OpenSandbox returned no endpoint", this.connectionConfig.baseUrl);
|
|
48557
|
+
}
|
|
48558
|
+
let hostPort = data.endpoint.replace(/\/proxy\/\d+\/?$/, "");
|
|
48559
|
+
if (this.connectionConfig.replaceDockerInternalWithLocalhost) {
|
|
48560
|
+
hostPort = hostPort.replace(/^host\.docker\.internal\b/, "localhost");
|
|
48561
|
+
}
|
|
48562
|
+
const url = new URL(/^https?:\/\//.test(hostPort) ? hostPort : `http://${hostPort}`);
|
|
48563
|
+
return url.origin;
|
|
48564
|
+
}
|
|
48330
48565
|
async getInfo() {
|
|
48331
48566
|
if (!this._sandbox) {
|
|
48332
48567
|
return null;
|
|
@@ -48335,7 +48570,7 @@ class OpenSandboxAdapter extends BaseSandboxAdapter {
|
|
|
48335
48570
|
const info = await this.sandbox.getInfo();
|
|
48336
48571
|
return {
|
|
48337
48572
|
id: info.id,
|
|
48338
|
-
image: typeof info.image === "string" ?
|
|
48573
|
+
image: typeof info.image === "string" ? parseImageSpec(info.image) : ("uri" in info.image) ? parseImageSpec(info.image.uri) : info.image,
|
|
48339
48574
|
entrypoint: info.entrypoint,
|
|
48340
48575
|
metadata: info.metadata,
|
|
48341
48576
|
status: this.mapStatus(info.status),
|
|
@@ -48837,7 +49072,7 @@ function createSandbox(provider, config, createConfig) {
|
|
|
48837
49072
|
case "opensandbox":
|
|
48838
49073
|
return new OpenSandboxAdapter(config, createConfig);
|
|
48839
49074
|
case "sealosdevbox":
|
|
48840
|
-
return new SealosDevboxAdapter(config);
|
|
49075
|
+
return new SealosDevboxAdapter(config, createConfig);
|
|
48841
49076
|
case "e2b":
|
|
48842
49077
|
return new E2BAdapter(config);
|
|
48843
49078
|
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
|
}
|
package/dist/types/index.d.ts
CHANGED
|
@@ -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';
|
package/dist/types/sandbox.d.ts
CHANGED
|
@@ -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
|
+
}
|
package/package.json
CHANGED