@fastgpt-sdk/sandbox-adapter 0.0.2 → 0.0.4
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/README.md +11 -146
- package/dist/adapters/BaseSandboxAdapter.d.ts +0 -3
- package/dist/adapters/MinimalProviderAdapter.d.ts +3 -9
- package/dist/adapters/OpenSandboxAdapter.d.ts +6 -159
- package/dist/adapters/SealosDevboxAdapter/api.d.ts +29 -0
- package/dist/adapters/SealosDevboxAdapter/index.d.ts +33 -0
- package/dist/adapters/SealosDevboxAdapter/type.d.ts +107 -0
- package/dist/adapters/index.d.ts +4 -4
- package/dist/index.js +241 -173
- package/dist/interfaces/ISandbox.d.ts +0 -5
- package/dist/interfaces/ISandboxLifecycle.d.ts +0 -10
- package/dist/types/sandbox.d.ts +1 -1
- package/package.json +3 -14
- package/dist/adapters/FastGPTSandboxAdapter/index.d.ts +0 -85
package/dist/index.js
CHANGED
|
@@ -1,8 +1,3 @@
|
|
|
1
|
-
// src/adapters/FastGPTSandboxAdapter/index.ts
|
|
2
|
-
import {
|
|
3
|
-
createSDK
|
|
4
|
-
} from "@fastgpt-sdk/sandbox-server";
|
|
5
|
-
|
|
6
1
|
// src/errors/SandboxException.ts
|
|
7
2
|
class SandboxException extends Error {
|
|
8
3
|
code;
|
|
@@ -413,9 +408,7 @@ POLYFILL_EOF`);
|
|
|
413
408
|
class BaseSandboxAdapter {
|
|
414
409
|
_status = { state: "Creating" };
|
|
415
410
|
polyfillService;
|
|
416
|
-
constructor() {
|
|
417
|
-
this.polyfillService = new CommandPolyfillService(this);
|
|
418
|
-
}
|
|
411
|
+
constructor() {}
|
|
419
412
|
get status() {
|
|
420
413
|
return this._status;
|
|
421
414
|
}
|
|
@@ -633,52 +626,151 @@ class BaseSandboxAdapter {
|
|
|
633
626
|
}
|
|
634
627
|
}
|
|
635
628
|
|
|
636
|
-
// src/adapters/
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
629
|
+
// src/adapters/SealosDevboxAdapter/api.ts
|
|
630
|
+
class DevboxApi {
|
|
631
|
+
baseUrl;
|
|
632
|
+
token;
|
|
633
|
+
constructor(config) {
|
|
634
|
+
this.baseUrl = config.baseUrl.replace(/\/+$/, "");
|
|
635
|
+
this.token = config.token;
|
|
636
|
+
}
|
|
637
|
+
url(path, params) {
|
|
638
|
+
const u = new URL(path, this.baseUrl);
|
|
639
|
+
if (params) {
|
|
640
|
+
for (const [k, v] of Object.entries(params)) {
|
|
641
|
+
u.searchParams.set(k, v);
|
|
642
|
+
}
|
|
643
|
+
}
|
|
644
|
+
return u.toString();
|
|
645
|
+
}
|
|
646
|
+
async request(input, init) {
|
|
647
|
+
const headers = new Headers(init?.headers);
|
|
648
|
+
headers.set("Authorization", `Bearer ${this.token}`);
|
|
649
|
+
if (!headers.has("Content-Type")) {
|
|
650
|
+
headers.set("Content-Type", "application/json");
|
|
651
|
+
}
|
|
652
|
+
const res = await fetch(input, { ...init, headers });
|
|
653
|
+
return await res.json();
|
|
654
|
+
}
|
|
655
|
+
async create(name) {
|
|
656
|
+
return this.request(this.url("/api/v1/devbox"), {
|
|
657
|
+
method: "POST",
|
|
658
|
+
body: JSON.stringify({ name })
|
|
659
|
+
});
|
|
660
|
+
}
|
|
661
|
+
async info(name) {
|
|
662
|
+
return this.request(this.url(`/api/v1/devbox/${name}`), {
|
|
663
|
+
method: "GET"
|
|
664
|
+
});
|
|
665
|
+
}
|
|
666
|
+
async pause(name) {
|
|
667
|
+
return this.request(this.url(`/api/v1/devbox/${name}/pause`), {
|
|
668
|
+
method: "POST"
|
|
669
|
+
});
|
|
670
|
+
}
|
|
671
|
+
async resume(name) {
|
|
672
|
+
return this.request(this.url(`/api/v1/devbox/${name}/resume`), {
|
|
673
|
+
method: "POST"
|
|
674
|
+
});
|
|
675
|
+
}
|
|
676
|
+
async delete(name) {
|
|
677
|
+
return this.request(this.url(`/api/v1/devbox/${name}`), {
|
|
678
|
+
method: "DELETE"
|
|
679
|
+
});
|
|
680
|
+
}
|
|
681
|
+
async exec(name, req) {
|
|
682
|
+
return this.request(this.url(`/api/v1/devbox/${name}/exec`), {
|
|
683
|
+
method: "POST",
|
|
684
|
+
body: JSON.stringify(req)
|
|
685
|
+
});
|
|
686
|
+
}
|
|
687
|
+
async uploadFile(name, params, content) {
|
|
688
|
+
const queryParams = { path: params.path };
|
|
689
|
+
if (params.mode)
|
|
690
|
+
queryParams.mode = params.mode;
|
|
691
|
+
if (params.timeoutSeconds != null)
|
|
692
|
+
queryParams.timeoutSeconds = String(params.timeoutSeconds);
|
|
693
|
+
if (params.container)
|
|
694
|
+
queryParams.container = params.container;
|
|
695
|
+
return this.request(this.url(`/api/v1/devbox/${name}/files/upload`, queryParams), {
|
|
696
|
+
method: "POST",
|
|
697
|
+
headers: { "Content-Type": "application/octet-stream" },
|
|
698
|
+
body: content
|
|
699
|
+
});
|
|
700
|
+
}
|
|
701
|
+
async downloadFile(name, params) {
|
|
702
|
+
const queryParams = { path: params.path };
|
|
703
|
+
if (params.filename)
|
|
704
|
+
queryParams.filename = params.filename;
|
|
705
|
+
if (params.timeoutSeconds != null)
|
|
706
|
+
queryParams.timeoutSeconds = String(params.timeoutSeconds);
|
|
707
|
+
if (params.container)
|
|
708
|
+
queryParams.container = params.container;
|
|
709
|
+
const res = await fetch(this.url(`/api/v1/devbox/${name}/files/download`, queryParams), {
|
|
710
|
+
headers: { Authorization: `Bearer ${this.token}` }
|
|
711
|
+
});
|
|
712
|
+
return res.arrayBuffer();
|
|
650
713
|
}
|
|
651
714
|
}
|
|
652
715
|
|
|
653
|
-
|
|
716
|
+
// src/adapters/SealosDevboxAdapter/index.ts
|
|
717
|
+
class SealosDevboxAdapter extends BaseSandboxAdapter {
|
|
654
718
|
config;
|
|
655
|
-
provider = "
|
|
656
|
-
|
|
657
|
-
_id
|
|
719
|
+
provider = "sealos-devbox";
|
|
720
|
+
api;
|
|
721
|
+
_id;
|
|
658
722
|
constructor(config) {
|
|
659
723
|
super();
|
|
660
724
|
this.config = config;
|
|
661
|
-
this.
|
|
662
|
-
this._id = config.
|
|
725
|
+
this.api = new DevboxApi({ baseUrl: config.baseUrl, token: config.token });
|
|
726
|
+
this._id = config.sandboxId;
|
|
727
|
+
this.polyfillService = new CommandPolyfillService(this);
|
|
663
728
|
}
|
|
664
729
|
get id() {
|
|
665
730
|
return this._id;
|
|
666
731
|
}
|
|
732
|
+
StatusAdapt(data) {
|
|
733
|
+
if (data.deletionTimestamp) {
|
|
734
|
+
return "Deleting";
|
|
735
|
+
}
|
|
736
|
+
switch (data.state.phase) {
|
|
737
|
+
case "Running" /* Running */:
|
|
738
|
+
return "Running";
|
|
739
|
+
case "Pending" /* Pending */:
|
|
740
|
+
return "Creating";
|
|
741
|
+
case "Paused" /* Paused */:
|
|
742
|
+
return "Stopped";
|
|
743
|
+
case "Pausing" /* Pausing */:
|
|
744
|
+
return "Stopping";
|
|
745
|
+
default:
|
|
746
|
+
return "Error";
|
|
747
|
+
}
|
|
748
|
+
}
|
|
749
|
+
async waitUntilDeleted() {
|
|
750
|
+
const startTime = Date.now();
|
|
751
|
+
const checkInterval = 1000;
|
|
752
|
+
while (Date.now() - startTime < 120000) {
|
|
753
|
+
const data = await this.getInfo().catch(() => true);
|
|
754
|
+
if (!data) {
|
|
755
|
+
return;
|
|
756
|
+
}
|
|
757
|
+
await this.sleep(checkInterval);
|
|
758
|
+
}
|
|
759
|
+
throw new TimeoutError("Sandbox not deleted", 120000, "waitUntilDeleted");
|
|
760
|
+
}
|
|
667
761
|
async getInfo() {
|
|
668
762
|
try {
|
|
669
|
-
const
|
|
670
|
-
if (
|
|
763
|
+
const res = await this.api.info(this._id);
|
|
764
|
+
if (res.code !== 200)
|
|
671
765
|
return null;
|
|
672
|
-
|
|
673
|
-
this._status =
|
|
766
|
+
const data = res.data;
|
|
767
|
+
this._status = { state: this.StatusAdapt(data), message: res.message };
|
|
674
768
|
return {
|
|
675
|
-
id:
|
|
676
|
-
image: {
|
|
677
|
-
repository: info.image.imageName
|
|
678
|
-
},
|
|
769
|
+
id: data.name,
|
|
770
|
+
image: { repository: "" },
|
|
679
771
|
entrypoint: [],
|
|
680
|
-
status:
|
|
681
|
-
createdAt:
|
|
772
|
+
status: this._status,
|
|
773
|
+
createdAt: new Date
|
|
682
774
|
};
|
|
683
775
|
} catch (error) {
|
|
684
776
|
throw new CommandExecutionError("Failed to get sandbox info", "getInfo", error instanceof Error ? error : undefined);
|
|
@@ -686,45 +778,48 @@ class FastGPTSandboxAdapter extends BaseSandboxAdapter {
|
|
|
686
778
|
}
|
|
687
779
|
async create(_config) {
|
|
688
780
|
try {
|
|
689
|
-
const
|
|
690
|
-
if (
|
|
691
|
-
|
|
781
|
+
const sandbox = await this.getInfo();
|
|
782
|
+
if (sandbox) {
|
|
783
|
+
const status = sandbox.status.state;
|
|
784
|
+
switch (status) {
|
|
785
|
+
case "Running":
|
|
786
|
+
return;
|
|
787
|
+
case "Creating":
|
|
788
|
+
case "Starting":
|
|
789
|
+
await this.waitUntilReady();
|
|
790
|
+
return;
|
|
791
|
+
case "Stopping":
|
|
792
|
+
case "Stopped":
|
|
793
|
+
await this.start();
|
|
794
|
+
return;
|
|
795
|
+
case "Deleting":
|
|
796
|
+
await this.waitUntilDeleted();
|
|
797
|
+
default:
|
|
798
|
+
throw new ConnectionError(`Failed to create sandbox: ${status}, ${sandbox.status.message}`);
|
|
799
|
+
}
|
|
692
800
|
}
|
|
693
801
|
this._status = { state: "Creating" };
|
|
694
|
-
await this.
|
|
802
|
+
await this.api.create(this._id);
|
|
695
803
|
await this.waitUntilReady();
|
|
696
804
|
this._status = { state: "Running" };
|
|
697
805
|
} catch (error) {
|
|
698
806
|
throw new ConnectionError("Failed to create sandbox", this.config.baseUrl, error);
|
|
699
807
|
}
|
|
700
808
|
}
|
|
701
|
-
async start() {
|
|
702
|
-
try {
|
|
703
|
-
await this.sdk.container.start(this._id);
|
|
704
|
-
this._status = { state: "Running" };
|
|
705
|
-
} catch (error) {
|
|
706
|
-
throw new CommandExecutionError("Failed to start sandbox", "start", error instanceof Error ? error : undefined);
|
|
707
|
-
}
|
|
708
|
-
}
|
|
709
809
|
async stop() {
|
|
710
810
|
try {
|
|
711
|
-
|
|
712
|
-
this.
|
|
713
|
-
|
|
714
|
-
throw new CommandExecutionError("Failed to stop sandbox", "stop", error instanceof Error ? error : undefined);
|
|
715
|
-
}
|
|
716
|
-
}
|
|
717
|
-
async pause() {
|
|
718
|
-
try {
|
|
719
|
-
await this.sdk.container.pause(this._id);
|
|
720
|
-
this._status = { state: "Paused" };
|
|
811
|
+
this._status = { state: "Stopping" };
|
|
812
|
+
await this.api.pause(this._id);
|
|
813
|
+
this._status = { state: "Stopped" };
|
|
721
814
|
} catch (error) {
|
|
722
815
|
throw new CommandExecutionError("Failed to pause sandbox", "pause", error instanceof Error ? error : undefined);
|
|
723
816
|
}
|
|
724
817
|
}
|
|
725
|
-
async
|
|
818
|
+
async start() {
|
|
726
819
|
try {
|
|
727
|
-
|
|
820
|
+
this._status = { state: "Starting" };
|
|
821
|
+
await this.api.resume(this._id);
|
|
822
|
+
await this.waitUntilReady();
|
|
728
823
|
this._status = { state: "Running" };
|
|
729
824
|
} catch (error) {
|
|
730
825
|
throw new CommandExecutionError("Failed to resume sandbox", "resume", error instanceof Error ? error : undefined);
|
|
@@ -732,26 +827,26 @@ class FastGPTSandboxAdapter extends BaseSandboxAdapter {
|
|
|
732
827
|
}
|
|
733
828
|
async delete() {
|
|
734
829
|
try {
|
|
735
|
-
|
|
736
|
-
this.
|
|
830
|
+
this._status = { state: "Deleting" };
|
|
831
|
+
await this.api.delete(this._id);
|
|
832
|
+
await this.waitUntilDeleted();
|
|
833
|
+
this._status = { state: "UnExist" };
|
|
737
834
|
} catch (error) {
|
|
738
835
|
throw new CommandExecutionError("Failed to delete sandbox", "delete", error instanceof Error ? error : undefined);
|
|
739
836
|
}
|
|
740
837
|
}
|
|
741
|
-
async close() {
|
|
742
|
-
return this.delete();
|
|
743
|
-
}
|
|
744
838
|
async execute(command, options) {
|
|
745
839
|
try {
|
|
746
840
|
await this.waitUntilReady();
|
|
747
|
-
const
|
|
748
|
-
|
|
749
|
-
|
|
841
|
+
const cmd = options?.workingDirectory ? ["sh", "-lc", `cd ${options.workingDirectory} && ${command}`] : ["sh", "-lc", command];
|
|
842
|
+
const res = await this.api.exec(this._id, {
|
|
843
|
+
command: cmd,
|
|
844
|
+
timeoutSeconds: options?.timeoutMs ? Math.ceil(options.timeoutMs / 1000) : undefined
|
|
750
845
|
});
|
|
751
846
|
return {
|
|
752
|
-
stdout:
|
|
753
|
-
stderr:
|
|
754
|
-
exitCode:
|
|
847
|
+
stdout: res.data.stdout,
|
|
848
|
+
stderr: res.data.stderr,
|
|
849
|
+
exitCode: res.data.exitCode
|
|
755
850
|
};
|
|
756
851
|
} catch (error) {
|
|
757
852
|
throw new CommandExecutionError(`Command execution failed: ${command}`, command, error instanceof Error ? error : undefined);
|
|
@@ -759,7 +854,10 @@ class FastGPTSandboxAdapter extends BaseSandboxAdapter {
|
|
|
759
854
|
}
|
|
760
855
|
async ping() {
|
|
761
856
|
try {
|
|
762
|
-
|
|
857
|
+
const res = await this.api.info(this._id);
|
|
858
|
+
if (res.code !== 200)
|
|
859
|
+
return false;
|
|
860
|
+
return res.data.state.phase === "Running" /* Running */;
|
|
763
861
|
} catch {
|
|
764
862
|
return false;
|
|
765
863
|
}
|
|
@@ -775,23 +873,26 @@ class MinimalProviderAdapter extends BaseSandboxAdapter {
|
|
|
775
873
|
constructor(config) {
|
|
776
874
|
super();
|
|
777
875
|
this.config = config;
|
|
876
|
+
this.polyfillService = new CommandPolyfillService(this);
|
|
778
877
|
}
|
|
779
878
|
get id() {
|
|
780
879
|
return this._id;
|
|
781
880
|
}
|
|
782
|
-
get status() {
|
|
783
|
-
return this._status;
|
|
784
|
-
}
|
|
785
881
|
async create(config) {
|
|
786
|
-
if (this.config?.connectionFactory) {
|
|
882
|
+
if (!this.config?.connectionFactory) {
|
|
883
|
+
throw new ConnectionError("Connection factory not provided");
|
|
884
|
+
}
|
|
885
|
+
try {
|
|
886
|
+
this._status = { state: "Creating" };
|
|
787
887
|
this.connection = await this.config.connectionFactory();
|
|
788
888
|
this._id = this.connection.id;
|
|
789
889
|
this._status = { state: "Running" };
|
|
790
890
|
if (config.entrypoint && config.entrypoint.length > 0) {
|
|
791
891
|
await this.execute(config.entrypoint.join(" "));
|
|
792
892
|
}
|
|
793
|
-
}
|
|
794
|
-
|
|
893
|
+
} catch (error) {
|
|
894
|
+
this._status = { state: "Error", message: String(error) };
|
|
895
|
+
throw new ConnectionError("Failed to create sandbox", undefined, error);
|
|
795
896
|
}
|
|
796
897
|
}
|
|
797
898
|
async connect(connection) {
|
|
@@ -804,19 +905,16 @@ class MinimalProviderAdapter extends BaseSandboxAdapter {
|
|
|
804
905
|
}
|
|
805
906
|
async stop() {
|
|
806
907
|
await this.execute("exit 0").catch(() => {});
|
|
807
|
-
this.
|
|
808
|
-
|
|
809
|
-
async pause() {
|
|
810
|
-
throw new FeatureNotSupportedError("Pause not supported by minimal provider", "pause", this.provider);
|
|
811
|
-
}
|
|
812
|
-
async resume() {
|
|
813
|
-
throw new FeatureNotSupportedError("Resume not supported by minimal provider", "resume", this.provider);
|
|
908
|
+
await this.connection?.close();
|
|
909
|
+
this._status = { state: "Stopped" };
|
|
814
910
|
}
|
|
815
911
|
async delete() {
|
|
816
912
|
await this.stop();
|
|
817
|
-
|
|
913
|
+
this._status = { state: "UnExist" };
|
|
818
914
|
}
|
|
819
915
|
async getInfo() {
|
|
916
|
+
if (!this.connection)
|
|
917
|
+
return null;
|
|
820
918
|
return {
|
|
821
919
|
id: this._id,
|
|
822
920
|
image: { repository: "minimal", tag: "latest" },
|
|
@@ -825,9 +923,6 @@ class MinimalProviderAdapter extends BaseSandboxAdapter {
|
|
|
825
923
|
createdAt: new Date
|
|
826
924
|
};
|
|
827
925
|
}
|
|
828
|
-
async close() {
|
|
829
|
-
await this.connection?.close();
|
|
830
|
-
}
|
|
831
926
|
async execute(command, options) {
|
|
832
927
|
if (!this.connection) {
|
|
833
928
|
throw new Error("Not connected to minimal provider");
|
|
@@ -862,7 +957,6 @@ class OpenSandboxAdapter extends BaseSandboxAdapter {
|
|
|
862
957
|
_sandbox;
|
|
863
958
|
_connection;
|
|
864
959
|
_id = "";
|
|
865
|
-
_connectionState = "disconnected";
|
|
866
960
|
constructor(connectionConfig = {}) {
|
|
867
961
|
super();
|
|
868
962
|
this.connectionConfig = connectionConfig;
|
|
@@ -872,12 +966,9 @@ class OpenSandboxAdapter extends BaseSandboxAdapter {
|
|
|
872
966
|
get id() {
|
|
873
967
|
return this._id;
|
|
874
968
|
}
|
|
875
|
-
get connectionState() {
|
|
876
|
-
return this._connectionState;
|
|
877
|
-
}
|
|
878
969
|
get sandbox() {
|
|
879
970
|
if (!this._sandbox) {
|
|
880
|
-
throw new SandboxStateError("Sandbox not initialized. Call create() or connect() first.",
|
|
971
|
+
throw new SandboxStateError("Sandbox not initialized. Call create() or connect() first.", "UnExist", "Running");
|
|
881
972
|
}
|
|
882
973
|
return this._sandbox;
|
|
883
974
|
}
|
|
@@ -891,6 +982,25 @@ class OpenSandboxAdapter extends BaseSandboxAdapter {
|
|
|
891
982
|
apiKey
|
|
892
983
|
});
|
|
893
984
|
}
|
|
985
|
+
static STATE_MAP = {
|
|
986
|
+
running: "Running",
|
|
987
|
+
creating: "Creating",
|
|
988
|
+
starting: "Starting",
|
|
989
|
+
stopping: "Stopping",
|
|
990
|
+
stopped: "Stopped",
|
|
991
|
+
deleting: "Deleting",
|
|
992
|
+
error: "Error",
|
|
993
|
+
paused: "Stopped",
|
|
994
|
+
deleted: "UnExist"
|
|
995
|
+
};
|
|
996
|
+
mapStatus(sdkStatus) {
|
|
997
|
+
const state = OpenSandboxAdapter.STATE_MAP[sdkStatus.state.toLowerCase()] ?? "Error";
|
|
998
|
+
return {
|
|
999
|
+
state,
|
|
1000
|
+
reason: sdkStatus.reason,
|
|
1001
|
+
message: sdkStatus.message
|
|
1002
|
+
};
|
|
1003
|
+
}
|
|
894
1004
|
convertImageSpec(image) {
|
|
895
1005
|
const parts = [image.repository];
|
|
896
1006
|
if (image.tag) {
|
|
@@ -904,22 +1014,17 @@ class OpenSandboxAdapter extends BaseSandboxAdapter {
|
|
|
904
1014
|
parseImageSpec(image) {
|
|
905
1015
|
const atIndex = image.indexOf("@");
|
|
906
1016
|
if (atIndex > -1) {
|
|
907
|
-
|
|
908
|
-
const digest = image.slice(atIndex + 1);
|
|
909
|
-
return { repository, digest };
|
|
1017
|
+
return { repository: image.slice(0, atIndex), digest: image.slice(atIndex + 1) };
|
|
910
1018
|
}
|
|
911
1019
|
const colonIndex = image.indexOf(":");
|
|
912
1020
|
if (colonIndex > -1) {
|
|
913
|
-
|
|
914
|
-
const tag = image.slice(colonIndex + 1);
|
|
915
|
-
return { repository, tag };
|
|
1021
|
+
return { repository: image.slice(0, colonIndex), tag: image.slice(colonIndex + 1) };
|
|
916
1022
|
}
|
|
917
1023
|
return { repository: image };
|
|
918
1024
|
}
|
|
919
1025
|
convertResourceLimits(resourceLimits) {
|
|
920
|
-
if (!resourceLimits)
|
|
1026
|
+
if (!resourceLimits)
|
|
921
1027
|
return;
|
|
922
|
-
}
|
|
923
1028
|
const result = {};
|
|
924
1029
|
if (resourceLimits.cpuCount !== undefined) {
|
|
925
1030
|
result.cpu = resourceLimits.cpuCount.toString();
|
|
@@ -933,42 +1038,35 @@ class OpenSandboxAdapter extends BaseSandboxAdapter {
|
|
|
933
1038
|
return result;
|
|
934
1039
|
}
|
|
935
1040
|
parseResourceLimits(resource) {
|
|
936
|
-
if (!resource)
|
|
1041
|
+
if (!resource)
|
|
937
1042
|
return;
|
|
938
|
-
}
|
|
939
1043
|
const result = {};
|
|
940
1044
|
const cpu = resource.cpu;
|
|
941
1045
|
if (cpu) {
|
|
942
1046
|
const cpuCount = Number.parseInt(cpu, 10);
|
|
943
|
-
if (!Number.isNaN(cpuCount))
|
|
1047
|
+
if (!Number.isNaN(cpuCount))
|
|
944
1048
|
result.cpuCount = cpuCount;
|
|
945
|
-
}
|
|
946
1049
|
}
|
|
947
1050
|
const memory = resource.memory;
|
|
948
1051
|
if (memory) {
|
|
949
1052
|
const match = memory.match(/^(\d+)(Mi|Gi)$/);
|
|
950
1053
|
if (match) {
|
|
951
1054
|
const value = Number.parseInt(match[1] || "0", 10);
|
|
952
|
-
|
|
953
|
-
result.memoryMiB = value;
|
|
954
|
-
} else {
|
|
955
|
-
result.memoryMiB = value * 1024;
|
|
956
|
-
}
|
|
1055
|
+
result.memoryMiB = match[2] === "Mi" ? value : value * 1024;
|
|
957
1056
|
}
|
|
958
1057
|
}
|
|
959
1058
|
const disk = resource.disk;
|
|
960
1059
|
if (disk) {
|
|
961
1060
|
const match = disk.match(/^(\d+)Gi$/);
|
|
962
1061
|
if (match) {
|
|
963
|
-
|
|
964
|
-
result.diskGiB = value;
|
|
1062
|
+
result.diskGiB = Number.parseInt(match[1] || "0", 10);
|
|
965
1063
|
}
|
|
966
1064
|
}
|
|
967
1065
|
return result;
|
|
968
1066
|
}
|
|
969
1067
|
async create(config) {
|
|
970
|
-
this._connectionState = "connecting";
|
|
971
1068
|
try {
|
|
1069
|
+
this._status = { state: "Creating" };
|
|
972
1070
|
const image = this.convertImageSpec(config.image);
|
|
973
1071
|
const resource = this.convertResourceLimits(config.resourceLimits);
|
|
974
1072
|
this._sandbox = await Sandbox.create({
|
|
@@ -982,74 +1080,61 @@ class OpenSandboxAdapter extends BaseSandboxAdapter {
|
|
|
982
1080
|
});
|
|
983
1081
|
this._id = this._sandbox.id;
|
|
984
1082
|
this._status = { state: "Running" };
|
|
985
|
-
this._connectionState = "connected";
|
|
986
1083
|
} catch (error) {
|
|
987
|
-
this.
|
|
1084
|
+
this._status = { state: "Error", message: String(error) };
|
|
988
1085
|
throw new ConnectionError("Failed to create sandbox", this.connectionConfig.baseUrl, error);
|
|
989
1086
|
}
|
|
990
1087
|
}
|
|
991
1088
|
async connect(sandboxId) {
|
|
992
|
-
this._connectionState = "connecting";
|
|
993
1089
|
try {
|
|
1090
|
+
this._status = { state: "Starting" };
|
|
994
1091
|
this._sandbox = await Sandbox.connect({
|
|
995
1092
|
sandboxId,
|
|
996
1093
|
connectionConfig: this._connection
|
|
997
1094
|
});
|
|
998
1095
|
this._id = this._sandbox.id;
|
|
999
1096
|
this._status = { state: "Running" };
|
|
1000
|
-
this._connectionState = "connected";
|
|
1001
1097
|
} catch (error) {
|
|
1002
|
-
this.
|
|
1098
|
+
this._status = { state: "Error", message: String(error) };
|
|
1003
1099
|
throw new ConnectionError(`Failed to connect to sandbox ${sandboxId}`, this.connectionConfig.baseUrl, error);
|
|
1004
1100
|
}
|
|
1005
1101
|
}
|
|
1006
1102
|
async start() {
|
|
1007
|
-
if (this._status.state === "Paused") {
|
|
1008
|
-
await this.resume();
|
|
1009
|
-
}
|
|
1010
|
-
}
|
|
1011
|
-
async stop() {
|
|
1012
|
-
try {
|
|
1013
|
-
await this.sandbox.kill();
|
|
1014
|
-
this._status = { state: "Deleted" };
|
|
1015
|
-
this._connectionState = "disconnected";
|
|
1016
|
-
} catch (error) {
|
|
1017
|
-
throw new CommandExecutionError("Failed to stop sandbox", "stop", error instanceof Error ? error : undefined);
|
|
1018
|
-
}
|
|
1019
|
-
}
|
|
1020
|
-
async pause() {
|
|
1021
1103
|
try {
|
|
1022
|
-
|
|
1023
|
-
this.
|
|
1104
|
+
this._status = { state: "Starting" };
|
|
1105
|
+
this._sandbox = await this.sandbox.resume();
|
|
1106
|
+
this._id = this.sandbox.id;
|
|
1107
|
+
this._status = { state: "Running" };
|
|
1024
1108
|
} catch (error) {
|
|
1025
1109
|
if (error && typeof error === "object" && "code" in error && error.code === "SANDBOX::API_NOT_SUPPORTED") {
|
|
1026
|
-
throw new FeatureNotSupportedError("
|
|
1110
|
+
throw new FeatureNotSupportedError("Start/resume not supported by this runtime", "start", this.provider);
|
|
1027
1111
|
}
|
|
1028
|
-
throw new CommandExecutionError("Failed to
|
|
1112
|
+
throw new CommandExecutionError("Failed to start sandbox", "start", error instanceof Error ? error : undefined);
|
|
1029
1113
|
}
|
|
1030
1114
|
}
|
|
1031
|
-
async
|
|
1115
|
+
async stop() {
|
|
1032
1116
|
try {
|
|
1033
|
-
this.
|
|
1034
|
-
|
|
1035
|
-
this._status = { state: "
|
|
1117
|
+
this._status = { state: "Stopping" };
|
|
1118
|
+
await this.sandbox.kill();
|
|
1119
|
+
this._status = { state: "Stopped" };
|
|
1036
1120
|
} catch (error) {
|
|
1037
|
-
|
|
1038
|
-
throw new FeatureNotSupportedError("Resume operation is not supported by this runtime (e.g., Kubernetes)", "resume", this.provider);
|
|
1039
|
-
}
|
|
1040
|
-
throw new CommandExecutionError("Failed to resume sandbox", "resume", error instanceof Error ? error : undefined);
|
|
1121
|
+
throw new CommandExecutionError("Failed to stop sandbox", "stop", error instanceof Error ? error : undefined);
|
|
1041
1122
|
}
|
|
1042
1123
|
}
|
|
1043
1124
|
async delete() {
|
|
1044
1125
|
try {
|
|
1126
|
+
this._status = { state: "Deleting" };
|
|
1045
1127
|
await this.sandbox.kill();
|
|
1046
|
-
this.
|
|
1047
|
-
this.
|
|
1128
|
+
this._sandbox = undefined;
|
|
1129
|
+
this._id = "";
|
|
1130
|
+
this._status = { state: "UnExist" };
|
|
1048
1131
|
} catch (error) {
|
|
1049
1132
|
throw new CommandExecutionError("Failed to delete sandbox", "delete", error instanceof Error ? error : undefined);
|
|
1050
1133
|
}
|
|
1051
1134
|
}
|
|
1052
1135
|
async getInfo() {
|
|
1136
|
+
if (!this._sandbox)
|
|
1137
|
+
return null;
|
|
1053
1138
|
try {
|
|
1054
1139
|
const info = await this.sandbox.getInfo();
|
|
1055
1140
|
return {
|
|
@@ -1057,7 +1142,7 @@ class OpenSandboxAdapter extends BaseSandboxAdapter {
|
|
|
1057
1142
|
image: typeof info.image === "string" ? this.parseImageSpec(info.image) : ("uri" in info.image) ? this.parseImageSpec(info.image.uri) : info.image,
|
|
1058
1143
|
entrypoint: info.entrypoint,
|
|
1059
1144
|
metadata: info.metadata,
|
|
1060
|
-
status: info.status,
|
|
1145
|
+
status: this.mapStatus(info.status),
|
|
1061
1146
|
createdAt: info.createdAt,
|
|
1062
1147
|
expiresAt: info.expiresAt,
|
|
1063
1148
|
resourceLimits: this.parseResourceLimits(info.resourceLimits)
|
|
@@ -1066,16 +1151,6 @@ class OpenSandboxAdapter extends BaseSandboxAdapter {
|
|
|
1066
1151
|
throw new CommandExecutionError("Failed to get sandbox info", "getInfo", error instanceof Error ? error : undefined);
|
|
1067
1152
|
}
|
|
1068
1153
|
}
|
|
1069
|
-
async close() {
|
|
1070
|
-
try {
|
|
1071
|
-
await this._sandbox?.close();
|
|
1072
|
-
} finally {
|
|
1073
|
-
this._sandbox = undefined;
|
|
1074
|
-
this._id = "";
|
|
1075
|
-
this._connectionState = "closed";
|
|
1076
|
-
this._status = { state: "Deleted" };
|
|
1077
|
-
}
|
|
1078
|
-
}
|
|
1079
1154
|
async renewExpiration(additionalSeconds) {
|
|
1080
1155
|
try {
|
|
1081
1156
|
await this.sandbox.renew(additionalSeconds);
|
|
@@ -1098,16 +1173,10 @@ class OpenSandboxAdapter extends BaseSandboxAdapter {
|
|
|
1098
1173
|
const stderrLength = execution.logs.stderr.reduce((sum, msg) => sum + msg.text.length, 0);
|
|
1099
1174
|
const MaxOutputSize = 1024 * 1024;
|
|
1100
1175
|
const truncated = stdoutLength >= MaxOutputSize || stderrLength >= MaxOutputSize;
|
|
1101
|
-
return {
|
|
1102
|
-
stdout,
|
|
1103
|
-
stderr,
|
|
1104
|
-
exitCode,
|
|
1105
|
-
truncated
|
|
1106
|
-
};
|
|
1176
|
+
return { stdout, stderr, exitCode, truncated };
|
|
1107
1177
|
} catch (error) {
|
|
1108
|
-
if (error instanceof SandboxStateError)
|
|
1178
|
+
if (error instanceof SandboxStateError)
|
|
1109
1179
|
throw error;
|
|
1110
|
-
}
|
|
1111
1180
|
throw new CommandExecutionError(`Command execution failed: ${command}`, command, error instanceof Error ? error : undefined);
|
|
1112
1181
|
}
|
|
1113
1182
|
}
|
|
@@ -1170,9 +1239,8 @@ class OpenSandboxAdapter extends BaseSandboxAdapter {
|
|
|
1170
1239
|
}
|
|
1171
1240
|
};
|
|
1172
1241
|
} catch (error) {
|
|
1173
|
-
if (error instanceof CommandExecutionError)
|
|
1242
|
+
if (error instanceof CommandExecutionError)
|
|
1174
1243
|
throw error;
|
|
1175
|
-
}
|
|
1176
1244
|
throw new CommandExecutionError(`Background command execution failed: ${command}`, command, error instanceof Error ? error : undefined);
|
|
1177
1245
|
}
|
|
1178
1246
|
}
|
|
@@ -1202,8 +1270,8 @@ var createSandbox = ({ provider, config }) => {
|
|
|
1202
1270
|
return new OpenSandboxAdapter(config);
|
|
1203
1271
|
case "minimal":
|
|
1204
1272
|
return new MinimalProviderAdapter(config);
|
|
1205
|
-
case "
|
|
1206
|
-
return new
|
|
1273
|
+
case "sealos-devbox":
|
|
1274
|
+
return new SealosDevboxAdapter(config);
|
|
1207
1275
|
default:
|
|
1208
1276
|
throw new Error(`Unknown provider: ${provider}`);
|
|
1209
1277
|
}
|
|
@@ -1211,6 +1279,7 @@ var createSandbox = ({ provider, config }) => {
|
|
|
1211
1279
|
export {
|
|
1212
1280
|
createSandbox,
|
|
1213
1281
|
TimeoutError,
|
|
1282
|
+
SealosDevboxAdapter,
|
|
1214
1283
|
SandboxStateError,
|
|
1215
1284
|
SandboxReadyTimeoutError,
|
|
1216
1285
|
SandboxException,
|
|
@@ -1218,7 +1287,6 @@ export {
|
|
|
1218
1287
|
MinimalProviderAdapter,
|
|
1219
1288
|
FileOperationError,
|
|
1220
1289
|
FeatureNotSupportedError,
|
|
1221
|
-
FastGPTSandboxAdapter,
|
|
1222
1290
|
ConnectionError,
|
|
1223
1291
|
CommandExecutionError,
|
|
1224
1292
|
BaseSandboxAdapter
|