@midscene/core 1.9.7 → 1.9.8-beta-20260618014851.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.
- package/dist/es/agent/agent.mjs +29 -20
- package/dist/es/agent/agent.mjs.map +1 -1
- package/dist/es/agent/record-to-report.mjs +14 -0
- package/dist/es/agent/record-to-report.mjs.map +1 -0
- package/dist/es/agent/utils.mjs +14 -2
- package/dist/es/agent/utils.mjs.map +1 -1
- package/dist/es/index.mjs.map +1 -1
- package/dist/es/report-markdown.mjs +2 -1
- package/dist/es/report-markdown.mjs.map +1 -1
- package/dist/es/skill/index.mjs +1 -1
- package/dist/es/skill/index.mjs.map +1 -1
- package/dist/es/types.mjs.map +1 -1
- package/dist/es/utils.mjs +2 -2
- package/dist/lib/agent/agent.js +29 -20
- package/dist/lib/agent/agent.js.map +1 -1
- package/dist/lib/agent/record-to-report.js +48 -0
- package/dist/lib/agent/record-to-report.js.map +1 -0
- package/dist/lib/agent/utils.js +16 -1
- package/dist/lib/agent/utils.js.map +1 -1
- package/dist/lib/index.js.map +1 -1
- package/dist/lib/report-markdown.js +2 -1
- package/dist/lib/report-markdown.js.map +1 -1
- package/dist/lib/skill/index.js +1 -1
- package/dist/lib/skill/index.js.map +1 -1
- package/dist/lib/types.js.map +1 -1
- package/dist/lib/utils.js +2 -2
- package/dist/types/agent/agent.d.ts +2 -5
- package/dist/types/agent/index.d.ts +1 -0
- package/dist/types/agent/record-to-report.d.ts +2 -0
- package/dist/types/agent/utils.d.ts +2 -1
- package/dist/types/index.d.ts +1 -1
- package/dist/types/types.d.ts +19 -0
- package/dist/types/yaml.d.ts +13 -0
- package/package.json +3 -3
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"agent/utils.mjs","sources":["../../../src/agent/utils.ts"],"sourcesContent":["import { pixelBboxToRect } from '@/ai-model/workflows/inspect/locate-result-rect';\nimport type { TMultimodalPrompt, TUserPrompt } from '@/common';\nimport type { AbstractInterface } from '@/device';\nimport { ScreenshotItem } from '@/screenshot-item';\nimport type {\n ElementCacheFeature,\n LocateResultElement,\n PixelBbox,\n PlanningLocateParam,\n PlanningLocateParamWithLocatedPixelBbox,\n Rect,\n Size,\n UIContext,\n} from '@/types';\nimport { uploadTestInfoToServer } from '@/utils';\nimport {\n MIDSCENE_REPORT_QUIET,\n MIDSCENE_REPORT_TAG_NAME,\n globalConfigManager,\n} from '@midscene/shared/env';\nimport { generateElementByRect } from '@midscene/shared/extractor';\nimport {\n imageInfoOfBase64,\n normalizeBase64Image,\n resizeImgBase64,\n} from '@midscene/shared/img';\nimport { getDebug } from '@midscene/shared/logger';\nimport { _keyDefinitions } from '@midscene/shared/us-keyboard-layout';\nimport { assert, logMsg, uuid } from '@midscene/shared/utils';\nimport dayjs from 'dayjs';\nimport type { TaskCache } from './task-cache';\nimport { debug as cacheDebug } from './task-cache';\n\nconst agentDebug = getDebug('agent');\n\nexport async function commonContextParser(\n interfaceInstance: AbstractInterface,\n _opt: {\n uploadServerUrl?: string;\n screenshotShrinkFactor?: number;\n },\n): Promise<UIContext> {\n const debug = getDebug('commonContextParser');\n\n assert(interfaceInstance, 'interfaceInstance is required');\n\n debug('Getting interface description');\n const description = interfaceInstance.describe?.() || '';\n debug('Interface description end');\n\n debug('Uploading test info to server');\n uploadTestInfoToServer({\n testUrl: description,\n serverUrl: _opt.uploadServerUrl,\n });\n debug('UploadTestInfoToServer end');\n\n debug('will get size');\n const interfaceSize = await interfaceInstance.size();\n const { width: logicalWidth, height: logicalHeight } = interfaceSize;\n\n if ((interfaceSize as unknown as { dpr: number }).dpr) {\n console.warn(\n 'Warning: return value of interface.size() include a dpr property, which is not expected and ignored. ',\n );\n }\n\n if (!Number.isFinite(logicalWidth) || !Number.isFinite(logicalHeight)) {\n throw new Error(\n `Invalid interface size: width and height must be finite numbers. Received width: ${logicalWidth}, height: ${logicalHeight}`,\n );\n }\n\n if (logicalWidth <= 0 || logicalHeight <= 0) {\n throw new Error(\n `Invalid interface size: width and height must be positive numbers. Received width: ${logicalWidth}, height: ${logicalHeight}`,\n );\n }\n\n debug(`size: ${logicalWidth}x${logicalHeight}`);\n\n const screenshotBase64 = await interfaceInstance.screenshotBase64();\n const screenshotCapturedAt = Date.now();\n assert(screenshotBase64!, 'screenshotBase64 is required');\n\n // Get physical screenshot dimensions\n debug('will get screenshot dimensions');\n const { width: imgWidth, height: imgHeight } =\n await imageInfoOfBase64(screenshotBase64);\n\n if (!Number.isFinite(imgWidth) || !Number.isFinite(imgHeight)) {\n throw new Error(\n `Invalid screenshot dimensions: width and height must be finite numbers. Received width: ${imgWidth}, height: ${imgHeight}`,\n );\n }\n if (imgWidth <= 0 || imgHeight <= 0) {\n throw new Error(\n `Invalid screenshot dimensions: width and height must be positive numbers. Received width: ${imgWidth}, height: ${imgHeight}`,\n );\n }\n debug('screenshot dimensions', imgWidth, 'x', imgHeight);\n\n // Detect orientation mismatch between logical size and screenshot.\n // Some devices (e.g. OPPO) report wrong orientation via ADB, causing\n // size() to return portrait dimensions even when the device is landscape.\n // We detect this by comparing aspect ratios and swap if they disagree.\n const logicalIsPortrait = logicalWidth < logicalHeight;\n const screenshotIsPortrait = imgWidth < imgHeight;\n let finalLogicalWidth = logicalWidth;\n let finalLogicalHeight = logicalHeight;\n if (logicalIsPortrait !== screenshotIsPortrait) {\n debug(\n `Orientation mismatch detected: logical size ${logicalWidth}x${logicalHeight} (${logicalIsPortrait ? 'portrait' : 'landscape'}) vs screenshot ${imgWidth}x${imgHeight} (${screenshotIsPortrait ? 'portrait' : 'landscape'}). Swapping logical dimensions.`,\n );\n finalLogicalWidth = logicalHeight;\n finalLogicalHeight = logicalWidth;\n }\n\n const userShrinkFactor = _opt.screenshotShrinkFactor ?? 1;\n\n if (!Number.isFinite(userShrinkFactor) || userShrinkFactor < 1) {\n throw new Error(\n `Invalid screenshotShrinkFactor: must be a finite number >= 1. Received: ${userShrinkFactor}`,\n );\n }\n\n const dpr = imgWidth / finalLogicalWidth;\n\n debug('calculated dpr:', dpr);\n\n const shrunkShotToLogicalRatio = dpr / userShrinkFactor;\n\n debug('shrunkShotToLogicalRatio', shrunkShotToLogicalRatio);\n\n if (userShrinkFactor !== 1) {\n const targetWidth = Math.round(imgWidth / userShrinkFactor);\n const targetHeight = Math.round(imgHeight / userShrinkFactor);\n\n debug(\n `Applying screenshot shrink factor: ${userShrinkFactor} (physical: ${imgWidth}x${imgHeight} -> target: ${targetWidth}x${targetHeight})`,\n );\n\n const resizedBase64 = await resizeImgBase64(screenshotBase64, {\n width: targetWidth,\n height: targetHeight,\n });\n return {\n shotSize: {\n width: targetWidth,\n height: targetHeight,\n },\n deprecatedDpr: dpr,\n screenshot: ScreenshotItem.create(resizedBase64, screenshotCapturedAt),\n shrunkShotToLogicalRatio,\n };\n }\n\n return {\n shotSize: {\n width: imgWidth,\n height: imgHeight,\n },\n deprecatedDpr: dpr,\n screenshot: ScreenshotItem.create(screenshotBase64, screenshotCapturedAt),\n shrunkShotToLogicalRatio,\n };\n}\n\nexport async function createScreenshotBoundUIContext(\n screenshotBase64: string,\n opt: {\n screenshotSize?: Size;\n },\n): Promise<UIContext> {\n const normalizedScreenshotBase64 = normalizeBase64Image(screenshotBase64);\n const actualScreenshotSize = await imageInfoOfBase64(\n normalizedScreenshotBase64,\n );\n if (\n opt.screenshotSize &&\n (opt.screenshotSize.width !== actualScreenshotSize.width ||\n opt.screenshotSize.height !== actualScreenshotSize.height)\n ) {\n agentDebug(\n 'describeElementAtPoint screenshotSize mismatch, use actual size',\n {\n provided: opt.screenshotSize,\n actual: actualScreenshotSize,\n },\n );\n }\n\n return {\n screenshot: ScreenshotItem.create(normalizedScreenshotBase64, Date.now()),\n shotSize: actualScreenshotSize,\n shrunkShotToLogicalRatio: 1,\n _isFrozen: true,\n };\n}\n\nexport function getReportFileName(tag = 'web') {\n const reportTagName = globalConfigManager.getEnvConfigValue(\n MIDSCENE_REPORT_TAG_NAME,\n );\n const dateTimeInFileName = dayjs().format('YYYY-MM-DD_HH-mm-ss');\n // ensure uniqueness at the same time\n const uniqueId = uuid().substring(0, 8);\n return `${reportTagName || tag}-${dateTimeInFileName}-${uniqueId}`;\n}\n\nexport function printReportMsg(filepath: string) {\n if (globalConfigManager.getEnvConfigInBoolean(MIDSCENE_REPORT_QUIET)) {\n return;\n }\n logMsg(`Midscene - report file updated: ${filepath}`);\n}\n\nexport function isPixelBbox(value: unknown): value is PixelBbox {\n return (\n Array.isArray(value) &&\n value.length === 4 &&\n value.every((item) => typeof item === 'number' && Number.isFinite(item))\n );\n}\n\ntype PlanningLocateParamWithMaybeLocatedPixelBbox = PlanningLocateParam & {\n locatedPixelBbox?: unknown;\n};\n\nexport function ifPlanLocateParamHasLocatedPixelBbox(\n planLocateParam: PlanningLocateParamWithMaybeLocatedPixelBbox,\n): planLocateParam is PlanningLocateParamWithLocatedPixelBbox {\n return isPixelBbox(planLocateParam.locatedPixelBbox);\n}\n\nexport function matchElementFromPlan(\n planLocateParam: PlanningLocateParamWithLocatedPixelBbox,\n): LocateResultElement | undefined {\n if (!planLocateParam) {\n return undefined;\n }\n\n const rect = pixelBboxToRect(planLocateParam.locatedPixelBbox);\n\n const element = generateElementByRect(\n rect,\n typeof planLocateParam.prompt === 'string'\n ? planLocateParam.prompt\n : planLocateParam.prompt?.prompt || '',\n );\n return element;\n}\n\nexport async function matchElementFromCache(\n context: {\n taskCache?: TaskCache;\n interfaceInstance: AbstractInterface;\n },\n cacheEntry: ElementCacheFeature | undefined,\n cachePrompt: TUserPrompt,\n cacheable: boolean | undefined,\n): Promise<LocateResultElement | undefined> {\n if (!cacheEntry) {\n return undefined;\n }\n\n if (cacheable === false) {\n cacheDebug('cache disabled for prompt: %s', cachePrompt);\n return undefined;\n }\n\n if (!context.taskCache?.isCacheResultUsed) {\n return undefined;\n }\n\n if (!context.interfaceInstance.rectMatchesCacheFeature) {\n cacheDebug(\n 'interface does not implement rectMatchesCacheFeature, skip cache',\n );\n return undefined;\n }\n\n try {\n const rect =\n await context.interfaceInstance.rectMatchesCacheFeature(cacheEntry);\n const element: LocateResultElement = {\n center: [\n Math.round(rect.left + rect.width / 2),\n Math.round(rect.top + rect.height / 2),\n ],\n rect,\n description:\n typeof cachePrompt === 'string'\n ? cachePrompt\n : cachePrompt.prompt || '',\n };\n\n cacheDebug('cache hit, prompt: %s', cachePrompt);\n return element;\n } catch (error) {\n cacheDebug('rectMatchesCacheFeature error: %s', error);\n return undefined;\n }\n}\n\ndeclare const __VERSION__: string | undefined;\n\nexport const getMidsceneVersion = (): string => {\n if (typeof __VERSION__ !== 'undefined') {\n return __VERSION__;\n } else if (\n process.env.__VERSION__ &&\n process.env.__VERSION__ !== 'undefined'\n ) {\n return process.env.__VERSION__;\n }\n throw new Error('__VERSION__ inject failed during build');\n};\n\nexport const parsePrompt = (\n prompt: TUserPrompt,\n): {\n textPrompt: string;\n multimodalPrompt?: TMultimodalPrompt;\n} => {\n if (typeof prompt === 'string') {\n return {\n textPrompt: prompt,\n multimodalPrompt: undefined,\n };\n }\n return {\n textPrompt: prompt.prompt,\n multimodalPrompt: prompt.images\n ? {\n images: prompt.images,\n convertHttpImage2Base64: !!prompt.convertHttpImage2Base64,\n }\n : undefined,\n };\n};\n\nexport const transformLogicalElementToScreenshot = (\n element: LocateResultElement,\n shrunkShotToLogicalRatio: number,\n): LocateResultElement => {\n if (shrunkShotToLogicalRatio === 1) {\n return element;\n }\n\n return {\n ...element,\n center: [\n Math.round(element.center[0] * shrunkShotToLogicalRatio),\n Math.round(element.center[1] * shrunkShotToLogicalRatio),\n ],\n rect: {\n ...element.rect,\n left: Math.round(element.rect.left * shrunkShotToLogicalRatio),\n top: Math.round(element.rect.top * shrunkShotToLogicalRatio),\n width: Math.round(element.rect.width * shrunkShotToLogicalRatio),\n height: Math.round(element.rect.height * shrunkShotToLogicalRatio),\n },\n };\n};\n\nexport const transformLogicalRectToScreenshotRect = (\n rect: Rect,\n shrunkShotToLogicalRatio: number,\n): Rect => {\n if (shrunkShotToLogicalRatio === 1) {\n return rect;\n }\n\n return {\n ...rect,\n left: Math.round(rect.left * shrunkShotToLogicalRatio),\n top: Math.round(rect.top * shrunkShotToLogicalRatio),\n width: Math.round(rect.width * shrunkShotToLogicalRatio),\n height: Math.round(rect.height * shrunkShotToLogicalRatio),\n };\n};\n"],"names":["agentDebug","getDebug","commonContextParser","interfaceInstance","_opt","debug","assert","description","uploadTestInfoToServer","interfaceSize","logicalWidth","logicalHeight","console","Number","Error","screenshotBase64","screenshotCapturedAt","Date","imgWidth","imgHeight","imageInfoOfBase64","logicalIsPortrait","screenshotIsPortrait","finalLogicalWidth","userShrinkFactor","dpr","shrunkShotToLogicalRatio","targetWidth","Math","targetHeight","resizedBase64","resizeImgBase64","ScreenshotItem","createScreenshotBoundUIContext","opt","normalizedScreenshotBase64","normalizeBase64Image","actualScreenshotSize","getReportFileName","tag","reportTagName","globalConfigManager","MIDSCENE_REPORT_TAG_NAME","dateTimeInFileName","dayjs","uniqueId","uuid","printReportMsg","filepath","MIDSCENE_REPORT_QUIET","logMsg","isPixelBbox","value","Array","item","ifPlanLocateParamHasLocatedPixelBbox","planLocateParam","matchElementFromPlan","rect","pixelBboxToRect","element","generateElementByRect","matchElementFromCache","context","cacheEntry","cachePrompt","cacheable","cacheDebug","error","getMidsceneVersion","__VERSION__","parsePrompt","prompt","undefined","transformLogicalElementToScreenshot","transformLogicalRectToScreenshotRect"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"agent/utils.mjs","sources":["../../../src/agent/utils.ts"],"sourcesContent":["import { pixelBboxToRect } from '@/ai-model/workflows/inspect/locate-result-rect';\nimport type { TMultimodalPrompt, TUserPrompt } from '@/common';\nimport type { AbstractInterface } from '@/device';\nimport { ScreenshotItem } from '@/screenshot-item';\nimport type {\n ElementCacheFeature,\n LocateResultElement,\n PixelBbox,\n PlanningLocateParam,\n PlanningLocateParamWithLocatedPixelBbox,\n Rect,\n ScrollParam,\n Size,\n UIContext,\n} from '@/types';\nimport { uploadTestInfoToServer } from '@/utils';\nimport {\n MIDSCENE_REPORT_QUIET,\n MIDSCENE_REPORT_TAG_NAME,\n globalConfigManager,\n} from '@midscene/shared/env';\nimport { generateElementByRect } from '@midscene/shared/extractor';\nimport {\n imageInfoOfBase64,\n normalizeBase64Image,\n resizeImgBase64,\n} from '@midscene/shared/img';\nimport { getDebug } from '@midscene/shared/logger';\nimport { _keyDefinitions } from '@midscene/shared/us-keyboard-layout';\nimport { assert, logMsg, uuid } from '@midscene/shared/utils';\nimport dayjs from 'dayjs';\nimport type { TaskCache } from './task-cache';\nimport { debug as cacheDebug } from './task-cache';\n\nconst agentDebug = getDebug('agent');\n\nconst legacyScrollTypeMap = {\n once: 'singleAction',\n untilBottom: 'scrollToBottom',\n untilTop: 'scrollToTop',\n untilRight: 'scrollToRight',\n untilLeft: 'scrollToLeft',\n} as const;\n\nexport const normalizeScrollType = (\n scrollType: string | undefined,\n): ScrollParam['scrollType'] | undefined => {\n if (!scrollType) {\n return undefined;\n }\n\n if (scrollType in legacyScrollTypeMap) {\n return legacyScrollTypeMap[scrollType as keyof typeof legacyScrollTypeMap];\n }\n\n return scrollType as ScrollParam['scrollType'];\n};\n\nexport async function commonContextParser(\n interfaceInstance: AbstractInterface,\n _opt: {\n uploadServerUrl?: string;\n screenshotShrinkFactor?: number;\n },\n): Promise<UIContext> {\n const debug = getDebug('commonContextParser');\n\n assert(interfaceInstance, 'interfaceInstance is required');\n\n debug('Getting interface description');\n const description = interfaceInstance.describe?.() || '';\n debug('Interface description end');\n\n debug('Uploading test info to server');\n uploadTestInfoToServer({\n testUrl: description,\n serverUrl: _opt.uploadServerUrl,\n });\n debug('UploadTestInfoToServer end');\n\n debug('will get size');\n const interfaceSize = await interfaceInstance.size();\n const { width: logicalWidth, height: logicalHeight } = interfaceSize;\n\n if ((interfaceSize as unknown as { dpr: number }).dpr) {\n console.warn(\n 'Warning: return value of interface.size() include a dpr property, which is not expected and ignored. ',\n );\n }\n\n if (!Number.isFinite(logicalWidth) || !Number.isFinite(logicalHeight)) {\n throw new Error(\n `Invalid interface size: width and height must be finite numbers. Received width: ${logicalWidth}, height: ${logicalHeight}`,\n );\n }\n\n if (logicalWidth <= 0 || logicalHeight <= 0) {\n throw new Error(\n `Invalid interface size: width and height must be positive numbers. Received width: ${logicalWidth}, height: ${logicalHeight}`,\n );\n }\n\n debug(`size: ${logicalWidth}x${logicalHeight}`);\n\n const screenshotBase64 = await interfaceInstance.screenshotBase64();\n const screenshotCapturedAt = Date.now();\n assert(screenshotBase64!, 'screenshotBase64 is required');\n\n // Get physical screenshot dimensions\n debug('will get screenshot dimensions');\n const { width: imgWidth, height: imgHeight } =\n await imageInfoOfBase64(screenshotBase64);\n\n if (!Number.isFinite(imgWidth) || !Number.isFinite(imgHeight)) {\n throw new Error(\n `Invalid screenshot dimensions: width and height must be finite numbers. Received width: ${imgWidth}, height: ${imgHeight}`,\n );\n }\n if (imgWidth <= 0 || imgHeight <= 0) {\n throw new Error(\n `Invalid screenshot dimensions: width and height must be positive numbers. Received width: ${imgWidth}, height: ${imgHeight}`,\n );\n }\n debug('screenshot dimensions', imgWidth, 'x', imgHeight);\n\n // Detect orientation mismatch between logical size and screenshot.\n // Some devices (e.g. OPPO) report wrong orientation via ADB, causing\n // size() to return portrait dimensions even when the device is landscape.\n // We detect this by comparing aspect ratios and swap if they disagree.\n const logicalIsPortrait = logicalWidth < logicalHeight;\n const screenshotIsPortrait = imgWidth < imgHeight;\n let finalLogicalWidth = logicalWidth;\n let finalLogicalHeight = logicalHeight;\n if (logicalIsPortrait !== screenshotIsPortrait) {\n debug(\n `Orientation mismatch detected: logical size ${logicalWidth}x${logicalHeight} (${logicalIsPortrait ? 'portrait' : 'landscape'}) vs screenshot ${imgWidth}x${imgHeight} (${screenshotIsPortrait ? 'portrait' : 'landscape'}). Swapping logical dimensions.`,\n );\n finalLogicalWidth = logicalHeight;\n finalLogicalHeight = logicalWidth;\n }\n\n const userShrinkFactor = _opt.screenshotShrinkFactor ?? 1;\n\n if (!Number.isFinite(userShrinkFactor) || userShrinkFactor < 1) {\n throw new Error(\n `Invalid screenshotShrinkFactor: must be a finite number >= 1. Received: ${userShrinkFactor}`,\n );\n }\n\n const dpr = imgWidth / finalLogicalWidth;\n\n debug('calculated dpr:', dpr);\n\n const shrunkShotToLogicalRatio = dpr / userShrinkFactor;\n\n debug('shrunkShotToLogicalRatio', shrunkShotToLogicalRatio);\n\n if (userShrinkFactor !== 1) {\n const targetWidth = Math.round(imgWidth / userShrinkFactor);\n const targetHeight = Math.round(imgHeight / userShrinkFactor);\n\n debug(\n `Applying screenshot shrink factor: ${userShrinkFactor} (physical: ${imgWidth}x${imgHeight} -> target: ${targetWidth}x${targetHeight})`,\n );\n\n const resizedBase64 = await resizeImgBase64(screenshotBase64, {\n width: targetWidth,\n height: targetHeight,\n });\n return {\n shotSize: {\n width: targetWidth,\n height: targetHeight,\n },\n deprecatedDpr: dpr,\n screenshot: ScreenshotItem.create(resizedBase64, screenshotCapturedAt),\n shrunkShotToLogicalRatio,\n };\n }\n\n return {\n shotSize: {\n width: imgWidth,\n height: imgHeight,\n },\n deprecatedDpr: dpr,\n screenshot: ScreenshotItem.create(screenshotBase64, screenshotCapturedAt),\n shrunkShotToLogicalRatio,\n };\n}\n\nexport async function createScreenshotBoundUIContext(\n screenshotBase64: string,\n opt: {\n screenshotSize?: Size;\n },\n): Promise<UIContext> {\n const normalizedScreenshotBase64 = normalizeBase64Image(screenshotBase64);\n const actualScreenshotSize = await imageInfoOfBase64(\n normalizedScreenshotBase64,\n );\n if (\n opt.screenshotSize &&\n (opt.screenshotSize.width !== actualScreenshotSize.width ||\n opt.screenshotSize.height !== actualScreenshotSize.height)\n ) {\n agentDebug(\n 'describeElementAtPoint screenshotSize mismatch, use actual size',\n {\n provided: opt.screenshotSize,\n actual: actualScreenshotSize,\n },\n );\n }\n\n return {\n screenshot: ScreenshotItem.create(normalizedScreenshotBase64, Date.now()),\n shotSize: actualScreenshotSize,\n shrunkShotToLogicalRatio: 1,\n _isFrozen: true,\n };\n}\n\nexport function getReportFileName(tag = 'web') {\n const reportTagName = globalConfigManager.getEnvConfigValue(\n MIDSCENE_REPORT_TAG_NAME,\n );\n const dateTimeInFileName = dayjs().format('YYYY-MM-DD_HH-mm-ss');\n // ensure uniqueness at the same time\n const uniqueId = uuid().substring(0, 8);\n return `${reportTagName || tag}-${dateTimeInFileName}-${uniqueId}`;\n}\n\nexport function printReportMsg(filepath: string) {\n if (globalConfigManager.getEnvConfigInBoolean(MIDSCENE_REPORT_QUIET)) {\n return;\n }\n logMsg(`Midscene - report file updated: ${filepath}`);\n}\n\nexport function isPixelBbox(value: unknown): value is PixelBbox {\n return (\n Array.isArray(value) &&\n value.length === 4 &&\n value.every((item) => typeof item === 'number' && Number.isFinite(item))\n );\n}\n\ntype PlanningLocateParamWithMaybeLocatedPixelBbox = PlanningLocateParam & {\n locatedPixelBbox?: unknown;\n};\n\nexport function ifPlanLocateParamHasLocatedPixelBbox(\n planLocateParam: PlanningLocateParamWithMaybeLocatedPixelBbox,\n): planLocateParam is PlanningLocateParamWithLocatedPixelBbox {\n return isPixelBbox(planLocateParam.locatedPixelBbox);\n}\n\nexport function matchElementFromPlan(\n planLocateParam: PlanningLocateParamWithLocatedPixelBbox,\n): LocateResultElement | undefined {\n if (!planLocateParam) {\n return undefined;\n }\n\n const rect = pixelBboxToRect(planLocateParam.locatedPixelBbox);\n\n const element = generateElementByRect(\n rect,\n typeof planLocateParam.prompt === 'string'\n ? planLocateParam.prompt\n : planLocateParam.prompt?.prompt || '',\n );\n return element;\n}\n\nexport async function matchElementFromCache(\n context: {\n taskCache?: TaskCache;\n interfaceInstance: AbstractInterface;\n },\n cacheEntry: ElementCacheFeature | undefined,\n cachePrompt: TUserPrompt,\n cacheable: boolean | undefined,\n): Promise<LocateResultElement | undefined> {\n if (!cacheEntry) {\n return undefined;\n }\n\n if (cacheable === false) {\n cacheDebug('cache disabled for prompt: %s', cachePrompt);\n return undefined;\n }\n\n if (!context.taskCache?.isCacheResultUsed) {\n return undefined;\n }\n\n if (!context.interfaceInstance.rectMatchesCacheFeature) {\n cacheDebug(\n 'interface does not implement rectMatchesCacheFeature, skip cache',\n );\n return undefined;\n }\n\n try {\n const rect =\n await context.interfaceInstance.rectMatchesCacheFeature(cacheEntry);\n const element: LocateResultElement = {\n center: [\n Math.round(rect.left + rect.width / 2),\n Math.round(rect.top + rect.height / 2),\n ],\n rect,\n description:\n typeof cachePrompt === 'string'\n ? cachePrompt\n : cachePrompt.prompt || '',\n };\n\n cacheDebug('cache hit, prompt: %s', cachePrompt);\n return element;\n } catch (error) {\n cacheDebug('rectMatchesCacheFeature error: %s', error);\n return undefined;\n }\n}\n\ndeclare const __VERSION__: string | undefined;\n\nexport const getMidsceneVersion = (): string => {\n if (typeof __VERSION__ !== 'undefined') {\n return __VERSION__;\n } else if (\n process.env.__VERSION__ &&\n process.env.__VERSION__ !== 'undefined'\n ) {\n return process.env.__VERSION__;\n }\n throw new Error('__VERSION__ inject failed during build');\n};\n\nexport const parsePrompt = (\n prompt: TUserPrompt,\n): {\n textPrompt: string;\n multimodalPrompt?: TMultimodalPrompt;\n} => {\n if (typeof prompt === 'string') {\n return {\n textPrompt: prompt,\n multimodalPrompt: undefined,\n };\n }\n return {\n textPrompt: prompt.prompt,\n multimodalPrompt: prompt.images\n ? {\n images: prompt.images,\n convertHttpImage2Base64: !!prompt.convertHttpImage2Base64,\n }\n : undefined,\n };\n};\n\nexport const transformLogicalElementToScreenshot = (\n element: LocateResultElement,\n shrunkShotToLogicalRatio: number,\n): LocateResultElement => {\n if (shrunkShotToLogicalRatio === 1) {\n return element;\n }\n\n return {\n ...element,\n center: [\n Math.round(element.center[0] * shrunkShotToLogicalRatio),\n Math.round(element.center[1] * shrunkShotToLogicalRatio),\n ],\n rect: {\n ...element.rect,\n left: Math.round(element.rect.left * shrunkShotToLogicalRatio),\n top: Math.round(element.rect.top * shrunkShotToLogicalRatio),\n width: Math.round(element.rect.width * shrunkShotToLogicalRatio),\n height: Math.round(element.rect.height * shrunkShotToLogicalRatio),\n },\n };\n};\n\nexport const transformLogicalRectToScreenshotRect = (\n rect: Rect,\n shrunkShotToLogicalRatio: number,\n): Rect => {\n if (shrunkShotToLogicalRatio === 1) {\n return rect;\n }\n\n return {\n ...rect,\n left: Math.round(rect.left * shrunkShotToLogicalRatio),\n top: Math.round(rect.top * shrunkShotToLogicalRatio),\n width: Math.round(rect.width * shrunkShotToLogicalRatio),\n height: Math.round(rect.height * shrunkShotToLogicalRatio),\n };\n};\n"],"names":["agentDebug","getDebug","legacyScrollTypeMap","normalizeScrollType","scrollType","commonContextParser","interfaceInstance","_opt","debug","assert","description","uploadTestInfoToServer","interfaceSize","logicalWidth","logicalHeight","console","Number","Error","screenshotBase64","screenshotCapturedAt","Date","imgWidth","imgHeight","imageInfoOfBase64","logicalIsPortrait","screenshotIsPortrait","finalLogicalWidth","userShrinkFactor","dpr","shrunkShotToLogicalRatio","targetWidth","Math","targetHeight","resizedBase64","resizeImgBase64","ScreenshotItem","createScreenshotBoundUIContext","opt","normalizedScreenshotBase64","normalizeBase64Image","actualScreenshotSize","getReportFileName","tag","reportTagName","globalConfigManager","MIDSCENE_REPORT_TAG_NAME","dateTimeInFileName","dayjs","uniqueId","uuid","printReportMsg","filepath","MIDSCENE_REPORT_QUIET","logMsg","isPixelBbox","value","Array","item","ifPlanLocateParamHasLocatedPixelBbox","planLocateParam","matchElementFromPlan","rect","pixelBboxToRect","element","generateElementByRect","matchElementFromCache","context","cacheEntry","cachePrompt","cacheable","cacheDebug","error","getMidsceneVersion","__VERSION__","parsePrompt","prompt","undefined","transformLogicalElementToScreenshot","transformLogicalRectToScreenshotRect"],"mappings":"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"}
|
package/dist/es/index.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.mjs","sources":["../../src/index.ts"],"sourcesContent":["import { z } from 'zod';\nimport Service from './service/index';\nimport { TaskRunner } from './task-runner';\nimport { getVersion } from './utils';\n\nexport {\n plan,\n AiLocateElement,\n runConnectivityTest,\n getMidsceneLocationSchema,\n PointSchema,\n SizeSchema,\n RectSchema,\n TMultimodalPromptSchema,\n TUserPromptSchema,\n type TMultimodalPrompt,\n type TUserPrompt,\n type ConnectivityCheckResultItem,\n type ConnectivityTestConfig,\n type ConnectivityTestResult,\n} from './ai-model/index';\n\nexport {\n MIDSCENE_MODEL_NAME,\n type CreateOpenAIClientFn,\n} from '@midscene/shared/env';\n\nexport type * from './types';\nexport {\n ServiceError,\n ExecutionDump,\n ReportActionDump,\n GroupedActionDump,\n type IExecutionDump,\n type IReportActionDump,\n type IGroupedActionDump,\n type ReportMeta,\n type GroupMeta,\n} from './types';\n\nexport { z };\n\nexport default Service;\nexport { TaskRunner, Service, getVersion };\n\nexport type {\n MidsceneYamlScript,\n MidsceneYamlTask,\n MidsceneYamlFlowItem,\n MidsceneYamlConfigResult,\n MidsceneYamlConfig,\n MidsceneYamlScriptWebEnv,\n MidsceneYamlScriptAndroidEnv,\n MidsceneYamlScriptIOSEnv,\n MidsceneYamlScriptEnv,\n LocateOption,\n DetailedLocateParam,\n} from './yaml';\n\nexport {
|
|
1
|
+
{"version":3,"file":"index.mjs","sources":["../../src/index.ts"],"sourcesContent":["import { z } from 'zod';\nimport Service from './service/index';\nimport { TaskRunner } from './task-runner';\nimport { getVersion } from './utils';\n\nexport {\n plan,\n AiLocateElement,\n runConnectivityTest,\n getMidsceneLocationSchema,\n PointSchema,\n SizeSchema,\n RectSchema,\n TMultimodalPromptSchema,\n TUserPromptSchema,\n type TMultimodalPrompt,\n type TUserPrompt,\n type ConnectivityCheckResultItem,\n type ConnectivityTestConfig,\n type ConnectivityTestResult,\n} from './ai-model/index';\n\nexport {\n MIDSCENE_MODEL_NAME,\n type CreateOpenAIClientFn,\n} from '@midscene/shared/env';\n\nexport type * from './types';\nexport {\n ServiceError,\n ExecutionDump,\n ReportActionDump,\n GroupedActionDump,\n type IExecutionDump,\n type IReportActionDump,\n type IGroupedActionDump,\n type ReportMeta,\n type GroupMeta,\n} from './types';\n\nexport { z };\n\nexport default Service;\nexport { TaskRunner, Service, getVersion };\n\nexport type {\n MidsceneYamlScript,\n MidsceneYamlTask,\n MidsceneYamlFlowItem,\n MidsceneYamlConfigResult,\n MidsceneYamlConfig,\n MidsceneYamlScriptWebEnv,\n MidsceneYamlScriptAndroidEnv,\n MidsceneYamlScriptIOSEnv,\n MidsceneYamlScriptEnv,\n LocateOption,\n DetailedLocateParam,\n} from './yaml';\n\nexport {\n Agent,\n type AgentOpt,\n type AiActOptions,\n createAgent,\n} from './agent';\n\n// Dump utilities\nexport {\n restoreImageReferences,\n escapeContent,\n unescapeContent,\n parseImageScripts,\n parseDumpScript,\n parseDumpScriptAttributes,\n generateImageScriptTag,\n generateDumpScriptTag,\n} from './dump';\nexport {\n getTaskSearchArea,\n getTaskServiceDump,\n} from './dump/task-service-dump';\n\n// Report generator\nexport type { IReportGenerator } from './report-generator';\nexport { ReportGenerator, nullReportGenerator } from './report-generator';\nexport {\n collectDedupedExecutions,\n ReportMergingTool,\n dedupeExecutionsKeepLatest,\n splitReportHtmlByExecution,\n} from './report';\nexport {\n createReportCliCommands,\n reportFileToMarkdown,\n splitReportFile,\n mergeReportFiles,\n type ConsumeReportFileAction,\n type ReportFileToMarkdownOptions,\n type ReportCliCommandDefinition,\n type ReportCliCommandEntry,\n type SplitReportFileOptions,\n type MergeReportFilesOptions,\n type MergeReportFilesResult,\n} from './report-cli';\n\n// ScreenshotItem\nexport { ScreenshotItem } from './screenshot-item';\nexport { ScreenshotStore, type ScreenshotRef } from './dump/screenshot-store';\n\nexport {\n executionToMarkdown,\n reportToMarkdown,\n type ExecutionMarkdownOptions,\n type ExecutionMarkdownResult,\n type ReportMarkdownResult,\n type MarkdownAttachment,\n} from './report-markdown';\n"],"names":["Service"],"mappings":";;;;;;;;;;;;;;;;AA0CA,YAAeA"}
|
|
@@ -124,7 +124,8 @@ function recorderMarkdownSection(recorder, screenshotBaseDir, executionIndex, ta
|
|
|
124
124
|
];
|
|
125
125
|
const attachments = [];
|
|
126
126
|
recorder.forEach((item, recorderIndex)=>{
|
|
127
|
-
|
|
127
|
+
const descriptionText = item.description ? `, description=${item.description}` : '';
|
|
128
|
+
lines.push(`- #${recorderIndex + 1} type=${item.type}, ts=${formatTime(item.ts)}, timing=${item.timing || 'N/A'}${descriptionText}`);
|
|
128
129
|
if (!item.screenshot) return;
|
|
129
130
|
const imageResult = screenshotAttachment(item.screenshot, screenshotBaseDir, executionIndex, taskIndex);
|
|
130
131
|
lines.push(imageResult.markdown);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"report-markdown.mjs","sources":["../../src/report-markdown.ts"],"sourcesContent":["import { basename } from 'node:path';\nimport { extractInsightParam, paramStr, typeStr } from '@/agent/ui-utils';\nimport { ScreenshotItem } from '@/screenshot-item';\nimport type {\n ExecutionDump,\n ExecutionRecorderItem,\n ExecutionTask,\n IExecutionDump,\n IReportActionDump,\n ReportActionDump,\n} from '@/types';\nimport type { ScreenshotRef } from './dump/screenshot-store';\nimport { normalizeScreenshotRef } from './dump/screenshot-store';\n\nexport interface MarkdownAttachment {\n id: string;\n /**\n * Stable, prefixed file name for the exported copy. The markdown image link\n * always points at `${screenshotBaseDir}/${suggestedFileName}`, so consumers\n * write the screenshot under this name to keep links in sync. See #2392.\n */\n suggestedFileName: string;\n mimeType?: string;\n /**\n * Reference to the screenshot in the source report, used to locate the\n * original bytes when copying them to the exported name. Absent for in-memory\n * screenshots, which carry their data in `base64Data` instead.\n */\n sourceRef?: ScreenshotRef;\n executionIndex: number;\n taskIndex: number;\n /** Populated when screenshot data is available in memory (e.g. browser context). */\n base64Data?: string;\n}\n\nexport interface ExecutionMarkdownOptions {\n screenshotBaseDir?: string;\n}\n\nexport interface ExecutionMarkdownResult {\n markdown: string;\n attachments: MarkdownAttachment[];\n}\n\nexport interface ReportMarkdownResult {\n markdown: string;\n attachments: MarkdownAttachment[];\n}\n\nfunction toExecutionDump(\n execution: ExecutionDump | IExecutionDump,\n): IExecutionDump {\n if (!execution || typeof execution !== 'object') {\n throw new Error('executionToMarkdown: execution is required');\n }\n\n if (!Array.isArray(execution.tasks)) {\n throw new Error('executionToMarkdown: execution.tasks must be an array');\n }\n\n if (!execution.name) {\n throw new Error('executionToMarkdown: execution.name is required');\n }\n\n return execution;\n}\n\nfunction toReportDump(\n report: ReportActionDump | IReportActionDump,\n): IReportActionDump {\n if (!report || typeof report !== 'object') {\n throw new Error('reportToMarkdown: report is required');\n }\n\n if (!Array.isArray(report.executions)) {\n throw new Error('reportToMarkdown: report.executions must be an array');\n }\n\n return report;\n}\n\nfunction formatTime(ts?: number): string {\n if (typeof ts !== 'number' || Number.isNaN(ts)) {\n return 'N/A';\n }\n return new Date(ts).toISOString();\n}\n\nfunction resolveTaskTiming(task: ExecutionTask): {\n start?: number;\n end?: number;\n cost?: number;\n} {\n const timing = task.timing;\n if (!timing) {\n return {};\n }\n\n const start = timing.start ?? timing.callAiStart ?? timing.callActionStart;\n const end =\n timing.end ??\n timing.callAiEnd ??\n timing.callActionEnd ??\n timing.captureAfterCallingSnapshotEnd;\n const cost =\n timing.cost ??\n (typeof start === 'number' && typeof end === 'number'\n ? end - start\n : undefined);\n\n return { start, end, cost };\n}\n\nfunction safeTaskParam(task: ExecutionTask): string {\n const readable = paramStr(task);\n if (readable) {\n return readable;\n }\n\n if (task.type === 'Insight') {\n return extractInsightParam((task as any).param).content;\n }\n\n return '';\n}\n\nfunction formatSize(\n size?: { width?: number; height?: number } | null,\n): string | undefined {\n if (\n !size ||\n typeof size.width !== 'number' ||\n typeof size.height !== 'number' ||\n Number.isNaN(size.width) ||\n Number.isNaN(size.height)\n ) {\n return undefined;\n }\n\n return `${size.width} x ${size.height}`;\n}\n\nfunction extractLocateCenter(\n task: ExecutionTask,\n): [number, number] | undefined {\n const outputCenter = (task.output as { element?: { center?: unknown } })\n ?.element?.center;\n if (\n Array.isArray(outputCenter) &&\n outputCenter.length >= 2 &&\n typeof outputCenter[0] === 'number' &&\n typeof outputCenter[1] === 'number'\n ) {\n return [outputCenter[0], outputCenter[1]];\n }\n\n const paramLocateCenter = (task.param as { locate?: { center?: unknown } })\n ?.locate?.center;\n if (\n Array.isArray(paramLocateCenter) &&\n paramLocateCenter.length >= 2 &&\n typeof paramLocateCenter[0] === 'number' &&\n typeof paramLocateCenter[1] === 'number'\n ) {\n return [paramLocateCenter[0], paramLocateCenter[1]];\n }\n\n const paramCenter = (task.param as { center?: unknown })?.center;\n if (\n Array.isArray(paramCenter) &&\n paramCenter.length >= 2 &&\n typeof paramCenter[0] === 'number' &&\n typeof paramCenter[1] === 'number'\n ) {\n return [paramCenter[0], paramCenter[1]];\n }\n\n return undefined;\n}\n\nfunction tryExtractBase64(screenshot: unknown): string | undefined {\n if (!screenshot || typeof screenshot !== 'object') return undefined;\n const s = screenshot as Record<string, unknown>;\n if (typeof s.base64 === 'string' && s.base64.length > 0) {\n return s.base64;\n }\n return undefined;\n}\n\nfunction screenshotAttachment(\n screenshot: unknown,\n screenshotBaseDir: string,\n executionIndex: number,\n taskIndex: number,\n): { markdown: string; attachment: MarkdownAttachment } {\n if (screenshot instanceof ScreenshotItem) {\n const ext = screenshot.extension;\n const suggestedFileName = `execution-${executionIndex + 1}-task-${taskIndex + 1}-${screenshot.id}.${ext}`;\n return {\n markdown: `\\n`,\n attachment: {\n id: screenshot.id,\n suggestedFileName,\n mimeType: `image/${ext === 'jpeg' ? 'jpeg' : 'png'}`,\n executionIndex,\n taskIndex,\n base64Data: tryExtractBase64(screenshot),\n },\n };\n }\n\n const ref = normalizeScreenshotRef(screenshot);\n if (ref) {\n const ext = ref.mimeType === 'image/jpeg' ? 'jpeg' : 'png';\n const suggestedFileName = `execution-${executionIndex + 1}-task-${taskIndex + 1}-${ref.id}.${ext}`;\n return {\n markdown: `\\n`,\n attachment: {\n id: ref.id,\n suggestedFileName,\n sourceRef: ref,\n mimeType: ref.mimeType,\n executionIndex,\n taskIndex,\n base64Data: tryExtractBase64(screenshot),\n },\n };\n }\n\n const base64 = tryExtractBase64(screenshot);\n if (base64) {\n const ext = base64.startsWith('data:image/jpeg') ? 'jpeg' : 'png';\n const id = `restored-${executionIndex + 1}-${taskIndex + 1}`;\n const suggestedFileName = `execution-${executionIndex + 1}-task-${taskIndex + 1}-${id}.${ext}`;\n return {\n markdown: `\\n`,\n attachment: {\n id,\n suggestedFileName,\n mimeType: `image/${ext}`,\n executionIndex,\n taskIndex,\n base64Data: base64,\n },\n };\n }\n\n throw new Error(\n `executionToMarkdown: missing screenshot for execution #${executionIndex + 1} task #${taskIndex + 1}`,\n );\n}\n\nfunction recorderMarkdownSection(\n recorder: ExecutionRecorderItem[] | undefined,\n screenshotBaseDir: string,\n executionIndex: number,\n taskIndex: number,\n): { lines: string[]; attachments: MarkdownAttachment[] } {\n if (!recorder?.length) {\n return { lines: [], attachments: [] };\n }\n\n const lines: string[] = ['', '### Recorder'];\n const attachments: MarkdownAttachment[] = [];\n\n recorder.forEach((item, recorderIndex) => {\n lines.push(\n `- #${recorderIndex + 1} type=${item.type}, ts=${formatTime(item.ts)}, timing=${item.timing || 'N/A'}`,\n );\n\n if (!item.screenshot) {\n return;\n }\n\n const imageResult = screenshotAttachment(\n item.screenshot,\n screenshotBaseDir,\n executionIndex,\n taskIndex,\n );\n\n lines.push(imageResult.markdown);\n attachments.push(imageResult.attachment);\n });\n\n return { lines, attachments };\n}\n\nfunction renderExecution(\n executionRaw: ExecutionDump | IExecutionDump,\n executionIndex: number,\n options?: ExecutionMarkdownOptions,\n): ExecutionMarkdownResult {\n const execution = toExecutionDump(executionRaw);\n const screenshotBaseDir = options?.screenshotBaseDir ?? './screenshots';\n\n const lines: string[] = [];\n const attachments: MarkdownAttachment[] = [];\n\n lines.push(`# ${execution.name}`);\n if (execution.description) {\n lines.push('', execution.description);\n }\n\n lines.push('', `- Execution start: ${formatTime(execution.logTime)}`);\n lines.push(`- Task count: ${execution.tasks.length}`);\n\n execution.tasks.forEach((task, taskIndex) => {\n const title = typeStr(task);\n const detail = safeTaskParam(task);\n const time = resolveTaskTiming(task);\n\n lines.push(\n '',\n `## ${taskIndex + 1}. ${title}${detail ? ` - ${detail}` : ''}`,\n );\n lines.push(`- Status: ${task.status || 'unknown'}`);\n lines.push(`- Start: ${formatTime(time.start)}`);\n lines.push(`- End: ${formatTime(time.end)}`);\n lines.push(\n `- Cost(ms): ${typeof time.cost === 'number' ? time.cost : 'N/A'}`,\n );\n lines.push(\n `- Screen size: ${formatSize(task.uiContext?.shotSize) || 'N/A'}`,\n );\n\n if (task.subType === 'Locate') {\n const locateCenter = extractLocateCenter(task);\n if (locateCenter) {\n lines.push(`- Locate center: (${locateCenter[0]}, ${locateCenter[1]})`);\n }\n }\n\n if (task.errorMessage) {\n lines.push(`- Error: ${task.errorMessage}`);\n }\n\n if (task.uiContext?.screenshot) {\n const imageResult = screenshotAttachment(\n task.uiContext.screenshot,\n screenshotBaseDir,\n executionIndex,\n taskIndex,\n );\n\n lines.push(imageResult.markdown);\n attachments.push(imageResult.attachment);\n }\n\n const recorderSection = recorderMarkdownSection(\n task.recorder,\n screenshotBaseDir,\n executionIndex,\n taskIndex,\n );\n if (recorderSection.lines.length) {\n lines.push(...recorderSection.lines);\n attachments.push(...recorderSection.attachments);\n }\n });\n\n return {\n markdown: lines.join('\\n'),\n attachments,\n };\n}\n\nfunction reportFileName(\n execution: IExecutionDump,\n executionIndex: number,\n): string {\n const safeName =\n execution.name\n .trim()\n .replace(/\\s+/g, '-')\n .replace(/[^a-zA-Z0-9-_]/g, '') || `execution-${executionIndex + 1}`;\n return `${executionIndex + 1}-${basename(safeName)}.md`;\n}\n\nexport function executionToMarkdown(\n execution: ExecutionDump | IExecutionDump,\n options?: ExecutionMarkdownOptions,\n): ExecutionMarkdownResult {\n return renderExecution(execution, 0, options);\n}\n\nexport function reportToMarkdown(\n report: ReportActionDump | IReportActionDump,\n): ReportMarkdownResult {\n const reportDump = toReportDump(report);\n\n const executionResults = reportDump.executions.map((execution, index) => {\n const rendered = renderExecution(execution, index);\n return {\n executionIndex: index,\n executionName: execution.name,\n markdown: rendered.markdown,\n attachments: rendered.attachments,\n suggestedFileName: reportFileName(execution, index),\n };\n });\n\n const attachments = executionResults.flatMap((item) => item.attachments);\n\n const header = [\n `# ${reportDump.groupName}`,\n reportDump.groupDescription ? `\\n${reportDump.groupDescription}` : '',\n `\\n- SDK Version: ${reportDump.sdkVersion}`,\n `- Execution count: ${reportDump.executions.length}`,\n '\\n## Suggested execution markdown files',\n ...executionResults.map(\n (item) => `- ${item.suggestedFileName} (${item.executionName})`,\n ),\n ]\n .filter(Boolean)\n .join('\\n');\n\n return {\n markdown: `${header}\\n\\n${executionResults.map((item) => item.markdown).join('\\n\\n---\\n\\n')}`,\n attachments,\n };\n}\n"],"names":["toExecutionDump","execution","Error","Array","toReportDump","report","formatTime","ts","Number","Date","resolveTaskTiming","task","timing","start","end","cost","undefined","safeTaskParam","readable","paramStr","extractInsightParam","formatSize","size","extractLocateCenter","outputCenter","paramLocateCenter","paramCenter","tryExtractBase64","screenshot","s","screenshotAttachment","screenshotBaseDir","executionIndex","taskIndex","ScreenshotItem","ext","suggestedFileName","ref","normalizeScreenshotRef","base64","id","recorderMarkdownSection","recorder","lines","attachments","item","recorderIndex","imageResult","renderExecution","executionRaw","options","title","typeStr","detail","time","locateCenter","recorderSection","reportFileName","safeName","basename","executionToMarkdown","reportToMarkdown","reportDump","executionResults","index","rendered","header","Boolean"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"report-markdown.mjs","sources":["../../src/report-markdown.ts"],"sourcesContent":["import { basename } from 'node:path';\nimport { extractInsightParam, paramStr, typeStr } from '@/agent/ui-utils';\nimport { ScreenshotItem } from '@/screenshot-item';\nimport type {\n ExecutionDump,\n ExecutionRecorderItem,\n ExecutionTask,\n IExecutionDump,\n IReportActionDump,\n ReportActionDump,\n} from '@/types';\nimport type { ScreenshotRef } from './dump/screenshot-store';\nimport { normalizeScreenshotRef } from './dump/screenshot-store';\n\nexport interface MarkdownAttachment {\n id: string;\n /**\n * Stable, prefixed file name for the exported copy. The markdown image link\n * always points at `${screenshotBaseDir}/${suggestedFileName}`, so consumers\n * write the screenshot under this name to keep links in sync. See #2392.\n */\n suggestedFileName: string;\n mimeType?: string;\n /**\n * Reference to the screenshot in the source report, used to locate the\n * original bytes when copying them to the exported name. Absent for in-memory\n * screenshots, which carry their data in `base64Data` instead.\n */\n sourceRef?: ScreenshotRef;\n executionIndex: number;\n taskIndex: number;\n /** Populated when screenshot data is available in memory (e.g. browser context). */\n base64Data?: string;\n}\n\nexport interface ExecutionMarkdownOptions {\n screenshotBaseDir?: string;\n}\n\nexport interface ExecutionMarkdownResult {\n markdown: string;\n attachments: MarkdownAttachment[];\n}\n\nexport interface ReportMarkdownResult {\n markdown: string;\n attachments: MarkdownAttachment[];\n}\n\nfunction toExecutionDump(\n execution: ExecutionDump | IExecutionDump,\n): IExecutionDump {\n if (!execution || typeof execution !== 'object') {\n throw new Error('executionToMarkdown: execution is required');\n }\n\n if (!Array.isArray(execution.tasks)) {\n throw new Error('executionToMarkdown: execution.tasks must be an array');\n }\n\n if (!execution.name) {\n throw new Error('executionToMarkdown: execution.name is required');\n }\n\n return execution;\n}\n\nfunction toReportDump(\n report: ReportActionDump | IReportActionDump,\n): IReportActionDump {\n if (!report || typeof report !== 'object') {\n throw new Error('reportToMarkdown: report is required');\n }\n\n if (!Array.isArray(report.executions)) {\n throw new Error('reportToMarkdown: report.executions must be an array');\n }\n\n return report;\n}\n\nfunction formatTime(ts?: number): string {\n if (typeof ts !== 'number' || Number.isNaN(ts)) {\n return 'N/A';\n }\n return new Date(ts).toISOString();\n}\n\nfunction resolveTaskTiming(task: ExecutionTask): {\n start?: number;\n end?: number;\n cost?: number;\n} {\n const timing = task.timing;\n if (!timing) {\n return {};\n }\n\n const start = timing.start ?? timing.callAiStart ?? timing.callActionStart;\n const end =\n timing.end ??\n timing.callAiEnd ??\n timing.callActionEnd ??\n timing.captureAfterCallingSnapshotEnd;\n const cost =\n timing.cost ??\n (typeof start === 'number' && typeof end === 'number'\n ? end - start\n : undefined);\n\n return { start, end, cost };\n}\n\nfunction safeTaskParam(task: ExecutionTask): string {\n const readable = paramStr(task);\n if (readable) {\n return readable;\n }\n\n if (task.type === 'Insight') {\n return extractInsightParam((task as any).param).content;\n }\n\n return '';\n}\n\nfunction formatSize(\n size?: { width?: number; height?: number } | null,\n): string | undefined {\n if (\n !size ||\n typeof size.width !== 'number' ||\n typeof size.height !== 'number' ||\n Number.isNaN(size.width) ||\n Number.isNaN(size.height)\n ) {\n return undefined;\n }\n\n return `${size.width} x ${size.height}`;\n}\n\nfunction extractLocateCenter(\n task: ExecutionTask,\n): [number, number] | undefined {\n const outputCenter = (task.output as { element?: { center?: unknown } })\n ?.element?.center;\n if (\n Array.isArray(outputCenter) &&\n outputCenter.length >= 2 &&\n typeof outputCenter[0] === 'number' &&\n typeof outputCenter[1] === 'number'\n ) {\n return [outputCenter[0], outputCenter[1]];\n }\n\n const paramLocateCenter = (task.param as { locate?: { center?: unknown } })\n ?.locate?.center;\n if (\n Array.isArray(paramLocateCenter) &&\n paramLocateCenter.length >= 2 &&\n typeof paramLocateCenter[0] === 'number' &&\n typeof paramLocateCenter[1] === 'number'\n ) {\n return [paramLocateCenter[0], paramLocateCenter[1]];\n }\n\n const paramCenter = (task.param as { center?: unknown })?.center;\n if (\n Array.isArray(paramCenter) &&\n paramCenter.length >= 2 &&\n typeof paramCenter[0] === 'number' &&\n typeof paramCenter[1] === 'number'\n ) {\n return [paramCenter[0], paramCenter[1]];\n }\n\n return undefined;\n}\n\nfunction tryExtractBase64(screenshot: unknown): string | undefined {\n if (!screenshot || typeof screenshot !== 'object') return undefined;\n const s = screenshot as Record<string, unknown>;\n if (typeof s.base64 === 'string' && s.base64.length > 0) {\n return s.base64;\n }\n return undefined;\n}\n\nfunction screenshotAttachment(\n screenshot: unknown,\n screenshotBaseDir: string,\n executionIndex: number,\n taskIndex: number,\n): { markdown: string; attachment: MarkdownAttachment } {\n if (screenshot instanceof ScreenshotItem) {\n const ext = screenshot.extension;\n const suggestedFileName = `execution-${executionIndex + 1}-task-${taskIndex + 1}-${screenshot.id}.${ext}`;\n return {\n markdown: `\\n`,\n attachment: {\n id: screenshot.id,\n suggestedFileName,\n mimeType: `image/${ext === 'jpeg' ? 'jpeg' : 'png'}`,\n executionIndex,\n taskIndex,\n base64Data: tryExtractBase64(screenshot),\n },\n };\n }\n\n const ref = normalizeScreenshotRef(screenshot);\n if (ref) {\n const ext = ref.mimeType === 'image/jpeg' ? 'jpeg' : 'png';\n const suggestedFileName = `execution-${executionIndex + 1}-task-${taskIndex + 1}-${ref.id}.${ext}`;\n return {\n markdown: `\\n`,\n attachment: {\n id: ref.id,\n suggestedFileName,\n sourceRef: ref,\n mimeType: ref.mimeType,\n executionIndex,\n taskIndex,\n base64Data: tryExtractBase64(screenshot),\n },\n };\n }\n\n const base64 = tryExtractBase64(screenshot);\n if (base64) {\n const ext = base64.startsWith('data:image/jpeg') ? 'jpeg' : 'png';\n const id = `restored-${executionIndex + 1}-${taskIndex + 1}`;\n const suggestedFileName = `execution-${executionIndex + 1}-task-${taskIndex + 1}-${id}.${ext}`;\n return {\n markdown: `\\n`,\n attachment: {\n id,\n suggestedFileName,\n mimeType: `image/${ext}`,\n executionIndex,\n taskIndex,\n base64Data: base64,\n },\n };\n }\n\n throw new Error(\n `executionToMarkdown: missing screenshot for execution #${executionIndex + 1} task #${taskIndex + 1}`,\n );\n}\n\nfunction recorderMarkdownSection(\n recorder: ExecutionRecorderItem[] | undefined,\n screenshotBaseDir: string,\n executionIndex: number,\n taskIndex: number,\n): { lines: string[]; attachments: MarkdownAttachment[] } {\n if (!recorder?.length) {\n return { lines: [], attachments: [] };\n }\n\n const lines: string[] = ['', '### Recorder'];\n const attachments: MarkdownAttachment[] = [];\n\n recorder.forEach((item, recorderIndex) => {\n const descriptionText = item.description\n ? `, description=${item.description}`\n : '';\n lines.push(\n `- #${recorderIndex + 1} type=${item.type}, ts=${formatTime(item.ts)}, timing=${item.timing || 'N/A'}${descriptionText}`,\n );\n\n if (!item.screenshot) {\n return;\n }\n\n const imageResult = screenshotAttachment(\n item.screenshot,\n screenshotBaseDir,\n executionIndex,\n taskIndex,\n );\n\n lines.push(imageResult.markdown);\n attachments.push(imageResult.attachment);\n });\n\n return { lines, attachments };\n}\n\nfunction renderExecution(\n executionRaw: ExecutionDump | IExecutionDump,\n executionIndex: number,\n options?: ExecutionMarkdownOptions,\n): ExecutionMarkdownResult {\n const execution = toExecutionDump(executionRaw);\n const screenshotBaseDir = options?.screenshotBaseDir ?? './screenshots';\n\n const lines: string[] = [];\n const attachments: MarkdownAttachment[] = [];\n\n lines.push(`# ${execution.name}`);\n if (execution.description) {\n lines.push('', execution.description);\n }\n\n lines.push('', `- Execution start: ${formatTime(execution.logTime)}`);\n lines.push(`- Task count: ${execution.tasks.length}`);\n\n execution.tasks.forEach((task, taskIndex) => {\n const title = typeStr(task);\n const detail = safeTaskParam(task);\n const time = resolveTaskTiming(task);\n\n lines.push(\n '',\n `## ${taskIndex + 1}. ${title}${detail ? ` - ${detail}` : ''}`,\n );\n lines.push(`- Status: ${task.status || 'unknown'}`);\n lines.push(`- Start: ${formatTime(time.start)}`);\n lines.push(`- End: ${formatTime(time.end)}`);\n lines.push(\n `- Cost(ms): ${typeof time.cost === 'number' ? time.cost : 'N/A'}`,\n );\n lines.push(\n `- Screen size: ${formatSize(task.uiContext?.shotSize) || 'N/A'}`,\n );\n\n if (task.subType === 'Locate') {\n const locateCenter = extractLocateCenter(task);\n if (locateCenter) {\n lines.push(`- Locate center: (${locateCenter[0]}, ${locateCenter[1]})`);\n }\n }\n\n if (task.errorMessage) {\n lines.push(`- Error: ${task.errorMessage}`);\n }\n\n if (task.uiContext?.screenshot) {\n const imageResult = screenshotAttachment(\n task.uiContext.screenshot,\n screenshotBaseDir,\n executionIndex,\n taskIndex,\n );\n\n lines.push(imageResult.markdown);\n attachments.push(imageResult.attachment);\n }\n\n const recorderSection = recorderMarkdownSection(\n task.recorder,\n screenshotBaseDir,\n executionIndex,\n taskIndex,\n );\n if (recorderSection.lines.length) {\n lines.push(...recorderSection.lines);\n attachments.push(...recorderSection.attachments);\n }\n });\n\n return {\n markdown: lines.join('\\n'),\n attachments,\n };\n}\n\nfunction reportFileName(\n execution: IExecutionDump,\n executionIndex: number,\n): string {\n const safeName =\n execution.name\n .trim()\n .replace(/\\s+/g, '-')\n .replace(/[^a-zA-Z0-9-_]/g, '') || `execution-${executionIndex + 1}`;\n return `${executionIndex + 1}-${basename(safeName)}.md`;\n}\n\nexport function executionToMarkdown(\n execution: ExecutionDump | IExecutionDump,\n options?: ExecutionMarkdownOptions,\n): ExecutionMarkdownResult {\n return renderExecution(execution, 0, options);\n}\n\nexport function reportToMarkdown(\n report: ReportActionDump | IReportActionDump,\n): ReportMarkdownResult {\n const reportDump = toReportDump(report);\n\n const executionResults = reportDump.executions.map((execution, index) => {\n const rendered = renderExecution(execution, index);\n return {\n executionIndex: index,\n executionName: execution.name,\n markdown: rendered.markdown,\n attachments: rendered.attachments,\n suggestedFileName: reportFileName(execution, index),\n };\n });\n\n const attachments = executionResults.flatMap((item) => item.attachments);\n\n const header = [\n `# ${reportDump.groupName}`,\n reportDump.groupDescription ? `\\n${reportDump.groupDescription}` : '',\n `\\n- SDK Version: ${reportDump.sdkVersion}`,\n `- Execution count: ${reportDump.executions.length}`,\n '\\n## Suggested execution markdown files',\n ...executionResults.map(\n (item) => `- ${item.suggestedFileName} (${item.executionName})`,\n ),\n ]\n .filter(Boolean)\n .join('\\n');\n\n return {\n markdown: `${header}\\n\\n${executionResults.map((item) => item.markdown).join('\\n\\n---\\n\\n')}`,\n attachments,\n };\n}\n"],"names":["toExecutionDump","execution","Error","Array","toReportDump","report","formatTime","ts","Number","Date","resolveTaskTiming","task","timing","start","end","cost","undefined","safeTaskParam","readable","paramStr","extractInsightParam","formatSize","size","extractLocateCenter","outputCenter","paramLocateCenter","paramCenter","tryExtractBase64","screenshot","s","screenshotAttachment","screenshotBaseDir","executionIndex","taskIndex","ScreenshotItem","ext","suggestedFileName","ref","normalizeScreenshotRef","base64","id","recorderMarkdownSection","recorder","lines","attachments","item","recorderIndex","descriptionText","imageResult","renderExecution","executionRaw","options","title","typeStr","detail","time","locateCenter","recorderSection","reportFileName","safeName","basename","executionToMarkdown","reportToMarkdown","reportDump","executionResults","index","rendered","header","Boolean"],"mappings":"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"}
|
package/dist/es/skill/index.mjs
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
+
import { BaseMidsceneTools } from "@midscene/shared/agent-tools/base-tools";
|
|
1
2
|
import { reportCLIError, runToolsCLI } from "@midscene/shared/cli";
|
|
2
|
-
import { BaseMidsceneTools } from "@midscene/shared/mcp/base-tools";
|
|
3
3
|
import { Agent } from "../agent/agent.mjs";
|
|
4
4
|
function _define_property(obj, key, value) {
|
|
5
5
|
if (key in obj) Object.defineProperty(obj, key, {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"skill/index.mjs","sources":["../../../src/skill/index.ts"],"sourcesContent":["import {
|
|
1
|
+
{"version":3,"file":"skill/index.mjs","sources":["../../../src/skill/index.ts"],"sourcesContent":["import { BaseMidsceneTools } from '@midscene/shared/agent-tools/base-tools';\nimport type { BaseAgent, BaseDevice } from '@midscene/shared/agent-tools/types';\nimport { reportCLIError, runToolsCLI } from '@midscene/shared/cli';\nimport { Agent } from '../agent/agent';\nimport type { AbstractInterface } from '../device';\n\ntype DeviceClass = new (...args: any[]) => AbstractInterface;\n\n/**\n * Skill tools manager that lazily creates Agent from a Device class.\n * Used by runSkillCLI for CLI / Agent Skills scenarios where no agent exists at startup.\n */\nclass SkillMidsceneTools extends BaseMidsceneTools<BaseAgent> {\n constructor(private DeviceClass: DeviceClass) {\n super();\n }\n\n protected createTemporaryDevice(): BaseDevice {\n return new this.DeviceClass() as unknown as BaseDevice;\n }\n\n protected async ensureAgent(): Promise<BaseAgent> {\n if (!this.agent) {\n const device = new this.DeviceClass();\n this.agent = new Agent(device) as unknown as BaseAgent;\n }\n return this.agent;\n }\n}\n\nexport interface SkillCLIOptions {\n scriptName: string;\n DeviceClass: DeviceClass;\n}\n\n/**\n * Launch a Skill CLI for a custom interface Device class.\n * This enables AI coding assistants (Claude Code, Cline, etc.) to control\n * your custom interface through CLI commands.\n *\n * @example\n * ```typescript\n * #!/usr/bin/env node\n * import { runSkillCLI } from '@midscene/core/skill';\n * import { SampleDevice } from './sample-device';\n *\n * runSkillCLI({\n * DeviceClass: SampleDevice,\n * scriptName: 'my-device',\n * });\n * ```\n */\nexport function runSkillCLI(options: SkillCLIOptions): Promise<void> {\n const tools = new SkillMidsceneTools(options.DeviceClass);\n return runToolsCLI(tools, options.scriptName).catch((e) => {\n process.exit(reportCLIError(e));\n });\n}\n"],"names":["SkillMidsceneTools","BaseMidsceneTools","device","Agent","DeviceClass","runSkillCLI","options","tools","runToolsCLI","e","process","reportCLIError"],"mappings":";;;;;;;;;;;;;AAYA,MAAMA,2BAA2BC;IAKrB,wBAAoC;QAC5C,OAAO,IAAI,IAAI,CAAC,WAAW;IAC7B;IAEA,MAAgB,cAAkC;QAChD,IAAI,CAAC,IAAI,CAAC,KAAK,EAAE;YACf,MAAMC,SAAS,IAAI,IAAI,CAAC,WAAW;YACnC,IAAI,CAAC,KAAK,GAAG,IAAIC,MAAMD;QACzB;QACA,OAAO,IAAI,CAAC,KAAK;IACnB;IAdA,YAAoBE,WAAwB,CAAE;QAC5C,KAAK,wDADaA,WAAW,GAAXA;IAEpB;AAaF;AAwBO,SAASC,YAAYC,OAAwB;IAClD,MAAMC,QAAQ,IAAIP,mBAAmBM,QAAQ,WAAW;IACxD,OAAOE,YAAYD,OAAOD,QAAQ,UAAU,EAAE,KAAK,CAAC,CAACG;QACnDC,QAAQ,IAAI,CAACC,eAAeF;IAC9B;AACF"}
|
package/dist/es/types.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types.mjs","sources":["../../src/types.ts"],"sourcesContent":["/* eslint-disable @typescript-eslint/no-explicit-any */\n\nimport type { NodeType } from '@midscene/shared/constants';\nimport type { CreateOpenAIClientFn, TModelConfig } from '@midscene/shared/env';\nimport type {\n BaseElement,\n LocateResultElement,\n Rect,\n Size,\n} from '@midscene/shared/types';\nimport type { z } from 'zod';\nimport type { TUserPrompt } from './common';\nimport type { ScreenshotItem } from './screenshot-item';\nimport type {\n DetailedLocateParam,\n MidsceneYamlFlowItem,\n ServiceExtractOption,\n} from './yaml';\n\nexport type {\n ElementTreeNode,\n BaseElement,\n Rect,\n Size,\n Point,\n} from '@midscene/shared/types';\nexport * from './yaml';\n\nexport { ServiceError } from './errors';\nexport {\n ExecutionDump,\n ReportActionDump,\n GroupedActionDump,\n} from './dump/report-action-dump';\n\nexport type AIUsageInfo = Record<string, any> & {\n prompt_tokens: number | undefined;\n completion_tokens: number | undefined;\n total_tokens: number | undefined;\n cached_input: number | undefined;\n time_cost: number | undefined;\n model_name: string | undefined;\n model_description: string | undefined;\n /**\n * Raw top-level `.model` value returned by the model service response.\n */\n response_model_name: string | undefined;\n /**\n * Semantic intent of the model call, such as default, planning, or insight.\n */\n intent: string | undefined;\n /**\n * Config slot where the model config was resolved from. For example, a\n * planning call may use the default slot when no planning model is configured.\n */\n slot: string | undefined;\n request_id: string | undefined;\n};\n\nexport type { LocateResultElement };\n\nexport type AISingleElementResponseByPosition = {\n position?: {\n x: number;\n y: number;\n };\n bbox?: [number, number, number, number];\n reason: string;\n text: string;\n};\n\nexport type LocateResultPoint = [number, number];\nexport type Bbox = [number, number, number, number];\nexport type LocateResultBbox = Bbox;\nexport type PixelBbox = Bbox;\n\nexport interface AIElementLocateResponse {\n bbox?: LocateResultBbox;\n point?: LocateResultPoint;\n errors?: string[];\n}\n\nexport interface AIDataExtractionResponse<DataDemand> {\n data: DataDemand;\n errors?: string[];\n thought?: string;\n}\n\nexport interface AISectionLocatorResponse {\n bbox?: LocateResultBbox;\n point?: LocateResultPoint;\n references_bbox?: LocateResultBbox[];\n references_point?: LocateResultPoint[];\n error?: string;\n}\n\nexport interface AIAssertionResponse {\n pass: boolean;\n thought: string;\n}\n\nexport interface AIDescribeElementResponse {\n description: string;\n error?: string;\n}\n\nexport interface LocatorValidatorOption {\n centerDistanceThreshold?: number;\n}\n\nexport interface LocateValidatorResult {\n pass: boolean;\n rect: Rect;\n center: [number, number];\n centerDistance?: number;\n includedInRect?: boolean;\n}\n\nexport interface AgentDescribeElementAtPointResult {\n prompt: string;\n deepLocate: boolean;\n verifyResult?: LocateValidatorResult;\n}\n\n/**\n * context\n */\n\nexport abstract class UIContext {\n /**\n * screenshot of the current UI state. which size is shotSize(be shrunk by screenshotShrinkFactor),\n */\n abstract screenshot: ScreenshotItem;\n\n /**\n * screenshot size after shrinking\n */\n abstract shotSize: Size;\n\n /**\n * The ratio for converting shrunk screenshot coordinates to logical coordinates.\n *\n * Example:\n * - Physical screen width: 3000px, dpr=6\n * - Logical width: 500px\n * - User-defined screenshotShrinkFactor: 2\n * - Actual shrunk screenshot width: 3000 / 2 = 1500px\n * - shrunkShotToLogicalRatio: dpr / screenshotShrinkFactor = 6 / 2 = 3\n * - To map back to logical coordinates: 1500 / shrunkShotToLogicalRatio = 500px\n */\n abstract shrunkShotToLogicalRatio: number;\n\n abstract _isFrozen?: boolean;\n\n // @deprecated - backward compatibility for aiLocate\n abstract deprecatedDpr?: number;\n}\n\nexport type EnsureObject<T> = { [K in keyof T]: any };\n\nexport type ServiceAction = 'locate' | 'extract' | 'assert' | 'describe';\n\nexport type ServiceExtractParam = string | Record<string, string>;\n\nexport type ElementCacheFeature = Record<string, unknown>;\n\nexport interface LocateResult {\n element: LocateResultElement | null;\n rect?: Rect;\n}\n\nexport type ThinkingLevel = 'off' | 'medium' | 'high';\n\nexport type DeepThinkOption = 'unset' | true | false;\n\nexport interface ServiceTaskInfo {\n durationMs: number;\n formatResponse?: string;\n /**\n * Adapter-extracted content used by Midscene for parsing. This is not the\n * full provider response or choices[0].message.\n */\n rawResponse?: string;\n rawChoiceMessage?: unknown;\n usage?: AIUsageInfo;\n searchArea?: Rect;\n /**\n * Adapter-extracted content from the search-area model call. This is not the\n * full provider response or choices[0].message.\n */\n searchAreaRawResponse?: string;\n searchAreaRawChoiceMessage?: unknown;\n searchAreaUsage?: AIUsageInfo;\n reasoning_content?: string;\n}\n\nexport interface DumpMeta {\n logTime: number;\n}\n\nexport type ReportAttributes = Record<\n string,\n string | number | boolean | null | undefined\n>;\n\nexport interface ReportDumpWithAttributes {\n dumpString: string;\n attributes?: ReportAttributes;\n}\n\nexport interface ServiceDump extends DumpMeta {\n type: 'locate' | 'extract' | 'assert';\n logId: string;\n userQuery: {\n element?: TUserPrompt;\n dataDemand?: ServiceExtractParam;\n assertion?: TUserPrompt;\n };\n matchedElement?: LocateResultElement[];\n matchedRect?: Rect;\n deepLocate?: boolean;\n data: any;\n assertionPass?: boolean;\n assertionThought?: string;\n taskInfo: ServiceTaskInfo;\n error?: string;\n output?: any;\n}\n\nexport type PartialServiceDumpFromSDK = Omit<\n ServiceDump,\n 'logTime' | 'logId' | 'model_name'\n>;\n\nexport interface ServiceResultBase {\n dump: ServiceDump;\n}\n\nexport type LocateResultWithDump = LocateResult & ServiceResultBase;\n\nexport interface ServiceExtractResult<T> extends ServiceResultBase {\n data: T;\n thought?: string;\n usage?: AIUsageInfo;\n reasoning_content?: string;\n}\n\n// intermediate variables to optimize the return value by AI\nexport interface LiteUISection {\n name: string;\n description: string;\n sectionCharacteristics: string;\n textIds: string[];\n}\n\nexport type ElementById = (id: string) => BaseElement | null;\n\nexport type ServiceAssertionResponse = AIAssertionResponse & {\n usage?: AIUsageInfo;\n};\n\n/**\n * agent\n */\n\nexport type OnTaskStartTip = (tip: string) => Promise<void> | void;\n\nexport interface AgentWaitForOpt extends ServiceExtractOption {\n checkIntervalMs?: number;\n timeoutMs?: number;\n}\n\nexport interface AgentAssertOpt {\n keepRawResponse?: boolean;\n}\n\n/**\n * planning\n *\n */\n\nexport interface PlanningLocateParam extends DetailedLocateParam {\n bbox?: LocateResultBbox;\n point?: LocateResultPoint;\n}\n\nexport type PlanningLocateParamWithLocatedPixelBbox = PlanningLocateParam & {\n /** Pixel bbox of the located element in screenshot coordinates. */\n locatedPixelBbox: PixelBbox;\n};\n\nexport interface PlanningAction<ParamType = any> {\n thought?: string;\n log?: string; // a brief preamble to the user explaining what you’re about to do\n type: string;\n param: ParamType;\n}\n\nexport type SubGoalStatus = 'pending' | 'running' | 'finished';\n\nexport interface SubGoal {\n index: number;\n status: SubGoalStatus;\n description: string;\n logs?: string[];\n}\n\nexport interface RawResponsePlanningAIResponse {\n action: PlanningAction;\n thought?: string;\n log: string;\n memory?: string;\n error?: string;\n finalizeMessage?: string;\n finalizeSuccess?: boolean;\n updateSubGoals?: SubGoal[];\n markFinishedIndexes?: number[];\n}\n\nexport interface PlanningAIResponse\n extends Omit<RawResponsePlanningAIResponse, 'action'> {\n actions?: PlanningAction[];\n usage?: AIUsageInfo;\n /**\n * Adapter-extracted content used by Midscene for parsing. This is not the\n * full provider response or choices[0].message.\n */\n rawResponse?: string;\n rawChoiceMessage?: unknown;\n yamlFlow?: MidsceneYamlFlowItem[];\n yamlString?: string;\n error?: string;\n reasoning_content?: string;\n shouldContinuePlanning: boolean;\n output?: string; // Output message from <complete> tag (same as finalizeMessage)\n}\n\nexport interface PlanningActionParamSleep {\n timeMs: number;\n}\n\nexport interface PlanningActionParamError {\n thought: string;\n}\n\nexport type PlanningActionParamWaitFor = AgentWaitForOpt & {};\n\nexport interface LongPressParam {\n duration?: number;\n}\n\nexport interface PullParam {\n direction: 'up' | 'down';\n distance?: number;\n duration?: number;\n}\n/**\n * misc\n */\n\nexport interface Color {\n name: string;\n hex: string;\n}\n\nexport interface BaseAgentParserOpt {\n selector?: string;\n}\n// eslint-disable-next-line @typescript-eslint/no-empty-interface\nexport interface PuppeteerParserOpt extends BaseAgentParserOpt {}\n\n// eslint-disable-next-line @typescript-eslint/no-empty-interface\nexport interface PlaywrightParserOpt extends BaseAgentParserOpt {}\n\n/*\naction\n*/\nexport interface ExecutionTaskProgressOptions {\n onTaskStart?: (task: ExecutionTask) => Promise<void> | void;\n}\n\nexport interface ExecutionRecorderItem {\n type: 'screenshot';\n ts: number;\n screenshot?: ScreenshotItem;\n timing?: string;\n}\n\nexport type ExecutionTaskType = 'Planning' | 'Insight' | 'Action Space' | 'Log';\n\nexport interface ExecutorContext {\n task: ExecutionTask;\n element?: LocateResultElement | null;\n uiContext?: UIContext;\n}\n\nexport interface ExecutionTaskApply<\n Type extends ExecutionTaskType = any,\n TaskParam = any,\n TaskOutput = any,\n TaskLog = any,\n> {\n type: Type;\n subType?: string;\n param?: TaskParam;\n thought?: string;\n uiContext?: UIContext;\n executor: (\n param: TaskParam,\n context: ExecutorContext,\n ) => // biome-ignore lint/suspicious/noConfusingVoidType: void is intentionally allowed as some executors may not return a value\n | Promise<ExecutionTaskReturn<TaskOutput, TaskLog> | undefined | void>\n | undefined\n | void;\n}\n\nexport interface ExecutionTaskHitBy {\n from: string;\n context: Record<string, any>;\n}\n\nexport interface ExecutionTaskReturn<TaskOutput = unknown, TaskLog = unknown> {\n output?: TaskOutput;\n log?: TaskLog;\n recorder?: ExecutionRecorderItem[];\n hitBy?: ExecutionTaskHitBy;\n}\n\nexport type ExecutionTask<\n E extends ExecutionTaskApply<any, any, any> = ExecutionTaskApply<\n any,\n any,\n any\n >,\n> = E &\n ExecutionTaskReturn<\n E extends ExecutionTaskApply<any, any, infer TaskOutput, any>\n ? TaskOutput\n : unknown,\n E extends ExecutionTaskApply<any, any, any, infer TaskLog>\n ? TaskLog\n : unknown\n > & {\n taskId: string;\n status: 'pending' | 'running' | 'finished' | 'failed' | 'cancelled';\n /**\n * Optional feedback produced by a task for the next planning round.\n * This is execution metadata, not part of the action return value.\n */\n planningFeedback?: string;\n error?: Error;\n errorMessage?: string;\n errorStack?: string;\n timing?: {\n start: number;\n getUiContextStart?: number;\n getUiContextEnd?: number;\n callAiStart?: number;\n callAiEnd?: number;\n beforeInvokeActionHookStart?: number;\n beforeInvokeActionHookEnd?: number;\n callActionStart?: number;\n callActionEnd?: number;\n afterInvokeActionHookStart?: number;\n afterInvokeActionHookEnd?: number;\n captureAfterCallingSnapshotStart?: number;\n captureAfterCallingSnapshotEnd?: number;\n end?: number;\n cost?: number;\n };\n usage?: AIUsageInfo;\n /**\n * Pixel rect of the deepLocate first-stage search area in screenshot\n * coordinates. Used by reports to explain the crop/zoom area that the\n * final locate ran against.\n */\n searchArea?: Rect;\n searchAreaUsage?: AIUsageInfo;\n reasoning_content?: string;\n };\n\nexport interface IExecutionDump extends DumpMeta {\n /** Stable unique identifier for this execution run */\n id?: string;\n name: string;\n description?: string;\n tasks: ExecutionTask[];\n aiActContext?: string;\n}\n\n/*\ntask - service-locate\n*/\nexport type ExecutionTaskInsightLocateParam = PlanningLocateParam;\n\nexport interface ExecutionTaskInsightLocateOutput {\n element: LocateResultElement | null;\n}\n\nexport type ExecutionTaskInsightDump = ServiceDump;\n\nexport type ExecutionTaskInsightLocateApply = ExecutionTaskApply<\n 'Insight',\n ExecutionTaskInsightLocateParam,\n ExecutionTaskInsightLocateOutput,\n ExecutionTaskInsightDump\n>;\n\nexport type ExecutionTaskInsightLocate =\n ExecutionTask<ExecutionTaskInsightLocateApply>;\n\n/*\ntask - service-query\n*/\nexport interface ExecutionTaskInsightQueryParam {\n dataDemand: ServiceExtractParam;\n domIncluded?: boolean | 'visible-only';\n}\n\nexport interface ExecutionTaskInsightQueryOutput {\n data: any;\n}\n\nexport type ExecutionTaskInsightQueryApply = ExecutionTaskApply<\n 'Insight',\n ExecutionTaskInsightQueryParam,\n any,\n ExecutionTaskInsightDump\n>;\n\nexport type ExecutionTaskInsightQuery =\n ExecutionTask<ExecutionTaskInsightQueryApply>;\n\n/*\ntask - assertion\n*/\nexport interface ExecutionTaskInsightAssertionParam {\n assertion: string;\n}\n\nexport type ExecutionTaskInsightAssertionApply = ExecutionTaskApply<\n 'Insight',\n ExecutionTaskInsightAssertionParam,\n ServiceAssertionResponse,\n ExecutionTaskInsightDump\n>;\n\nexport type ExecutionTaskInsightAssertion =\n ExecutionTask<ExecutionTaskInsightAssertionApply>;\n\n/*\ntask - action (i.e. interact) \n*/\nexport type ExecutionTaskActionApply<ActionParam = any> = ExecutionTaskApply<\n 'Action Space',\n ActionParam,\n void,\n void\n>;\n\nexport type ExecutionTaskAction = ExecutionTask<ExecutionTaskActionApply>;\n\n/*\ntask - Log\n*/\n\nexport type ExecutionTaskLogApply<\n LogParam = {\n content: string;\n },\n> = ExecutionTaskApply<'Log', LogParam, void, void>;\n\nexport type ExecutionTaskLog = ExecutionTask<ExecutionTaskLogApply>;\n\n/*\ntask - planning\n*/\n\nexport interface ExecutionTaskPlanningParam {\n userInstruction: TUserPrompt;\n userInstructionDisplay?: string;\n aiActContext?: string;\n imagesIncludeCount?: number;\n deepThink?: DeepThinkOption;\n subGoalStatus?: string;\n memoriesStatus?: string;\n}\n\nexport type ExecutionTaskPlanningApply = ExecutionTaskApply<\n 'Planning',\n ExecutionTaskPlanningParam,\n PlanningAIResponse\n>;\n\nexport type ExecutionTaskPlanning = ExecutionTask<ExecutionTaskPlanningApply>;\n\n/*\ntask - planning-locate\n*/\nexport type ExecutionTaskPlanningLocateParam = PlanningLocateParam;\n\nexport interface ExecutionTaskPlanningLocateOutput {\n element: LocateResultElement | null;\n}\n\nexport type ExecutionTaskPlanningDump = ServiceDump;\n\nexport type ExecutionTaskPlanningLocateApply = ExecutionTaskApply<\n 'Planning',\n ExecutionTaskPlanningLocateParam,\n ExecutionTaskPlanningLocateOutput,\n ExecutionTaskPlanningDump\n>;\n\nexport type ExecutionTaskPlanningLocate =\n ExecutionTask<ExecutionTaskPlanningLocateApply>;\n\n/*\nReport metadata - extracted from ReportActionDump for per-execution writes\n*/\nexport interface ReportMeta {\n groupName: string;\n groupDescription?: string;\n sdkVersion: string;\n modelBriefs: ModelBrief[];\n deviceType?: string;\n}\n\n// Backward-compatible aliases for existing external consumers.\nexport type GroupMeta = ReportMeta;\n\n/*\nReport dump\n*/\nexport interface IReportActionDump {\n sdkVersion: string;\n groupName: string;\n groupDescription?: string;\n modelBriefs: ModelBrief[];\n executions: IExecutionDump[];\n deviceType?: string;\n}\n\n// Backward-compatible aliases for existing external consumers.\nexport type IGroupedActionDump = IReportActionDump;\n\nexport interface ModelBrief {\n /**\n * The intent/category of the model call, for example \"planning\" or \"insight\".\n */\n intent?: string;\n\n /**\n * The model name returned by usage metadata, for example \"gpt-4o\".\n */\n name?: string;\n\n /**\n * Optional human-readable model description, for example \"qwen2.5-vl mode\".\n */\n modelDescription?: string;\n}\n\nexport type InterfaceType =\n | 'puppeteer'\n | 'playwright'\n | 'static'\n | 'chrome-extension-proxy'\n | 'android'\n | string;\n\nexport interface StreamingCodeGenerationOptions {\n /** Whether to enable streaming output */\n stream?: boolean;\n /** Callback function to handle streaming chunks */\n onChunk?: StreamingCallback;\n /** Callback function to handle streaming completion */\n onComplete?: (finalCode: string) => void;\n /** Callback function to handle streaming errors */\n onError?: (error: Error) => void;\n}\n\nexport type StreamingCallback = (chunk: CodeGenerationChunk) => void;\n\nexport interface CodeGenerationChunk {\n /** The incremental content chunk */\n content: string;\n /** The reasoning content */\n reasoning_content: string;\n /** The accumulated content so far */\n accumulated: string;\n /** Whether this is the final chunk */\n isComplete: boolean;\n /** Token usage information if available */\n usage?: AIUsageInfo;\n}\n\nexport interface StreamingAIResponse {\n /** The final accumulated content */\n content: string;\n /** Token usage information */\n usage?: AIUsageInfo;\n /** Whether the response was streamed */\n isStreamed: boolean;\n}\n\nexport interface DeviceAction<TParam = any, TReturn = any> {\n name: string;\n description?: string;\n interfaceAlias?: string;\n paramSchema?: z.ZodType<TParam>;\n call: (\n param: TParam,\n context?: ExecutorContext,\n ) => Promise<TReturn> | TReturn;\n delayBeforeRunner?: number;\n delayAfterRunner?: number;\n /**\n * An example param object for this action.\n * Locate fields with { prompt } may be resolved to internal pixel bboxes when needed.\n */\n sample?: { [K in keyof TParam]?: any };\n}\n\n/**\n * Type utilities for extracting types from DeviceAction definitions\n */\n\n/**\n * Extract parameter type from a DeviceAction\n */\nexport type ActionParam<Action extends DeviceAction<any, any>> =\n Action extends DeviceAction<infer P, any> ? P : never;\n\n/**\n * Extract return type from a DeviceAction\n */\nexport type ActionReturn<Action extends DeviceAction<any, any>> =\n Action extends DeviceAction<any, infer R> ? R : never;\n\n/**\n * Web-specific types\n */\nexport interface WebElementInfo extends BaseElement {\n id: string;\n attributes: {\n nodeType: NodeType;\n [key: string]: string;\n };\n}\n\n/**\n * Agent\n */\n\nexport type CacheConfig = {\n strategy?: 'read-only' | 'read-write' | 'write-only';\n id: string;\n /**\n * Optional cache directory path.\n * When set, cache files are written to this directory instead of\n * `<MIDSCENE_RUN_DIR>/cache`.\n */\n cacheDir?: string;\n};\n\nexport type Cache =\n | false // No read, no write\n | true // Will throw error at runtime - deprecated\n | CacheConfig; // Object configuration (requires explicit id)\n\nexport interface AgentOpt {\n // @deprecated Use `reportFileName` and `cache.id` instead.\n testId?: string;\n // @deprecated\n cacheId?: string; // Keep backward compatibility, but marked as deprecated\n groupName?: string;\n groupDescription?: string;\n /* if auto generate report, default true */\n generateReport?: boolean;\n /* if persist per-execution dump files next to the report, default false */\n persistExecutionDump?: boolean;\n /* if auto print report msg, default true */\n autoPrintReportMsg?: boolean;\n\n /**\n * Use directory-based report format with separate image files.\n *\n * When enabled:\n * - Screenshots are saved as PNG files in a `screenshots/` subdirectory\n * - Report is generated as `index.html` with relative image paths\n * - Reduces memory usage and report file size\n *\n * IMPORTANT: 'html-and-external-assets' reports must be served via HTTP server\n * (e.g., `npx serve ./report-dir`). The file:// protocol will not\n * work due to browser CORS restrictions.\n *\n * @default 'single-html'\n */\n outputFormat?: 'single-html' | 'html-and-external-assets';\n\n onTaskStartTip?: OnTaskStartTip;\n aiActContext?: string;\n aiActionContext?: string;\n /* custom report file name */\n reportFileName?: string;\n reportAttributes?: ReportAttributes;\n modelConfig?: TModelConfig;\n cache?: Cache;\n /**\n * Maximum number of replanning cycles for aiAct.\n * Defaults are resolved by the active model adapter: 20 for standard planning,\n * 40 for UI-TARS, and 100 for Auto-GLM.\n * If omitted, the agent will also read `MIDSCENE_REPLANNING_CYCLE_LIMIT` for backward compatibility.\n */\n replanningCycleLimit?: number;\n\n /**\n * Wait time in milliseconds after each action execution.\n * This allows the UI to settle and stabilize before the next action.\n * Defaults to 300ms when not provided.\n */\n waitAfterAction?: number;\n\n /**\n * When set to true, Midscene will use the target device's formatted local\n * time instead of the runtime system time. The target interface must implement\n * getDeviceLocalTimeString to provide device-local wall-clock time.\n * Default: false\n */\n useDeviceTime?: boolean;\n\n /**\n * Custom screenshot shrink factor to reduce AI token usage.\n * When set, the screenshot will be scaled down by this factor from the physical resolution.\n *\n * Example:\n * - Physical screen width: 3000px, dpr=6\n * - Logical width: 500px\n * - screenshotShrinkFactor: 2\n * - Actual shrunk screenshot width: 3000 / 2 = 1500px\n * - AI analyzes the 1500px screenshot\n * - Coordinates are transformed back to logical (500px) before actions execute\n *\n * Benefits:\n * - Reduces token usage for high-resolution screenshots\n * - Maintains accuracy by scaling coordinates appropriately\n *\n * Must be >= 1 (shrinking only, enlarging is not supported).\n *\n * @default 1 (no shrinking, uses original physical screenshot)\n */\n screenshotShrinkFactor?: number;\n\n /**\n * Custom OpenAI client factory function\n *\n * If provided, this function will be called to create OpenAI client instances\n * for each AI call, allowing you to:\n * - Wrap clients with observability tools (langsmith, langfuse)\n * - Use custom OpenAI-compatible clients\n * - Apply different configurations based on intent\n *\n * @param config - Resolved model configuration\n * @returns OpenAI client instance (original or wrapped)\n *\n * @example\n * ```typescript\n * createOpenAIClient: async (openai, opts) => {\n * // Wrap with langsmith for planning tasks\n * if (opts.baseURL?.includes('planning')) {\n * return wrapOpenAI(openai, { metadata: { task: 'planning' } });\n * }\n *\n * return openai;\n * }\n * ```\n */\n createOpenAIClient?: CreateOpenAIClientFn;\n}\n\nexport type TestStatus =\n | 'passed'\n | 'failed'\n | 'timedOut'\n | 'skipped'\n | 'interrupted';\n\nexport interface ReportFileAttributes {\n testDuration: number;\n testStatus: TestStatus;\n testTitle: string;\n testId: string;\n testDescription: string;\n}\n\nexport type ReportFileWithAttributes =\n | {\n reportFilePath: string;\n reportAttributes: ReportFileAttributes;\n }\n | {\n reportFilePath?: string;\n reportAttributes: ReportFileAttributes & { testStatus: 'skipped' };\n };\n"],"names":["UIContext"],"mappings":";;;AAgIO,MAAeA;AA4BtB"}
|
|
1
|
+
{"version":3,"file":"types.mjs","sources":["../../src/types.ts"],"sourcesContent":["/* eslint-disable @typescript-eslint/no-explicit-any */\n\nimport type { NodeType } from '@midscene/shared/constants';\nimport type { CreateOpenAIClientFn, TModelConfig } from '@midscene/shared/env';\nimport type {\n BaseElement,\n LocateResultElement,\n Rect,\n Size,\n} from '@midscene/shared/types';\nimport type { z } from 'zod';\nimport type { TUserPrompt } from './common';\nimport type { ScreenshotItem } from './screenshot-item';\nimport type {\n DetailedLocateParam,\n MidsceneYamlFlowItem,\n ServiceExtractOption,\n} from './yaml';\n\nexport type {\n ElementTreeNode,\n BaseElement,\n Rect,\n Size,\n Point,\n} from '@midscene/shared/types';\nexport * from './yaml';\n\nexport { ServiceError } from './errors';\nexport {\n ExecutionDump,\n ReportActionDump,\n GroupedActionDump,\n} from './dump/report-action-dump';\n\nexport type AIUsageInfo = Record<string, any> & {\n prompt_tokens: number | undefined;\n completion_tokens: number | undefined;\n total_tokens: number | undefined;\n cached_input: number | undefined;\n time_cost: number | undefined;\n model_name: string | undefined;\n model_description: string | undefined;\n /**\n * Raw top-level `.model` value returned by the model service response.\n */\n response_model_name: string | undefined;\n /**\n * Semantic intent of the model call, such as default, planning, or insight.\n */\n intent: string | undefined;\n /**\n * Config slot where the model config was resolved from. For example, a\n * planning call may use the default slot when no planning model is configured.\n */\n slot: string | undefined;\n request_id: string | undefined;\n};\n\nexport type { LocateResultElement };\n\nexport type AISingleElementResponseByPosition = {\n position?: {\n x: number;\n y: number;\n };\n bbox?: [number, number, number, number];\n reason: string;\n text: string;\n};\n\nexport type LocateResultPoint = [number, number];\nexport type Bbox = [number, number, number, number];\nexport type LocateResultBbox = Bbox;\nexport type PixelBbox = Bbox;\n\nexport interface AIElementLocateResponse {\n bbox?: LocateResultBbox;\n point?: LocateResultPoint;\n errors?: string[];\n}\n\nexport interface AIDataExtractionResponse<DataDemand> {\n data: DataDemand;\n errors?: string[];\n thought?: string;\n}\n\nexport interface AISectionLocatorResponse {\n bbox?: LocateResultBbox;\n point?: LocateResultPoint;\n references_bbox?: LocateResultBbox[];\n references_point?: LocateResultPoint[];\n error?: string;\n}\n\nexport interface AIAssertionResponse {\n pass: boolean;\n thought: string;\n}\n\nexport interface AIDescribeElementResponse {\n description: string;\n error?: string;\n}\n\nexport interface LocatorValidatorOption {\n centerDistanceThreshold?: number;\n}\n\nexport interface LocateValidatorResult {\n pass: boolean;\n rect: Rect;\n center: [number, number];\n centerDistance?: number;\n includedInRect?: boolean;\n}\n\nexport interface AgentDescribeElementAtPointResult {\n prompt: string;\n deepLocate: boolean;\n verifyResult?: LocateValidatorResult;\n}\n\n/**\n * context\n */\n\nexport abstract class UIContext {\n /**\n * screenshot of the current UI state. which size is shotSize(be shrunk by screenshotShrinkFactor),\n */\n abstract screenshot: ScreenshotItem;\n\n /**\n * screenshot size after shrinking\n */\n abstract shotSize: Size;\n\n /**\n * The ratio for converting shrunk screenshot coordinates to logical coordinates.\n *\n * Example:\n * - Physical screen width: 3000px, dpr=6\n * - Logical width: 500px\n * - User-defined screenshotShrinkFactor: 2\n * - Actual shrunk screenshot width: 3000 / 2 = 1500px\n * - shrunkShotToLogicalRatio: dpr / screenshotShrinkFactor = 6 / 2 = 3\n * - To map back to logical coordinates: 1500 / shrunkShotToLogicalRatio = 500px\n */\n abstract shrunkShotToLogicalRatio: number;\n\n abstract _isFrozen?: boolean;\n\n // @deprecated - backward compatibility for aiLocate\n abstract deprecatedDpr?: number;\n}\n\nexport type EnsureObject<T> = { [K in keyof T]: any };\n\nexport type ServiceAction = 'locate' | 'extract' | 'assert' | 'describe';\n\nexport type ServiceExtractParam = string | Record<string, string>;\n\nexport type ElementCacheFeature = Record<string, unknown>;\n\nexport interface LocateResult {\n element: LocateResultElement | null;\n rect?: Rect;\n}\n\nexport type ThinkingLevel = 'off' | 'medium' | 'high';\n\nexport type DeepThinkOption = 'unset' | true | false;\n\nexport interface ServiceTaskInfo {\n durationMs: number;\n formatResponse?: string;\n /**\n * Adapter-extracted content used by Midscene for parsing. This is not the\n * full provider response or choices[0].message.\n */\n rawResponse?: string;\n rawChoiceMessage?: unknown;\n usage?: AIUsageInfo;\n searchArea?: Rect;\n /**\n * Adapter-extracted content from the search-area model call. This is not the\n * full provider response or choices[0].message.\n */\n searchAreaRawResponse?: string;\n searchAreaRawChoiceMessage?: unknown;\n searchAreaUsage?: AIUsageInfo;\n reasoning_content?: string;\n}\n\nexport interface DumpMeta {\n logTime: number;\n}\n\nexport type ReportAttributes = Record<\n string,\n string | number | boolean | null | undefined\n>;\n\nexport interface ReportDumpWithAttributes {\n dumpString: string;\n attributes?: ReportAttributes;\n}\n\nexport interface ServiceDump extends DumpMeta {\n type: 'locate' | 'extract' | 'assert';\n logId: string;\n userQuery: {\n element?: TUserPrompt;\n dataDemand?: ServiceExtractParam;\n assertion?: TUserPrompt;\n };\n matchedElement?: LocateResultElement[];\n matchedRect?: Rect;\n deepLocate?: boolean;\n data: any;\n assertionPass?: boolean;\n assertionThought?: string;\n taskInfo: ServiceTaskInfo;\n error?: string;\n output?: any;\n}\n\nexport type PartialServiceDumpFromSDK = Omit<\n ServiceDump,\n 'logTime' | 'logId' | 'model_name'\n>;\n\nexport interface ServiceResultBase {\n dump: ServiceDump;\n}\n\nexport type LocateResultWithDump = LocateResult & ServiceResultBase;\n\nexport interface ServiceExtractResult<T> extends ServiceResultBase {\n data: T;\n thought?: string;\n usage?: AIUsageInfo;\n reasoning_content?: string;\n}\n\n// intermediate variables to optimize the return value by AI\nexport interface LiteUISection {\n name: string;\n description: string;\n sectionCharacteristics: string;\n textIds: string[];\n}\n\nexport type ElementById = (id: string) => BaseElement | null;\n\nexport type ServiceAssertionResponse = AIAssertionResponse & {\n usage?: AIUsageInfo;\n};\n\n/**\n * agent\n */\n\nexport type OnTaskStartTip = (tip: string) => Promise<void> | void;\n\nexport interface AgentWaitForOpt extends ServiceExtractOption {\n checkIntervalMs?: number;\n timeoutMs?: number;\n}\n\nexport interface AgentAssertOpt {\n keepRawResponse?: boolean;\n}\n\n/**\n * planning\n *\n */\n\nexport interface PlanningLocateParam extends DetailedLocateParam {\n bbox?: LocateResultBbox;\n point?: LocateResultPoint;\n}\n\nexport type PlanningLocateParamWithLocatedPixelBbox = PlanningLocateParam & {\n /** Pixel bbox of the located element in screenshot coordinates. */\n locatedPixelBbox: PixelBbox;\n};\n\nexport interface PlanningAction<ParamType = any> {\n thought?: string;\n log?: string; // a brief preamble to the user explaining what you’re about to do\n type: string;\n param: ParamType;\n}\n\nexport type SubGoalStatus = 'pending' | 'running' | 'finished';\n\nexport interface SubGoal {\n index: number;\n status: SubGoalStatus;\n description: string;\n logs?: string[];\n}\n\nexport interface RawResponsePlanningAIResponse {\n action: PlanningAction;\n thought?: string;\n log: string;\n memory?: string;\n error?: string;\n finalizeMessage?: string;\n finalizeSuccess?: boolean;\n updateSubGoals?: SubGoal[];\n markFinishedIndexes?: number[];\n}\n\nexport interface PlanningAIResponse\n extends Omit<RawResponsePlanningAIResponse, 'action'> {\n actions?: PlanningAction[];\n usage?: AIUsageInfo;\n /**\n * Adapter-extracted content used by Midscene for parsing. This is not the\n * full provider response or choices[0].message.\n */\n rawResponse?: string;\n rawChoiceMessage?: unknown;\n yamlFlow?: MidsceneYamlFlowItem[];\n yamlString?: string;\n error?: string;\n reasoning_content?: string;\n shouldContinuePlanning: boolean;\n output?: string; // Output message from <complete> tag (same as finalizeMessage)\n}\n\nexport interface PlanningActionParamSleep {\n timeMs: number;\n}\n\nexport interface PlanningActionParamError {\n thought: string;\n}\n\nexport type PlanningActionParamWaitFor = AgentWaitForOpt & {};\n\nexport interface LongPressParam {\n duration?: number;\n}\n\nexport interface PullParam {\n direction: 'up' | 'down';\n distance?: number;\n duration?: number;\n}\n/**\n * misc\n */\n\nexport interface Color {\n name: string;\n hex: string;\n}\n\nexport interface BaseAgentParserOpt {\n selector?: string;\n}\n// eslint-disable-next-line @typescript-eslint/no-empty-interface\nexport interface PuppeteerParserOpt extends BaseAgentParserOpt {}\n\n// eslint-disable-next-line @typescript-eslint/no-empty-interface\nexport interface PlaywrightParserOpt extends BaseAgentParserOpt {}\n\n/*\naction\n*/\nexport interface ExecutionTaskProgressOptions {\n onTaskStart?: (task: ExecutionTask) => Promise<void> | void;\n}\n\nexport interface ExecutionRecorderItem {\n type: 'screenshot';\n ts: number;\n screenshot?: ScreenshotItem;\n description?: string;\n timing?: string;\n}\n\nexport interface RecordToReportScreenshot {\n /**\n * PNG/JPEG data URI, or raw PNG base64 body.\n */\n base64: string;\n description?: string;\n}\n\nexport interface RecordToReportOptions {\n content?: string;\n /**\n * @deprecated Use `screenshots: [{ base64 }]` instead.\n */\n screenshotBase64?: string;\n /**\n * Custom screenshots to display under a single report entry.\n */\n screenshots?: RecordToReportScreenshot[];\n}\n\nexport type ExecutionTaskType = 'Planning' | 'Insight' | 'Action Space' | 'Log';\n\nexport interface ExecutorContext {\n task: ExecutionTask;\n element?: LocateResultElement | null;\n uiContext?: UIContext;\n}\n\nexport interface ExecutionTaskApply<\n Type extends ExecutionTaskType = any,\n TaskParam = any,\n TaskOutput = any,\n TaskLog = any,\n> {\n type: Type;\n subType?: string;\n param?: TaskParam;\n thought?: string;\n uiContext?: UIContext;\n executor: (\n param: TaskParam,\n context: ExecutorContext,\n ) => // biome-ignore lint/suspicious/noConfusingVoidType: void is intentionally allowed as some executors may not return a value\n | Promise<ExecutionTaskReturn<TaskOutput, TaskLog> | undefined | void>\n | undefined\n | void;\n}\n\nexport interface ExecutionTaskHitBy {\n from: string;\n context: Record<string, any>;\n}\n\nexport interface ExecutionTaskReturn<TaskOutput = unknown, TaskLog = unknown> {\n output?: TaskOutput;\n log?: TaskLog;\n recorder?: ExecutionRecorderItem[];\n hitBy?: ExecutionTaskHitBy;\n}\n\nexport type ExecutionTask<\n E extends ExecutionTaskApply<any, any, any> = ExecutionTaskApply<\n any,\n any,\n any\n >,\n> = E &\n ExecutionTaskReturn<\n E extends ExecutionTaskApply<any, any, infer TaskOutput, any>\n ? TaskOutput\n : unknown,\n E extends ExecutionTaskApply<any, any, any, infer TaskLog>\n ? TaskLog\n : unknown\n > & {\n taskId: string;\n status: 'pending' | 'running' | 'finished' | 'failed' | 'cancelled';\n /**\n * Optional feedback produced by a task for the next planning round.\n * This is execution metadata, not part of the action return value.\n */\n planningFeedback?: string;\n error?: Error;\n errorMessage?: string;\n errorStack?: string;\n timing?: {\n start: number;\n getUiContextStart?: number;\n getUiContextEnd?: number;\n callAiStart?: number;\n callAiEnd?: number;\n beforeInvokeActionHookStart?: number;\n beforeInvokeActionHookEnd?: number;\n callActionStart?: number;\n callActionEnd?: number;\n afterInvokeActionHookStart?: number;\n afterInvokeActionHookEnd?: number;\n captureAfterCallingSnapshotStart?: number;\n captureAfterCallingSnapshotEnd?: number;\n end?: number;\n cost?: number;\n };\n usage?: AIUsageInfo;\n /**\n * Pixel rect of the deepLocate first-stage search area in screenshot\n * coordinates. Used by reports to explain the crop/zoom area that the\n * final locate ran against.\n */\n searchArea?: Rect;\n searchAreaUsage?: AIUsageInfo;\n reasoning_content?: string;\n };\n\nexport interface IExecutionDump extends DumpMeta {\n /** Stable unique identifier for this execution run */\n id?: string;\n name: string;\n description?: string;\n tasks: ExecutionTask[];\n aiActContext?: string;\n}\n\n/*\ntask - service-locate\n*/\nexport type ExecutionTaskInsightLocateParam = PlanningLocateParam;\n\nexport interface ExecutionTaskInsightLocateOutput {\n element: LocateResultElement | null;\n}\n\nexport type ExecutionTaskInsightDump = ServiceDump;\n\nexport type ExecutionTaskInsightLocateApply = ExecutionTaskApply<\n 'Insight',\n ExecutionTaskInsightLocateParam,\n ExecutionTaskInsightLocateOutput,\n ExecutionTaskInsightDump\n>;\n\nexport type ExecutionTaskInsightLocate =\n ExecutionTask<ExecutionTaskInsightLocateApply>;\n\n/*\ntask - service-query\n*/\nexport interface ExecutionTaskInsightQueryParam {\n dataDemand: ServiceExtractParam;\n domIncluded?: boolean | 'visible-only';\n}\n\nexport interface ExecutionTaskInsightQueryOutput {\n data: any;\n}\n\nexport type ExecutionTaskInsightQueryApply = ExecutionTaskApply<\n 'Insight',\n ExecutionTaskInsightQueryParam,\n any,\n ExecutionTaskInsightDump\n>;\n\nexport type ExecutionTaskInsightQuery =\n ExecutionTask<ExecutionTaskInsightQueryApply>;\n\n/*\ntask - assertion\n*/\nexport interface ExecutionTaskInsightAssertionParam {\n assertion: string;\n}\n\nexport type ExecutionTaskInsightAssertionApply = ExecutionTaskApply<\n 'Insight',\n ExecutionTaskInsightAssertionParam,\n ServiceAssertionResponse,\n ExecutionTaskInsightDump\n>;\n\nexport type ExecutionTaskInsightAssertion =\n ExecutionTask<ExecutionTaskInsightAssertionApply>;\n\n/*\ntask - action (i.e. interact) \n*/\nexport type ExecutionTaskActionApply<ActionParam = any> = ExecutionTaskApply<\n 'Action Space',\n ActionParam,\n void,\n void\n>;\n\nexport type ExecutionTaskAction = ExecutionTask<ExecutionTaskActionApply>;\n\n/*\ntask - Log\n*/\n\nexport type ExecutionTaskLogApply<\n LogParam = {\n content: string;\n },\n> = ExecutionTaskApply<'Log', LogParam, void, void>;\n\nexport type ExecutionTaskLog = ExecutionTask<ExecutionTaskLogApply>;\n\n/*\ntask - planning\n*/\n\nexport interface ExecutionTaskPlanningParam {\n userInstruction: TUserPrompt;\n userInstructionDisplay?: string;\n aiActContext?: string;\n imagesIncludeCount?: number;\n deepThink?: DeepThinkOption;\n subGoalStatus?: string;\n memoriesStatus?: string;\n}\n\nexport type ExecutionTaskPlanningApply = ExecutionTaskApply<\n 'Planning',\n ExecutionTaskPlanningParam,\n PlanningAIResponse\n>;\n\nexport type ExecutionTaskPlanning = ExecutionTask<ExecutionTaskPlanningApply>;\n\n/*\ntask - planning-locate\n*/\nexport type ExecutionTaskPlanningLocateParam = PlanningLocateParam;\n\nexport interface ExecutionTaskPlanningLocateOutput {\n element: LocateResultElement | null;\n}\n\nexport type ExecutionTaskPlanningDump = ServiceDump;\n\nexport type ExecutionTaskPlanningLocateApply = ExecutionTaskApply<\n 'Planning',\n ExecutionTaskPlanningLocateParam,\n ExecutionTaskPlanningLocateOutput,\n ExecutionTaskPlanningDump\n>;\n\nexport type ExecutionTaskPlanningLocate =\n ExecutionTask<ExecutionTaskPlanningLocateApply>;\n\n/*\nReport metadata - extracted from ReportActionDump for per-execution writes\n*/\nexport interface ReportMeta {\n groupName: string;\n groupDescription?: string;\n sdkVersion: string;\n modelBriefs: ModelBrief[];\n deviceType?: string;\n}\n\n// Backward-compatible aliases for existing external consumers.\nexport type GroupMeta = ReportMeta;\n\n/*\nReport dump\n*/\nexport interface IReportActionDump {\n sdkVersion: string;\n groupName: string;\n groupDescription?: string;\n modelBriefs: ModelBrief[];\n executions: IExecutionDump[];\n deviceType?: string;\n}\n\n// Backward-compatible aliases for existing external consumers.\nexport type IGroupedActionDump = IReportActionDump;\n\nexport interface ModelBrief {\n /**\n * The intent/category of the model call, for example \"planning\" or \"insight\".\n */\n intent?: string;\n\n /**\n * The model name returned by usage metadata, for example \"gpt-4o\".\n */\n name?: string;\n\n /**\n * Optional human-readable model description, for example \"qwen2.5-vl mode\".\n */\n modelDescription?: string;\n}\n\nexport type InterfaceType =\n | 'puppeteer'\n | 'playwright'\n | 'static'\n | 'chrome-extension-proxy'\n | 'android'\n | string;\n\nexport interface StreamingCodeGenerationOptions {\n /** Whether to enable streaming output */\n stream?: boolean;\n /** Callback function to handle streaming chunks */\n onChunk?: StreamingCallback;\n /** Callback function to handle streaming completion */\n onComplete?: (finalCode: string) => void;\n /** Callback function to handle streaming errors */\n onError?: (error: Error) => void;\n}\n\nexport type StreamingCallback = (chunk: CodeGenerationChunk) => void;\n\nexport interface CodeGenerationChunk {\n /** The incremental content chunk */\n content: string;\n /** The reasoning content */\n reasoning_content: string;\n /** The accumulated content so far */\n accumulated: string;\n /** Whether this is the final chunk */\n isComplete: boolean;\n /** Token usage information if available */\n usage?: AIUsageInfo;\n}\n\nexport interface StreamingAIResponse {\n /** The final accumulated content */\n content: string;\n /** Token usage information */\n usage?: AIUsageInfo;\n /** Whether the response was streamed */\n isStreamed: boolean;\n}\n\nexport interface DeviceAction<TParam = any, TReturn = any> {\n name: string;\n description?: string;\n interfaceAlias?: string;\n paramSchema?: z.ZodType<TParam>;\n call: (\n param: TParam,\n context?: ExecutorContext,\n ) => Promise<TReturn> | TReturn;\n delayBeforeRunner?: number;\n delayAfterRunner?: number;\n /**\n * An example param object for this action.\n * Locate fields with { prompt } may be resolved to internal pixel bboxes when needed.\n */\n sample?: { [K in keyof TParam]?: any };\n}\n\n/**\n * Type utilities for extracting types from DeviceAction definitions\n */\n\n/**\n * Extract parameter type from a DeviceAction\n */\nexport type ActionParam<Action extends DeviceAction<any, any>> =\n Action extends DeviceAction<infer P, any> ? P : never;\n\n/**\n * Extract return type from a DeviceAction\n */\nexport type ActionReturn<Action extends DeviceAction<any, any>> =\n Action extends DeviceAction<any, infer R> ? R : never;\n\n/**\n * Web-specific types\n */\nexport interface WebElementInfo extends BaseElement {\n id: string;\n attributes: {\n nodeType: NodeType;\n [key: string]: string;\n };\n}\n\n/**\n * Agent\n */\n\nexport type CacheConfig = {\n strategy?: 'read-only' | 'read-write' | 'write-only';\n id: string;\n /**\n * Optional cache directory path.\n * When set, cache files are written to this directory instead of\n * `<MIDSCENE_RUN_DIR>/cache`.\n */\n cacheDir?: string;\n};\n\nexport type Cache =\n | false // No read, no write\n | true // Will throw error at runtime - deprecated\n | CacheConfig; // Object configuration (requires explicit id)\n\nexport interface AgentOpt {\n // @deprecated Use `reportFileName` and `cache.id` instead.\n testId?: string;\n // @deprecated\n cacheId?: string; // Keep backward compatibility, but marked as deprecated\n groupName?: string;\n groupDescription?: string;\n /* if auto generate report, default true */\n generateReport?: boolean;\n /* if persist per-execution dump files next to the report, default false */\n persistExecutionDump?: boolean;\n /* if auto print report msg, default true */\n autoPrintReportMsg?: boolean;\n\n /**\n * Use directory-based report format with separate image files.\n *\n * When enabled:\n * - Screenshots are saved as PNG files in a `screenshots/` subdirectory\n * - Report is generated as `index.html` with relative image paths\n * - Reduces memory usage and report file size\n *\n * IMPORTANT: 'html-and-external-assets' reports must be served via HTTP server\n * (e.g., `npx serve ./report-dir`). The file:// protocol will not\n * work due to browser CORS restrictions.\n *\n * @default 'single-html'\n */\n outputFormat?: 'single-html' | 'html-and-external-assets';\n\n onTaskStartTip?: OnTaskStartTip;\n aiActContext?: string;\n aiActionContext?: string;\n /* custom report file name */\n reportFileName?: string;\n reportAttributes?: ReportAttributes;\n modelConfig?: TModelConfig;\n cache?: Cache;\n /**\n * Maximum number of replanning cycles for aiAct.\n * Defaults are resolved by the active model adapter: 20 for standard planning,\n * 40 for UI-TARS, and 100 for Auto-GLM.\n * If omitted, the agent will also read `MIDSCENE_REPLANNING_CYCLE_LIMIT` for backward compatibility.\n */\n replanningCycleLimit?: number;\n\n /**\n * Wait time in milliseconds after each action execution.\n * This allows the UI to settle and stabilize before the next action.\n * Defaults to 300ms when not provided.\n */\n waitAfterAction?: number;\n\n /**\n * When set to true, Midscene will use the target device's formatted local\n * time instead of the runtime system time. The target interface must implement\n * getDeviceLocalTimeString to provide device-local wall-clock time.\n * Default: false\n */\n useDeviceTime?: boolean;\n\n /**\n * Custom screenshot shrink factor to reduce AI token usage.\n * When set, the screenshot will be scaled down by this factor from the physical resolution.\n *\n * Example:\n * - Physical screen width: 3000px, dpr=6\n * - Logical width: 500px\n * - screenshotShrinkFactor: 2\n * - Actual shrunk screenshot width: 3000 / 2 = 1500px\n * - AI analyzes the 1500px screenshot\n * - Coordinates are transformed back to logical (500px) before actions execute\n *\n * Benefits:\n * - Reduces token usage for high-resolution screenshots\n * - Maintains accuracy by scaling coordinates appropriately\n *\n * Must be >= 1 (shrinking only, enlarging is not supported).\n *\n * @default 1 (no shrinking, uses original physical screenshot)\n */\n screenshotShrinkFactor?: number;\n\n /**\n * Custom OpenAI client factory function\n *\n * If provided, this function will be called to create OpenAI client instances\n * for each AI call, allowing you to:\n * - Wrap clients with observability tools (langsmith, langfuse)\n * - Use custom OpenAI-compatible clients\n * - Apply different configurations based on intent\n *\n * @param config - Resolved model configuration\n * @returns OpenAI client instance (original or wrapped)\n *\n * @example\n * ```typescript\n * createOpenAIClient: async (openai, opts) => {\n * // Wrap with langsmith for planning tasks\n * if (opts.baseURL?.includes('planning')) {\n * return wrapOpenAI(openai, { metadata: { task: 'planning' } });\n * }\n *\n * return openai;\n * }\n * ```\n */\n createOpenAIClient?: CreateOpenAIClientFn;\n}\n\nexport type TestStatus =\n | 'passed'\n | 'failed'\n | 'timedOut'\n | 'skipped'\n | 'interrupted';\n\nexport interface ReportFileAttributes {\n testDuration: number;\n testStatus: TestStatus;\n testTitle: string;\n testId: string;\n testDescription: string;\n}\n\nexport type ReportFileWithAttributes =\n | {\n reportFilePath: string;\n reportAttributes: ReportFileAttributes;\n }\n | {\n reportFilePath?: string;\n reportAttributes: ReportFileAttributes & { testStatus: 'skipped' };\n };\n"],"names":["UIContext"],"mappings":";;;AAgIO,MAAeA;AA4BtB"}
|