@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.
@@ -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.parseIsoDate("expiresAt", raw?.expiresAt)
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.parseIsoDate("expiresAt", raw?.expiresAt)
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
- return {
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
- async interrupt(sessionId) {
875
- const { error, response } = await this.client.DELETE("/command", {
876
- params: { query: { id: sessionId } }
877
- });
878
- throwOnOpenApiFetchError({ error, response }, "Interrupt command failed");
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
- async *runStream(command, opts, signal) {
881
- const url = joinUrl2(this.opts.baseUrl, "/command");
882
- const body = JSON.stringify(toRunCommandRequest(command, opts));
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
- "accept": "text/event-stream",
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, { fallbackErrorMessage: "Run command failed" })) {
958
+ for await (const ev of parseJsonEventStream(res, {
959
+ fallbackErrorMessage: spec.fallbackErrorMessage
960
+ })) {
894
961
  yield ev;
895
962
  }
896
963
  }
897
- async run(command, opts, handlers, signal) {
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 this.runStream(command, opts, signal)) {
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 = {