@eyepop.ai/eyepop 3.16.0 → 3.17.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.
@@ -8632,6 +8632,204 @@ var require_mime_types = __commonJS({
8632
8632
  }
8633
8633
  });
8634
8634
 
8635
+ // compute/compute_session.ts
8636
+ var SESSION_DEAD = /* @__PURE__ */ new Set(["error" /* ERROR */, "failed" /* FAILED */, "stopped" /* STOPPED */, "pipeline_error" /* PIPELINE_ERROR */, "unknown" /* UNKNOWN */]);
8637
+ var CHECK_FOR_IS_READY_INTERVAL = 250;
8638
+ var sleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
8639
+ function pipelineIdFromSession(session) {
8640
+ if (session.pipeline_uuid) {
8641
+ return session.pipeline_uuid;
8642
+ }
8643
+ const pipeline = session.pipelines?.[0];
8644
+ return pipeline?.id || pipeline?.pipeline_id || null;
8645
+ }
8646
+ function sessionCreateBody(options) {
8647
+ const body = {};
8648
+ if (options.sessionName) {
8649
+ body.session_name = options.sessionName;
8650
+ }
8651
+ if (options.pipelineImage) {
8652
+ body.pipeline_image = options.pipelineImage;
8653
+ }
8654
+ if (options.pipelineVersion) {
8655
+ body.pipeline_version = options.pipelineVersion;
8656
+ }
8657
+ if (options.pop) {
8658
+ body.pop = options.pop;
8659
+ }
8660
+ return Object.keys(body).length > 0 ? JSON.stringify(body) : void 0;
8661
+ }
8662
+ function sessionHealthReady(body, contentType) {
8663
+ if (contentType.includes("application/json")) {
8664
+ const health = JSON.parse(body);
8665
+ if (health && typeof health == "object") {
8666
+ if (health.message && !health.session_status) {
8667
+ return true;
8668
+ }
8669
+ return health.session_status == "running" /* RUNNING */;
8670
+ }
8671
+ }
8672
+ return contentType.includes("text/plain") && body.includes("I'm fine");
8673
+ }
8674
+ function accessTokenValidUntil(session) {
8675
+ if (session.access_token_expires_at) {
8676
+ const expiresAt = Date.parse(session.access_token_expires_at);
8677
+ return Number.isNaN(expiresAt) ? null : expiresAt;
8678
+ }
8679
+ if (session.access_token_expires_in) {
8680
+ return Date.now() + session.access_token_expires_in * 1e3;
8681
+ }
8682
+ return null;
8683
+ }
8684
+ var ComputeSessionClient = class {
8685
+ constructor(options) {
8686
+ this.options = options;
8687
+ }
8688
+ async resolve() {
8689
+ const session = this.options.sessionUuid ? await this.getPermanentComputeSession(this.options.sessionUuid) : await this.getOnDemandComputeSession();
8690
+ const accessToken = this.sessionAccessToken(session);
8691
+ await this.waitUntilReady(session, accessToken);
8692
+ const pipelineId = pipelineIdFromSession(session);
8693
+ if (this.options.pop && !pipelineId) {
8694
+ throw new Error("Compute session did not provide a pipeline for the requested pop");
8695
+ }
8696
+ return {
8697
+ session,
8698
+ pipelineId,
8699
+ accessToken,
8700
+ accessTokenValidUntil: accessTokenValidUntil(session)
8701
+ };
8702
+ }
8703
+ async getOnDemandComputeSession() {
8704
+ let session = null;
8705
+ const sessionsUrl = `${this.options.computeUrl}/v1/sessions`;
8706
+ const headers = {
8707
+ Authorization: await this.options.authorizationHeader()
8708
+ };
8709
+ let sessions;
8710
+ if (!this.options.pop) {
8711
+ this.options.requestLogger?.debug("fetching sessions from: %s", sessionsUrl);
8712
+ const response = await this.options.httpClient.fetch(sessionsUrl, {
8713
+ headers
8714
+ });
8715
+ if (response.status == 404) {
8716
+ sessions = [];
8717
+ } else if (response.status != 200) {
8718
+ const message = await response.text();
8719
+ throw new Error(`Unexpected status ${response.status} fetching compute sessions: ${message}`);
8720
+ } else {
8721
+ const response_content = await response.json();
8722
+ sessions = response_content;
8723
+ }
8724
+ if (sessions.length > 0) {
8725
+ this.options.logger?.debug(`User has ${sessions.length} sessions, inspecting for usable session`);
8726
+ for (let s of sessions) {
8727
+ if (!SESSION_DEAD.has(s.session_status) && !s.persistent && (!this.options.sessionName || s.session_name === this.options.sessionName)) {
8728
+ session = s;
8729
+ this.options.logger?.debug(`Use active session ${s.session_uuid} with pipeline ${pipelineIdFromSession(s)}`);
8730
+ break;
8731
+ }
8732
+ }
8733
+ }
8734
+ }
8735
+ if (!session) {
8736
+ this.options.requestLogger?.debug("creating a new session at: %s", sessionsUrl);
8737
+ const createBody = sessionCreateBody(this.options);
8738
+ const createHeaders = {
8739
+ ...headers,
8740
+ ...createBody ? { "Content-Type": "application/json" } : {}
8741
+ };
8742
+ const request = {
8743
+ headers: createHeaders,
8744
+ method: "POST"
8745
+ };
8746
+ if (createBody) {
8747
+ request.body = createBody;
8748
+ }
8749
+ const query = "wait=true";
8750
+ const response = await this.options.httpClient.fetch(`${sessionsUrl}?${query}`, request);
8751
+ if (response.status != 200) {
8752
+ const message = await response.text();
8753
+ throw new Error(`Unexpected status ${response.status} creating a compute session: ${message}`);
8754
+ } else {
8755
+ sessions = await response.json();
8756
+ }
8757
+ if (sessions.length == 0) {
8758
+ throw new Error("Unexpected, no compute session after attempt to create one");
8759
+ }
8760
+ session = sessions[0] || null;
8761
+ }
8762
+ if (!session) {
8763
+ throw new Error("unexpected undefined session");
8764
+ }
8765
+ if (this.options.pop && !pipelineIdFromSession(session)) {
8766
+ throw new Error("Compute session did not provide a pipeline for the requested pop");
8767
+ }
8768
+ return session;
8769
+ }
8770
+ async getPermanentComputeSession(sessionUuid) {
8771
+ let session = null;
8772
+ const sessionUrl = `${this.options.computeUrl}/v1/sessions/${sessionUuid}`;
8773
+ this.options.requestLogger?.debug("fetching session from: %s", sessionUrl);
8774
+ const headers = {
8775
+ Authorization: await this.options.authorizationHeader()
8776
+ };
8777
+ let response = await this.options.httpClient.fetch(sessionUrl, {
8778
+ headers
8779
+ });
8780
+ if (response.status == 404) {
8781
+ throw new Error(`Unexpected status ${response.status} compute sessions ${sessionUuid} not found`);
8782
+ } else if (response.status != 200) {
8783
+ const message = await response.text();
8784
+ throw new Error(`Unexpected status ${response.status} fetching compute sessions: ${message}`);
8785
+ } else {
8786
+ const response_content = await response.json();
8787
+ session = response_content;
8788
+ }
8789
+ return session;
8790
+ }
8791
+ sessionAccessToken(session) {
8792
+ if (!session.access_token) {
8793
+ throw new Error(`Compute session ${session.session_uuid} did not provide an access token`);
8794
+ }
8795
+ return session.access_token;
8796
+ }
8797
+ async waitUntilReady(session, accessToken) {
8798
+ let isReady = false;
8799
+ const deadline = Date.now() + this.options.readyTimeoutMs;
8800
+ while (!isReady) {
8801
+ const health_url = `${session.session_endpoint}/health`;
8802
+ const response = await this.options.httpClient.fetch(health_url, {
8803
+ headers: {
8804
+ Authorization: `Bearer ${accessToken}`,
8805
+ Accept: "application/json"
8806
+ }
8807
+ });
8808
+ if (response.status == 200) {
8809
+ const contentType = response.headers.get("content-type") || "";
8810
+ const body = await response.text();
8811
+ try {
8812
+ isReady = sessionHealthReady(body, contentType);
8813
+ } catch (error) {
8814
+ this.options.logger?.debug(`session ${session.session_uuid} health check returned invalid JSON, wait and poll for update`);
8815
+ }
8816
+ if (!isReady && Date.now() > deadline) {
8817
+ throw new Error(`Created session ${session.session_uuid} did not become healthy after ${this.options.readyTimeoutMs / 1e3} seconds`);
8818
+ }
8819
+ if (!isReady) {
8820
+ this.options.logger?.debug(`session ${session.session_uuid} health check not ready, wait and poll for update`);
8821
+ await sleep(CHECK_FOR_IS_READY_INTERVAL);
8822
+ }
8823
+ } else if (Date.now() > deadline) {
8824
+ throw new Error(`Created session ${session.session_uuid} did not become healthy after ${this.options.readyTimeoutMs / 1e3} seconds`);
8825
+ } else {
8826
+ this.options.logger?.debug(`session ${session.session_uuid} health check status ${response.status}, wait and poll for update`);
8827
+ await sleep(CHECK_FOR_IS_READY_INTERVAL);
8828
+ }
8829
+ }
8830
+ }
8831
+ };
8832
+
8635
8833
  // types.ts
8636
8834
  var EndpointState = /* @__PURE__ */ ((EndpointState2) => {
8637
8835
  EndpointState2["Idle"] = "Idle";
@@ -8644,6 +8842,7 @@ var EndpointState = /* @__PURE__ */ ((EndpointState2) => {
8644
8842
  })(EndpointState || {});
8645
8843
 
8646
8844
  // streaming.ts
8845
+ import { v4 as uuidv4 } from "uuid";
8647
8846
  var Stream = class _Stream {
8648
8847
  constructor(iterator, controller) {
8649
8848
  this.iterator = iterator;
@@ -8824,6 +9023,76 @@ function readableStreamFromString(s) {
8824
9023
  }
8825
9024
  });
8826
9025
  }
9026
+ function randomMultipartBoundary() {
9027
+ return `----formdata-boundary-${uuidv4()}`;
9028
+ }
9029
+ function toPartStream(content) {
9030
+ if (content instanceof ReadableStream) {
9031
+ return content;
9032
+ }
9033
+ if (content instanceof Blob) {
9034
+ return content.stream();
9035
+ }
9036
+ const bytes = ArrayBuffer.isView(content) ? new Uint8Array(content.buffer, content.byteOffset, content.byteLength) : new Uint8Array(content);
9037
+ return new ReadableStream({
9038
+ start(controller) {
9039
+ controller.enqueue(bytes);
9040
+ controller.close();
9041
+ }
9042
+ });
9043
+ }
9044
+ async function cancelMultipartContent(content, reason) {
9045
+ if (content instanceof ReadableStream) {
9046
+ await content.cancel(reason);
9047
+ }
9048
+ }
9049
+ function encodeMultipartFormData(parts, boundary) {
9050
+ const encoder = new TextEncoder();
9051
+ let partIndex = 0;
9052
+ let reader = null;
9053
+ return new ReadableStream({
9054
+ async pull(controller) {
9055
+ if (reader) {
9056
+ const { done, value } = await reader.read();
9057
+ if (!done) {
9058
+ controller.enqueue(value);
9059
+ return;
9060
+ }
9061
+ reader = null;
9062
+ controller.enqueue(encoder.encode("\r\n"));
9063
+ partIndex++;
9064
+ }
9065
+ if (partIndex >= parts.length) {
9066
+ controller.enqueue(encoder.encode(`--${boundary}--\r
9067
+ `));
9068
+ controller.close();
9069
+ return;
9070
+ }
9071
+ const part = parts[partIndex];
9072
+ let header = `--${boundary}\r
9073
+ Content-Disposition: form-data; name="${part.name}"\r
9074
+ `;
9075
+ if (part.contentType) {
9076
+ header += `Content-Type: ${part.contentType}\r
9077
+ `;
9078
+ }
9079
+ header += "\r\n";
9080
+ controller.enqueue(encoder.encode(header));
9081
+ if (typeof part.content === "string") {
9082
+ controller.enqueue(encoder.encode(part.content));
9083
+ controller.enqueue(encoder.encode("\r\n"));
9084
+ partIndex++;
9085
+ } else {
9086
+ reader = toPartStream(part.content).getReader();
9087
+ }
9088
+ },
9089
+ async cancel(reason) {
9090
+ await reader?.cancel(reason);
9091
+ const firstUnstartedPart = reader ? partIndex + 1 : partIndex;
9092
+ await Promise.all(parts.slice(firstUnstartedPart).map((part) => cancelMultipartContent(part.content, reason)));
9093
+ }
9094
+ });
9095
+ }
8827
9096
 
8828
9097
  // worker/worker_types.ts
8829
9098
  var PredictionVersion = /* @__PURE__ */ ((PredictionVersion3) => {
@@ -8881,7 +9150,7 @@ var ContourType = /* @__PURE__ */ ((ContourType2) => {
8881
9150
  })(ContourType || {});
8882
9151
 
8883
9152
  // worker/webrtc_whip.ts
8884
- import { v4 as uuidv4 } from "uuid";
9153
+ import { v4 as uuidv42 } from "uuid";
8885
9154
 
8886
9155
  // worker/webrtc_base.ts
8887
9156
  import path from "path";
@@ -9156,7 +9425,7 @@ var WebrtcBase = class _WebrtcBase {
9156
9425
  // worker/webrtc_whip.ts
9157
9426
  var WebrtcWhip = class extends WebrtcBase {
9158
9427
  constructor(stream, getSession, client, is_pipeline_direct, requestLogger) {
9159
- super(getSession, client, uuidv4().toString(), "liveIngress/whip", is_pipeline_direct, requestLogger);
9428
+ super(getSession, client, uuidv42().toString(), "liveIngress/whip", is_pipeline_direct, requestLogger);
9160
9429
  this._stream = stream;
9161
9430
  }
9162
9431
  async start() {
@@ -9551,6 +9820,111 @@ var LoadFromJob = class extends AbstractJob {
9551
9820
  });
9552
9821
  }
9553
9822
  };
9823
+ var UploadGroupJob = class extends AbstractJob {
9824
+ get [Symbol.toStringTag]() {
9825
+ return "uploadGroupJob";
9826
+ }
9827
+ constructor(sources, params, getSession, client, requestLogger) {
9828
+ super(params, getSession, client, requestLogger);
9829
+ if (sources.length === 0) {
9830
+ throw new Error("upload group requires at least one source");
9831
+ }
9832
+ this._sources = sources;
9833
+ }
9834
+ async startJob() {
9835
+ const session = await this._getSession();
9836
+ if (!session.baseUrl) {
9837
+ return Promise.reject(new Error("session.baseUrl must not be null"));
9838
+ }
9839
+ const queryParams = new URLSearchParams({
9840
+ mode: "queue",
9841
+ processing: "sync"
9842
+ });
9843
+ if (this._version !== void 0) {
9844
+ queryParams.set("version", this._version.toString());
9845
+ }
9846
+ if (this._params.motionDetect) {
9847
+ for (const [key, value] of Object.entries(this._params.motionDetect)) {
9848
+ queryParams.set(key, value);
9849
+ }
9850
+ }
9851
+ const postUrl = `${session.baseUrl.replace(/\/+$/, "")}/pipelines/${session.pipelineId}/source?${queryParams.toString()}`;
9852
+ const parts = [];
9853
+ if (this._params.componentParams) {
9854
+ parts.push({ name: "params", contentType: "application/json", content: JSON.stringify(this._params.componentParams) });
9855
+ }
9856
+ if (this._params.roi) {
9857
+ parts.push({ name: "roi", contentType: "application/json", content: JSON.stringify(this._params.roi) });
9858
+ }
9859
+ if (this._params.fps) {
9860
+ parts.push({ name: "fps", contentType: "application/json", content: JSON.stringify(this._params.fps) });
9861
+ }
9862
+ for (const source of this._sources) {
9863
+ parts.push({ name: "file", contentType: source.mimeType, content: source.stream });
9864
+ }
9865
+ const boundary = randomMultipartBoundary();
9866
+ const headers = {
9867
+ ...session.authenticationHeaders(),
9868
+ Accept: "application/jsonl",
9869
+ "Content-Type": `multipart/form-data; boundary=${boundary}`
9870
+ };
9871
+ return await this._client.fetch(postUrl, {
9872
+ headers,
9873
+ method: "POST",
9874
+ body: encodeMultipartFormData(parts, boundary),
9875
+ signal: this._controller.signal,
9876
+ // @ts-ignore
9877
+ duplex: "half",
9878
+ eyepop: { responseStreaming: true }
9879
+ });
9880
+ }
9881
+ };
9882
+ var LoadFromGroupJob = class extends AbstractJob {
9883
+ get [Symbol.toStringTag]() {
9884
+ return "loadFromGroupJob";
9885
+ }
9886
+ constructor(urls, params, getSession, client, requestLogger) {
9887
+ super(params, getSession, client, requestLogger);
9888
+ if (urls.length === 0) {
9889
+ throw new Error("load from group requires at least one url");
9890
+ }
9891
+ this._urls = urls;
9892
+ }
9893
+ async startJob() {
9894
+ const session = await this._getSession();
9895
+ if (!session.baseUrl) {
9896
+ return Promise.reject(new Error("session.baseUrl must not be null"));
9897
+ }
9898
+ const body = {
9899
+ sourceType: "GROUP",
9900
+ sources: this._urls.map((url) => ({ sourceType: "URL", url })),
9901
+ version: this._version
9902
+ };
9903
+ if (this._params.componentParams) {
9904
+ body["params"] = this._params.componentParams;
9905
+ }
9906
+ if (this._params.roi) {
9907
+ body["roi"] = this._params.roi;
9908
+ }
9909
+ if (this._params.motionDetect) {
9910
+ Object.assign(body, this._params.motionDetect);
9911
+ }
9912
+ const headers = {
9913
+ ...session.authenticationHeaders(),
9914
+ Accept: "application/jsonl",
9915
+ "Content-Type": "application/json"
9916
+ };
9917
+ const patchUrl = `${session.baseUrl.replace(/\/+$/, "")}/pipelines/${session.pipelineId}/source?mode=queue&processing=sync`;
9918
+ return await this._client.fetch(patchUrl, {
9919
+ headers,
9920
+ method: "PATCH",
9921
+ body: JSON.stringify(body),
9922
+ signal: this._controller.signal,
9923
+ // @ts-ignore
9924
+ eyepop: { responseStreaming: true }
9925
+ });
9926
+ }
9927
+ };
9554
9928
  var LoadFromAssetUuidJob = class extends AbstractJob {
9555
9929
  constructor(assetUuid, params, getSession, client, requestLogger) {
9556
9930
  super(params, getSession, client, requestLogger);
@@ -10152,21 +10526,6 @@ var TransientPopId = /* @__PURE__ */ ((TransientPopId2) => {
10152
10526
  })(TransientPopId || {});
10153
10527
 
10154
10528
  // worker/worker_endpoint.ts
10155
- var SessionStatus = /* @__PURE__ */ ((SessionStatus2) => {
10156
- SessionStatus2["UNKNOWN"] = "unknown";
10157
- SessionStatus2["PENDING"] = "pending";
10158
- SessionStatus2["RUNNING"] = "running";
10159
- SessionStatus2["STOPPED"] = "stopped";
10160
- SessionStatus2["FAILED"] = "failed";
10161
- SessionStatus2["ERROR"] = "error";
10162
- SessionStatus2["PIPELINE_CREATING"] = "pipeline_creating";
10163
- SessionStatus2["PIPELINE_OK"] = "pipeline_ok";
10164
- SessionStatus2["PIPELINE_CHECKING"] = "pipeline_checking";
10165
- SessionStatus2["PIPELINE_ERROR"] = "pipeline_error";
10166
- SessionStatus2["UPGRADING"] = "upgrading";
10167
- return SessionStatus2;
10168
- })(SessionStatus || {});
10169
- var sleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
10170
10529
  var _WorkerEndpoint = class _WorkerEndpoint extends Endpoint {
10171
10530
  constructor(options) {
10172
10531
  super(options);
@@ -10329,6 +10688,114 @@ var _WorkerEndpoint = class _WorkerEndpoint extends Endpoint {
10329
10688
  return Promise.reject("unknown source type");
10330
10689
  }
10331
10690
  }
10691
+ async uploadGroup(paths, params = {}) {
10692
+ if (paths.length === 0) {
10693
+ throw new Error("upload group requires at least one path");
10694
+ }
10695
+ if (!this._baseUrl || !this._pipelineId || !this._client || !this._limit) {
10696
+ throw new Error("endpoint not connected, use connect()");
10697
+ }
10698
+ await this._limit.acquire();
10699
+ try {
10700
+ this.updateState();
10701
+ const sources = await Promise.all(
10702
+ paths.map(async (path2) => {
10703
+ let streamSource;
10704
+ if (this._options.platformSupport?.resolvePath) {
10705
+ streamSource = await this._options.platformSupport.resolvePath({ path: path2 }, this._logger);
10706
+ } else {
10707
+ streamSource = await resolvePath({ path: path2 }, this._logger);
10708
+ }
10709
+ return { stream: streamSource.stream, mimeType: streamSource.mimeType };
10710
+ })
10711
+ );
10712
+ const job = new UploadGroupJob(
10713
+ sources,
10714
+ params,
10715
+ async () => this.session(),
10716
+ this._client,
10717
+ this._requestLogger
10718
+ );
10719
+ return job.start(
10720
+ () => {
10721
+ this.jobDone(job);
10722
+ },
10723
+ (statusCode) => {
10724
+ this.jobStatus(job, statusCode);
10725
+ }
10726
+ );
10727
+ } catch (e) {
10728
+ this._limit.release();
10729
+ throw e;
10730
+ }
10731
+ }
10732
+ async uploadStreamGroup(streams, mimeTypes, params = {}) {
10733
+ if (streams.length === 0) {
10734
+ throw new Error("upload stream group requires at least one stream");
10735
+ }
10736
+ if (mimeTypes !== void 0 && mimeTypes.length !== streams.length) {
10737
+ throw new Error("mimeTypes must have the same length as streams");
10738
+ }
10739
+ if (!this._baseUrl || !this._pipelineId || !this._client || !this._limit) {
10740
+ throw new Error("endpoint not connected, use connect()");
10741
+ }
10742
+ await this._limit.acquire();
10743
+ try {
10744
+ this.updateState();
10745
+ const sources = streams.map((stream, i) => ({
10746
+ stream,
10747
+ mimeType: mimeTypes?.[i] ?? null
10748
+ }));
10749
+ const job = new UploadGroupJob(
10750
+ sources,
10751
+ params,
10752
+ async () => this.session(),
10753
+ this._client,
10754
+ this._requestLogger
10755
+ );
10756
+ return job.start(
10757
+ () => {
10758
+ this.jobDone(job);
10759
+ },
10760
+ (statusCode) => {
10761
+ this.jobStatus(job, statusCode);
10762
+ }
10763
+ );
10764
+ } catch (e) {
10765
+ this._limit.release();
10766
+ throw e;
10767
+ }
10768
+ }
10769
+ async loadFromGroup(urls, params = {}) {
10770
+ if (urls.length === 0) {
10771
+ throw new Error("load from group requires at least one url");
10772
+ }
10773
+ if (!this._baseUrl || !this._pipelineId || !this._client || !this._limit) {
10774
+ throw new Error("endpoint not connected, use connect()");
10775
+ }
10776
+ await this._limit.acquire();
10777
+ try {
10778
+ this.updateState();
10779
+ const job = new LoadFromGroupJob(
10780
+ urls,
10781
+ params,
10782
+ async () => this.session(),
10783
+ this._client,
10784
+ this._requestLogger
10785
+ );
10786
+ return job.start(
10787
+ () => {
10788
+ this.jobDone(job);
10789
+ },
10790
+ (statusCode) => {
10791
+ this.jobStatus(job, statusCode);
10792
+ }
10793
+ );
10794
+ } catch (e) {
10795
+ this._limit.release();
10796
+ throw e;
10797
+ }
10798
+ }
10332
10799
  async uploadFile(source, params) {
10333
10800
  await this.ensureTransientPipelineStarted();
10334
10801
  if (!this._baseUrl || !this._pipelineId || !this._client || !this._limit) {
@@ -10649,161 +11116,38 @@ var _WorkerEndpoint = class _WorkerEndpoint extends Endpoint {
10649
11116
  const seconds = this.options().sessionReadyTimeoutSeconds;
10650
11117
  return typeof seconds == "number" && Number.isFinite(seconds) && seconds > 0 ? seconds * 1e3 : _WorkerEndpoint.WAIT_FOR_IS_READY;
10651
11118
  }
10652
- sessionHealthReady(body, contentType) {
10653
- if (contentType.includes("application/json")) {
10654
- const health = JSON.parse(body);
10655
- if (health && typeof health == "object") {
10656
- if (health.message && !health.session_status) {
10657
- return true;
10658
- }
10659
- return health.session_status == "running" /* RUNNING */;
10660
- }
10661
- }
10662
- return contentType.includes("text/plain") && body.includes("I'm fine");
10663
- }
10664
11119
  async getComputeSession() {
10665
11120
  if (!this._client) {
10666
11121
  throw new Error("endpoint not initialized");
10667
11122
  }
10668
- let session;
10669
- const sessionUuid = this.options().sessionUuid;
10670
- if (sessionUuid) {
10671
- session = await this.getPermanentComputeSession(this._client, sessionUuid);
10672
- } else {
10673
- session = await this.getOnDemandComputeSession(this._client);
10674
- }
10675
- this.setComputeSessionAccessToken(session);
10676
- const headers = {
10677
- Authorization: await this.authorizationHeader()
10678
- };
10679
- let isReady = false;
10680
- const readyTimeoutMs = this.sessionReadyTimeoutMs();
10681
- const deadline = Date.now() + readyTimeoutMs;
10682
- while (!isReady) {
10683
- const health_url = `${session.session_endpoint}/health`;
10684
- const response = await this._client.fetch(health_url, {
10685
- headers: {
10686
- ...headers,
10687
- Accept: "application/json"
10688
- }
10689
- });
10690
- if (response.status == 200) {
10691
- const contentType = response.headers.get("content-type") || "";
10692
- const body = await response.text();
10693
- try {
10694
- isReady = this.sessionHealthReady(body, contentType);
10695
- } catch (error) {
10696
- this._logger.debug(`session ${session.session_uuid} health check returned invalid JSON, wait and poll for update`);
10697
- }
10698
- if (!isReady && Date.now() > deadline) {
10699
- throw new Error(`Created session ${session.session_uuid} did not become healthy after ${readyTimeoutMs / 1e3} seconds`);
10700
- }
10701
- if (!isReady) {
10702
- this._logger.debug(`session ${session.session_uuid} health check not ready, wait and poll for update`);
10703
- await sleep(_WorkerEndpoint.CHECK_FOR_IS_READY_INTERVAL);
10704
- }
10705
- } else if (Date.now() > deadline) {
10706
- throw new Error(`Created session ${session.session_uuid} did not become healthy after ${readyTimeoutMs / 1e3} seconds`);
10707
- } else {
10708
- this._logger.debug(`session ${session.session_uuid} health check status ${response.status}, wait and poll for update`);
10709
- await sleep(_WorkerEndpoint.CHECK_FOR_IS_READY_INTERVAL);
10710
- }
11123
+ let resolved;
11124
+ try {
11125
+ resolved = await new ComputeSessionClient({
11126
+ computeUrl: this.eyepopUrl(),
11127
+ httpClient: this._client,
11128
+ authorizationHeader: () => this.computeAuthorizationHeader(),
11129
+ sessionUuid: this.options().sessionUuid,
11130
+ sessionName: this.options().sessionName,
11131
+ pipelineImage: this.options().pipelineImage,
11132
+ pipelineVersion: this.options().pipelineVersion,
11133
+ pop: this.options().pop,
11134
+ readyTimeoutMs: this.sessionReadyTimeoutMs(),
11135
+ logger: this._logger,
11136
+ requestLogger: this._requestLogger
11137
+ }).resolve();
11138
+ } catch (error) {
11139
+ this.updateState("Error" /* Error */);
11140
+ throw error;
10711
11141
  }
10712
- this._baseUrl = session.session_endpoint;
11142
+ this._sessionAccessToken = resolved.accessToken;
11143
+ this._sessionAccessTokenValidUntil = resolved.accessTokenValidUntil;
11144
+ this._baseUrl = resolved.session.session_endpoint;
10713
11145
  this._pipeline = null;
10714
- this._pipelineId = this.pipelineIdFromSession(session);
11146
+ this._pipelineId = resolved.pipelineId;
10715
11147
  if (this.options().pop) {
10716
11148
  this.setTransientPipelinePop();
10717
11149
  }
10718
11150
  }
10719
- async getOnDemandComputeSession(httpClient) {
10720
- let session = null;
10721
- const sessionsUrl = `${this.eyepopUrl()}/v1/sessions`;
10722
- const headers = {
10723
- Authorization: await this.computeAuthorizationHeader()
10724
- };
10725
- let sessions;
10726
- if (!this.options().pop) {
10727
- this._requestLogger.debug("fetching sessions from: %s", sessionsUrl);
10728
- const response = await httpClient.fetch(sessionsUrl, {
10729
- headers
10730
- });
10731
- if (response.status == 404) {
10732
- sessions = [];
10733
- } else if (response.status != 200) {
10734
- this.updateState("Error" /* Error */);
10735
- const message = await response.text();
10736
- throw new Error(`Unexpected status ${response.status} fetching compute sessions: ${message}`);
10737
- } else {
10738
- const response_content = await response.json();
10739
- sessions = response_content;
10740
- }
10741
- if (sessions.length > 0) {
10742
- this._logger.debug(`User has ${sessions.length} sessions, inspecting for usable session`);
10743
- for (let s of sessions) {
10744
- if (!_WorkerEndpoint.SESSION_DEAD.has(s.session_status) && !s.persistent) {
10745
- session = s;
10746
- this._logger.debug(`Use active session ${s.session_uuid} with pipeline ${this.pipelineIdFromSession(s)}`);
10747
- break;
10748
- }
10749
- }
10750
- }
10751
- }
10752
- if (!session) {
10753
- this._requestLogger.debug("creating a new session at: %s", sessionsUrl);
10754
- const createBody = this.sessionCreateBody();
10755
- const createHeaders = {
10756
- ...headers,
10757
- ...createBody ? { "Content-Type": "application/json" } : {}
10758
- };
10759
- const request = {
10760
- headers: createHeaders,
10761
- method: "POST"
10762
- };
10763
- if (createBody) {
10764
- request.body = createBody;
10765
- }
10766
- const response = await httpClient.fetch(`${sessionsUrl}?wait=true`, request);
10767
- if (response.status != 200) {
10768
- this.updateState("Error" /* Error */);
10769
- const message = await response.text();
10770
- throw new Error(`Unexpected status ${response.status} creating a compute session: ${message}`);
10771
- } else {
10772
- sessions = await response.json();
10773
- }
10774
- if (sessions.length == 0) {
10775
- throw new Error("Unexpected, no compute session after attempt to create one");
10776
- }
10777
- session = sessions[0] || null;
10778
- }
10779
- if (!session) {
10780
- throw new Error("unexpected undefined session");
10781
- }
10782
- return session;
10783
- }
10784
- async getPermanentComputeSession(httpClient, sessionUuid) {
10785
- let session = null;
10786
- const sessionUrl = `${this.eyepopUrl()}/v1/sessions/${sessionUuid}`;
10787
- this._requestLogger.debug("fetching session from: %s", sessionUrl);
10788
- const headers = {
10789
- Authorization: await this.computeAuthorizationHeader()
10790
- };
10791
- let response = await httpClient.fetch(sessionUrl, {
10792
- headers
10793
- });
10794
- if (response.status == 404) {
10795
- this.updateState("Error" /* Error */);
10796
- throw new Error(`Unexpected status ${response.status} compute sessions ${sessionUuid} not found`);
10797
- } else if (response.status != 200) {
10798
- this.updateState("Error" /* Error */);
10799
- const message = await response.text();
10800
- throw new Error(`Unexpected status ${response.status} fetching compute sessions: ${message}`);
10801
- } else {
10802
- const response_content = await response.json();
10803
- session = response_content;
10804
- }
10805
- return session;
10806
- }
10807
11151
  async startTransientPipeline() {
10808
11152
  const client = this._client;
10809
11153
  const baseUrl = this._baseUrl;
@@ -10860,6 +11204,13 @@ var _WorkerEndpoint = class _WorkerEndpoint extends Endpoint {
10860
11204
  if (this._pipelineId) {
10861
11205
  return;
10862
11206
  }
11207
+ if (this.options().pop) {
11208
+ await this.reconnect();
11209
+ if (!this._pipelineId) {
11210
+ throw new Error("Compute session did not provide a pipeline for the requested pop");
11211
+ }
11212
+ return;
11213
+ }
10863
11214
  if (!this._pipelineCreatePromise) {
10864
11215
  this._pipelineCreatePromise = this.startTransientPipeline();
10865
11216
  }
@@ -10875,7 +11226,9 @@ var _WorkerEndpoint = class _WorkerEndpoint extends Endpoint {
10875
11226
  throw new Error("endpoint not initialized");
10876
11227
  }
10877
11228
  const previousPop = this.options().pop;
10878
- const previousPipeline = this._pipeline;
11229
+ const previousBaseUrl = this._baseUrl;
11230
+ const previousSessionAccessToken = this._sessionAccessToken;
11231
+ const previousSessionAccessTokenValidUntil = this._sessionAccessTokenValidUntil;
10879
11232
  if (this._pipelineId) {
10880
11233
  await this.deleteTransientPipeline(this._pipelineId);
10881
11234
  this._pipelineId = null;
@@ -10883,10 +11236,14 @@ var _WorkerEndpoint = class _WorkerEndpoint extends Endpoint {
10883
11236
  this._pipeline = null;
10884
11237
  try {
10885
11238
  this.options().pop = pop;
10886
- await this.ensureTransientPipelineStarted();
11239
+ await this.getComputeSession();
10887
11240
  } catch (error) {
10888
11241
  this.options().pop = previousPop;
10889
- this._pipeline = previousPipeline;
11242
+ this._pipeline = null;
11243
+ this._pipelineId = null;
11244
+ this._baseUrl = previousPop ? null : previousBaseUrl;
11245
+ this._sessionAccessToken = previousSessionAccessToken;
11246
+ this._sessionAccessTokenValidUntil = previousSessionAccessTokenValidUntil;
10890
11247
  throw error;
10891
11248
  }
10892
11249
  if (!this._pipelineId) {
@@ -10911,13 +11268,6 @@ var _WorkerEndpoint = class _WorkerEndpoint extends Endpoint {
10911
11268
  this._logger.info(`stopping pipeline failed, status ${response.status}: ${message}`);
10912
11269
  }
10913
11270
  }
10914
- pipelineIdFromSession(session) {
10915
- if (session.pipeline_uuid) {
10916
- return session.pipeline_uuid;
10917
- }
10918
- const pipeline = session.pipelines?.[0];
10919
- return pipeline?.id || pipeline?.pipeline_id || null;
10920
- }
10921
11271
  async authorizationHeader() {
10922
11272
  if (this._sessionAccessToken) {
10923
11273
  return `Bearer ${this._sessionAccessToken}`;
@@ -10933,20 +11283,6 @@ var _WorkerEndpoint = class _WorkerEndpoint extends Endpoint {
10933
11283
  }
10934
11284
  return super.authorizationHeader();
10935
11285
  }
10936
- setComputeSessionAccessToken(session) {
10937
- if (!session.access_token) {
10938
- throw new Error(`Compute session ${session.session_uuid} did not provide an access token`);
10939
- }
10940
- this._sessionAccessToken = session.access_token;
10941
- if (session.access_token_expires_at) {
10942
- const expiresAt = Date.parse(session.access_token_expires_at);
10943
- this._sessionAccessTokenValidUntil = Number.isNaN(expiresAt) ? null : expiresAt;
10944
- } else if (session.access_token_expires_in) {
10945
- this._sessionAccessTokenValidUntil = Date.now() + session.access_token_expires_in * 1e3;
10946
- } else {
10947
- this._sessionAccessTokenValidUntil = null;
10948
- }
10949
- }
10950
11286
  setTransientPipelinePop() {
10951
11287
  const pop = this.options().pop;
10952
11288
  if (pop && this._pipelineId) {
@@ -10958,26 +11294,8 @@ var _WorkerEndpoint = class _WorkerEndpoint extends Endpoint {
10958
11294
  };
10959
11295
  }
10960
11296
  }
10961
- sessionCreateBody() {
10962
- const body = {};
10963
- if (this.options().sessionName) {
10964
- body.session_name = this.options().sessionName;
10965
- }
10966
- if (this.options().pipelineImage) {
10967
- body.pipeline_image = this.options().pipelineImage;
10968
- }
10969
- if (this.options().pipelineVersion) {
10970
- body.pipeline_version = this.options().pipelineVersion;
10971
- }
10972
- if (this.options().pop) {
10973
- body.pop = this.options().pop;
10974
- }
10975
- return Object.keys(body).length > 0 ? JSON.stringify(body) : void 0;
10976
- }
10977
11297
  };
10978
- _WorkerEndpoint.SESSION_DEAD = /* @__PURE__ */ new Set(["error" /* ERROR */, "failed" /* FAILED */, "stopped" /* STOPPED */, "pipeline_error" /* PIPELINE_ERROR */, "unknown" /* UNKNOWN */]);
10979
11298
  _WorkerEndpoint.WAIT_FOR_IS_READY = 60 * 1e3;
10980
- _WorkerEndpoint.CHECK_FOR_IS_READY_INTERVAL = 250;
10981
11299
  var WorkerEndpoint = _WorkerEndpoint;
10982
11300
 
10983
11301
  // shims/websockets.ts
@@ -12468,6 +12786,7 @@ export {
12468
12786
  ForwardOperatorType,
12469
12787
  InferenceType,
12470
12788
  LoadFromAssetUuidJob,
12789
+ LoadFromGroupJob,
12471
12790
  LoadFromJob,
12472
12791
  LoadMediaStreamJob,
12473
12792
  ModelExportStatus,
@@ -12481,12 +12800,12 @@ export {
12481
12800
  PopComponentType,
12482
12801
  PredictionVersion,
12483
12802
  Semaphore,
12484
- SessionStatus,
12485
12803
  Stream,
12486
12804
  TaskMode,
12487
12805
  TaskType,
12488
12806
  TranscodeMode,
12489
12807
  TransientPopId,
12808
+ UploadGroupJob,
12490
12809
  UploadJob,
12491
12810
  UserReview,
12492
12811
  VideoMode,
@@ -12497,7 +12816,9 @@ export {
12497
12816
  WorkerEndpoint,
12498
12817
  WorkflowPhase,
12499
12818
  index_default as default,
12819
+ encodeMultipartFormData,
12500
12820
  getBaseUrl,
12821
+ randomMultipartBoundary,
12501
12822
  readableStreamFromString
12502
12823
  };
12503
12824
  /*! Bundled license information: