"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 __export = (target, all) => {
  for (var name in all)
    __defProp(target, name, { get: all[name], enumerable: true });
};
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 __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);

// src/yaml/index.ts
var yaml_exports = {};
__export(yaml_exports, {
  ScriptPlayer: () => ScriptPlayer,
  buildYaml: () => buildYaml,
  parseYamlScript: () => parseYamlScript
});
module.exports = __toCommonJS(yaml_exports);

// src/yaml/player.ts
var import_node_fs = require("fs");
var import_node_path = require("path");
var import_utils = require("@midscene_spp/shared/utils");
var import_common = require("@midscene_spp/shared/common");
var ScriptPlayer = class {
  constructor(script, setupAgent, onTaskStatusChange) {
    this.script = script;
    this.setupAgent = setupAgent;
    this.onTaskStatusChange = onTaskStatusChange;
    this.taskStatusList = [];
    this.status = "init";
    this.unnamedResultIndex = 0;
    this.pageAgent = null;
    this.result = {};
    if (import_utils.ifInBrowser) {
      this.output = void 0;
    } else if (script.target?.output) {
      this.output = (0, import_node_path.resolve)(process.cwd(), script.target.output);
    } else {
      this.output = (0, import_node_path.join)((0, import_common.getMidsceneRunSubDir)("output"), `${process.pid}.json`);
    }
    this.taskStatusList = (script.tasks || []).map((task, taskIndex) => ({
      ...task,
      index: taskIndex,
      status: "init",
      totalSteps: task.flow?.length || 0
    }));
  }
  setResult(key, value) {
    const keyToUse = key || this.unnamedResultIndex++;
    if (this.result[keyToUse]) {
      console.warn(`result key ${keyToUse} already exists, will overwrite`);
    }
    this.result[keyToUse] = value;
    this.flushResult();
  }
  setPlayerStatus(status, error) {
    this.status = status;
    this.errorInSetup = error;
  }
  notifyCurrentTaskStatusChange(taskIndex) {
    const taskIndexToNotify = typeof taskIndex === "number" ? taskIndex : this.currentTaskIndex;
    if (typeof taskIndexToNotify !== "number") {
      return;
    }
    const taskStatus = this.taskStatusList[taskIndexToNotify];
    if (this.onTaskStatusChange) {
      this.onTaskStatusChange(taskStatus);
    }
  }
  async setTaskStatus(index, statusValue, error) {
    this.taskStatusList[index].status = statusValue;
    if (error) {
      this.taskStatusList[index].error = error;
    }
    this.notifyCurrentTaskStatusChange(index);
  }
  setTaskIndex(taskIndex) {
    this.currentTaskIndex = taskIndex;
  }
  flushResult() {
    if (Object.keys(this.result).length && this.output) {
      const output = (0, import_node_path.resolve)(process.cwd(), this.output);
      const outputDir = (0, import_node_path.dirname)(output);
      if (!(0, import_node_fs.existsSync)(outputDir)) {
        (0, import_node_fs.mkdirSync)(outputDir, { recursive: true });
      }
      (0, import_node_fs.writeFileSync)(output, JSON.stringify(this.result, void 0, 2));
    }
  }
  async playTask(taskStatus, agent) {
    const { flow } = taskStatus;
    (0, import_utils.assert)(flow, "missing flow in task");
    for (const flowItemIndex in flow) {
      const currentStep = Number.parseInt(flowItemIndex, 10);
      taskStatus.currentStep = currentStep;
      const flowItem = flow[flowItemIndex];
      if ("aiAction" in flowItem || "ai" in flowItem) {
        const actionTask = flowItem;
        const prompt = actionTask.aiAction || actionTask.ai;
        (0, import_utils.assert)(prompt, "missing prompt for ai (aiAction)");
        (0, import_utils.assert)(
          typeof prompt === "string",
          "prompt for aiAction must be a string"
        );
        await agent.aiAction(prompt, {
          cacheable: actionTask.cacheable
        });
      } else if ("aiAssert" in flowItem) {
        const assertTask = flowItem;
        const prompt = assertTask.aiAssert;
        (0, import_utils.assert)(prompt, "missing prompt for aiAssert");
        (0, import_utils.assert)(
          typeof prompt === "string",
          "prompt for aiAssert must be a string"
        );
        await agent.aiAssert(prompt);
      } else if ("aiQuery" in flowItem) {
        const queryTask = flowItem;
        const prompt = queryTask.aiQuery;
        (0, import_utils.assert)(prompt, "missing prompt for aiQuery");
        (0, import_utils.assert)(
          typeof prompt === "string",
          "prompt for aiQuery must be a string"
        );
        const queryResult = await agent.aiQuery(prompt);
        this.setResult(queryTask.name, queryResult);
      } else if ("aiNumber" in flowItem) {
        const numberTask = flowItem;
        const prompt = numberTask.aiNumber;
        (0, import_utils.assert)(prompt, "missing prompt for number");
        (0, import_utils.assert)(
          typeof prompt === "string",
          "prompt for number must be a string"
        );
        const numberResult = await agent.aiNumber(prompt);
        this.setResult(numberTask.name, numberResult);
      } else if ("aiString" in flowItem) {
        const stringTask = flowItem;
        const prompt = stringTask.aiString;
        (0, import_utils.assert)(prompt, "missing prompt for string");
        (0, import_utils.assert)(
          typeof prompt === "string",
          "prompt for string must be a string"
        );
        const stringResult = await agent.aiString(prompt);
        this.setResult(stringTask.name, stringResult);
      } else if ("aiBoolean" in flowItem) {
        const booleanTask = flowItem;
        const prompt = booleanTask.aiBoolean;
        (0, import_utils.assert)(prompt, "missing prompt for boolean");
        (0, import_utils.assert)(
          typeof prompt === "string",
          "prompt for boolean must be a string"
        );
        const booleanResult = await agent.aiBoolean(prompt);
        this.setResult(booleanTask.name, booleanResult);
      } else if ("aiLocate" in flowItem) {
        const locateTask = flowItem;
        const prompt = locateTask.aiLocate;
        (0, import_utils.assert)(prompt, "missing prompt for aiLocate");
        (0, import_utils.assert)(
          typeof prompt === "string",
          "prompt for aiLocate must be a string"
        );
        const locateResult = await agent.aiLocate(prompt);
        this.setResult(locateTask.name, locateResult);
      } else if ("aiWaitFor" in flowItem) {
        const waitForTask = flowItem;
        const prompt = waitForTask.aiWaitFor;
        (0, import_utils.assert)(prompt, "missing prompt for aiWaitFor");
        (0, import_utils.assert)(
          typeof prompt === "string",
          "prompt for aiWaitFor must be a string"
        );
        const timeout = waitForTask.timeout;
        await agent.aiWaitFor(prompt, { timeoutMs: timeout });
      } else if ("sleep" in flowItem) {
        const sleepTask = flowItem;
        const ms = sleepTask.sleep;
        let msNumber = ms;
        if (typeof ms === "string") {
          msNumber = Number.parseInt(ms, 10);
        }
        (0, import_utils.assert)(
          msNumber && msNumber > 0,
          `ms for sleep must be greater than 0, but got ${ms}`
        );
        await new Promise((resolve2) => setTimeout(resolve2, msNumber));
      } else if ("aiTap" in flowItem) {
        const tapTask = flowItem;
        await agent.aiTap(tapTask.aiTap, tapTask);
      } else if ("aiHover" in flowItem) {
        const hoverTask = flowItem;
        await agent.aiHover(hoverTask.aiHover, hoverTask);
      } else if ("aiInput" in flowItem) {
        const inputTask = flowItem;
        await agent.aiInput(inputTask.aiInput, inputTask.locate, inputTask);
      } else if ("aiKeyboardPress" in flowItem) {
        const keyboardPressTask = flowItem;
        await agent.aiKeyboardPress(
          keyboardPressTask.aiKeyboardPress,
          keyboardPressTask.locate,
          keyboardPressTask
        );
      } else if ("aiScroll" in flowItem) {
        const scrollTask = flowItem;
        await agent.aiScroll(scrollTask, scrollTask.locate, scrollTask);
      } else if ("javascript" in flowItem) {
        const evaluateJavaScriptTask = flowItem;
        const result = await agent.evaluateJavaScript(
          evaluateJavaScriptTask.javascript
        );
        this.setResult(evaluateJavaScriptTask.name, result);
      } else {
        throw new Error(`unknown flowItem: ${JSON.stringify(flowItem)}`);
      }
    }
    this.reportFile = agent.reportFile;
  }
  async run() {
    const { target, web, android, tasks } = this.script;
    const webEnv = web || target;
    const androidEnv = android;
    const platform = webEnv || androidEnv;
    this.setPlayerStatus("running");
    let agent = null;
    let freeFn = [];
    try {
      const { agent: newAgent, freeFn: newFreeFn } = await this.setupAgent(
        platform
      );
      agent = newAgent;
      const originalOnTaskStartTip = agent.onTaskStartTip;
      agent.onTaskStartTip = (tip) => {
        if (this.status === "running") {
          this.agentStatusTip = tip;
        }
        originalOnTaskStartTip?.(tip);
      };
      freeFn = [
        ...newFreeFn || [],
        {
          name: "restore-agent-onTaskStartTip",
          fn: () => {
            if (agent) {
              agent.onTaskStartTip = originalOnTaskStartTip;
            }
          }
        }
      ];
    } catch (e) {
      this.setPlayerStatus("error", e);
      return;
    }
    this.pageAgent = agent;
    let taskIndex = 0;
    this.setPlayerStatus("running");
    let errorFlag = false;
    while (taskIndex < tasks.length) {
      const taskStatus = this.taskStatusList[taskIndex];
      this.setTaskStatus(taskIndex, "running");
      this.setTaskIndex(taskIndex);
      try {
        await this.playTask(taskStatus, this.pageAgent);
        this.setTaskStatus(taskIndex, "done");
      } catch (e) {
        this.setTaskStatus(taskIndex, "error", e);
        if (taskStatus.continueOnError) {
        } else {
          this.reportFile = agent.reportFile;
          errorFlag = true;
          break;
        }
      }
      this.reportFile = agent.reportFile;
      taskIndex++;
    }
    if (errorFlag) {
      this.setPlayerStatus("error");
    } else {
      this.setPlayerStatus("done");
    }
    this.agentStatusTip = "";
    for (const fn of freeFn) {
      try {
        await fn.fn();
      } catch (e) {
      }
    }
  }
};

// src/yaml/builder.ts
var import_js_yaml = __toESM(require("js-yaml"));
function buildYaml(env, tasks) {
  const result = {
    target: env,
    tasks
  };
  return import_js_yaml.default.dump(result, {
    indent: 2
  });
}

// src/yaml/utils.ts
var import_utils2 = require("@midscene_spp/shared/utils");
var import_js_yaml2 = __toESM(require("js-yaml"));
function interpolateEnvVars(content) {
  return content.replace(/\$\{([^}]+)\}/g, (_, envVar) => {
    const value = process.env[envVar.trim()];
    if (value === void 0) {
      throw new Error(`Environment variable "${envVar.trim()}" is not defined`);
    }
    return value;
  });
}
function parseYamlScript(content, filePath, ignoreCheckingTarget) {
  let processedContent = content;
  if (content.indexOf("android") !== -1 && content.match(/deviceId:\s*(\d+)/)) {
    let matchedDeviceId;
    processedContent = content.replace(
      /deviceId:\s*(\d+)/g,
      (match, deviceId) => {
        matchedDeviceId = deviceId;
        return `deviceId: '${deviceId}'`;
      }
    );
    console.warn(
      `please use string-style deviceId in yaml script, for example: deviceId: "${matchedDeviceId}"`
    );
  }
  const interpolatedContent = interpolateEnvVars(processedContent);
  const obj = import_js_yaml2.default.load(interpolatedContent, {
    schema: import_js_yaml2.default.JSON_SCHEMA
  });
  const pathTip = filePath ? `, failed to load ${filePath}` : "";
  const android = typeof obj.android !== "undefined" ? Object.assign({}, obj.android || {}) : void 0;
  const webConfig = obj.web || obj.target;
  const web = typeof webConfig !== "undefined" ? Object.assign({}, webConfig || {}) : void 0;
  if (!ignoreCheckingTarget) {
    (0, import_utils2.assert)(
      web || android,
      `at least one of "target", "web", or "android" properties is required in yaml script${pathTip}`
    );
    (0, import_utils2.assert)(
      web && !android || !web && android,
      `only one of "target", "web", or "android" properties is allowed in yaml script${pathTip}`
    );
    if (web || android) {
      (0, import_utils2.assert)(
        typeof web === "object" || typeof android === "object",
        `property "target/web/android" must be an object${pathTip}`
      );
    }
  }
  (0, import_utils2.assert)(obj.tasks, `property "tasks" is required in yaml script ${pathTip}`);
  (0, import_utils2.assert)(
    Array.isArray(obj.tasks),
    `property "tasks" must be an array in yaml script, but got ${obj.tasks}`
  );
  return obj;
}
// Annotate the CommonJS export names for ESM import in node:
0 && (module.exports = {
  ScriptPlayer,
  buildYaml,
  parseYamlScript
});

//# sourceMappingURL=yaml.js.map