"use strict";
var __create = Object.create;
var __defProp = Object.defineProperty;
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
var __getOwnPropNames = Object.getOwnPropertyNames;
var __getProtoOf = Object.getPrototypeOf;
var __hasOwnProp = Object.prototype.hasOwnProperty;
var __copyProps = (to, from, except, desc) => {
  if (from && typeof from === "object" || typeof from === "function") {
    for (let key of __getOwnPropNames(from))
      if (!__hasOwnProp.call(to, key) && key !== except)
        __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
  }
  return to;
};
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
  // If the importer is in node compatibility mode or this is not an ESM
  // file that has been converted to a CommonJS file using a Babel-
  // compatible transform (i.e. "__esModule" has not been set), then set
  // "default" to the CommonJS "module.exports" for node compatibility.
  isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
  mod
));
var import_rtc_node = require("@livekit/rtc-node");
var import_throws = require("@livekit/throws-transformer/throws");
var import_node_events = require("node:events");
var import_node_url = require("node:url");
var import_generator = require("../generator.cjs");
var import_job = require("../job.cjs");
var import_log = require("../log.cjs");
var import_utils = require("../utils.cjs");
var import_worker = require("../worker.cjs");
const ORPHANED_TIMEOUT = 15 * 1e3;
const SESSION_CLOSE_TIMEOUT = 60 * 1e3;
const safeSend = (msg) => {
  try {
    if (process.connected && process.send) {
      process.send(msg);
      return true;
    }
    return false;
  } catch (error) {
    if (error instanceof Error && error.message.includes("Channel closed")) {
      (0, import_log.log)().debug({ msgCase: msg.case }, "IPC channel closed, message not sent");
    } else {
      (0, import_log.log)().error({ error, msgCase: msg.case }, "IPC send failed unexpectedly");
    }
    return false;
  }
};
class PendingInference {
  promise = new import_throws.ThrowsPromise(
    (resolve) => {
      this.resolve = resolve;
    }
  );
  resolve(arg) {
    arg;
  }
}
class InfClient {
  #requests = {};
  #logger = (0, import_log.log)();
  constructor() {
    process.on("message", (msg) => {
      switch (msg.case) {
        case "inferenceResponse":
          const fut = this.#requests[msg.value.requestId];
          delete this.#requests[msg.value.requestId];
          if (!fut) {
            this.#logger.child({ resp: msg.value }).warn("received unexpected inference response");
            return;
          }
          fut.resolve(msg.value);
          break;
      }
    });
  }
  async doInference(method, data) {
    const requestId = (0, import_utils.shortuuid)("inference_job_");
    if (!safeSend({ case: "inferenceRequest", value: { requestId, method, data } })) {
      this.#logger.debug(
        { method, requestId },
        "IPC channel closed during inference, aborting gracefully"
      );
      throw new Error(`Inference ${method} aborted: IPC channel closed (expected during shutdown)`);
    }
    this.#requests[requestId] = new PendingInference();
    const resp = await this.#requests[requestId].promise;
    if (resp.error) {
      throw new Error(`inference of ${method} failed: ${resp.error.message}`);
    }
    return resp.data;
  }
}
const startJob = (proc, func, info, closeEvent, logger, joinFuture) => {
  let connect = false;
  let shutdown = false;
  const room = new import_rtc_node.Room();
  room.on(import_rtc_node.RoomEvent.Disconnected, () => {
    if (!shutdown) {
      closeEvent.emit("close", false);
    }
  });
  const onConnect = () => {
    connect = true;
  };
  const onShutdown = (reason) => {
    shutdown = true;
    closeEvent.emit("close", true, reason);
  };
  const ctx = new import_job.JobContext(proc, info, room, onConnect, onShutdown, new InfClient());
  const task = (async () => {
    const unconnectedTimeout = setTimeout(() => {
      if (!(connect || shutdown)) {
        logger.warn(
          "room not connect after job_entry was called after 10 seconds, ",
          "did you forget to call ctx.connect()?"
        );
      }
    }, 1e4);
    try {
      const closePromise = (0, import_node_events.once)(closeEvent, "close").then((close) => {
        logger.debug("shutting down");
        shutdown = true;
        safeSend({ case: "exiting", value: { reason: close[1] } });
      });
      await (0, import_job.runWithJobContextAsync)(ctx, async () => {
        const { tracer, traceTypes } = await import("../telemetry/index.cjs");
        return tracer.startActiveSpan(
          async (span) => {
            var _a;
            span.setAttribute(traceTypes.ATTR_JOB_ID, info.job.id);
            span.setAttribute(traceTypes.ATTR_AGENT_NAME, info.job.agentName);
            span.setAttribute(traceTypes.ATTR_ROOM_NAME, ((_a = info.job.room) == null ? void 0 : _a.name) ?? "");
            return func(ctx);
          },
          { name: "job_entrypoint" }
        );
      }).then(async () => {
        if (!shutdown) {
          await closePromise;
        }
      }).finally(async () => {
        clearTimeout(unconnectedTimeout);
      });
    } catch (error) {
      logger.error({ error }, "error in entry function");
      shutdown = true;
      safeSend({
        case: "exiting",
        value: { reason: error instanceof Error ? error.message : String(error) }
      });
    }
    if (ctx._primaryAgentSession) {
      const sessionClosePromise = ctx._primaryAgentSession.close();
      try {
        await (0, import_utils.waitUntilTimeout)(sessionClosePromise, SESSION_CLOSE_TIMEOUT);
      } catch (error) {
        if (!(error instanceof import_utils.IdleTimeoutError)) {
          throw error;
        }
        void sessionClosePromise.catch(
          (sessionCloseError) => logger.debug(
            { error: sessionCloseError },
            "AgentSession.close() rejected after shutdown timeout"
          )
        );
        logger.error(
          { timeout: SESSION_CLOSE_TIMEOUT },
          "AgentSession.close() timed out; proceeding with shutdown so registered callbacks still run."
        );
      }
    }
    try {
      await ctx._onSessionEnd();
    } catch (error) {
      logger.error({ error }, "error in ctx._onSessionEnd");
    }
    await room.disconnect();
    logger.debug("disconnected from room");
    const shutdownTasks = [];
    for (const callback of ctx.shutdownCallbacks) {
      shutdownTasks.push(callback());
    }
    await import_throws.ThrowsPromise.all(shutdownTasks).catch(
      (error) => logger.error({ error }, "error while shutting down the job")
    );
    safeSend({ case: "done", value: void 0 });
    joinFuture.resolve();
  })();
  return { ctx, task };
};
(async () => {
  if (process.send) {
    const join = new import_utils.Future();
    const moduleFile = process.argv[2];
    const agent = await import((0, import_node_url.pathToFileURL)(moduleFile).pathname).then((module2) => {
      var _a;
      const agent2 = typeof module2.default === "function" || (0, import_generator.isAgent)(module2.default) ? module2.default : (_a = module2.default) == null ? void 0 : _a.default;
      if (agent2 === void 0 || !(0, import_generator.isAgent)(agent2)) {
        throw new Error(`Unable to load agent: Missing or invalid default export in ${moduleFile}`);
      }
      return agent2;
    });
    if (!agent.prewarm) {
      agent.prewarm = import_worker.defaultInitializeProcessFunc;
    }
    process.on("SIGINT", () => {
      logger.debug("SIGINT received in job proc");
    });
    process.on("SIGTERM", () => {
      logger.debug("SIGTERM received in job proc");
    });
    await (0, import_node_events.once)(process, "message").then(([msg]) => {
      msg = msg;
      if (msg.case !== "initializeRequest") {
        throw new Error("first message must be InitializeRequest");
      }
      (0, import_log.initializeLogger)(msg.value.loggerOptions);
    });
    const proc = new import_job.JobProcess();
    let logger = (0, import_log.log)().child({ pid: proc.pid });
    process.on("unhandledRejection", (reason) => {
      logger.debug({ error: reason }, "Unhandled promise rejection");
    });
    logger.debug("initializing job runner");
    await agent.prewarm(proc);
    logger.debug("job runner initialized");
    safeSend({ case: "initializeResponse", value: void 0 });
    let job = void 0;
    const closeEvent = new import_node_events.EventEmitter();
    const orphanedTimeout = setTimeout(() => {
      logger.warn("job process orphaned, shutting down.");
      join.resolve();
    }, ORPHANED_TIMEOUT);
    const messageHandler = (msg) => {
      switch (msg.case) {
        case "pingRequest": {
          orphanedTimeout.refresh();
          safeSend({
            case: "pongResponse",
            value: { lastTimestamp: msg.value.timestamp, timestamp: Date.now() }
          });
          break;
        }
        case "startJobRequest": {
          if (job) {
            throw new Error("job task already running");
          }
          logger = logger.child({ jobID: msg.value.runningJob.job.id });
          job = startJob(proc, agent.entry, msg.value.runningJob, closeEvent, logger, join);
          logger.debug("job started");
          break;
        }
        case "shutdownRequest": {
          if (!job) {
            join.resolve();
          }
          closeEvent.emit("close", "shutdownRequest");
          clearTimeout(orphanedTimeout);
          process.off("message", messageHandler);
        }
      }
    };
    process.on("message", messageHandler);
    await join.await;
    try {
      await (0, import_rtc_node.dispose)();
      logger.debug("native resources disposed");
    } catch (error) {
      logger.warn({ error }, "failed to dispose native resources");
    }
    logger.debug("Job process shutdown");
    process.exit(0);
  }
})();
//# sourceMappingURL=job_proc_lazy_main.cjs.map