@alibaba-group/opensandbox 0.1.4 → 0.1.6

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) {
@@ -858,6 +863,9 @@ function joinUrl2(baseUrl, pathname) {
858
863
  return `${base}${path}`;
859
864
  }
860
865
  function toRunCommandRequest(command, opts) {
866
+ if (opts?.gid != null && opts.uid == null) {
867
+ throw new Error("uid is required when gid is provided");
868
+ }
861
869
  const body = {
862
870
  command,
863
871
  cwd: opts?.workingDirectory,
@@ -866,8 +874,39 @@ function toRunCommandRequest(command, opts) {
866
874
  if (opts?.timeoutSeconds != null) {
867
875
  body.timeout = Math.round(opts.timeoutSeconds * 1e3);
868
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?.timeoutSeconds != null) {
896
+ body.timeout = Math.round(opts.timeoutSeconds * 1e3);
897
+ }
869
898
  return body;
870
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
+ }
871
910
  function parseOptionalDate(value, field) {
872
911
  if (value == null) return void 0;
873
912
  if (value instanceof Date) return value;
@@ -887,6 +926,58 @@ var CommandsAdapter = class {
887
926
  this.fetch = opts.fetch ?? fetch;
888
927
  }
889
928
  fetch;
929
+ buildRunStreamSpec(command, opts) {
930
+ assertNonBlank(command, "command");
931
+ return {
932
+ pathname: "/command",
933
+ body: toRunCommandRequest(command, opts),
934
+ fallbackErrorMessage: "Run command failed"
935
+ };
936
+ }
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);
948
+ const res = await this.fetch(url, {
949
+ method: "POST",
950
+ headers: {
951
+ accept: "text/event-stream",
952
+ "content-type": "application/json",
953
+ ...this.opts.headers ?? {}
954
+ },
955
+ body: JSON.stringify(spec.body),
956
+ signal
957
+ });
958
+ for await (const ev of parseJsonEventStream(res, {
959
+ fallbackErrorMessage: spec.fallbackErrorMessage
960
+ })) {
961
+ yield ev;
962
+ }
963
+ }
964
+ async consumeExecutionStream(stream, handlers, inferExitCode = false) {
965
+ const execution = {
966
+ logs: { stdout: [], stderr: [] },
967
+ result: []
968
+ };
969
+ const dispatcher = new ExecutionEventDispatcher(execution, handlers);
970
+ for await (const ev of stream) {
971
+ if (ev.type === "init" && (ev.text ?? "") === "" && execution.id) {
972
+ ev.text = execution.id;
973
+ }
974
+ await dispatcher.dispatch(ev);
975
+ }
976
+ if (inferExitCode) {
977
+ execution.exitCode = inferForegroundExitCode(execution);
978
+ }
979
+ return execution;
980
+ }
890
981
  async interrupt(sessionId) {
891
982
  const { error, response } = await this.client.DELETE("/command", {
892
983
  params: { query: { id: sessionId } }
@@ -930,35 +1021,53 @@ var CommandsAdapter = class {
930
1021
  };
931
1022
  }
932
1023
  async *runStream(command, opts, signal) {
933
- const url = joinUrl2(this.opts.baseUrl, "/command");
934
- const body = JSON.stringify(toRunCommandRequest(command, opts));
935
- const res = await this.fetch(url, {
936
- method: "POST",
937
- headers: {
938
- "accept": "text/event-stream",
939
- "content-type": "application/json",
940
- ...this.opts.headers ?? {}
941
- },
942
- body,
1024
+ for await (const ev of this.streamExecution(
1025
+ this.buildRunStreamSpec(command, opts),
943
1026
  signal
944
- });
945
- for await (const ev of parseJsonEventStream(res, { fallbackErrorMessage: "Run command failed" })) {
1027
+ )) {
946
1028
  yield ev;
947
1029
  }
948
1030
  }
949
1031
  async run(command, opts, handlers, signal) {
950
- const execution = {
951
- logs: { stdout: [], stderr: [] },
952
- result: []
953
- };
954
- const dispatcher = new ExecutionEventDispatcher(execution, handlers);
955
- for await (const ev of this.runStream(command, opts, signal)) {
956
- if (ev.type === "init" && (ev.text ?? "") === "" && execution.id) {
957
- ev.text = execution.id;
958
- }
959
- await dispatcher.dispatch(ev);
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");
960
1047
  }
961
- return execution;
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");
962
1071
  }
963
1072
  };
964
1073
  // Annotate the CommonJS export names for ESM import in node: