@alibaba-group/opensandbox 0.1.3 → 0.1.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +76 -32
- package/dist/{chunk-4EF4ODU2.js → chunk-XHEWHFQ6.js} +184 -22
- package/dist/chunk-XHEWHFQ6.js.map +1 -0
- package/dist/cjs/index.cjs +345 -41
- package/dist/cjs/index.cjs.map +1 -1
- package/dist/cjs/internal.cjs +182 -21
- package/dist/cjs/internal.cjs.map +1 -1
- package/dist/index.d.ts +47 -7
- package/dist/index.js +163 -20
- package/dist/index.js.map +1 -1
- package/dist/internal.d.ts +365 -9
- package/dist/internal.js +1 -1
- package/dist/{sandboxes-CLy12BN1.d.ts → sandboxes-DL9uUHGR.d.ts} +296 -134
- package/package.json +3 -2
- package/src/adapters/commandsAdapter.ts +250 -23
- package/src/adapters/egressAdapter.ts +46 -0
- package/src/adapters/sandboxesAdapter.ts +14 -5
- package/src/api/egress.ts +184 -0
- package/src/api/execd.ts +223 -0
- package/src/api/lifecycle.ts +122 -7
- package/src/config/connection.ts +11 -0
- package/src/core/constants.ts +2 -1
- package/src/core/exceptions.ts +5 -4
- package/src/factory/adapterFactory.ts +14 -1
- package/src/factory/defaultAdapterFactory.ts +34 -5
- package/src/index.ts +8 -2
- package/src/models/execd.ts +32 -6
- package/src/models/execution.ts +2 -1
- package/src/models/sandboxes.ts +121 -4
- package/src/openapi/egressClient.ts +45 -0
- package/src/sandbox.ts +114 -12
- package/src/services/egress.ts +27 -0
- package/src/services/execdCommands.ts +41 -2
- package/src/services/sandboxes.ts +6 -2
- package/dist/chunk-4EF4ODU2.js.map +0 -1
package/dist/cjs/internal.cjs
CHANGED
|
@@ -90,25 +90,26 @@ var SandboxException = class extends Error {
|
|
|
90
90
|
name = "SandboxException";
|
|
91
91
|
error;
|
|
92
92
|
cause;
|
|
93
|
+
requestId;
|
|
93
94
|
constructor(opts = {}) {
|
|
94
95
|
super(opts.message);
|
|
95
96
|
this.cause = opts.cause;
|
|
96
97
|
this.error = opts.error ?? new SandboxError(SandboxError.INTERNAL_UNKNOWN_ERROR);
|
|
98
|
+
this.requestId = opts.requestId;
|
|
97
99
|
}
|
|
98
100
|
};
|
|
99
101
|
var SandboxApiException = class extends SandboxException {
|
|
100
102
|
name = "SandboxApiException";
|
|
101
103
|
statusCode;
|
|
102
|
-
requestId;
|
|
103
104
|
rawBody;
|
|
104
105
|
constructor(opts) {
|
|
105
106
|
super({
|
|
106
107
|
message: opts.message,
|
|
107
108
|
cause: opts.cause,
|
|
108
|
-
error: opts.error ?? new SandboxError(SandboxError.UNEXPECTED_RESPONSE, opts.message)
|
|
109
|
+
error: opts.error ?? new SandboxError(SandboxError.UNEXPECTED_RESPONSE, opts.message),
|
|
110
|
+
requestId: opts.requestId
|
|
109
111
|
});
|
|
110
112
|
this.statusCode = opts.statusCode;
|
|
111
|
-
this.requestId = opts.requestId;
|
|
112
113
|
this.rawBody = opts.rawBody;
|
|
113
114
|
}
|
|
114
115
|
};
|
|
@@ -153,11 +154,15 @@ var SandboxesAdapter = class {
|
|
|
153
154
|
}
|
|
154
155
|
return d;
|
|
155
156
|
}
|
|
157
|
+
parseOptionalIsoDate(field, v) {
|
|
158
|
+
if (v == null) return null;
|
|
159
|
+
return this.parseIsoDate(field, v);
|
|
160
|
+
}
|
|
156
161
|
mapSandboxInfo(raw) {
|
|
157
162
|
return {
|
|
158
163
|
...raw ?? {},
|
|
159
164
|
createdAt: this.parseIsoDate("createdAt", raw?.createdAt),
|
|
160
|
-
expiresAt: this.
|
|
165
|
+
expiresAt: this.parseOptionalIsoDate("expiresAt", raw?.expiresAt)
|
|
161
166
|
};
|
|
162
167
|
}
|
|
163
168
|
async createSandbox(req) {
|
|
@@ -173,7 +178,7 @@ var SandboxesAdapter = class {
|
|
|
173
178
|
return {
|
|
174
179
|
...raw ?? {},
|
|
175
180
|
createdAt: this.parseIsoDate("createdAt", raw?.createdAt),
|
|
176
|
-
expiresAt: this.
|
|
181
|
+
expiresAt: this.parseOptionalIsoDate("expiresAt", raw?.expiresAt)
|
|
177
182
|
};
|
|
178
183
|
}
|
|
179
184
|
async getSandbox(sandboxId) {
|
|
@@ -244,9 +249,9 @@ var SandboxesAdapter = class {
|
|
|
244
249
|
expiresAt: raw?.expiresAt ? this.parseIsoDate("expiresAt", raw.expiresAt) : void 0
|
|
245
250
|
};
|
|
246
251
|
}
|
|
247
|
-
async getSandboxEndpoint(sandboxId, port) {
|
|
252
|
+
async getSandboxEndpoint(sandboxId, port, useServerProxy = false) {
|
|
248
253
|
const { data, error, response } = await this.client.GET("/sandboxes/{sandboxId}/endpoints/{port}", {
|
|
249
|
-
params: { path: { sandboxId, port } }
|
|
254
|
+
params: { path: { sandboxId, port }, query: { use_server_proxy: useServerProxy } }
|
|
250
255
|
});
|
|
251
256
|
throwOnOpenApiFetchError({ error, response }, "Get sandbox endpoint failed");
|
|
252
257
|
const ok = data;
|
|
@@ -858,11 +863,61 @@ function joinUrl2(baseUrl, pathname) {
|
|
|
858
863
|
return `${base}${path}`;
|
|
859
864
|
}
|
|
860
865
|
function toRunCommandRequest(command, opts) {
|
|
861
|
-
|
|
866
|
+
if (opts?.gid != null && opts.uid == null) {
|
|
867
|
+
throw new Error("uid is required when gid is provided");
|
|
868
|
+
}
|
|
869
|
+
const body = {
|
|
862
870
|
command,
|
|
863
871
|
cwd: opts?.workingDirectory,
|
|
864
872
|
background: !!opts?.background
|
|
865
873
|
};
|
|
874
|
+
if (opts?.timeoutSeconds != null) {
|
|
875
|
+
body.timeout = Math.round(opts.timeoutSeconds * 1e3);
|
|
876
|
+
}
|
|
877
|
+
if (opts?.uid != null) {
|
|
878
|
+
body.uid = opts.uid;
|
|
879
|
+
}
|
|
880
|
+
if (opts?.gid != null) {
|
|
881
|
+
body.gid = opts.gid;
|
|
882
|
+
}
|
|
883
|
+
if (opts?.envs != null) {
|
|
884
|
+
body.envs = opts.envs;
|
|
885
|
+
}
|
|
886
|
+
return body;
|
|
887
|
+
}
|
|
888
|
+
function toRunInSessionRequest(command, opts) {
|
|
889
|
+
const body = {
|
|
890
|
+
command
|
|
891
|
+
};
|
|
892
|
+
if (opts?.workingDirectory != null) {
|
|
893
|
+
body.cwd = opts.workingDirectory;
|
|
894
|
+
}
|
|
895
|
+
if (opts?.timeout != null) {
|
|
896
|
+
body.timeout = opts.timeout;
|
|
897
|
+
}
|
|
898
|
+
return body;
|
|
899
|
+
}
|
|
900
|
+
function inferForegroundExitCode(execution) {
|
|
901
|
+
const errorValue = execution.error?.value?.trim();
|
|
902
|
+
const parsedExitCode = errorValue && /^-?\d+$/.test(errorValue) ? Number(errorValue) : Number.NaN;
|
|
903
|
+
return execution.error != null ? Number.isFinite(parsedExitCode) ? parsedExitCode : null : execution.complete ? 0 : null;
|
|
904
|
+
}
|
|
905
|
+
function assertNonBlank(value, field) {
|
|
906
|
+
if (!value.trim()) {
|
|
907
|
+
throw new Error(`${field} cannot be empty`);
|
|
908
|
+
}
|
|
909
|
+
}
|
|
910
|
+
function parseOptionalDate(value, field) {
|
|
911
|
+
if (value == null) return void 0;
|
|
912
|
+
if (value instanceof Date) return value;
|
|
913
|
+
if (typeof value !== "string") {
|
|
914
|
+
throw new Error(`Invalid ${field}: expected ISO string, got ${typeof value}`);
|
|
915
|
+
}
|
|
916
|
+
const parsed = new Date(value);
|
|
917
|
+
if (Number.isNaN(parsed.getTime())) {
|
|
918
|
+
throw new Error(`Invalid ${field}: ${value}`);
|
|
919
|
+
}
|
|
920
|
+
return parsed;
|
|
866
921
|
}
|
|
867
922
|
var CommandsAdapter = class {
|
|
868
923
|
constructor(client, opts) {
|
|
@@ -871,43 +926,149 @@ var CommandsAdapter = class {
|
|
|
871
926
|
this.fetch = opts.fetch ?? fetch;
|
|
872
927
|
}
|
|
873
928
|
fetch;
|
|
874
|
-
|
|
875
|
-
|
|
876
|
-
|
|
877
|
-
|
|
878
|
-
|
|
929
|
+
buildRunStreamSpec(command, opts) {
|
|
930
|
+
assertNonBlank(command, "command");
|
|
931
|
+
return {
|
|
932
|
+
pathname: "/command",
|
|
933
|
+
body: toRunCommandRequest(command, opts),
|
|
934
|
+
fallbackErrorMessage: "Run command failed"
|
|
935
|
+
};
|
|
879
936
|
}
|
|
880
|
-
|
|
881
|
-
|
|
882
|
-
|
|
937
|
+
buildRunInSessionStreamSpec(sessionId, command, opts) {
|
|
938
|
+
assertNonBlank(sessionId, "sessionId");
|
|
939
|
+
assertNonBlank(command, "command");
|
|
940
|
+
return {
|
|
941
|
+
pathname: `/session/${encodeURIComponent(sessionId)}/run`,
|
|
942
|
+
body: toRunInSessionRequest(command, opts),
|
|
943
|
+
fallbackErrorMessage: "Run in session failed"
|
|
944
|
+
};
|
|
945
|
+
}
|
|
946
|
+
async *streamExecution(spec, signal) {
|
|
947
|
+
const url = joinUrl2(this.opts.baseUrl, spec.pathname);
|
|
883
948
|
const res = await this.fetch(url, {
|
|
884
949
|
method: "POST",
|
|
885
950
|
headers: {
|
|
886
|
-
|
|
951
|
+
accept: "text/event-stream",
|
|
887
952
|
"content-type": "application/json",
|
|
888
953
|
...this.opts.headers ?? {}
|
|
889
954
|
},
|
|
890
|
-
body,
|
|
955
|
+
body: JSON.stringify(spec.body),
|
|
891
956
|
signal
|
|
892
957
|
});
|
|
893
|
-
for await (const ev of parseJsonEventStream(res, {
|
|
958
|
+
for await (const ev of parseJsonEventStream(res, {
|
|
959
|
+
fallbackErrorMessage: spec.fallbackErrorMessage
|
|
960
|
+
})) {
|
|
894
961
|
yield ev;
|
|
895
962
|
}
|
|
896
963
|
}
|
|
897
|
-
async
|
|
964
|
+
async consumeExecutionStream(stream, handlers, inferExitCode = false) {
|
|
898
965
|
const execution = {
|
|
899
966
|
logs: { stdout: [], stderr: [] },
|
|
900
967
|
result: []
|
|
901
968
|
};
|
|
902
969
|
const dispatcher = new ExecutionEventDispatcher(execution, handlers);
|
|
903
|
-
for await (const ev of
|
|
970
|
+
for await (const ev of stream) {
|
|
904
971
|
if (ev.type === "init" && (ev.text ?? "") === "" && execution.id) {
|
|
905
972
|
ev.text = execution.id;
|
|
906
973
|
}
|
|
907
974
|
await dispatcher.dispatch(ev);
|
|
908
975
|
}
|
|
976
|
+
if (inferExitCode) {
|
|
977
|
+
execution.exitCode = inferForegroundExitCode(execution);
|
|
978
|
+
}
|
|
909
979
|
return execution;
|
|
910
980
|
}
|
|
981
|
+
async interrupt(sessionId) {
|
|
982
|
+
const { error, response } = await this.client.DELETE("/command", {
|
|
983
|
+
params: { query: { id: sessionId } }
|
|
984
|
+
});
|
|
985
|
+
throwOnOpenApiFetchError({ error, response }, "Interrupt command failed");
|
|
986
|
+
}
|
|
987
|
+
async getCommandStatus(commandId) {
|
|
988
|
+
const { data, error, response } = await this.client.GET("/command/status/{id}", {
|
|
989
|
+
params: { path: { id: commandId } }
|
|
990
|
+
});
|
|
991
|
+
throwOnOpenApiFetchError({ error, response }, "Get command status failed");
|
|
992
|
+
const ok = data;
|
|
993
|
+
if (!ok || typeof ok !== "object") {
|
|
994
|
+
throw new Error("Get command status failed: unexpected response shape");
|
|
995
|
+
}
|
|
996
|
+
return {
|
|
997
|
+
id: ok.id,
|
|
998
|
+
content: ok.content,
|
|
999
|
+
running: ok.running,
|
|
1000
|
+
exitCode: ok.exit_code ?? null,
|
|
1001
|
+
error: ok.error,
|
|
1002
|
+
startedAt: parseOptionalDate(ok.started_at, "startedAt"),
|
|
1003
|
+
finishedAt: parseOptionalDate(ok.finished_at, "finishedAt") ?? null
|
|
1004
|
+
};
|
|
1005
|
+
}
|
|
1006
|
+
async getBackgroundCommandLogs(commandId, cursor) {
|
|
1007
|
+
const { data, error, response } = await this.client.GET("/command/{id}/logs", {
|
|
1008
|
+
params: { path: { id: commandId }, query: cursor == null ? {} : { cursor } },
|
|
1009
|
+
parseAs: "text"
|
|
1010
|
+
});
|
|
1011
|
+
throwOnOpenApiFetchError({ error, response }, "Get command logs failed");
|
|
1012
|
+
const ok = data;
|
|
1013
|
+
if (typeof ok !== "string") {
|
|
1014
|
+
throw new Error("Get command logs failed: unexpected response shape");
|
|
1015
|
+
}
|
|
1016
|
+
const cursorHeader = response.headers.get("EXECD-COMMANDS-TAIL-CURSOR");
|
|
1017
|
+
const parsedCursor = cursorHeader != null && cursorHeader !== "" ? Number(cursorHeader) : void 0;
|
|
1018
|
+
return {
|
|
1019
|
+
content: ok,
|
|
1020
|
+
cursor: Number.isFinite(parsedCursor ?? NaN) ? parsedCursor : void 0
|
|
1021
|
+
};
|
|
1022
|
+
}
|
|
1023
|
+
async *runStream(command, opts, signal) {
|
|
1024
|
+
for await (const ev of this.streamExecution(
|
|
1025
|
+
this.buildRunStreamSpec(command, opts),
|
|
1026
|
+
signal
|
|
1027
|
+
)) {
|
|
1028
|
+
yield ev;
|
|
1029
|
+
}
|
|
1030
|
+
}
|
|
1031
|
+
async run(command, opts, handlers, signal) {
|
|
1032
|
+
return this.consumeExecutionStream(
|
|
1033
|
+
this.runStream(command, opts, signal),
|
|
1034
|
+
handlers,
|
|
1035
|
+
!opts?.background
|
|
1036
|
+
);
|
|
1037
|
+
}
|
|
1038
|
+
async createSession(options) {
|
|
1039
|
+
const body = options?.workingDirectory != null ? { cwd: options.workingDirectory } : {};
|
|
1040
|
+
const { data, error, response } = await this.client.POST("/session", {
|
|
1041
|
+
body
|
|
1042
|
+
});
|
|
1043
|
+
throwOnOpenApiFetchError({ error, response }, "Create session failed");
|
|
1044
|
+
const ok = data;
|
|
1045
|
+
if (!ok || typeof ok.session_id !== "string") {
|
|
1046
|
+
throw new Error("Create session failed: unexpected response shape");
|
|
1047
|
+
}
|
|
1048
|
+
return ok.session_id;
|
|
1049
|
+
}
|
|
1050
|
+
async *runInSessionStream(sessionId, command, opts, signal) {
|
|
1051
|
+
for await (const ev of this.streamExecution(
|
|
1052
|
+
this.buildRunInSessionStreamSpec(sessionId, command, opts),
|
|
1053
|
+
signal
|
|
1054
|
+
)) {
|
|
1055
|
+
yield ev;
|
|
1056
|
+
}
|
|
1057
|
+
}
|
|
1058
|
+
async runInSession(sessionId, command, options, handlers, signal) {
|
|
1059
|
+
return this.consumeExecutionStream(
|
|
1060
|
+
this.runInSessionStream(sessionId, command, options, signal),
|
|
1061
|
+
handlers,
|
|
1062
|
+
true
|
|
1063
|
+
);
|
|
1064
|
+
}
|
|
1065
|
+
async deleteSession(sessionId) {
|
|
1066
|
+
const { error, response } = await this.client.DELETE(
|
|
1067
|
+
"/session/{sessionId}",
|
|
1068
|
+
{ params: { path: { sessionId } } }
|
|
1069
|
+
);
|
|
1070
|
+
throwOnOpenApiFetchError({ error, response }, "Delete session failed");
|
|
1071
|
+
}
|
|
911
1072
|
};
|
|
912
1073
|
// Annotate the CommonJS export names for ESM import in node:
|
|
913
1074
|
0 && (module.exports = {
|