// src/playground/server.ts
import { randomUUID } from "crypto";
import { existsSync, readFileSync, writeFileSync } from "fs";
import { join } from "path";

// src/common/utils.ts
import { elementByPositionWithElementInfo } from "@midscene_spp/core/ai-model";
import { uploadTestInfoToServer } from "@midscene_spp/core/utils";
import { MIDSCENE_REPORT_TAG_NAME, getAIConfig } from "@midscene_spp/shared/env";
import {
  generateElementByPosition,
  getNodeFromCacheList,
  traverseTree,
  treeToList
} from "@midscene_spp/shared/extractor";
import { resizeImgBase64 } from "@midscene_spp/shared/img";
import { assert, logMsg, uuid } from "@midscene_spp/shared/utils";
import dayjs from "dayjs";
var ERROR_CODE_NOT_IMPLEMENTED_AS_DESIGNED = "NOT_IMPLEMENTED_AS_DESIGNED";

// src/playground/server.ts
import { getTmpDir } from "@midscene_spp/core/utils";
import { PLAYGROUND_SERVER_PORT } from "@midscene_spp/shared/constants";
import { overrideAIConfig } from "@midscene_spp/shared/env";
import { ifInBrowser } from "@midscene_spp/shared/utils";
import cors from "cors";
import dotenv from "dotenv";
import express from "express";
var defaultPort = PLAYGROUND_SERVER_PORT;
var errorHandler = (err, req, res, next) => {
  console.error(err);
  res.status(500).json({
    error: err.message
  });
};
var setup = async () => {
  if (!ifInBrowser) {
    const { parsed } = dotenv.config();
    if (parsed) {
      overrideAIConfig(parsed);
    }
  }
};
var PlaygroundServer = class {
  constructor(pageClass, agentClass, staticPath) {
    this.app = express();
    this.tmpDir = getTmpDir();
    this.pageClass = pageClass;
    this.agentClass = agentClass;
    this.staticPath = staticPath;
    this.taskProgressTips = {};
    setup();
  }
  filePathForUuid(uuid2) {
    return join(this.tmpDir, `${uuid2}.json`);
  }
  saveContextFile(uuid2, context) {
    const tmpFile = this.filePathForUuid(uuid2);
    console.log(`save context file: ${tmpFile}`);
    writeFileSync(tmpFile, context);
    return tmpFile;
  }
  async launch(port) {
    this.port = port || defaultPort;
    this.app.use(errorHandler);
    this.app.use(
      cors({
        origin: "*",
        credentials: true
      })
    );
    this.app.get("/status", cors(), async (req, res) => {
      res.send({
        status: "ok"
      });
    });
    this.app.get("/context/:uuid", async (req, res) => {
      const { uuid: uuid2 } = req.params;
      const contextFile = this.filePathForUuid(uuid2);
      if (!existsSync(contextFile)) {
        return res.status(404).json({
          error: "Context not found"
        });
      }
      const context = readFileSync(contextFile, "utf8");
      res.json({
        context
      });
    });
    this.app.get("/task-progress/:requestId", cors(), async (req, res) => {
      const { requestId } = req.params;
      res.json({
        tip: this.taskProgressTips[requestId] || ""
      });
    });
    this.app.post(
      "/playground-with-context",
      express.json({ limit: "50mb" }),
      async (req, res) => {
        const context = req.body.context;
        if (!context) {
          return res.status(400).json({
            error: "context is required"
          });
        }
        const uuid2 = randomUUID();
        this.saveContextFile(uuid2, context);
        return res.json({
          location: `/playground/${uuid2}`,
          uuid: uuid2
        });
      }
    );
    this.app.post(
      "/execute",
      express.json({ limit: "30mb" }),
      async (req, res) => {
        const { context, type, prompt, requestId, deepThink } = req.body;
        if (!context) {
          return res.status(400).json({
            error: "context is required"
          });
        }
        if (!type) {
          return res.status(400).json({
            error: "type is required"
          });
        }
        if (!prompt) {
          return res.status(400).json({
            error: "prompt is required"
          });
        }
        const page = new this.pageClass(context);
        const agent = new this.agentClass(page);
        if (requestId) {
          this.taskProgressTips[requestId] = "";
          agent.onTaskStartTip = (tip) => {
            this.taskProgressTips[requestId] = tip;
          };
        }
        const response = {
          result: null,
          dump: null,
          error: null,
          reportHTML: null,
          requestId
        };
        const startTime = Date.now();
        try {
          if (type === "aiQuery") {
            response.result = await agent.aiQuery(prompt);
          } else if (type === "aiAction") {
            response.result = await agent.aiAction(prompt);
          } else if (type === "aiAssert") {
            response.result = await agent.aiAssert(prompt, void 0, {
              keepRawResponse: true
            });
          } else if (type === "aiTap") {
            response.result = await agent.aiTap(prompt, {
              deepThink
            });
          } else {
            response.error = `Unknown type: ${type}`;
          }
        } catch (error) {
          if (!error.message.includes(ERROR_CODE_NOT_IMPLEMENTED_AS_DESIGNED)) {
            response.error = error.message;
          }
        }
        try {
          response.dump = JSON.parse(agent.dumpDataString());
          response.reportHTML = agent.reportHTMLString() || null;
          agent.writeOutActionDumps();
        } catch (error) {
          console.error(
            `write out dump failed: requestId: ${requestId}, ${error.message}`
          );
        }
        res.send(response);
        const timeCost = Date.now() - startTime;
        if (response.error) {
          console.error(
            `handle request failed after ${timeCost}ms: requestId: ${requestId}, ${response.error}`
          );
        } else {
          console.log(
            `handle request done after ${timeCost}ms: requestId: ${requestId}`
          );
        }
      }
    );
    this.app.post(
      "/config",
      express.json({ limit: "1mb" }),
      async (req, res) => {
        const { aiConfig } = req.body;
        if (!aiConfig || typeof aiConfig !== "object") {
          return res.status(400).json({
            error: "aiConfig is required and must be an object"
          });
        }
        try {
          overrideAIConfig(aiConfig);
          return res.json({
            status: "ok",
            message: "AI config updated successfully"
          });
        } catch (error) {
          console.error(`Failed to update AI config: ${error.message}`);
          return res.status(500).json({
            error: `Failed to update AI config: ${error.message}`
          });
        }
      }
    );
    if (this.staticPath) {
      this.app.get("/", (req, res) => {
        res.redirect("/index.html");
      });
      this.app.get("*", (req, res) => {
        const requestedPath = join(this.staticPath, req.path);
        if (existsSync(requestedPath)) {
          res.sendFile(requestedPath);
        } else {
          res.sendFile(join(this.staticPath, "index.html"));
        }
      });
    }
    return new Promise((resolve, reject) => {
      const port2 = this.port;
      this.server = this.app.listen(port2, () => {
        resolve(this);
      });
    });
  }
  close() {
    if (this.server) {
      return this.server.close();
    }
  }
};
export {
  PlaygroundServer as default
};

//# sourceMappingURL=midscene-server.js.map