{"version":3,"file":"yaml/utils.mjs","sources":["../../../src/yaml/utils.ts"],"sourcesContent":["import type { TMultimodalPrompt, TUserPrompt } from '@/common';\nimport type {\n  DetailedLocateParam,\n  LocateOption,\n  MidsceneYamlScript,\n} from '@/types';\nimport { getDebug } from '@midscene/shared/logger';\nimport { assert } from '@midscene/shared/utils';\nimport yaml from 'js-yaml';\n\nconst debugUtils = getDebug('yaml:utils');\n\nconst topLevelTasksPattern = /^tasks\\s*:/;\nconst topLevelYamlKeyPattern = /^[^\\s#][^:]*:/;\n\nfunction interpolateEnvVarRefs(\n  value: string,\n  keepUnresolvedRefs = false,\n): string {\n  let result = '';\n  let lastIndex = 0;\n  let searchFrom = 0;\n\n  while (searchFrom < value.length) {\n    const start = value.indexOf('${', searchFrom);\n    if (start === -1) {\n      break;\n    }\n\n    const end = value.indexOf('}', start + 2);\n    if (end === -1) {\n      break;\n    }\n\n    const rawName = value.slice(start + 2, end);\n    if (!rawName) {\n      searchFrom = end + 1;\n      continue;\n    }\n\n    result += value.slice(lastIndex, start);\n    const envVar = rawName.trim();\n    const envValue = process.env[envVar];\n    if (envValue === undefined) {\n      if (keepUnresolvedRefs) {\n        result += value.slice(start, end + 1);\n      } else {\n        throw new Error(`Environment variable \"${envVar}\" is not defined`);\n      }\n    } else {\n      result += envValue;\n    }\n    lastIndex = end + 1;\n    searchFrom = end + 1;\n  }\n\n  return result + value.slice(lastIndex);\n}\n\nconst multimodalLocateOptionFieldMap: Record<keyof TMultimodalPrompt, true> = {\n  images: true,\n  convertHttpImage2Base64: true,\n};\n\nconst multimodalLocateOptionKeys = Object.keys(\n  multimodalLocateOptionFieldMap,\n) as Array<keyof TMultimodalPrompt>;\n\nfunction extractMultimodalPrompt(\n  opt?: LocateOption,\n): Partial<TMultimodalPrompt> | undefined {\n  if (typeof opt !== 'object' || opt === null) {\n    return undefined;\n  }\n\n  const entries = multimodalLocateOptionKeys\n    .map((key) => [key, opt[key]] as const)\n    .filter(([, value]) => value !== undefined);\n\n  return entries.length\n    ? (Object.fromEntries(entries) as Partial<TMultimodalPrompt>)\n    : undefined;\n}\n\nexport function interpolateEnvVars(content: string): string {\n  // Process line by line to skip commented lines\n  const lines = content.split('\\n');\n  const processedLines = lines.map((line) => {\n    // Check if the line is a YAML comment (starts with # after optional whitespace)\n    const trimmedLine = line.trimStart();\n    if (trimmedLine.startsWith('#')) {\n      // Skip interpolation for comment lines\n      return line;\n    }\n\n    // Process environment variables for non-comment lines\n    return interpolateEnvVarRefs(line);\n  });\n\n  return processedLines.join('\\n');\n}\n\nfunction interpolateYamlScriptEnvVars(content: string): string {\n  let inTopLevelTasksBlock = false;\n\n  return content\n    .split('\\n')\n    .map((line) => {\n      const trimmedLine = line.trimStart();\n      if (!trimmedLine || trimmedLine.startsWith('#')) {\n        return line;\n      }\n\n      const indentSize = line.length - trimmedLine.length;\n      if (indentSize === 0) {\n        if (topLevelTasksPattern.test(trimmedLine)) {\n          inTopLevelTasksBlock = true;\n          return line;\n        }\n\n        if (topLevelYamlKeyPattern.test(trimmedLine)) {\n          inTopLevelTasksBlock = false;\n        }\n      }\n\n      return interpolateEnvVarRefs(line, inTopLevelTasksBlock);\n    })\n    .join('\\n');\n}\n\nexport function parseYamlScript(\n  content: string,\n  filePath?: string,\n): MidsceneYamlScript {\n  let processedContent = content;\n  if (content.indexOf('android') !== -1 && content.match(/deviceId:\\s*(\\d+)/)) {\n    let matchedDeviceId;\n    processedContent = content.replace(\n      /deviceId:\\s*(\\d+)/g,\n      (match, deviceId) => {\n        matchedDeviceId = deviceId;\n        return `deviceId: '${deviceId}'`;\n      },\n    );\n    console.warn(\n      `please use string-style deviceId in yaml script, for example: deviceId: \"${matchedDeviceId}\"`,\n    );\n  }\n  const interpolatedContent = interpolateYamlScriptEnvVars(processedContent);\n  const obj = yaml.load(interpolatedContent, {\n    schema: yaml.JSON_SCHEMA,\n  }) as MidsceneYamlScript;\n\n  const pathTip = filePath ? `, failed to load ${filePath}` : '';\n  assert(obj.tasks, `property \"tasks\" is required in yaml script ${pathTip}`);\n  assert(\n    Array.isArray(obj.tasks),\n    `property \"tasks\" must be an array in yaml script, but got ${obj.tasks}`,\n  );\n  return obj;\n}\n\nexport function buildDetailedLocateParam(\n  locatePrompt: TUserPrompt,\n  opt?: LocateOption,\n): DetailedLocateParam | undefined {\n  debugUtils('will call buildDetailedLocateParam', locatePrompt, opt);\n  // Normalize object-form TUserPrompt: when the object only contains a\n  // `prompt` string (no multimodal fields like `images`), unwrap it to\n  // avoid double nesting like { prompt: { prompt: '...' } }.\n  let normalizedLocatePrompt: TUserPrompt = locatePrompt;\n  if (\n    typeof locatePrompt === 'object' &&\n    locatePrompt !== null &&\n    'prompt' in locatePrompt\n  ) {\n    const { prompt: innerPrompt, ...rest } = locatePrompt;\n    const hasMultimodalFields = Object.keys(rest).length > 0;\n    normalizedLocatePrompt = hasMultimodalFields ? locatePrompt : innerPrompt;\n  }\n\n  let prompt = normalizedLocatePrompt || opt?.prompt || (opt as any)?.locate; // as a shortcut\n  let deepLocate = false;\n  let cacheable = true;\n  let xpath = undefined;\n\n  if (typeof opt === 'object' && opt !== null) {\n    // Backward-compatible: accept `deepThink` as a deprecated alias for `deepLocate`.\n    // All downstream code works on `deepLocate` only; the compatibility resolution\n    // is intentionally kept here at the entry point so it does not bleed through\n    // the rest of the call stack.\n    deepLocate = opt.deepLocate ?? opt.deepThink ?? false;\n    cacheable = opt.cacheable ?? true;\n    xpath = opt.xpath;\n    if (locatePrompt && opt.prompt && locatePrompt !== opt.prompt) {\n      console.warn(\n        'conflict prompt for item',\n        locatePrompt,\n        opt,\n        'maybe you put the prompt in the wrong place',\n      );\n    }\n    prompt = prompt || opt.prompt;\n  }\n\n  if (!prompt) {\n    debugUtils(\n      'no prompt, will return undefined in buildDetailedLocateParam',\n      opt,\n    );\n    return undefined;\n  }\n\n  const multimodalPrompt = extractMultimodalPrompt(opt);\n  if (multimodalPrompt) {\n    prompt =\n      typeof prompt === 'string'\n        ? {\n            prompt,\n            ...multimodalPrompt,\n          }\n        : {\n            ...prompt,\n            ...multimodalPrompt,\n          };\n  }\n\n  return {\n    prompt,\n    deepLocate,\n    cacheable,\n    xpath,\n  };\n}\n\nexport function buildDetailedLocateParamAndRestParams(\n  locatePrompt: TUserPrompt,\n  opt: LocateOption | undefined,\n  excludeKeys: string[] = [],\n): {\n  locateParam: DetailedLocateParam | undefined;\n  restParams: Record<string, any>;\n} {\n  const multimodalPrompt = extractMultimodalPrompt(opt);\n  const locateParam = buildDetailedLocateParam(locatePrompt, opt);\n\n  // Extract all keys from opt except the ones already included in locateParam\n  const restParams: Record<string, any> = {};\n\n  if (typeof opt === 'object' && opt !== null) {\n    // Get all keys from opt\n    const allKeys = Object.keys(opt);\n\n    // Keys already included in locateParam: prompt, deepLocate, cacheable, xpath\n    const locateParamKeys = Object.keys(locateParam || {});\n    const multimodalPromptKeys =\n      typeof locateParam?.prompt === 'object' && locateParam?.prompt !== null\n        ? Object.keys(multimodalPrompt || {})\n        : [];\n\n    // Extract all other keys\n    for (const key of allKeys) {\n      if (\n        !locateParamKeys.includes(key) &&\n        !multimodalPromptKeys.includes(key) &&\n        !excludeKeys.includes(key) &&\n        key !== 'locate'\n      ) {\n        restParams[key] = opt[key as keyof LocateOption];\n      }\n    }\n  }\n\n  return {\n    locateParam,\n    restParams,\n  };\n}\n"],"names":["debugUtils","getDebug","topLevelTasksPattern","topLevelYamlKeyPattern","interpolateEnvVarRefs","value","keepUnresolvedRefs","result","lastIndex","searchFrom","start","end","rawName","envVar","envValue","process","undefined","Error","multimodalLocateOptionFieldMap","multimodalLocateOptionKeys","Object","extractMultimodalPrompt","opt","entries","key","interpolateEnvVars","content","lines","processedLines","line","trimmedLine","interpolateYamlScriptEnvVars","inTopLevelTasksBlock","indentSize","parseYamlScript","filePath","processedContent","matchedDeviceId","match","deviceId","console","interpolatedContent","obj","yaml","pathTip","assert","Array","buildDetailedLocateParam","locatePrompt","normalizedLocatePrompt","innerPrompt","rest","hasMultimodalFields","prompt","deepLocate","cacheable","xpath","multimodalPrompt","buildDetailedLocateParamAndRestParams","excludeKeys","locateParam","restParams","allKeys","locateParamKeys","multimodalPromptKeys"],"mappings":"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"}